Merge pull request #2 from pembemiriam/master

fixed issues due to angular redux
diff --git a/.angular-cli.json b/.angular-cli.json
index f3e3c41..2b63cca 100644
--- a/.angular-cli.json
+++ b/.angular-cli.json
@@ -1,15 +1,16 @@
 {
   "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
   "project": {
-    "name": "fineract-cn-web-app"
+    "version": "1.0.0",
+    "name": "fims"
   },
   "apps": [
     {
       "root": "src",
       "outDir": "dist",
       "assets": [
-        "assets",
-        "favicon.ico"
+        "favicon.png",
+        "assets"
       ],
       "index": "index.html",
       "main": "main.ts",
@@ -18,10 +19,16 @@
       "tsconfig": "tsconfig.app.json",
       "testTsconfig": "tsconfig.spec.json",
       "prefix": "app",
+      "mobile": false,
       "styles": [
-        "styles.scss"
+        "styles.scss",
+        "theme.scss",
+        "../node_modules/@covalent/core/common/platform.css"
       ],
-      "scripts": [],
+      "scripts": [
+        "../node_modules/hammerjs/hammer.min.js",
+        "../node_modules/showdown/dist/showdown.js"
+      ],
       "environmentSource": "environments/environment.ts",
       "environments": {
         "dev": "environments/environment.ts",
@@ -29,25 +36,13 @@
       }
     }
   ],
+  "addons": [],
+  "packages": [],
   "e2e": {
     "protractor": {
       "config": "./protractor.conf.js"
     }
   },
-  "lint": [
-    {
-      "project": "src/tsconfig.app.json",
-      "exclude": "**/node_modules/**"
-    },
-    {
-      "project": "src/tsconfig.spec.json",
-      "exclude": "**/node_modules/**"
-    },
-    {
-      "project": "e2e/tsconfig.e2e.json",
-      "exclude": "**/node_modules/**"
-    }
-  ],
   "test": {
     "karma": {
       "config": "./karma.conf.js"
@@ -55,6 +50,9 @@
   },
   "defaults": {
     "styleExt": "scss",
-    "component": {}
+    "serve": {
+      "port": 4200,
+      "host": "localhost"
+    }
   }
 }
diff --git a/.editorconfig b/.editorconfig
index 6e87a00..da0310f 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -1,13 +1,13 @@
-# Editor configuration, see http://editorconfig.org
+# editorconfig.org
 root = true
 
 [*]
-charset = utf-8
 indent_style = space
 indent_size = 2
-insert_final_newline = true
+end_of_line = lf
+charset = utf-8
 trim_trailing_whitespace = true
+insert_final_newline = true
 
 [*.md]
-max_line_length = off
-trim_trailing_whitespace = false
+trim_trailing_whitespace = false
\ No newline at end of file
diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md
new file mode 100644
index 0000000..46ae083
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE.md
@@ -0,0 +1,30 @@
+This repository's issues are reserved for feature requests and bug reports.
+
+### Do you want to request a *feature* or report a *bug*?
+
+#### Feature Request
+
+please first make sure your request falls under the official Material Design spec guidelines https://material.google.com/
+
+#### Bug Report
+
+please provide steps to reproduce and if possible screenhots or animated Gifs.
+you can easily create animated Gif with this free PC/OSX App: http://www.cockos.com/licecap/
+
+##### Screenshots or link to CodePen/Plunker/JSfiddle
+
+
+#### What is the expected behavior?
+
+
+#### What is the motivation / use case for changing the behavior?
+
+
+#### Which version of Angular and Material, and which browser and OS does this issue affect?
+
+Did this work in previous versions of Angular / Material?
+Please also test with the latest stable and snapshot versions.
+
+
+##### Other information
+(e.g. detailed explanation, stacktraces, related issues, suggestions how to fix)
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
new file mode 100644
index 0000000..cff04b7
--- /dev/null
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -0,0 +1,17 @@
+## Description
+
+Talk about the great work you've done!
+
+### What's included?
+
+- One
+- Two
+- Three
+
+#### Test Steps
+
+- [ ] do this
+- [ ] then this
+- [ ] finally this
+
+##### Screenshots or link to CodePen/Plunker/JSfiddle
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index eabf65e..7346eca 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,43 +2,31 @@
 
 # compiled output
 /dist
-/dist-server
+/deploy
 /tmp
-/out-tsc
 
 # dependencies
 /node_modules
+/bower_components
 
 # IDEs and editors
-/.idea
-.project
-.classpath
-.c9/
-*.launch
-.settings/
-*.sublime-workspace
-
-# IDE - VSCode
-.vscode/*
-!.vscode/settings.json
-!.vscode/tasks.json
-!.vscode/launch.json
-!.vscode/extensions.json
+.idea
+*.iml
+/.vscode
 
 # misc
 /.sass-cache
 /connect.lock
-/coverage
+/coverage/*
 /libpeerconnection.log
 npm-debug.log
-yarn-error.log
 testem.log
 /typings
+/.vagrant
 
 # e2e
 /e2e/*.js
 /e2e/*.map
 
-# System Files
+#System Files
 .DS_Store
-Thumbs.db
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..4afbd4b
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,47 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+sudo: required
+
+dist: trusty
+
+language: node_js
+
+node_js:
+- '6.9.4'
+
+branches:
+  only:
+    - develop
+
+before_script:
+  - export CHROME_BIN=chromium-browser
+  - export DISPLAY=:99.0
+  - sh -e /etc/init.d/xvfb start
+
+install:
+  - npm install
+
+script:
+  - npm run tslint
+  - npm run test
+  - npm run build
+
+notifications:
+  email: false
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..0255d60
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,80 @@
+# Contributing
+
+## Take Your First Steps
+
+### Understand the basics
+
+Not sure what a pull request is, or how to submit one? Take a look at GitHub's excellent [help
+documentation](https://help.github.com/articles/using-pull-requests/) first.
+
+### Search Issues first; create an issue if necessary
+
+Is there already an issue that addresses your concern? Do a bit of searching in our [issue
+tracker](https://issues.apache.org/jira/browse/FINCN-3?jql=project%20%3D%20FINCN%20AND%20component%20%3D%20fineract-cn-fims-web-app) to see if you can find something
+similar. If you do not find something similar, please create a new issue before submitting a pull
+request unless the change is truly trivial -- for example: typo fixes, removing compiler warnings,
+etc.
+
+### Discuss non-trivial contribution ideas with committers
+
+If you're considering anything more than correcting a typo or fixing a minor bug, please discuss it
+on the [dev list](mailto:dev-subscribe@fineract.apage.org) before submitting a pull request. We're
+happy to provide guidance, but please spend an hour or two researching the subject on your own.
+
+### Sign the Contributor License Agreement
+
+Before we accept a non-trivial patch or pull request we will need you to sign the [Apache iCLA
+form](https://www.apache.org/licenses/icla.pdf). Signing the
+contributor's agreement does not grant anyone commit rights to the main repository, but it does mean
+that we can accept your contributions, and you will get an author credit if we do.
+
+## Create a Branch
+
+### Branch from `develop`
+
+Develop currently represents work toward Apache Fineract CN Framework 1.0.0. Please submit all pull requests
+there, even bug fixes and minor improvements.
+
+### Use short branch names
+
+Branches used when submitting pull requests should preferably be named according to issues prefixed
+FINCN followed by a dash and the issue number, e.g. 'FINCN-1234'. This is important, because
+branch names show up in the merge commits that result from accepting pull requests and should be as
+expressive and concise as possible.
+
+## Coding Conventions
+[Google Java Style](https://google.github.io/styleguide/javaguide.html) covers filenames, file
+organization, indentation, comments, declarations, statements, white space, naming conventions, and
+programming practices. All code written for Apache Fineract CN should follow these conventions except as noted
+below.
+
+### 3.1 License or copyright information, if present
+
+Content of the license header: 
+
+```javascript
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+```
+
+### 4.4 Column Limit
+We've chosen to use a column limit of 100 characters.
+
+## Contributors
+Huge thanks to the following contributors (by github username).
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..8dada3e
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,201 @@
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "{}"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright {yyyy} {name of copyright owner}
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
diff --git a/NOTICE b/NOTICE
new file mode 100644
index 0000000..a979824
--- /dev/null
+++ b/NOTICE
@@ -0,0 +1,21 @@
+Apache Fineract CN Fims Web App
+Copyright 2008-2018 The Apache Software Foundation
+
+This product includes software developed by The Apache Software
+Foundation (http://www.apache.org/).
+
+fims - notice for binary distribution
+==========================================
+fims includes caniuse(https://github.com/Fyrd/caniuse).
+fims elects to include this software in this distribution under the
+CC-BY-4.0 license. You can obtain a copy of the License at:
+https://creativecommons.org/licenses/by/4.0/
+
+fims includes spdx-expression-parse.js(https://github.com/kemitchell/spdx-expression-parse.js).
+fims includes spdx-exceptions(https://github.com/kemitchell/spdx-exceptions.json).
+The Linux Foundation and its contributors license the SPDX standard under the terms
+of the Creative Commons Attribution License 3.0 Unported (SPDX: "CC-BY-3.0").
+"SPDX" is a United States federally registered trademark of the Linux Foundation.
+fims elects to include this software in this distribution under the
+CC-BY-3.0 license. You can obtain a copy of the License at:
+http://spdx.org/licenses/CC-BY-3.0
diff --git a/README.md b/README.md
index 93ae45a..fefa308 100644
--- a/README.md
+++ b/README.md
@@ -1,27 +1,44 @@
-# FineractCnWebApp
+# QuickStart
 
-This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 1.7.2.
+## Setup
 
-## Development server
+* Ensure you have Node 6.10.0+ and NPM 3+ installed.
+* Install Node packages `npm i`
 
-Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files.
+## Development
+* Follow instructions at https://github.com/apache/fineract-cn-demo-server and start demo-server
+* Run local dev environment `npm run dev`
+* Go to http://localhost:4200
 
-## Code scaffolding
+## Production build
+* Run license check `npm run checkLicenses`
+* Run in production mode `npm run runProd`. This is only to test if AOT is working and should never be used in a production environment.
+* Build production assets `npm run build`. Files will be stored under /dist.
 
-Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.
+## Tests
+* Please follow the best practices here [Angular Testing](https://angular.io/docs/ts/latest/guide/testing.html)
+* Run karma tests `npm run test`
 
-## Build
 
-Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `-prod` flag for a production build.
+## Versioning
+The version numbers follow the [Semantic Versioning](http://semver.org/) scheme.
 
-## Running unit tests
+In addition to MAJOR.MINOR.PATCH the following postfixes are used to indicate the development state.
 
-Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
+* snapshot - A release currently in development. 
+* m - A _milestone_ release include specific sets of functions and are released as soon as the functionality is complete.
+* rc - A _release candidate_ is a version with potential to be a final product, considered _code complete_.
+* ga - _General availability_ indicates that this release is the best available version and is recommended for all usage.
 
-## Running end-to-end tests
+The versioning layout is {MAJOR}.{MINOR}.{PATCH}-{INDICATOR}[.{PATCH}]. Only milestones and release candidates can  have patch versions. Some examples:
 
-Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/).
+1.2.3-snapshot  
+1.3.5-m.1  
+1.5.7-rc.2  
+2.0.0-ga
 
-## Further help
+## Contributing
+See [CONTRIBUTING](CONTRIBUTING.md) file.
 
-To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md).
+## License
+See [LICENSE](LICENSE) file.
diff --git a/e2e/dashboard.e2e.ts b/e2e/dashboard.e2e.ts
new file mode 100644
index 0000000..4b50e78
--- /dev/null
+++ b/e2e/dashboard.e2e.ts
@@ -0,0 +1,18 @@
+import { browser, by, protractor, $, element, ProtractorExpectedConditions } from 'protractor';
+
+describe('basic e2e test with loading', function(): void {
+  let EC: ProtractorExpectedConditions = protractor.ExpectedConditions;
+  describe('home', function(): void {
+    browser.get('/');
+    it('should load quick access', function(): void {
+      expect(browser.getTitle()).toBe('Quick access');
+      // Waits for the element 'td-loading' to not be present on the dom.
+      browser.wait(EC.not(EC.presenceOf($('td-loading'))), 10000)
+        .then(() => {
+
+          // checks if elements were rendered
+          expect(element(by.id('dashboard-favorites-card')).isPresent()).toBe(true);
+        });
+    });
+  });
+});
diff --git a/e2e/tsconfig.json b/e2e/tsconfig.json
new file mode 100644
index 0000000..656bdb1
--- /dev/null
+++ b/e2e/tsconfig.json
@@ -0,0 +1,16 @@
+{
+  "compileOnSave": false,
+  "compilerOptions": {
+    "declaration": false,
+    "emitDecoratorMetadata": true,
+    "experimentalDecorators": true,
+    "module": "commonjs",
+    "moduleResolution": "node",
+    "outDir": "../dist/out-tsc-e2e",
+    "sourceMap": true,
+    "target": "es5",
+    "typeRoots": [
+      "../node_modules/@types"
+    ]
+  }
+}
diff --git a/karma.conf.js b/karma.conf.js
index af139fa..9065608 100644
--- a/karma.conf.js
+++ b/karma.conf.js
@@ -1,5 +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.
+*/
+
 // Karma configuration file, see link for more information
-// https://karma-runner.github.io/1.0/config/configuration-file.html
+// https://karma-runner.github.io/0.13/config/configuration-file.html
 
 module.exports = function (config) {
   config.set({
@@ -10,11 +29,29 @@
       require('karma-chrome-launcher'),
       require('karma-jasmine-html-reporter'),
       require('karma-coverage-istanbul-reporter'),
+      require('karma-firefox-launcher'),
       require('@angular/cli/plugins/karma')
     ],
+    customLaunchers: {
+      // chrome setup for travis CI using chromium
+      Chrome_travis_ci: {
+        base: 'Chrome',
+        flags: ['--no-sandbox']
+      }
+    },
     client:{
       clearContext: false // leave Jasmine Spec Runner output visible in browser
     },
+    files: [
+      { pattern: './src/test.ts', watched: false },
+      { pattern: './node_modules/@angular/material/prebuilt-themes/indigo-pink.css', included: true, watched: true }
+    ],
+    preprocessors: {
+      './src/test.ts': ['@angular/cli']
+    },
+    mime: {
+      'text/x-typescript': ['ts','tsx']
+    },
     coverageIstanbulReporter: {
       reports: [ 'html', 'lcovonly' ],
       fixWebpackSourcePaths: true
@@ -22,12 +59,15 @@
     angularCli: {
       environment: 'dev'
     },
-    reporters: ['progress', 'kjhtml'],
+    reporters: config.angularCli && config.angularCli.codeCoverage
+      ? ['progress', 'coverage-istanbul']
+      : ['progress', 'kjhtml'],
     port: 9876,
     colors: true,
     logLevel: config.LOG_INFO,
     autoWatch: true,
     browsers: ['Chrome'],
-    singleRun: false
+    singleRun: false,
+    browserNoActivityTimeout: 20000
   });
 };
diff --git a/license.config.js b/license.config.js
new file mode 100644
index 0000000..c18cc52
--- /dev/null
+++ b/license.config.js
@@ -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.
+*/
+
+module.exports = {
+  "allowedPackages": [
+    {
+      "name": "xmldom",
+      "reason": "License offers option between LGPL or MIT(https://github.com/jindw/xmldom/blob/master/LICENSE)"
+    },
+    {
+      "name": "css-select",
+      "reason": "BSD-2-Clause license"
+    },
+    {
+      "name": "css-what",
+      "reason": "BSD-2-Clause license"
+    },
+    {
+      "name": "entities",
+      "reason": "BSD-2-Clause license"
+    },
+    {
+      "name": "spdx-expression-parse",
+      "reason": "Uses CC-BY-3.0 and therefore added to NOTICE file"
+    },
+    {
+      "name": "spdx-exceptions",
+      "reason": "Uses CC-BY-3.0 and therefore added to NOTICE file"
+    },
+    {
+      "name": "caniuse-db",
+      "reason": "Uses CC-BY-4.0 and therefore added to NOTICE file"
+    }
+  ],
+  "disallowedPackages": [],
+  "allowedLicenses": [
+    "MIT",
+    "(MIT AND BSD-3-Clause)",
+    "(MIT AND Zlib)",
+    "MIT/X11",
+    "ISC",
+    "Apache-2.0",
+    "Apache version 2.0",
+    "BSD",
+    "BSD-like",
+    "BSD-2-Clause",
+    "BSD-3-Clause",
+    "WTFPL",
+    "JSF",
+    "Unlicense",
+    "Public Domain",
+    "CC0-1.0"
+  ],
+  "strictMode": true
+};
diff --git a/package-lock.json b/package-lock.json
index d049bfe..fe36391 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,13 +1,13 @@
 {
-  "name": "fineract-cn-web-app",
-  "version": "0.0.0",
+  "name": "fims",
+  "version": "0.1.0",
   "lockfileVersion": 1,
   "requires": true,
   "dependencies": {
     "@angular-devkit/build-optimizer": {
-      "version": "0.3.2",
-      "resolved": "https://registry.npmjs.org/@angular-devkit/build-optimizer/-/build-optimizer-0.3.2.tgz",
-      "integrity": "sha512-U0BCZtThq5rUfY08shHXpxe8ZhSsiYB/cJjUvAWRTs/ORrs8pbngS6xwseQws8d/vHoVrtqGD9GU9h8AmFRERQ==",
+      "version": "0.0.42",
+      "resolved": "https://registry.npmjs.org/@angular-devkit/build-optimizer/-/build-optimizer-0.0.42.tgz",
+      "integrity": "sha512-BAYCVZ10ro6mgZQDZiNiVbX8ppygw4q7z/stpwG8WjMswgMRIcxsxYoC1VFuWcUPAf4UyfTIav6e8UZWA5+xnQ==",
       "dev": true,
       "requires": {
         "loader-utils": "1.1.0",
@@ -25,150 +25,161 @@
       }
     },
     "@angular-devkit/core": {
-      "version": "0.3.2",
-      "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-0.3.2.tgz",
-      "integrity": "sha512-zABk/iP7YX5SVbmK4e+IX7j2d0D37MQJQiKgWdV3JzfvVJhNJzddiirtT980pIafoq+KyvTgVwXtc+vnux0oeQ==",
+      "version": "0.0.20",
+      "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-0.0.20.tgz",
+      "integrity": "sha512-lg5BvMxOfbVD//SOQvpq6TPIKTXYNMj0I9N/kfXbXkUGgiBGFLyFMf2fc+qNvDoa7lulKMPT8OJWS1YlGt93eg==",
       "dev": true,
       "requires": {
-        "ajv": "5.5.2",
-        "chokidar": "1.7.0",
-        "rxjs": "5.5.11",
         "source-map": "0.5.7"
-      },
-      "dependencies": {
-        "ajv": {
-          "version": "5.5.2",
-          "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz",
-          "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=",
-          "dev": true,
-          "requires": {
-            "co": "4.6.0",
-            "fast-deep-equal": "1.1.0",
-            "fast-json-stable-stringify": "2.0.0",
-            "json-schema-traverse": "0.3.1"
-          }
-        }
       }
     },
     "@angular-devkit/schematics": {
-      "version": "0.3.2",
-      "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-0.3.2.tgz",
-      "integrity": "sha512-B6zZoqvHaTJy+vVdA6EtlxnCdGMa5elCa4j9lQLC3JI8DLvMXUWkCIPVbPzJ/GSRR9nsKWpvYMYaJyfBDUqfhw==",
+      "version": "0.0.52",
+      "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-0.0.52.tgz",
+      "integrity": "sha512-NtG8VB5aWtg0cw1Y7EJinJMuAnXsNdkQkkVe/i7CO6TPLyFQSFQCN1YojCr43l8jTWTRebRslrBawPCMOxsOgw==",
       "dev": true,
       "requires": {
-        "@ngtools/json-schema": "1.2.0",
+        "@ngtools/json-schema": "1.1.0",
         "rxjs": "5.5.11"
+      },
+      "dependencies": {
+        "rxjs": {
+          "version": "5.5.11",
+          "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-5.5.11.tgz",
+          "integrity": "sha512-3bjO7UwWfA2CV7lmwYMBzj4fQ6Cq+ftHc2MvUe+WMS7wcdJ1LosDWmdjPQanYp2dBRj572p7PeU81JUxHKOcBA==",
+          "dev": true,
+          "requires": {
+            "symbol-observable": "1.0.1"
+          }
+        },
+        "symbol-observable": {
+          "version": "1.0.1",
+          "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.0.1.tgz",
+          "integrity": "sha1-g0D8RwLDEi310iKI+IKD9RPT/dQ=",
+          "dev": true
+        }
       }
     },
     "@angular/animations": {
-      "version": "5.2.11",
-      "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-5.2.11.tgz",
-      "integrity": "sha512-J7wKHkFn3wV28/Y1Qm4yjGXVCwXzj1JR5DRjGDTFnxTRacUFx7Nj0ApGhN0b2+V0NOvgxQOvEW415Y22kGoblw==",
+      "version": "4.4.5",
+      "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-4.4.5.tgz",
+      "integrity": "sha1-WlpVHXV+WlVgCY9vhTXBAtk5VNc=",
       "requires": {
-        "tslib": "1.9.2"
+        "tslib": "1.9.1"
       }
     },
     "@angular/cdk": {
-      "version": "5.2.5",
-      "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-5.2.5.tgz",
-      "integrity": "sha512-GN8m1d+VcCE9+Bgwv06Y8YJKyZ0i9ZIq2ZPBcJYt+KVgnVVRg4JkyUNxud07LNsvzOX22DquHqmIZiC4hAG7Ag==",
+      "version": "2.0.0-beta.12",
+      "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-2.0.0-beta.12.tgz",
+      "integrity": "sha1-OiQ8tiuT9OA5EgunD5ANyeI1Yi4=",
       "requires": {
-        "tslib": "1.9.2"
+        "tslib": "1.9.1"
       }
     },
     "@angular/cli": {
-      "version": "1.7.4",
-      "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-1.7.4.tgz",
-      "integrity": "sha512-URdb1QtnQf+Ievy93wjq7gE81s25BkWUwJFPey+YkphBA3G1lbCAQPiEh2pntBwaIKavgEuCw+Sf2YZdgTVhDA==",
+      "version": "1.4.7",
+      "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-1.4.7.tgz",
+      "integrity": "sha512-VJB95B49nh8LjJJDkHNAoklJIsKQ0Xvpu39q/yaicpuzVaSiOUgHO6SfQrW6RwN3uxLAWTREy+FMRtIv1UIXNw==",
       "dev": true,
       "requires": {
-        "@angular-devkit/build-optimizer": "0.3.2",
-        "@angular-devkit/core": "0.3.2",
-        "@angular-devkit/schematics": "0.3.2",
-        "@ngtools/json-schema": "1.2.0",
-        "@ngtools/webpack": "1.10.2",
-        "@schematics/angular": "0.3.2",
-        "@schematics/package-update": "0.3.2",
-        "ajv": "6.5.1",
-        "autoprefixer": "7.2.6",
-        "cache-loader": "1.2.2",
-        "chalk": "2.2.2",
-        "circular-dependency-plugin": "4.4.0",
-        "clean-css": "4.1.11",
-        "common-tags": "1.8.0",
-        "copy-webpack-plugin": "4.4.3",
+        "@angular-devkit/build-optimizer": "0.0.42",
+        "@angular-devkit/schematics": "0.0.52",
+        "@ngtools/json-schema": "1.1.0",
+        "@ngtools/webpack": "1.7.4",
+        "@schematics/angular": "0.0.49",
+        "autoprefixer": "6.7.7",
+        "chalk": "2.4.1",
+        "circular-dependency-plugin": "3.0.0",
+        "common-tags": "1.7.2",
+        "copy-webpack-plugin": "4.5.1",
         "core-object": "3.1.5",
+        "css-loader": "0.28.11",
+        "cssnano": "3.10.0",
         "denodeify": "1.2.1",
         "ember-cli-string-utils": "1.1.0",
-        "extract-text-webpack-plugin": "3.0.2",
-        "file-loader": "1.1.11",
+        "exports-loader": "0.6.4",
+        "extract-text-webpack-plugin": "3.0.0",
+        "file-loader": "0.10.1",
         "fs-extra": "4.0.3",
         "glob": "7.1.2",
         "html-webpack-plugin": "2.30.1",
-        "istanbul-instrumenter-loader": "3.0.1",
+        "istanbul-instrumenter-loader": "2.0.0",
         "karma-source-map-support": "1.3.0",
         "less": "2.7.3",
         "less-loader": "4.1.0",
         "license-webpack-plugin": "1.3.1",
-        "loader-utils": "1.1.0",
         "lodash": "4.17.10",
         "memory-fs": "0.4.1",
-        "minimatch": "3.0.4",
         "node-modules-path": "1.0.1",
         "node-sass": "4.9.0",
         "nopt": "4.0.1",
         "opn": "5.1.0",
         "portfinder": "1.0.13",
-        "postcss": "6.0.22",
-        "postcss-import": "11.1.0",
-        "postcss-loader": "2.1.5",
-        "postcss-url": "7.3.2",
+        "postcss-loader": "1.3.3",
+        "postcss-url": "5.1.2",
         "raw-loader": "0.5.1",
-        "resolve": "1.8.1",
+        "resolve": "1.7.1",
         "rxjs": "5.5.11",
         "sass-loader": "6.0.7",
         "semver": "5.5.0",
         "silent-error": "1.1.0",
+        "source-map-loader": "0.2.3",
         "source-map-support": "0.4.18",
-        "style-loader": "0.19.1",
+        "style-loader": "0.13.2",
         "stylus": "0.54.5",
         "stylus-loader": "3.0.2",
-        "uglifyjs-webpack-plugin": "1.2.6",
+        "typescript": "2.3.4",
         "url-loader": "0.6.2",
-        "webpack": "3.11.0",
+        "webpack": "3.6.0",
+        "webpack-concat-plugin": "1.4.0",
         "webpack-dev-middleware": "1.12.2",
-        "webpack-dev-server": "2.11.2",
-        "webpack-merge": "4.1.3",
-        "webpack-sources": "1.1.0",
-        "webpack-subresource-integrity": "1.0.4"
+        "webpack-dev-server": "2.7.1",
+        "webpack-merge": "4.1.2",
+        "zone.js": "0.8.17"
+      },
+      "dependencies": {
+        "rxjs": {
+          "version": "5.5.11",
+          "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-5.5.11.tgz",
+          "integrity": "sha512-3bjO7UwWfA2CV7lmwYMBzj4fQ6Cq+ftHc2MvUe+WMS7wcdJ1LosDWmdjPQanYp2dBRj572p7PeU81JUxHKOcBA==",
+          "dev": true,
+          "requires": {
+            "symbol-observable": "1.0.1"
+          }
+        },
+        "symbol-observable": {
+          "version": "1.0.1",
+          "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.0.1.tgz",
+          "integrity": "sha1-g0D8RwLDEi310iKI+IKD9RPT/dQ=",
+          "dev": true
+        }
       }
     },
     "@angular/common": {
-      "version": "5.2.11",
-      "resolved": "https://registry.npmjs.org/@angular/common/-/common-5.2.11.tgz",
-      "integrity": "sha512-LniJjGAeftUJDJh+2+LEjltcGen08C/VMxQ/eUYmesytKy1sN+MWzh3GbpKfEWtWmyUsYTG9lAAJNo3L3jPwsw==",
+      "version": "4.4.5",
+      "resolved": "https://registry.npmjs.org/@angular/common/-/common-4.4.5.tgz",
+      "integrity": "sha1-vVF53JIq2/TD6m37Gec8uEn/3Dc=",
       "requires": {
-        "tslib": "1.9.2"
+        "tslib": "1.9.1"
       }
     },
     "@angular/compiler": {
-      "version": "5.2.11",
-      "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-5.2.11.tgz",
-      "integrity": "sha512-ICvB1ud1mxaXUYLb8vhJqiLhGBVocAZGxoHTglv6hMkbrRYcnlB3FZJFOzBvtj+krkd1jamoYLI43UAmesqQ6Q==",
+      "version": "4.4.5",
+      "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-4.4.5.tgz",
+      "integrity": "sha1-hyGlkQ8rtS8J4tQEytJk817eWQI=",
       "requires": {
-        "tslib": "1.9.2"
+        "tslib": "1.9.1"
       }
     },
     "@angular/compiler-cli": {
-      "version": "5.2.11",
-      "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-5.2.11.tgz",
-      "integrity": "sha512-dwrQ0yxoCM/XzKzlm7pTsyg4/6ECjT9emZufGj8t12bLMO8NDn1IJOsqXJA1+onEgQKhlr0Ziwi+96TvDTb1Cg==",
+      "version": "4.4.5",
+      "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-4.4.5.tgz",
+      "integrity": "sha1-YfoDNqzRogjF8cXG1N9nnpmVMkg=",
       "dev": true,
       "requires": {
-        "chokidar": "1.7.0",
+        "@angular/tsc-wrapped": "4.4.5",
         "minimist": "1.2.0",
-        "reflect-metadata": "0.1.12",
-        "tsickle": "0.27.5"
+        "reflect-metadata": "0.1.12"
       },
       "dependencies": {
         "minimist": {
@@ -180,114 +191,130 @@
       }
     },
     "@angular/core": {
-      "version": "5.2.11",
-      "resolved": "https://registry.npmjs.org/@angular/core/-/core-5.2.11.tgz",
-      "integrity": "sha512-h2vpvXNAdOqKzbVaZcHnHGMT5A8uDnizk6FgGq6SPyw9s3d+/VxZ9LJaPjUk3g2lICA7og1tUel+2YfF971MlQ==",
+      "version": "4.4.5",
+      "resolved": "https://registry.npmjs.org/@angular/core/-/core-4.4.5.tgz",
+      "integrity": "sha1-VKy8vaEXGfiDx4apBpdKvrEy8aA=",
       "requires": {
-        "tslib": "1.9.2"
+        "tslib": "1.9.1"
       }
     },
     "@angular/forms": {
-      "version": "5.2.11",
-      "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-5.2.11.tgz",
-      "integrity": "sha512-wBllFlIubPclAFRXUc84Kc7TMeKOftzrQraVZ7ooTNeFLLa/FZLN2K8HGyRde8X/XDsMu1XAmjNfkz++spwTzA==",
+      "version": "4.4.5",
+      "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-4.4.5.tgz",
+      "integrity": "sha1-6VUghiMqqyzh0I7xmLYiBOoTxDs=",
       "requires": {
-        "tslib": "1.9.2"
+        "tslib": "1.9.1"
       }
     },
     "@angular/http": {
-      "version": "5.2.11",
-      "resolved": "https://registry.npmjs.org/@angular/http/-/http-5.2.11.tgz",
-      "integrity": "sha512-eR7wNXh1+6MpcQNb3sq4bJVX03dx50Wl3kpPG+Q7N1VSL0oPQSobaTrR17ac3oFCEfSJn6kkUCqtUXha6wcNHg==",
+      "version": "4.4.5",
+      "resolved": "https://registry.npmjs.org/@angular/http/-/http-4.4.5.tgz",
+      "integrity": "sha1-LHNe2EJAH8I1ZBkmjiiNzyOW6E8=",
       "requires": {
-        "tslib": "1.9.2"
+        "tslib": "1.9.1"
       }
     },
-    "@angular/language-service": {
-      "version": "5.2.11",
-      "resolved": "https://registry.npmjs.org/@angular/language-service/-/language-service-5.2.11.tgz",
-      "integrity": "sha512-tgnFAhwBmUs1W0dmcmlBmUlMaOgkoyuSdrcF23lz8W5+nSLb+LnbH5a3blU2NVqA4ESvLKQkPW5dpKa/LuhrPQ==",
-      "dev": true
-    },
     "@angular/material": {
-      "version": "5.2.5",
-      "resolved": "https://registry.npmjs.org/@angular/material/-/material-5.2.5.tgz",
-      "integrity": "sha512-IltfBeTJWnmZehOQNQ7KoFs7MGWuZTe0g21hIitGkusVNt1cIoTD24xKH5jwztjH19c04IgiwonpurMKM6pBCQ==",
+      "version": "2.0.0-beta.12",
+      "resolved": "https://registry.npmjs.org/@angular/material/-/material-2.0.0-beta.12.tgz",
+      "integrity": "sha1-cbbQt7AhiR5dDjaIwdS9eMdFf1g=",
       "requires": {
-        "tslib": "1.9.2"
+        "tslib": "1.9.1"
       }
     },
     "@angular/platform-browser": {
-      "version": "5.2.11",
-      "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-5.2.11.tgz",
-      "integrity": "sha512-6YZ4IpBFqXx88vEzBZG2WWnaSYXbFWDgG0iT+bZPHAfwsbmqbcMcs7Ogu+XZ4VmK02dTqbrFh7U4P2W+sqrzow==",
+      "version": "4.4.5",
+      "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-4.4.5.tgz",
+      "integrity": "sha1-dOuRwLdYEm8m1T7lbHz0ZovZysU=",
       "requires": {
-        "tslib": "1.9.2"
+        "tslib": "1.9.1"
       }
     },
     "@angular/platform-browser-dynamic": {
-      "version": "5.2.11",
-      "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-5.2.11.tgz",
-      "integrity": "sha512-5kKPNULcXNwkyBjpHfF+pq+Yxi8Zl866YSOK9t8txoiQ9Ctw97kMkEJcTetk6MJgBp/NP3YyjtoTAm8oXLerug==",
+      "version": "4.4.5",
+      "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-4.4.5.tgz",
+      "integrity": "sha1-d029wdkPd12/HjGfbtQrJgYjth8=",
       "requires": {
-        "tslib": "1.9.2"
+        "tslib": "1.9.1"
+      }
+    },
+    "@angular/platform-server": {
+      "version": "4.4.5",
+      "resolved": "https://registry.npmjs.org/@angular/platform-server/-/platform-server-4.4.5.tgz",
+      "integrity": "sha1-dvI7LDhO1zldwXk8+Fl4iDuiy1A=",
+      "requires": {
+        "parse5": "3.0.3",
+        "tslib": "1.9.1",
+        "xhr2": "0.1.4"
       }
     },
     "@angular/router": {
-      "version": "5.2.11",
-      "resolved": "https://registry.npmjs.org/@angular/router/-/router-5.2.11.tgz",
-      "integrity": "sha512-NT8xYl7Vr3qPygisek3PlXqNROEjg48GXOEsDEc7c8lDBo3EB9Tf328fWJD0GbLtXZNhmmNNxwIe+qqPFFhFAA==",
+      "version": "4.4.5",
+      "resolved": "https://registry.npmjs.org/@angular/router/-/router-4.4.5.tgz",
+      "integrity": "sha1-9zEwz0h9mjLMGYiv2llmX0Siiok=",
       "requires": {
-        "tslib": "1.9.2"
+        "tslib": "1.9.1"
+      }
+    },
+    "@angular/tsc-wrapped": {
+      "version": "4.4.5",
+      "resolved": "https://registry.npmjs.org/@angular/tsc-wrapped/-/tsc-wrapped-4.4.5.tgz",
+      "integrity": "sha1-MKDLtDpmOqddyphIlL5IE3eN3Jw=",
+      "dev": true,
+      "requires": {
+        "tsickle": "0.21.6"
       }
     },
     "@covalent/core": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/@covalent/core/-/core-1.0.1.tgz",
-      "integrity": "sha512-br5KMBT8xXlctkSENGHnXsV4xyJuq0+yXopHH02zz9E0j1d1zlB43hM7zU1FkeGIFtWtPP7peM4tjD6S+ujkXw==",
+      "version": "1.0.0-beta.8-1",
+      "resolved": "https://registry.npmjs.org/@covalent/core/-/core-1.0.0-beta.8-1.tgz",
+      "integrity": "sha1-GL8J+RLMAvOcHleCVcxCco2hBCY=",
       "requires": {
-        "tslib": "1.9.2"
+        "tslib": "1.9.1"
       }
     },
+    "@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": "5.2.0",
-      "resolved": "https://registry.npmjs.org/@ngrx/effects/-/effects-5.2.0.tgz",
-      "integrity": "sha1-qnYractv1GRNckoc7NJlyqQrrwk="
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/@ngrx/effects/-/effects-2.0.3.tgz",
+      "integrity": "sha1-5UzjQIBt2RqBgmeW8T4kRq7q+3c="
     },
     "@ngrx/store": {
-      "version": "5.2.0",
-      "resolved": "https://registry.npmjs.org/@ngrx/store/-/store-5.2.0.tgz",
-      "integrity": "sha1-Yn7XTJzZVGKTBIXZEqVXEXsjkD4="
+      "version": "2.2.2",
+      "resolved": "https://registry.npmjs.org/@ngrx/store/-/store-2.2.2.tgz",
+      "integrity": "sha1-oAMFpkUgMqM4WIahHOUp3OLa5ls="
+    },
+    "@ngrx/store-devtools": {
+      "version": "3.2.4",
+      "resolved": "https://registry.npmjs.org/@ngrx/store-devtools/-/store-devtools-3.2.4.tgz",
+      "integrity": "sha1-LOTRO/NISKnlHsh+OxJe1ntR5VA="
     },
     "@ngtools/json-schema": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/@ngtools/json-schema/-/json-schema-1.2.0.tgz",
-      "integrity": "sha512-pMh+HDc6mOjUO3agRfB1tInimo7hf67u+0Cska2bfXFe6oU7rSMnr5PLVtiZVgwMoBHpx/6XjBymvcnWPo2Uzg==",
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/@ngtools/json-schema/-/json-schema-1.1.0.tgz",
+      "integrity": "sha1-w6DFRNYjkqzCgTpCyKDcb1j4aSI=",
       "dev": true
     },
     "@ngtools/webpack": {
-      "version": "1.10.2",
-      "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-1.10.2.tgz",
-      "integrity": "sha512-3u2zg2rarG3qNLSukBClGADWuq/iNn5SQtlSeAbfKzwBeyLGbF0gN1z1tVx1Bcr8YwFzR6NdRePQmJGcoqq1fg==",
+      "version": "1.7.4",
+      "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-1.7.4.tgz",
+      "integrity": "sha512-o0u1Oj1k1WEIamBNEncvXDWmUxCMDIlKrMFp4nIwh7bag4dndDShUVD1EinSpx1TvMjVbA42Z+7cIVmlq+240Q==",
       "dev": true,
       "requires": {
-        "chalk": "2.2.2",
         "enhanced-resolve": "3.4.1",
         "loader-utils": "1.1.0",
         "magic-string": "0.22.5",
-        "semver": "5.5.0",
-        "source-map": "0.5.7",
-        "tree-kill": "1.2.0",
-        "webpack-sources": "1.1.0"
+        "source-map": "0.5.7"
       }
     },
     "@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.2"
-      }
+      "version": "7.0.0",
+      "resolved": "https://registry.npmjs.org/@ngx-translate/core/-/core-7.0.0.tgz",
+      "integrity": "sha1-W29jvUBCFk1EzYX2hwOvluk5Ln0="
     },
     "@ngx-translate/http-loader": {
       "version": "0.1.0",
@@ -295,52 +322,30 @@
       "integrity": "sha1-YCkyVWHXho/jJaQZ3idw6Y/xUC4="
     },
     "@schematics/angular": {
-      "version": "0.3.2",
-      "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-0.3.2.tgz",
-      "integrity": "sha512-Elrk0BA951s0ScFZU0AWrpUeJBYVR52DZ1QTIO5R0AhwEd1PW4olI8szPLGQlVW5Sd6H0FA/fyFLIvn2r9v6Rw==",
+      "version": "0.0.49",
+      "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-0.0.49.tgz",
+      "integrity": "sha512-ZRuWY04P2fiJFKEzWfJBPsM7/ZxPp6bYkCQ38a5KQJwt5X5dKJBMVMcBmjX6TOqreN3eJaJGNr/lcpRNlCvnpw==",
       "dev": true,
       "requires": {
-        "typescript": "2.6.2"
-      },
-      "dependencies": {
-        "typescript": {
-          "version": "2.6.2",
-          "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.6.2.tgz",
-          "integrity": "sha1-PFtv1/beCRQmkCfwPAlGdY92c6Q=",
-          "dev": true
-        }
+        "@angular-devkit/core": "0.0.20"
       }
     },
-    "@schematics/package-update": {
-      "version": "0.3.2",
-      "resolved": "https://registry.npmjs.org/@schematics/package-update/-/package-update-0.3.2.tgz",
-      "integrity": "sha512-7aVP4994Hu8vRdTTohXkfGWEwLhrdNP3EZnWyBootm5zshWqlQojUGweZe5zwewsKcixeVOiy2YtW+aI4aGSLA==",
-      "dev": true,
-      "requires": {
-        "rxjs": "5.5.11",
-        "semver": "5.5.0",
-        "semver-intersect": "1.3.1"
-      }
-    },
-    "@types/jasmine": {
-      "version": "2.8.8",
-      "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-2.8.8.tgz",
-      "integrity": "sha512-OJSUxLaxXsjjhob2DBzqzgrkLmukM3+JMpRp0r0E4HTdT1nwDCWhaswjYxazPij6uOdzHCJfNbDjmQ1/rnNbCg==",
+    "@types/hammerjs": {
+      "version": "2.0.30",
+      "resolved": "https://registry.npmjs.org/@types/hammerjs/-/hammerjs-2.0.30.tgz",
+      "integrity": "sha1-RAM1tBM4kxmS+5v6UTgWa3GKlAc=",
       "dev": true
     },
-    "@types/jasminewd2": {
-      "version": "2.0.3",
-      "resolved": "https://registry.npmjs.org/@types/jasminewd2/-/jasminewd2-2.0.3.tgz",
-      "integrity": "sha512-hYDVmQZT5VA2kigd4H4bv7vl/OhlympwREUemqBdOqtrYTo5Ytm12a5W5/nGgGYdanGVxj0x/VhZ7J3hOg/YKg==",
-      "dev": true,
-      "requires": {
-        "@types/jasmine": "2.8.8"
-      }
+    "@types/jasmine": {
+      "version": "2.5.38",
+      "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-2.5.38.tgz",
+      "integrity": "sha1-pDeRJMSSHU4h3lTsdGacnps1Zxc=",
+      "dev": true
     },
     "@types/node": {
-      "version": "6.0.113",
-      "resolved": "https://registry.npmjs.org/@types/node/-/node-6.0.113.tgz",
-      "integrity": "sha512-f9XXUWFqryzjkZA1EqFvJHSFyqyasV17fq8zCDIzbRV4ctL7RrJGKvG+lcex86Rjbzd1GrER9h9VmF5sSjV0BQ==",
+      "version": "6.0.78",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-6.0.78.tgz",
+      "integrity": "sha512-+vD6E8ixntRzzZukoF3uP1iV+ZjVN3koTcaeK+BEoc/kSfGbLDIGC7RmCaUgVpUfN6cWvfczFRERCyKM9mkvXg==",
       "dev": true
     },
     "@types/q": {
@@ -350,21 +355,9 @@
       "dev": true
     },
     "@types/selenium-webdriver": {
-      "version": "2.53.43",
-      "resolved": "https://registry.npmjs.org/@types/selenium-webdriver/-/selenium-webdriver-2.53.43.tgz",
-      "integrity": "sha512-UBYHWph6P3tutkbXpW6XYg9ZPbTKjw/YC2hGG1/GEvWwTbvezBUv3h+mmUFw79T3RFPnmedpiXdOBbXX+4l0jg==",
-      "dev": true
-    },
-    "@types/strip-bom": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/@types/strip-bom/-/strip-bom-3.0.0.tgz",
-      "integrity": "sha1-FKjsOVbC6B7bdSB5CuzyHCkK69I=",
-      "dev": true
-    },
-    "@types/strip-json-comments": {
-      "version": "0.0.30",
-      "resolved": "https://registry.npmjs.org/@types/strip-json-comments/-/strip-json-comments-0.0.30.tgz",
-      "integrity": "sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ==",
+      "version": "2.53.36",
+      "resolved": "https://registry.npmjs.org/@types/selenium-webdriver/-/selenium-webdriver-2.53.36.tgz",
+      "integrity": "sha1-otJ9BwmzaK4IlPwwwOQK+CfW6FE=",
       "dev": true
     },
     "abbrev": {
@@ -384,9 +377,9 @@
       }
     },
     "acorn": {
-      "version": "5.7.1",
-      "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.1.tgz",
-      "integrity": "sha512-d+nbxBUGKg7Arpsvbnlq61mc12ek3EY8EQldM3GPAhWJ1UVxC6TDGbIvUMNU6obBX3i1+ptCIzV4vq0gFPEGVQ==",
+      "version": "5.5.3",
+      "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.5.3.tgz",
+      "integrity": "sha512-jd5MkIUlbbmb07nXH0DT3y7rDVtkzDi4XZOUVWAer8ajmF/DTSSbl5oNFyDOl/OXA33Bl79+ypHhl2pN20VeOQ==",
       "dev": true
     },
     "acorn-dynamic-import": {
@@ -406,13 +399,6 @@
         }
       }
     },
-    "addressparser": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/addressparser/-/addressparser-1.0.1.tgz",
-      "integrity": "sha1-R6++GiqSYhkdtoOOT9HTm0CCF0Y=",
-      "dev": true,
-      "optional": true
-    },
     "adm-zip": {
       "version": "0.4.11",
       "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.11.tgz",
@@ -426,44 +412,39 @@
       "dev": true
     },
     "agent-base": {
-      "version": "4.2.0",
-      "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.2.0.tgz",
-      "integrity": "sha512-c+R/U5X+2zz2+UCrCFv6odQzJdoqI+YecuhnAJLa1zYaMc13zPfwMwZrr91Pd1DYNo/yPRbiM4WVf9whgwFsIg==",
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-2.1.1.tgz",
+      "integrity": "sha1-1t4Q1a9hMtW9aSQn1G/FOFOQlMc=",
       "dev": true,
       "requires": {
-        "es6-promisify": "5.0.0"
-      }
-    },
-    "ajv": {
-      "version": "6.5.1",
-      "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.1.tgz",
-      "integrity": "sha512-pgZos1vgOHDiC7gKNbZW8eKvCnNXARv2oqrGQT7Hzbq5Azp7aZG6DJzADnkuSq7RH6qkXp4J/m68yPX/2uBHyQ==",
-      "dev": true,
-      "requires": {
-        "fast-deep-equal": "2.0.1",
-        "fast-json-stable-stringify": "2.0.0",
-        "json-schema-traverse": "0.4.1",
-        "uri-js": "4.2.2"
+        "extend": "3.0.1",
+        "semver": "5.0.3"
       },
       "dependencies": {
-        "fast-deep-equal": {
-          "version": "2.0.1",
-          "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz",
-          "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=",
-          "dev": true
-        },
-        "json-schema-traverse": {
-          "version": "0.4.1",
-          "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
-          "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+        "semver": {
+          "version": "5.0.3",
+          "resolved": "https://registry.npmjs.org/semver/-/semver-5.0.3.tgz",
+          "integrity": "sha1-d0Zt5YnNXTyV8TiqeLxWmjy10no=",
           "dev": true
         }
       }
     },
+    "ajv": {
+      "version": "5.5.2",
+      "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz",
+      "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=",
+      "dev": true,
+      "requires": {
+        "co": "4.6.0",
+        "fast-deep-equal": "1.1.0",
+        "fast-json-stable-stringify": "2.0.0",
+        "json-schema-traverse": "0.3.1"
+      }
+    },
     "ajv-keywords": {
-      "version": "3.2.0",
-      "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.2.0.tgz",
-      "integrity": "sha1-6GuBnGAs+IIa1jdBNpjx3sAhhHo=",
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-2.1.1.tgz",
+      "integrity": "sha1-YXmX/F9gV2iUxDX5QNgZ4TW4B2I=",
       "dev": true
     },
     "align-text": {
@@ -475,55 +456,37 @@
         "kind-of": "3.2.2",
         "longest": "1.0.1",
         "repeat-string": "1.6.1"
+      },
+      "dependencies": {
+        "kind-of": {
+          "version": "3.2.2",
+          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+          "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+          "dev": true,
+          "requires": {
+            "is-buffer": "1.1.6"
+          }
+        }
       }
     },
+    "alphanum-sort": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/alphanum-sort/-/alphanum-sort-1.0.2.tgz",
+      "integrity": "sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM=",
+      "dev": true
+    },
     "amdefine": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz",
       "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=",
       "dev": true
     },
-    "amqplib": {
-      "version": "0.5.2",
-      "resolved": "https://registry.npmjs.org/amqplib/-/amqplib-0.5.2.tgz",
-      "integrity": "sha512-l9mCs6LbydtHqRniRwYkKdqxVa6XMz3Vw1fh+2gJaaVgTM6Jk3o8RccAKWKtlhT1US5sWrFh+KKxsVUALURSIA==",
-      "dev": true,
-      "optional": true,
+    "angular2-text-mask": {
+      "version": "8.0.5",
+      "resolved": "https://registry.npmjs.org/angular2-text-mask/-/angular2-text-mask-8.0.5.tgz",
+      "integrity": "sha512-s6fqiQtIa16v0FzMhk1Y5LApAoq/0okKwtyCW70/UkHbRdRkN/uHa2LplX7F/SyTJFsYZJTRzH8IlpaMbQcccw==",
       "requires": {
-        "bitsyntax": "0.0.4",
-        "bluebird": "3.5.1",
-        "buffer-more-ints": "0.0.2",
-        "readable-stream": "1.1.14",
-        "safe-buffer": "5.1.2"
-      },
-      "dependencies": {
-        "isarray": {
-          "version": "0.0.1",
-          "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
-          "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=",
-          "dev": true,
-          "optional": true
-        },
-        "readable-stream": {
-          "version": "1.1.14",
-          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz",
-          "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=",
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "core-util-is": "1.0.2",
-            "inherits": "2.0.3",
-            "isarray": "0.0.1",
-            "string_decoder": "0.10.31"
-          }
-        },
-        "string_decoder": {
-          "version": "0.10.31",
-          "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
-          "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=",
-          "dev": true,
-          "optional": true
-        }
+        "text-mask-core": "5.1.1"
       }
     },
     "ansi-html": {
@@ -535,25 +498,21 @@
     "ansi-regex": {
       "version": "2.1.1",
       "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
-      "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
-      "dev": true
+      "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
     },
     "ansi-styles": {
-      "version": "3.2.1",
-      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
-      "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
-      "dev": true,
-      "requires": {
-        "color-convert": "1.9.2"
-      }
+      "version": "2.2.1",
+      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
+      "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
+      "dev": true
     },
     "anymatch": {
-      "version": "1.3.2",
-      "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-1.3.2.tgz",
-      "integrity": "sha512-0XNayC8lTHQ2OI8aljNCN3sSx6hsr/1+rlcDAotXJR7C1oZZHCNsfpbKwMjRA3Uqb5tF1Rae2oloTr4xpq+WjA==",
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz",
+      "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==",
       "dev": true,
       "requires": {
-        "micromatch": "2.3.11",
+        "micromatch": "3.1.10",
         "normalize-path": "2.1.1"
       }
     },
@@ -564,12 +523,12 @@
       "dev": true
     },
     "append-transform": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-1.0.0.tgz",
-      "integrity": "sha512-P009oYkeHyU742iSZJzZZywj4QRJdnTWffaKuJQLablCZ1uz6/cW4yaRgcDaoQ+uwOxxnt0gRUcwfsNP2ri0gw==",
+      "version": "0.4.0",
+      "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-0.4.0.tgz",
+      "integrity": "sha1-126/jKlNJ24keja61EpLdKthGZE=",
       "dev": true,
       "requires": {
-        "default-require-extensions": "2.0.0"
+        "default-require-extensions": "1.0.0"
       }
     },
     "aproba": {
@@ -579,9 +538,9 @@
       "dev": true
     },
     "are-we-there-yet": {
-      "version": "1.1.5",
-      "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz",
-      "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==",
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.4.tgz",
+      "integrity": "sha1-u13KOCu5TwXhUZQ3PRb9O6HKEQ0=",
       "dev": true,
       "requires": {
         "delegates": "1.0.0",
@@ -598,13 +557,10 @@
       }
     },
     "arr-diff": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz",
-      "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=",
-      "dev": true,
-      "requires": {
-        "arr-flatten": "1.1.0"
-      }
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz",
+      "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=",
+      "dev": true
     },
     "arr-flatten": {
       "version": "1.1.0",
@@ -630,16 +586,6 @@
       "integrity": "sha1-Qmu52oQJDBg42BLIFQryCoMx4pY=",
       "dev": true
     },
-    "array-includes": {
-      "version": "3.0.3",
-      "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.0.3.tgz",
-      "integrity": "sha1-GEtI9i2S10UrsxsyMWXH+L0CJm0=",
-      "dev": true,
-      "requires": {
-        "define-properties": "1.1.2",
-        "es-abstract": "1.12.0"
-      }
-    },
     "array-slice": {
       "version": "0.2.3",
       "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-0.2.3.tgz",
@@ -662,15 +608,15 @@
       "dev": true
     },
     "array-unique": {
-      "version": "0.2.1",
-      "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz",
-      "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=",
+      "version": "0.3.2",
+      "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz",
+      "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=",
       "dev": true
     },
     "arraybuffer.slice": {
-      "version": "0.0.7",
-      "resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz",
-      "integrity": "sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog==",
+      "version": "0.0.6",
+      "resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.6.tgz",
+      "integrity": "sha1-8zshWfBTKj8xB6JywMz70a0peco=",
       "dev": true
     },
     "arrify": {
@@ -683,8 +629,7 @@
       "version": "2.0.6",
       "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
       "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=",
-      "dev": true,
-      "optional": true
+      "dev": true
     },
     "asn1": {
       "version": "0.2.3",
@@ -710,23 +655,6 @@
       "dev": true,
       "requires": {
         "util": "0.10.3"
-      },
-      "dependencies": {
-        "inherits": {
-          "version": "2.0.1",
-          "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz",
-          "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=",
-          "dev": true
-        },
-        "util": {
-          "version": "0.10.3",
-          "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz",
-          "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=",
-          "dev": true,
-          "requires": {
-            "inherits": "2.0.1"
-          }
-        }
       }
     },
     "assert-plus": {
@@ -741,13 +669,6 @@
       "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=",
       "dev": true
     },
-    "ast-types": {
-      "version": "0.11.5",
-      "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.11.5.tgz",
-      "integrity": "sha512-oJjo+5e7/vEc2FBK8gUalV0pba4L3VdBIs2EKhOLHLcOd2FgQIVQN9xb0eZ9IjEWyAL7vq6fGJxOvVvdCHNyMw==",
-      "dev": true,
-      "optional": true
-    },
     "async": {
       "version": "2.6.1",
       "resolved": "https://registry.npmjs.org/async/-/async-2.6.1.tgz",
@@ -789,16 +710,16 @@
       "dev": true
     },
     "autoprefixer": {
-      "version": "7.2.6",
-      "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-7.2.6.tgz",
-      "integrity": "sha512-Iq8TRIB+/9eQ8rbGhcP7ct5cYb/3qjNYAR2SnzLCEcwF6rvVOax8+9+fccgXk4bEhQGjOZd5TLhsksmAdsbGqQ==",
+      "version": "6.7.7",
+      "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-6.7.7.tgz",
+      "integrity": "sha1-Hb0cg1ZY41zj+ZhAmdsAWFx4IBQ=",
       "dev": true,
       "requires": {
-        "browserslist": "2.11.3",
-        "caniuse-lite": "1.0.30000856",
+        "browserslist": "1.7.7",
+        "caniuse-db": "1.0.30000844",
         "normalize-range": "0.1.2",
         "num2fraction": "1.2.2",
-        "postcss": "6.0.22",
+        "postcss": "5.2.18",
         "postcss-value-parser": "3.3.0"
       }
     },
@@ -814,28 +735,6 @@
       "integrity": "sha512-32NDda82rhwD9/JBCCkB+MRYDp0oSvlo2IL6rQWA10PQi7tDUM3eqMSltXmY+Oyl/7N3P3qNtAlv7X0d9bI28w==",
       "dev": true
     },
-    "axios": {
-      "version": "0.15.3",
-      "resolved": "https://registry.npmjs.org/axios/-/axios-0.15.3.tgz",
-      "integrity": "sha1-LJ1jiy4ZGgjqHWzJiOrda6W9wFM=",
-      "dev": true,
-      "optional": true,
-      "requires": {
-        "follow-redirects": "1.0.0"
-      },
-      "dependencies": {
-        "follow-redirects": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.0.0.tgz",
-          "integrity": "sha1-jjQpjL0uF28lTv/sdaHHjMhJ/Tc=",
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "debug": "2.6.9"
-          }
-        }
-      }
-    },
     "babel-code-frame": {
       "version": "6.26.0",
       "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz",
@@ -847,12 +746,6 @@
         "js-tokens": "3.0.2"
       },
       "dependencies": {
-        "ansi-styles": {
-          "version": "2.2.1",
-          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
-          "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
-          "dev": true
-        },
         "chalk": {
           "version": "1.1.3",
           "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
@@ -888,6 +781,14 @@
         "lodash": "4.17.10",
         "source-map": "0.5.7",
         "trim-right": "1.0.1"
+      },
+      "dependencies": {
+        "jsesc": {
+          "version": "1.3.0",
+          "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz",
+          "integrity": "sha1-RsP+yMGJKxKwgz25vHYiF226s0s=",
+          "dev": true
+        }
       }
     },
     "babel-messages": {
@@ -905,7 +806,7 @@
       "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=",
       "dev": true,
       "requires": {
-        "core-js": "2.5.7",
+        "core-js": "2.4.1",
         "regenerator-runtime": "0.11.1"
       }
     },
@@ -1021,18 +922,6 @@
             "is-data-descriptor": "1.0.0",
             "kind-of": "6.0.2"
           }
-        },
-        "isobject": {
-          "version": "3.0.1",
-          "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
-          "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
-          "dev": true
-        },
-        "kind-of": {
-          "version": "6.0.2",
-          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
-          "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==",
-          "dev": true
         }
       }
     },
@@ -1079,6 +968,17 @@
         "callsite": "1.0.0"
       }
     },
+    "bfj-node4": {
+      "version": "5.3.1",
+      "resolved": "https://registry.npmjs.org/bfj-node4/-/bfj-node4-5.3.1.tgz",
+      "integrity": "sha512-SOmOsowQWfXc7ybFARsK3C4MCOWzERaOMV/Fl3Tgjs+5dJWyzo3oa127jL44eMbQiAN17J7SvAs2TRxEScTUmg==",
+      "dev": true,
+      "requires": {
+        "bluebird": "3.5.1",
+        "check-types": "7.3.0",
+        "tryer": "1.0.0"
+      }
+    },
     "big.js": {
       "version": "3.2.0",
       "resolved": "https://registry.npmjs.org/big.js/-/big.js-3.2.0.tgz",
@@ -1091,22 +991,11 @@
       "integrity": "sha1-RqoXUftqL5PuXmibsQh9SxTGwgU=",
       "dev": true
     },
-    "bitsyntax": {
-      "version": "0.0.4",
-      "resolved": "https://registry.npmjs.org/bitsyntax/-/bitsyntax-0.0.4.tgz",
-      "integrity": "sha1-6xDMb4K4xJDj6FaY8H6D1G4MuoI=",
-      "dev": true,
-      "optional": true,
-      "requires": {
-        "buffer-more-ints": "0.0.2"
-      }
-    },
     "bl": {
-      "version": "1.1.2",
-      "resolved": "https://registry.npmjs.org/bl/-/bl-1.1.2.tgz",
-      "integrity": "sha1-/cqHGplxOqANGeO7ukHER4emU5g=",
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/bl/-/bl-1.0.3.tgz",
+      "integrity": "sha1-/FQhoo/UImA2w7OJGmaiW8ZNIm4=",
       "dev": true,
-      "optional": true,
       "requires": {
         "readable-stream": "2.0.6"
       },
@@ -1115,15 +1004,13 @@
           "version": "1.0.7",
           "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz",
           "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=",
-          "dev": true,
-          "optional": true
+          "dev": true
         },
         "readable-stream": {
           "version": "2.0.6",
           "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz",
           "integrity": "sha1-j5A0HmilPMySh4jaz80Rs265t44=",
           "dev": true,
-          "optional": true,
           "requires": {
             "core-util-is": "1.0.2",
             "inherits": "2.0.3",
@@ -1137,8 +1024,7 @@
           "version": "0.10.31",
           "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
           "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=",
-          "dev": true,
-          "optional": true
+          "dev": true
         }
       }
     },
@@ -1159,9 +1045,9 @@
       }
     },
     "blocking-proxy": {
-      "version": "0.0.5",
-      "resolved": "https://registry.npmjs.org/blocking-proxy/-/blocking-proxy-0.0.5.tgz",
-      "integrity": "sha1-RikF4Nz76pcPQao3Ij3anAexkSs=",
+      "version": "0.0.4",
+      "resolved": "https://registry.npmjs.org/blocking-proxy/-/blocking-proxy-0.0.4.tgz",
+      "integrity": "sha1-SQFnMqw46NU6LH3NUCUgqg5Y4EQ=",
       "dev": true,
       "requires": {
         "minimist": "1.2.0"
@@ -1253,14 +1139,32 @@
       }
     },
     "braces": {
-      "version": "1.8.5",
-      "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz",
-      "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=",
+      "version": "2.3.2",
+      "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz",
+      "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==",
       "dev": true,
       "requires": {
-        "expand-range": "1.8.2",
-        "preserve": "0.2.0",
-        "repeat-element": "1.1.2"
+        "arr-flatten": "1.1.0",
+        "array-unique": "0.3.2",
+        "extend-shallow": "2.0.1",
+        "fill-range": "4.0.0",
+        "isobject": "3.0.1",
+        "repeat-element": "1.1.2",
+        "snapdragon": "0.8.2",
+        "snapdragon-node": "2.1.1",
+        "split-string": "3.1.0",
+        "to-regex": "3.0.2"
+      },
+      "dependencies": {
+        "extend-shallow": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+          "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+          "dev": true,
+          "requires": {
+            "is-extendable": "0.1.1"
+          }
+        }
       }
     },
     "brorand": {
@@ -1340,13 +1244,13 @@
       }
     },
     "browserslist": {
-      "version": "2.11.3",
-      "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-2.11.3.tgz",
-      "integrity": "sha512-yWu5cXT7Av6mVwzWc8lMsJMHWn4xyjSuGYi4IozbVTLUOEYPSagUB8kiMDUHA1fS3zjr8nkxkn9jdvug4BBRmA==",
+      "version": "1.7.7",
+      "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-1.7.7.tgz",
+      "integrity": "sha1-C9dnBCWL6CmyOYu1Dkti0aFmsLk=",
       "dev": true,
       "requires": {
-        "caniuse-lite": "1.0.30000856",
-        "electron-to-chromium": "1.3.49"
+        "caniuse-db": "1.0.30000844",
+        "electron-to-chromium": "1.3.48"
       }
     },
     "buffer": {
@@ -1356,14 +1260,14 @@
       "dev": true,
       "requires": {
         "base64-js": "1.3.0",
-        "ieee754": "1.1.12",
+        "ieee754": "1.1.11",
         "isarray": "1.0.0"
       }
     },
     "buffer-from": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.0.tgz",
-      "integrity": "sha512-c5mRlguI/Pe2dSZmpER62rSCu0ryKmWddzRYsuXc50U2/g8jMOulc31VZMa4mYx31U5xsmSOpDCgH88Vl9cDGQ==",
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.0.0.tgz",
+      "integrity": "sha512-83apNb8KK0Se60UE1+4Ukbe3HbfELJ6UlI4ldtOGs7So4KD26orJM8hIY9lxdzP+UpItH1Yh/Y8GUvNFWFFRxA==",
       "dev": true
     },
     "buffer-indexof": {
@@ -1372,48 +1276,16 @@
       "integrity": "sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g==",
       "dev": true
     },
-    "buffer-more-ints": {
-      "version": "0.0.2",
-      "resolved": "https://registry.npmjs.org/buffer-more-ints/-/buffer-more-ints-0.0.2.tgz",
-      "integrity": "sha1-JrOIXRD6E9t/wBquOquHAZngEkw=",
-      "dev": true
-    },
     "buffer-xor": {
       "version": "1.0.3",
       "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz",
       "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=",
       "dev": true
     },
-    "buildmail": {
-      "version": "4.0.1",
-      "resolved": "https://registry.npmjs.org/buildmail/-/buildmail-4.0.1.tgz",
-      "integrity": "sha1-h393OLeHKYccmhBeO4N9K+EaenI=",
-      "dev": true,
-      "optional": true,
-      "requires": {
-        "addressparser": "1.0.1",
-        "libbase64": "0.1.0",
-        "libmime": "3.0.0",
-        "libqp": "1.1.0",
-        "nodemailer-fetch": "1.6.0",
-        "nodemailer-shared": "1.1.0",
-        "punycode": "1.4.1"
-      },
-      "dependencies": {
-        "punycode": {
-          "version": "1.4.1",
-          "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
-          "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=",
-          "dev": true,
-          "optional": true
-        }
-      }
-    },
     "builtin-modules": {
       "version": "1.1.1",
       "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz",
-      "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=",
-      "dev": true
+      "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8="
     },
     "builtin-status-codes": {
       "version": "3.0.0",
@@ -1446,6 +1318,14 @@
         "ssri": "5.3.0",
         "unique-filename": "1.1.0",
         "y18n": "4.0.0"
+      },
+      "dependencies": {
+        "y18n": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz",
+          "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==",
+          "dev": true
+        }
       }
     },
     "cache-base": {
@@ -1463,26 +1343,6 @@
         "to-object-path": "0.3.0",
         "union-value": "1.0.0",
         "unset-value": "1.0.0"
-      },
-      "dependencies": {
-        "isobject": {
-          "version": "3.0.1",
-          "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
-          "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
-          "dev": true
-        }
-      }
-    },
-    "cache-loader": {
-      "version": "1.2.2",
-      "resolved": "https://registry.npmjs.org/cache-loader/-/cache-loader-1.2.2.tgz",
-      "integrity": "sha512-rsGh4SIYyB9glU+d0OcHwiXHXBoUgDhHZaQ1KAbiXqfz1CDPxtTboh1gPbJ0q2qdO8a9lfcjgC5CJ2Ms32y5bw==",
-      "dev": true,
-      "requires": {
-        "loader-utils": "1.1.0",
-        "mkdirp": "0.5.1",
-        "neo-async": "2.5.1",
-        "schema-utils": "0.4.5"
       }
     },
     "callsite": {
@@ -1502,10 +1362,9 @@
       }
     },
     "camelcase": {
-      "version": "2.1.1",
-      "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz",
-      "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=",
-      "dev": true
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz",
+      "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo="
     },
     "camelcase-keys": {
       "version": "2.1.0",
@@ -1515,12 +1374,32 @@
       "requires": {
         "camelcase": "2.1.1",
         "map-obj": "1.0.1"
+      },
+      "dependencies": {
+        "camelcase": {
+          "version": "2.1.1",
+          "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz",
+          "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=",
+          "dev": true
+        }
       }
     },
-    "caniuse-lite": {
-      "version": "1.0.30000856",
-      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000856.tgz",
-      "integrity": "sha512-x3mYcApHMQemyaHuH/RyqtKCGIYTgEA63fdi+VBvDz8xUSmRiVWTLeyKcoGQCGG6UPR9/+4qG4OKrTa6aSQRKg==",
+    "caniuse-api": {
+      "version": "1.6.1",
+      "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-1.6.1.tgz",
+      "integrity": "sha1-tTTnxzTE+B7F++isoq0kNUuWLGw=",
+      "dev": true,
+      "requires": {
+        "browserslist": "1.7.7",
+        "caniuse-db": "1.0.30000844",
+        "lodash.memoize": "4.1.2",
+        "lodash.uniq": "4.5.0"
+      }
+    },
+    "caniuse-db": {
+      "version": "1.0.30000844",
+      "resolved": "https://registry.npmjs.org/caniuse-db/-/caniuse-db-1.0.30000844.tgz",
+      "integrity": "sha1-vKV5jNoraTHWgQDC1p5V+zOMu0E=",
       "dev": true
     },
     "caseless": {
@@ -1540,31 +1419,72 @@
       }
     },
     "chalk": {
-      "version": "2.2.2",
-      "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.2.2.tgz",
-      "integrity": "sha512-LvixLAQ4MYhbf7hgL4o5PeK32gJKvVzDRiSNIApDofQvyhl8adgG2lJVXn4+ekQoK7HL9RF8lqxwerpe0x2pCw==",
+      "version": "2.4.1",
+      "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz",
+      "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==",
       "dev": true,
       "requires": {
         "ansi-styles": "3.2.1",
         "escape-string-regexp": "1.0.5",
-        "supports-color": "4.5.0"
+        "supports-color": "5.4.0"
+      },
+      "dependencies": {
+        "ansi-styles": {
+          "version": "3.2.1",
+          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
+          "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+          "dev": true,
+          "requires": {
+            "color-convert": "1.9.1"
+          }
+        },
+        "has-flag": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+          "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
+          "dev": true
+        },
+        "supports-color": {
+          "version": "5.4.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz",
+          "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==",
+          "dev": true,
+          "requires": {
+            "has-flag": "3.0.0"
+          }
+        }
       }
     },
+    "charenc": {
+      "version": "0.0.2",
+      "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz",
+      "integrity": "sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc=",
+      "dev": true
+    },
+    "check-types": {
+      "version": "7.3.0",
+      "resolved": "https://registry.npmjs.org/check-types/-/check-types-7.3.0.tgz",
+      "integrity": "sha1-Ro9XGkQ1wkJI9f0MsOjYfDw0Hn0=",
+      "dev": true
+    },
     "chokidar": {
-      "version": "1.7.0",
-      "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-1.7.0.tgz",
-      "integrity": "sha1-eY5ol3gVHIB2tLNg5e3SjNortGg=",
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.0.3.tgz",
+      "integrity": "sha512-zW8iXYZtXMx4kux/nuZVXjkLP+CyIK5Al5FHnj1OgTKGZfp4Oy6/ymtMSKFv3GD8DviEmUPmJg9eFdJ/JzudMg==",
       "dev": true,
       "requires": {
-        "anymatch": "1.3.2",
+        "anymatch": "2.0.0",
         "async-each": "1.0.1",
+        "braces": "2.3.2",
         "fsevents": "1.2.4",
-        "glob-parent": "2.0.0",
+        "glob-parent": "3.1.0",
         "inherits": "2.0.3",
         "is-binary-path": "1.0.1",
-        "is-glob": "2.0.1",
+        "is-glob": "4.0.0",
+        "normalize-path": "2.1.1",
         "path-is-absolute": "1.0.1",
-        "readdirp": "2.1.0"
+        "readdirp": "2.1.0",
+        "upath": "1.1.0"
       }
     },
     "chownr": {
@@ -1584,16 +1504,40 @@
       }
     },
     "circular-dependency-plugin": {
-      "version": "4.4.0",
-      "resolved": "https://registry.npmjs.org/circular-dependency-plugin/-/circular-dependency-plugin-4.4.0.tgz",
-      "integrity": "sha512-yEFtUNUYT4jBykEX5ZOHw+5goA3glGZr9wAXIQqoyakjz5H5TeUmScnWRc52douAhb9eYzK3s7V6bXfNnjFdzg==",
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/circular-dependency-plugin/-/circular-dependency-plugin-3.0.0.tgz",
+      "integrity": "sha1-m2hpLjWw41EJmNAWS2rlARvqV2A=",
       "dev": true
     },
-    "circular-json": {
-      "version": "0.5.4",
-      "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.5.4.tgz",
-      "integrity": "sha512-vnJA8KS0BfOihugYEUkLRcnmq21FbuivbxgzDLXNs3zIk4KllV4Mx4UuTzBXht9F00C7QfD1YqMXg1zP6EXpig==",
-      "dev": true
+    "clap": {
+      "version": "1.2.3",
+      "resolved": "https://registry.npmjs.org/clap/-/clap-1.2.3.tgz",
+      "integrity": "sha512-4CoL/A3hf90V3VIEjeuhSvlGFEHKzOz+Wfc2IVZc+FaUgU0ZQafJTP49fvnULipOPcAfqhyI2duwQyns6xqjYA==",
+      "dev": true,
+      "requires": {
+        "chalk": "1.1.3"
+      },
+      "dependencies": {
+        "chalk": {
+          "version": "1.1.3",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+          "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
+          "dev": true,
+          "requires": {
+            "ansi-styles": "2.2.1",
+            "escape-string-regexp": "1.0.5",
+            "has-ansi": "2.0.0",
+            "strip-ansi": "3.0.1",
+            "supports-color": "2.0.0"
+          }
+        },
+        "supports-color": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
+          "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
+          "dev": true
+        }
+      }
     },
     "class-utils": {
       "version": "0.3.6",
@@ -1615,12 +1559,6 @@
           "requires": {
             "is-descriptor": "0.1.6"
           }
-        },
-        "isobject": {
-          "version": "3.0.1",
-          "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
-          "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
-          "dev": true
         }
       }
     },
@@ -1637,7 +1575,6 @@
       "version": "3.2.0",
       "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz",
       "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=",
-      "dev": true,
       "requires": {
         "string-width": "1.0.2",
         "strip-ansi": "3.0.1",
@@ -1645,9 +1582,9 @@
       }
     },
     "clone": {
-      "version": "2.1.1",
-      "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.1.tgz",
-      "integrity": "sha1-0hfR6WERjjrJpLi7oyhVU79kfNs=",
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz",
+      "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=",
       "dev": true
     },
     "clone-deep": {
@@ -1660,23 +1597,6 @@
         "is-plain-object": "2.0.4",
         "kind-of": "6.0.2",
         "shallow-clone": "1.0.0"
-      },
-      "dependencies": {
-        "for-own": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz",
-          "integrity": "sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs=",
-          "dev": true,
-          "requires": {
-            "for-in": "1.0.2"
-          }
-        },
-        "kind-of": {
-          "version": "6.0.2",
-          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
-          "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==",
-          "dev": true
-        }
       }
     },
     "co": {
@@ -1685,16 +1605,24 @@
       "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=",
       "dev": true
     },
+    "coa": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/coa/-/coa-1.0.4.tgz",
+      "integrity": "sha1-qe8VNmDWqGqL3sAomlxoTSF0Mv0=",
+      "dev": true,
+      "requires": {
+        "q": "1.5.1"
+      }
+    },
     "code-point-at": {
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
-      "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=",
-      "dev": true
+      "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c="
     },
     "codelyzer": {
-      "version": "4.3.0",
-      "resolved": "https://registry.npmjs.org/codelyzer/-/codelyzer-4.3.0.tgz",
-      "integrity": "sha512-RLMrtLwrBS0dfo2/KTP+2NHofCpzcuh0bEp/A/naqvQonbUL4AW/qWQdbpn8dMNudtpmzEx9eS8KEpGdVPg1BA==",
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/codelyzer/-/codelyzer-3.0.0.tgz",
+      "integrity": "sha1-sz60iGJxtdggmg36SiD8J+76QCk=",
       "dev": true,
       "requires": {
         "app-root-path": "2.0.1",
@@ -1715,21 +1643,52 @@
         "object-visit": "1.0.1"
       }
     },
-    "color-convert": {
-      "version": "1.9.2",
-      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.2.tgz",
-      "integrity": "sha512-3NUJZdhMhcdPn8vJ9v2UQJoH0qqoGUkYTgFEPZaPjEtwmmKUfNV46zZmgB2M5M4DCEQHMaCfWHCxiBflLm04Tg==",
+    "color": {
+      "version": "0.11.4",
+      "resolved": "https://registry.npmjs.org/color/-/color-0.11.4.tgz",
+      "integrity": "sha1-bXtcdPtl6EHNSHkq0e1eB7kE12Q=",
       "dev": true,
       "requires": {
-        "color-name": "1.1.1"
+        "clone": "1.0.4",
+        "color-convert": "1.9.1",
+        "color-string": "0.3.0"
+      }
+    },
+    "color-convert": {
+      "version": "1.9.1",
+      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.1.tgz",
+      "integrity": "sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==",
+      "dev": true,
+      "requires": {
+        "color-name": "1.1.3"
       }
     },
     "color-name": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.1.tgz",
-      "integrity": "sha1-SxQVMEz1ACjqgWQ2Q72C6gWANok=",
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+      "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
       "dev": true
     },
+    "color-string": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmjs.org/color-string/-/color-string-0.3.0.tgz",
+      "integrity": "sha1-J9RvtnAlxcL6JZk7+/V55HhBuZE=",
+      "dev": true,
+      "requires": {
+        "color-name": "1.1.3"
+      }
+    },
+    "colormin": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/colormin/-/colormin-1.1.2.tgz",
+      "integrity": "sha1-6i90IKcrlogaOKrlnsEkpvcpgTM=",
+      "dev": true,
+      "requires": {
+        "color": "0.11.4",
+        "css-color-names": "0.0.4",
+        "has": "1.0.1"
+      }
+    },
     "colors": {
       "version": "1.1.2",
       "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz",
@@ -1761,10 +1720,13 @@
       "dev": true
     },
     "common-tags": {
-      "version": "1.8.0",
-      "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.0.tgz",
-      "integrity": "sha512-6P6g0uetGpW/sdyUy/iQQCbFF0kWVMSIVSyYz7Zgjcgh8mgw8PQzDNZeyZ5DQ2gM7LBoZPHmnjz8rUthkBG5tw==",
-      "dev": true
+      "version": "1.7.2",
+      "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.7.2.tgz",
+      "integrity": "sha512-joj9ZlUOjCrwdbmiLqafeUSgkUM74NqhLsZtSqDmhKudaIY197zTrb8JMl31fMnCUuxwFT23eC/oWvrZzDLRJQ==",
+      "dev": true,
+      "requires": {
+        "babel-runtime": "6.26.0"
+      }
     },
     "commondir": {
       "version": "1.0.1",
@@ -1773,9 +1735,9 @@
       "dev": true
     },
     "compare-versions": {
-      "version": "3.3.0",
-      "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-3.3.0.tgz",
-      "integrity": "sha512-MAAAIOdi2s4Gl6rZ76PNcUa9IOYB+5ICdT41o5uMRf09aEu/F9RK+qhe8RjXNPwcTjGV7KU7h2P/fljThFVqyQ==",
+      "version": "3.2.1",
+      "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-3.2.1.tgz",
+      "integrity": "sha512-2y2nHcopMG/NAyk6vWXlLs86XeM9sik4jmx1tKIgzMi9/RQ2eo758RGpxQO3ErihHmg0RlQITPqgz73y6s7quA==",
       "dev": true
     },
     "component-bind": {
@@ -1797,20 +1759,12 @@
       "dev": true
     },
     "compressible": {
-      "version": "2.0.14",
-      "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.14.tgz",
-      "integrity": "sha1-MmxfUH+7BV9UEWeCuWmoG2einac=",
+      "version": "2.0.13",
+      "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.13.tgz",
+      "integrity": "sha1-DRAgq5JLL9tNYnmHXH1tq6a6p6k=",
       "dev": true,
       "requires": {
-        "mime-db": "1.34.0"
-      },
-      "dependencies": {
-        "mime-db": {
-          "version": "1.34.0",
-          "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.34.0.tgz",
-          "integrity": "sha1-RS0Oz/XDA0am3B5kseruDTcZ/5o=",
-          "dev": true
-        }
+        "mime-db": "1.33.0"
       }
     },
     "compression": {
@@ -1821,7 +1775,7 @@
       "requires": {
         "accepts": "1.3.5",
         "bytes": "3.0.0",
-        "compressible": "2.0.14",
+        "compressible": "2.0.13",
         "debug": "2.6.9",
         "on-headers": "1.0.1",
         "safe-buffer": "5.1.1",
@@ -1848,7 +1802,7 @@
       "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==",
       "dev": true,
       "requires": {
-        "buffer-from": "1.1.0",
+        "buffer-from": "1.0.0",
         "inherits": "2.0.3",
         "readable-stream": "2.3.6",
         "typedarray": "0.0.6"
@@ -1967,9 +1921,9 @@
       "dev": true
     },
     "copy-webpack-plugin": {
-      "version": "4.4.3",
-      "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-4.4.3.tgz",
-      "integrity": "sha512-v4THQ24Tks2NkyOvZuFDgZVfDD9YaA9rwYLZTrWg2GHIA8lrH5DboEyeoorh5Skki+PUbgSmnsCwhMWqYrQZrA==",
+      "version": "4.5.1",
+      "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-4.5.1.tgz",
+      "integrity": "sha512-OlTo6DYg0XfTKOF8eLf79wcHm4Ut10xU2cRBRPMW/NA5F9VMjZGTfRHWDIYC3s+1kObGYrBLshXWU1K0hILkNQ==",
       "dev": true,
       "requires": {
         "cacache": "10.0.4",
@@ -1978,31 +1932,14 @@
         "is-glob": "4.0.0",
         "loader-utils": "1.1.0",
         "minimatch": "3.0.4",
-        "p-limit": "1.3.0",
+        "p-limit": "1.2.0",
         "serialize-javascript": "1.5.0"
-      },
-      "dependencies": {
-        "is-extglob": {
-          "version": "2.1.1",
-          "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
-          "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
-          "dev": true
-        },
-        "is-glob": {
-          "version": "4.0.0",
-          "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.0.tgz",
-          "integrity": "sha1-lSHHaEXMJhCoUgPd8ICpWML/q8A=",
-          "dev": true,
-          "requires": {
-            "is-extglob": "2.1.1"
-          }
-        }
       }
     },
     "core-js": {
-      "version": "2.5.7",
-      "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.7.tgz",
-      "integrity": "sha512-RszJCAxg/PP6uzXVXL6BsxSXx/B05oJAQ2vkJRjyjrEcNVycaqOmNb5OTxZPE3xa5gwZduqza6L9JOCenh/Ecw=="
+      "version": "2.4.1",
+      "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.4.1.tgz",
+      "integrity": "sha1-TekR5mew6ukSTjQlS1OupvxhjT4="
     },
     "core-object": {
       "version": "3.1.5",
@@ -2010,7 +1947,7 @@
       "integrity": "sha512-sA2/4+/PZ/KV6CKgjrVrrUVBKCkdDO02CUlQ0YKTQoYUwPYNOtOAcWlbYhd5v/1JqYaA6oZ4sDlOU4ppVw6Wbg==",
       "dev": true,
       "requires": {
-        "chalk": "2.2.2"
+        "chalk": "2.4.1"
       }
     },
     "core-util-is": {
@@ -2026,7 +1963,7 @@
       "dev": true,
       "requires": {
         "is-directory": "0.3.1",
-        "js-yaml": "3.12.0",
+        "js-yaml": "3.7.0",
         "minimist": "1.2.0",
         "object-assign": "4.1.1",
         "os-homedir": "1.0.2",
@@ -2087,9 +2024,15 @@
       "optional": true,
       "requires": {
         "lru-cache": "4.1.3",
-        "which": "1.3.1"
+        "which": "1.3.0"
       }
     },
+    "crypt": {
+      "version": "0.0.2",
+      "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz",
+      "integrity": "sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs=",
+      "dev": true
+    },
     "cryptiles": {
       "version": "2.0.5",
       "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz",
@@ -2118,6 +2061,34 @@
         "randomfill": "1.0.4"
       }
     },
+    "css-color-names": {
+      "version": "0.0.4",
+      "resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz",
+      "integrity": "sha1-gIrcLnnPhHOAabZGyyDsJ762KeA=",
+      "dev": true
+    },
+    "css-loader": {
+      "version": "0.28.11",
+      "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-0.28.11.tgz",
+      "integrity": "sha512-wovHgjAx8ZIMGSL8pTys7edA1ClmzxHeY6n/d97gg5odgsxEgKjULPR0viqyC+FWMCL9sfqoC/QCUBo62tLvPg==",
+      "dev": true,
+      "requires": {
+        "babel-code-frame": "6.26.0",
+        "css-selector-tokenizer": "0.7.0",
+        "cssnano": "3.10.0",
+        "icss-utils": "2.1.0",
+        "loader-utils": "1.1.0",
+        "lodash.camelcase": "4.3.0",
+        "object-assign": "4.1.1",
+        "postcss": "5.2.18",
+        "postcss-modules-extract-imports": "1.2.0",
+        "postcss-modules-local-by-default": "1.2.0",
+        "postcss-modules-scope": "1.1.0",
+        "postcss-modules-values": "1.3.0",
+        "postcss-value-parser": "3.3.0",
+        "source-list-map": "2.0.0"
+      }
+    },
     "css-parse": {
       "version": "1.7.0",
       "resolved": "https://registry.npmjs.org/css-parse/-/css-parse-1.7.0.tgz",
@@ -2168,11 +2139,55 @@
       "integrity": "sha1-yBSQPkViM3GgR3tAEJqq++6t27Q=",
       "dev": true
     },
-    "cuint": {
-      "version": "0.2.2",
-      "resolved": "https://registry.npmjs.org/cuint/-/cuint-0.2.2.tgz",
-      "integrity": "sha1-QICG1AlVDCYxFVYZ6fp7ytw7mRs=",
-      "dev": true
+    "cssnano": {
+      "version": "3.10.0",
+      "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-3.10.0.tgz",
+      "integrity": "sha1-Tzj2zqK5sX+gFJDyPx3GjqZcHDg=",
+      "dev": true,
+      "requires": {
+        "autoprefixer": "6.7.7",
+        "decamelize": "1.2.0",
+        "defined": "1.0.0",
+        "has": "1.0.1",
+        "object-assign": "4.1.1",
+        "postcss": "5.2.18",
+        "postcss-calc": "5.3.1",
+        "postcss-colormin": "2.2.2",
+        "postcss-convert-values": "2.6.1",
+        "postcss-discard-comments": "2.0.4",
+        "postcss-discard-duplicates": "2.1.0",
+        "postcss-discard-empty": "2.1.0",
+        "postcss-discard-overridden": "0.1.1",
+        "postcss-discard-unused": "2.2.3",
+        "postcss-filter-plugins": "2.0.3",
+        "postcss-merge-idents": "2.1.7",
+        "postcss-merge-longhand": "2.0.2",
+        "postcss-merge-rules": "2.1.2",
+        "postcss-minify-font-values": "1.0.5",
+        "postcss-minify-gradients": "1.0.5",
+        "postcss-minify-params": "1.2.2",
+        "postcss-minify-selectors": "2.1.1",
+        "postcss-normalize-charset": "1.1.1",
+        "postcss-normalize-url": "3.0.8",
+        "postcss-ordered-values": "2.2.3",
+        "postcss-reduce-idents": "2.4.0",
+        "postcss-reduce-initial": "1.0.1",
+        "postcss-reduce-transforms": "1.0.4",
+        "postcss-svgo": "2.1.6",
+        "postcss-unique-selectors": "2.0.2",
+        "postcss-value-parser": "3.3.0",
+        "postcss-zindex": "2.2.0"
+      }
+    },
+    "csso": {
+      "version": "2.3.2",
+      "resolved": "https://registry.npmjs.org/csso/-/csso-2.3.2.tgz",
+      "integrity": "sha1-3dUsWHAz9J6Utx/FVWnyUuj/X4U=",
+      "dev": true,
+      "requires": {
+        "clap": "1.2.3",
+        "source-map": "0.5.7"
+      }
     },
     "currently-unhandled": {
       "version": "0.4.1",
@@ -2201,7 +2216,7 @@
       "integrity": "sha1-dUu1v+VUUdpppYuU1F9MWwRi1Y8=",
       "dev": true,
       "requires": {
-        "es5-ext": "0.10.45"
+        "es5-ext": "0.10.42"
       }
     },
     "dashdash": {
@@ -2221,19 +2236,6 @@
         }
       }
     },
-    "data-uri-to-buffer": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-1.2.0.tgz",
-      "integrity": "sha512-vKQ9DTQPN1FLYiiEEOQ6IBGFqvjCa5rSK3cWMy/Nespm5d/x3dGFT9UBZnkLxCwua/IXBi2TYnwTEpsOvhC4UQ==",
-      "dev": true,
-      "optional": true
-    },
-    "date-format": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/date-format/-/date-format-1.2.0.tgz",
-      "integrity": "sha1-YV6CjiM90aubua4JUODOzPpuytg=",
-      "dev": true
-    },
     "date-now": {
       "version": "0.1.4",
       "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz",
@@ -2249,11 +2251,16 @@
         "ms": "2.0.0"
       }
     },
+    "debuglog": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/debuglog/-/debuglog-1.0.1.tgz",
+      "integrity": "sha1-qiT/uaw9+aI1GDfPstJ5NgzXhJI=",
+      "dev": true
+    },
     "decamelize": {
       "version": "1.2.0",
       "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
-      "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=",
-      "dev": true
+      "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA="
     },
     "decode-uri-component": {
       "version": "0.2.0",
@@ -2267,38 +2274,13 @@
       "integrity": "sha1-9dJgKStmDghO/0zbyfCK0yR0SLU=",
       "dev": true
     },
-    "deep-is": {
-      "version": "0.1.3",
-      "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz",
-      "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=",
-      "dev": true,
-      "optional": true
-    },
     "default-require-extensions": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-2.0.0.tgz",
-      "integrity": "sha1-9fj7sYp9bVCyH2QfZJ67Uiz+JPc=",
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-1.0.0.tgz",
+      "integrity": "sha1-836hXT4T/9m0N9M+GnW1+5eHTLg=",
       "dev": true,
       "requires": {
-        "strip-bom": "3.0.0"
-      },
-      "dependencies": {
-        "strip-bom": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
-          "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=",
-          "dev": true
-        }
-      }
-    },
-    "define-properties": {
-      "version": "1.1.2",
-      "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.2.tgz",
-      "integrity": "sha1-g6c/L+pWmJj7c3GTyPhzyvbUXJQ=",
-      "dev": true,
-      "requires": {
-        "foreach": "2.0.5",
-        "object-keys": "1.0.12"
+        "strip-bom": "2.0.0"
       }
     },
     "define-property": {
@@ -2339,41 +2321,14 @@
             "is-data-descriptor": "1.0.0",
             "kind-of": "6.0.2"
           }
-        },
-        "isobject": {
-          "version": "3.0.1",
-          "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
-          "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
-          "dev": true
-        },
-        "kind-of": {
-          "version": "6.0.2",
-          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
-          "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==",
-          "dev": true
         }
       }
     },
-    "degenerator": {
-      "version": "1.0.4",
-      "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-1.0.4.tgz",
-      "integrity": "sha1-/PSQo37OJmRk2cxDGrmMWBnO0JU=",
-      "dev": true,
-      "optional": true,
-      "requires": {
-        "ast-types": "0.11.5",
-        "escodegen": "1.10.0",
-        "esprima": "3.1.3"
-      },
-      "dependencies": {
-        "esprima": {
-          "version": "3.1.3",
-          "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz",
-          "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=",
-          "dev": true,
-          "optional": true
-        }
-      }
+    "defined": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz",
+      "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=",
+      "dev": true
     },
     "del": {
       "version": "3.0.0",
@@ -2409,6 +2364,12 @@
               "dev": true
             }
           }
+        },
+        "pify": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
+          "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=",
+          "dev": true
         }
       }
     },
@@ -2467,6 +2428,16 @@
       "integrity": "sha1-ogM8CcyOFY03dI+951B4Mr1s4Sc=",
       "dev": true
     },
+    "dezalgo": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.3.tgz",
+      "integrity": "sha1-f3Qt4Gb8dIvI24IFad3c5Jvw1FY=",
+      "dev": true,
+      "requires": {
+        "asap": "2.0.6",
+        "wrappy": "1.0.2"
+      }
+    },
     "di": {
       "version": "0.0.1",
       "resolved": "https://registry.npmjs.org/di/-/di-0.0.1.tgz",
@@ -2498,6 +2469,57 @@
       "requires": {
         "arrify": "1.0.1",
         "path-type": "3.0.0"
+      },
+      "dependencies": {
+        "path-type": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz",
+          "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==",
+          "dev": true,
+          "requires": {
+            "pify": "3.0.0"
+          }
+        },
+        "pify": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
+          "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=",
+          "dev": true
+        }
+      }
+    },
+    "directory-encoder": {
+      "version": "0.7.2",
+      "resolved": "https://registry.npmjs.org/directory-encoder/-/directory-encoder-0.7.2.tgz",
+      "integrity": "sha1-WbTiqk8lQi9sY7UntGL14tDdLFg=",
+      "dev": true,
+      "requires": {
+        "fs-extra": "0.23.1",
+        "handlebars": "1.3.0",
+        "img-stats": "0.5.2"
+      },
+      "dependencies": {
+        "fs-extra": {
+          "version": "0.23.1",
+          "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.23.1.tgz",
+          "integrity": "sha1-ZhHbpq3yq43Jxp+rN83fiBgVfj0=",
+          "dev": true,
+          "requires": {
+            "graceful-fs": "4.1.11",
+            "jsonfile": "2.4.0",
+            "path-is-absolute": "1.0.1",
+            "rimraf": "2.6.2"
+          }
+        },
+        "jsonfile": {
+          "version": "2.4.0",
+          "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz",
+          "integrity": "sha1-NzaitCi4e72gzIO1P6PWM6NcKug=",
+          "dev": true,
+          "requires": {
+            "graceful-fs": "4.1.11"
+          }
+        }
       }
     },
     "dns-equal": {
@@ -2603,12 +2625,11 @@
         "domelementtype": "1.3.0"
       }
     },
-    "double-ended-queue": {
-      "version": "2.1.0-0",
-      "resolved": "https://registry.npmjs.org/double-ended-queue/-/double-ended-queue-2.1.0-0.tgz",
-      "integrity": "sha1-ED01J/0xUo9AGIEwyEHv3XgmTlw=",
-      "dev": true,
-      "optional": true
+    "duplexer": {
+      "version": "0.1.1",
+      "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz",
+      "integrity": "sha1-rOb/gIwc5mtX0ev5eXessCM0z8E=",
+      "dev": true
     },
     "duplexify": {
       "version": "3.6.0",
@@ -2645,9 +2666,9 @@
       "dev": true
     },
     "electron-to-chromium": {
-      "version": "1.3.49",
-      "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.49.tgz",
-      "integrity": "sha1-ZROEsNgfB4qWY5srNpdRQbeRUAQ=",
+      "version": "1.3.48",
+      "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.48.tgz",
+      "integrity": "sha1-07DYWTgUBE4JLs4hCPw6ya6kuQA=",
       "dev": true
     },
     "elliptic": {
@@ -2658,7 +2679,7 @@
       "requires": {
         "bn.js": "4.11.8",
         "brorand": "1.1.0",
-        "hash.js": "1.1.4",
+        "hash.js": "1.1.3",
         "hmac-drbg": "1.0.1",
         "inherits": "2.0.3",
         "minimalistic-assert": "1.0.1",
@@ -2693,72 +2714,95 @@
       }
     },
     "engine.io": {
-      "version": "3.1.5",
-      "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.1.5.tgz",
-      "integrity": "sha512-D06ivJkYxyRrcEe0bTpNnBQNgP9d3xog+qZlLbui8EsMr/DouQpf5o9FzJnWYHEYE0YsFHllUv2R1dkgYZXHcA==",
+      "version": "1.8.2",
+      "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-1.8.2.tgz",
+      "integrity": "sha1-a1m+cws0jAElsKRYneHDVavPen4=",
       "dev": true,
       "requires": {
-        "accepts": "1.3.5",
+        "accepts": "1.3.3",
         "base64id": "1.0.0",
         "cookie": "0.3.1",
-        "debug": "3.1.0",
-        "engine.io-parser": "2.1.2",
-        "uws": "9.14.0",
-        "ws": "3.3.3"
+        "debug": "2.3.3",
+        "engine.io-parser": "1.3.2",
+        "ws": "1.1.1"
       },
       "dependencies": {
-        "debug": {
-          "version": "3.1.0",
-          "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
-          "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
+        "accepts": {
+          "version": "1.3.3",
+          "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.3.tgz",
+          "integrity": "sha1-w8p0NJOGSMPg2cHjKN1otiLChMo=",
           "dev": true,
           "requires": {
-            "ms": "2.0.0"
+            "mime-types": "2.1.18",
+            "negotiator": "0.6.1"
           }
+        },
+        "debug": {
+          "version": "2.3.3",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-2.3.3.tgz",
+          "integrity": "sha1-QMRT5n5uE8kB3ewxeviYbNqe/4w=",
+          "dev": true,
+          "requires": {
+            "ms": "0.7.2"
+          }
+        },
+        "ms": {
+          "version": "0.7.2",
+          "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz",
+          "integrity": "sha1-riXPJRKziFodldfwN4aNhDESR2U=",
+          "dev": true
         }
       }
     },
     "engine.io-client": {
-      "version": "3.1.6",
-      "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.1.6.tgz",
-      "integrity": "sha512-hnuHsFluXnsKOndS4Hv6SvUrgdYx1pk2NqfaDMW+GWdgfU3+/V25Cj7I8a0x92idSpa5PIhJRKxPvp9mnoLsfg==",
+      "version": "1.8.2",
+      "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-1.8.2.tgz",
+      "integrity": "sha1-w4dnVH8qfRhPV1L28K1QEAZwN2Y=",
       "dev": true,
       "requires": {
         "component-emitter": "1.2.1",
         "component-inherit": "0.0.3",
-        "debug": "3.1.0",
-        "engine.io-parser": "2.1.2",
+        "debug": "2.3.3",
+        "engine.io-parser": "1.3.2",
         "has-cors": "1.1.0",
         "indexof": "0.0.1",
+        "parsejson": "0.0.3",
         "parseqs": "0.0.5",
         "parseuri": "0.0.5",
-        "ws": "3.3.3",
-        "xmlhttprequest-ssl": "1.5.5",
+        "ws": "1.1.1",
+        "xmlhttprequest-ssl": "1.5.3",
         "yeast": "0.1.2"
       },
       "dependencies": {
         "debug": {
-          "version": "3.1.0",
-          "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
-          "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
+          "version": "2.3.3",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-2.3.3.tgz",
+          "integrity": "sha1-QMRT5n5uE8kB3ewxeviYbNqe/4w=",
           "dev": true,
           "requires": {
-            "ms": "2.0.0"
+            "ms": "0.7.2"
           }
+        },
+        "ms": {
+          "version": "0.7.2",
+          "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz",
+          "integrity": "sha1-riXPJRKziFodldfwN4aNhDESR2U=",
+          "dev": true
         }
       }
     },
     "engine.io-parser": {
-      "version": "2.1.2",
-      "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.1.2.tgz",
-      "integrity": "sha512-dInLFzr80RijZ1rGpx1+56/uFoH7/7InhH3kZt+Ms6hT8tNx3NGW/WNSA/f8As1WkOfkuyb3tnRyuXGxusclMw==",
+      "version": "1.3.2",
+      "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-1.3.2.tgz",
+      "integrity": "sha1-k3sHnwAH0Ik+xW1GyyILjLQ1Igo=",
       "dev": true,
       "requires": {
         "after": "0.8.2",
-        "arraybuffer.slice": "0.0.7",
+        "arraybuffer.slice": "0.0.6",
         "base64-arraybuffer": "0.1.5",
         "blob": "0.0.4",
-        "has-binary2": "1.0.3"
+        "has-binary": "0.1.7",
+        "wtf-8": "1.0.0"
       }
     },
     "enhanced-resolve": {
@@ -2795,42 +2839,17 @@
       }
     },
     "error-ex": {
-      "version": "1.3.2",
-      "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
-      "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
-      "dev": true,
+      "version": "1.3.1",
+      "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.1.tgz",
+      "integrity": "sha1-+FWobOYa3E6GIcPNoh56dhLDqNw=",
       "requires": {
         "is-arrayish": "0.2.1"
       }
     },
-    "es-abstract": {
-      "version": "1.12.0",
-      "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.12.0.tgz",
-      "integrity": "sha512-C8Fx/0jFmV5IPoMOFPA9P9G5NtqW+4cOPit3MIuvR2t7Ag2K15EJTpxnHAYTzL+aYQJIESYeXZmDBfOBE1HcpA==",
-      "dev": true,
-      "requires": {
-        "es-to-primitive": "1.1.1",
-        "function-bind": "1.1.1",
-        "has": "1.0.3",
-        "is-callable": "1.1.3",
-        "is-regex": "1.0.4"
-      }
-    },
-    "es-to-primitive": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.1.1.tgz",
-      "integrity": "sha1-RTVSSKiJeQNLZ5Lhm7gfK3l13Q0=",
-      "dev": true,
-      "requires": {
-        "is-callable": "1.1.3",
-        "is-date-object": "1.0.1",
-        "is-symbol": "1.0.1"
-      }
-    },
     "es5-ext": {
-      "version": "0.10.45",
-      "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.45.tgz",
-      "integrity": "sha512-FkfM6Vxxfmztilbxxz5UKSD4ICMf5tSpRFtDNtkAhOxZ0EKtX6qwmXNyH/sFyIbX2P/nU5AMiA9jilWsUGJzCQ==",
+      "version": "0.10.42",
+      "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.42.tgz",
+      "integrity": "sha512-AJxO1rmPe1bDEfSR6TJ/FgMFYuTBhR5R57KW58iCkYACMyFbrkqVyzXSurYoScDGvgyMpk7uRF/lPUPPTmsRSA==",
       "dev": true,
       "requires": {
         "es6-iterator": "2.0.3",
@@ -2845,7 +2864,7 @@
       "dev": true,
       "requires": {
         "d": "1.0.0",
-        "es5-ext": "0.10.45",
+        "es5-ext": "0.10.42",
         "es6-symbol": "3.1.1"
       }
     },
@@ -2856,28 +2875,13 @@
       "dev": true,
       "requires": {
         "d": "1.0.0",
-        "es5-ext": "0.10.45",
+        "es5-ext": "0.10.42",
         "es6-iterator": "2.0.3",
         "es6-set": "0.1.5",
         "es6-symbol": "3.1.1",
         "event-emitter": "0.3.5"
       }
     },
-    "es6-promise": {
-      "version": "4.2.4",
-      "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.4.tgz",
-      "integrity": "sha512-/NdNZVJg+uZgtm9eS3O6lrOLYmQag2DjdEXuPaHlZ6RuVqgqaVZfgYCepEIKsLqwdQArOPtC3XzRLqGGfT8KQQ==",
-      "dev": true
-    },
-    "es6-promisify": {
-      "version": "5.0.0",
-      "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz",
-      "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=",
-      "dev": true,
-      "requires": {
-        "es6-promise": "4.2.4"
-      }
-    },
     "es6-set": {
       "version": "0.1.5",
       "resolved": "https://registry.npmjs.org/es6-set/-/es6-set-0.1.5.tgz",
@@ -2885,7 +2889,7 @@
       "dev": true,
       "requires": {
         "d": "1.0.0",
-        "es5-ext": "0.10.45",
+        "es5-ext": "0.10.42",
         "es6-iterator": "2.0.3",
         "es6-symbol": "3.1.1",
         "event-emitter": "0.3.5"
@@ -2898,7 +2902,7 @@
       "dev": true,
       "requires": {
         "d": "1.0.0",
-        "es5-ext": "0.10.45"
+        "es5-ext": "0.10.42"
       }
     },
     "es6-weak-map": {
@@ -2908,7 +2912,7 @@
       "dev": true,
       "requires": {
         "d": "1.0.0",
-        "es5-ext": "0.10.45",
+        "es5-ext": "0.10.42",
         "es6-iterator": "2.0.3",
         "es6-symbol": "3.1.1"
       }
@@ -2925,36 +2929,6 @@
       "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
       "dev": true
     },
-    "escodegen": {
-      "version": "1.10.0",
-      "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.10.0.tgz",
-      "integrity": "sha512-fjUOf8johsv23WuIKdNQU4P9t9jhQ4Qzx6pC2uW890OloK3Zs1ZAoCNpg/2larNF501jLl3UNy0kIRcF6VI22g==",
-      "dev": true,
-      "optional": true,
-      "requires": {
-        "esprima": "3.1.3",
-        "estraverse": "4.2.0",
-        "esutils": "2.0.2",
-        "optionator": "0.8.2",
-        "source-map": "0.6.1"
-      },
-      "dependencies": {
-        "esprima": {
-          "version": "3.1.3",
-          "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz",
-          "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=",
-          "dev": true,
-          "optional": true
-        },
-        "source-map": {
-          "version": "0.6.1",
-          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
-          "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
-          "dev": true,
-          "optional": true
-        }
-      }
-    },
     "escope": {
       "version": "3.6.0",
       "resolved": "https://registry.npmjs.org/escope/-/escope-3.6.0.tgz",
@@ -2968,9 +2942,9 @@
       }
     },
     "esprima": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.0.tgz",
-      "integrity": "sha512-oftTcaMu/EGrEIu904mWteKIv8vMuOgGYo7EhVJJN00R/EED9DCua/xxHRdYnKtcECzVg7xOWhflvJMnqcFZjw==",
+      "version": "2.7.3",
+      "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz",
+      "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=",
       "dev": true
     },
     "esrecurse": {
@@ -3007,7 +2981,7 @@
       "dev": true,
       "requires": {
         "d": "1.0.0",
-        "es5-ext": "0.10.45"
+        "es5-ext": "0.10.42"
       }
     },
     "eventemitter3": {
@@ -3064,7 +3038,7 @@
           "requires": {
             "lru-cache": "4.1.3",
             "shebang-command": "1.2.0",
-            "which": "1.3.1"
+            "which": "1.3.0"
           }
         }
       }
@@ -3086,6 +3060,12 @@
         "braces": "0.1.5"
       },
       "dependencies": {
+        "array-unique": {
+          "version": "0.2.1",
+          "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz",
+          "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=",
+          "dev": true
+        },
         "braces": {
           "version": "0.1.5",
           "resolved": "https://registry.npmjs.org/braces/-/braces-0.1.5.tgz",
@@ -3120,12 +3100,38 @@
       }
     },
     "expand-brackets": {
-      "version": "0.1.5",
-      "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz",
-      "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=",
+      "version": "2.1.4",
+      "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz",
+      "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=",
       "dev": true,
       "requires": {
-        "is-posix-bracket": "0.1.1"
+        "debug": "2.6.9",
+        "define-property": "0.2.5",
+        "extend-shallow": "2.0.1",
+        "posix-character-classes": "0.1.1",
+        "regex-not": "1.0.2",
+        "snapdragon": "0.8.2",
+        "to-regex": "3.0.2"
+      },
+      "dependencies": {
+        "define-property": {
+          "version": "0.2.5",
+          "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+          "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+          "dev": true,
+          "requires": {
+            "is-descriptor": "0.1.6"
+          }
+        },
+        "extend-shallow": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+          "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+          "dev": true,
+          "requires": {
+            "is-extendable": "0.1.1"
+          }
+        }
       }
     },
     "expand-range": {
@@ -3135,6 +3141,58 @@
       "dev": true,
       "requires": {
         "fill-range": "2.2.4"
+      },
+      "dependencies": {
+        "fill-range": {
+          "version": "2.2.4",
+          "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.4.tgz",
+          "integrity": "sha512-cnrcCbj01+j2gTG921VZPnHbjmdAf8oQV/iGeV2kZxGSyfYjjTyY79ErsK1WJWMpw6DaApEX72binqJE+/d+5Q==",
+          "dev": true,
+          "requires": {
+            "is-number": "2.1.0",
+            "isobject": "2.1.0",
+            "randomatic": "3.0.0",
+            "repeat-element": "1.1.2",
+            "repeat-string": "1.6.1"
+          }
+        },
+        "is-number": {
+          "version": "2.1.0",
+          "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz",
+          "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=",
+          "dev": true,
+          "requires": {
+            "kind-of": "3.2.2"
+          }
+        },
+        "isobject": {
+          "version": "2.1.0",
+          "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz",
+          "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=",
+          "dev": true,
+          "requires": {
+            "isarray": "1.0.0"
+          }
+        },
+        "kind-of": {
+          "version": "3.2.2",
+          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+          "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+          "dev": true,
+          "requires": {
+            "is-buffer": "1.1.6"
+          }
+        }
+      }
+    },
+    "exports-loader": {
+      "version": "0.6.4",
+      "resolved": "https://registry.npmjs.org/exports-loader/-/exports-loader-0.6.4.tgz",
+      "integrity": "sha1-1w/GEhl1s1/BKDDPUnVL4nQPyIY=",
+      "dev": true,
+      "requires": {
+        "loader-utils": "1.1.0",
+        "source-map": "0.5.7"
       }
     },
     "express": {
@@ -3223,46 +3281,145 @@
       }
     },
     "extglob": {
-      "version": "0.3.2",
-      "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz",
-      "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=",
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz",
+      "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==",
       "dev": true,
       "requires": {
-        "is-extglob": "1.0.0"
+        "array-unique": "0.3.2",
+        "define-property": "1.0.0",
+        "expand-brackets": "2.1.4",
+        "extend-shallow": "2.0.1",
+        "fragment-cache": "0.2.1",
+        "regex-not": "1.0.2",
+        "snapdragon": "0.8.2",
+        "to-regex": "3.0.2"
+      },
+      "dependencies": {
+        "define-property": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
+          "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
+          "dev": true,
+          "requires": {
+            "is-descriptor": "1.0.2"
+          }
+        },
+        "extend-shallow": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+          "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+          "dev": true,
+          "requires": {
+            "is-extendable": "0.1.1"
+          }
+        },
+        "is-accessor-descriptor": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
+          "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
+          "dev": true,
+          "requires": {
+            "kind-of": "6.0.2"
+          }
+        },
+        "is-data-descriptor": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
+          "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
+          "dev": true,
+          "requires": {
+            "kind-of": "6.0.2"
+          }
+        },
+        "is-descriptor": {
+          "version": "1.0.2",
+          "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
+          "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
+          "dev": true,
+          "requires": {
+            "is-accessor-descriptor": "1.0.0",
+            "is-data-descriptor": "1.0.0",
+            "kind-of": "6.0.2"
+          }
+        }
       }
     },
     "extract-text-webpack-plugin": {
-      "version": "3.0.2",
-      "resolved": "https://registry.npmjs.org/extract-text-webpack-plugin/-/extract-text-webpack-plugin-3.0.2.tgz",
-      "integrity": "sha512-bt/LZ4m5Rqt/Crl2HiKuAl/oqg0psx1tsTLkvWbJen1CtD+fftkZhMaQ9HOtY2gWsl2Wq+sABmMVi9z3DhKWQQ==",
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/extract-text-webpack-plugin/-/extract-text-webpack-plugin-3.0.0.tgz",
+      "integrity": "sha1-kMqnkHvESfM1AF46x1MrQbAN5hI=",
       "dev": true,
       "requires": {
         "async": "2.6.1",
         "loader-utils": "1.1.0",
         "schema-utils": "0.3.0",
         "webpack-sources": "1.1.0"
+      }
+    },
+    "extract-zip": {
+      "version": "1.5.0",
+      "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-1.5.0.tgz",
+      "integrity": "sha1-ksz22B73Cp+kwXRxFMzvbYaIpsQ=",
+      "dev": true,
+      "requires": {
+        "concat-stream": "1.5.0",
+        "debug": "0.7.4",
+        "mkdirp": "0.5.0",
+        "yauzl": "2.4.1"
       },
       "dependencies": {
-        "ajv": {
-          "version": "5.5.2",
-          "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz",
-          "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=",
+        "concat-stream": {
+          "version": "1.5.0",
+          "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.5.0.tgz",
+          "integrity": "sha1-U/fUPFHF5D+ByP3QMyHGMb5o1hE=",
           "dev": true,
           "requires": {
-            "co": "4.6.0",
-            "fast-deep-equal": "1.1.0",
-            "fast-json-stable-stringify": "2.0.0",
-            "json-schema-traverse": "0.3.1"
+            "inherits": "2.0.3",
+            "readable-stream": "2.0.6",
+            "typedarray": "0.0.6"
           }
         },
-        "schema-utils": {
-          "version": "0.3.0",
-          "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.3.0.tgz",
-          "integrity": "sha1-9YdyIs4+kx7a4DnxfrNxbnE3+M8=",
+        "debug": {
+          "version": "0.7.4",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-0.7.4.tgz",
+          "integrity": "sha1-BuHqgILCyxTjmAbiLi9vdX+Srzk=",
+          "dev": true
+        },
+        "mkdirp": {
+          "version": "0.5.0",
+          "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.0.tgz",
+          "integrity": "sha1-HXMHam35hs2TROFecfzAWkyavxI=",
           "dev": true,
           "requires": {
-            "ajv": "5.5.2"
+            "minimist": "0.0.8"
           }
+        },
+        "process-nextick-args": {
+          "version": "1.0.7",
+          "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz",
+          "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=",
+          "dev": true
+        },
+        "readable-stream": {
+          "version": "2.0.6",
+          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz",
+          "integrity": "sha1-j5A0HmilPMySh4jaz80Rs265t44=",
+          "dev": true,
+          "requires": {
+            "core-util-is": "1.0.2",
+            "inherits": "2.0.3",
+            "isarray": "1.0.0",
+            "process-nextick-args": "1.0.7",
+            "string_decoder": "0.10.31",
+            "util-deprecate": "1.0.2"
+          }
+        },
+        "string_decoder": {
+          "version": "0.10.31",
+          "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
+          "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=",
+          "dev": true
         }
       }
     },
@@ -3284,13 +3441,6 @@
       "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=",
       "dev": true
     },
-    "fast-levenshtein": {
-      "version": "2.0.6",
-      "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
-      "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=",
-      "dev": true,
-      "optional": true
-    },
     "fastparse": {
       "version": "1.1.1",
       "resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.1.tgz",
@@ -3306,22 +3456,23 @@
         "websocket-driver": "0.7.0"
       }
     },
-    "file-loader": {
-      "version": "1.1.11",
-      "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-1.1.11.tgz",
-      "integrity": "sha512-TGR4HU7HUsGg6GCOPJnFk06RhWgEWFLAGWiT6rcD+GRC2keU3s9RGJ+b3Z6/U73jwwNb2gKLJ7YCrp+jvU4ALg==",
+    "fd-slicer": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.0.1.tgz",
+      "integrity": "sha1-i1vL2ewyfFBBv5qwI/1nUPEXfmU=",
       "dev": true,
       "requires": {
-        "loader-utils": "1.1.0",
-        "schema-utils": "0.4.5"
+        "pend": "1.2.0"
       }
     },
-    "file-uri-to-path": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
-      "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==",
+    "file-loader": {
+      "version": "0.10.1",
+      "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-0.10.1.tgz",
+      "integrity": "sha1-gVA0EZiR/GRB+1pkwRvJPCLd2EI=",
       "dev": true,
-      "optional": true
+      "requires": {
+        "loader-utils": "1.1.0"
+      }
     },
     "filename-regex": {
       "version": "2.0.1",
@@ -3339,17 +3490,33 @@
         "minimatch": "3.0.4"
       }
     },
+    "filesize": {
+      "version": "3.6.1",
+      "resolved": "https://registry.npmjs.org/filesize/-/filesize-3.6.1.tgz",
+      "integrity": "sha512-7KjR1vv6qnicaPMi1iiTcI85CyYwRO/PSFCu6SvqL8jN2Wjt/NIYQTFtFs7fSDCYOstUkEWIQGFUg5YZQfjlcg==",
+      "dev": true
+    },
     "fill-range": {
-      "version": "2.2.4",
-      "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.4.tgz",
-      "integrity": "sha512-cnrcCbj01+j2gTG921VZPnHbjmdAf8oQV/iGeV2kZxGSyfYjjTyY79ErsK1WJWMpw6DaApEX72binqJE+/d+5Q==",
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
+      "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=",
       "dev": true,
       "requires": {
-        "is-number": "2.1.0",
-        "isobject": "2.1.0",
-        "randomatic": "3.0.0",
-        "repeat-element": "1.1.2",
-        "repeat-string": "1.6.1"
+        "extend-shallow": "2.0.1",
+        "is-number": "3.0.0",
+        "repeat-string": "1.6.1",
+        "to-regex-range": "2.1.1"
+      },
+      "dependencies": {
+        "extend-shallow": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+          "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+          "dev": true,
+          "requires": {
+            "is-extendable": "0.1.1"
+          }
+        }
       }
     },
     "finalhandler": {
@@ -3379,14 +3546,44 @@
       }
     },
     "find-up": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz",
-      "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=",
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz",
+      "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=",
+      "requires": {
+        "path-exists": "2.1.0",
+        "pinkie-promise": "2.0.1"
+      }
+    },
+    "findup-sync": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-0.3.0.tgz",
+      "integrity": "sha1-N5MKpdgWt3fANEXhlmzGeQpMCxY=",
       "dev": true,
       "requires": {
-        "locate-path": "2.0.0"
+        "glob": "5.0.15"
+      },
+      "dependencies": {
+        "glob": {
+          "version": "5.0.15",
+          "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz",
+          "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=",
+          "dev": true,
+          "requires": {
+            "inflight": "1.0.6",
+            "inherits": "2.0.3",
+            "minimatch": "3.0.4",
+            "once": "1.4.0",
+            "path-is-absolute": "1.0.1"
+          }
+        }
       }
     },
+    "flatten": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/flatten/-/flatten-1.0.2.tgz",
+      "integrity": "sha1-2uRqnXj74lKSJYzB54CkHZXAN4I=",
+      "dev": true
+    },
     "flush-write-stream": {
       "version": "1.0.3",
       "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.0.3.tgz",
@@ -3424,20 +3621,14 @@
       "dev": true
     },
     "for-own": {
-      "version": "0.1.5",
-      "resolved": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz",
-      "integrity": "sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4=",
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz",
+      "integrity": "sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs=",
       "dev": true,
       "requires": {
         "for-in": "1.0.2"
       }
     },
-    "foreach": {
-      "version": "2.0.5",
-      "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz",
-      "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=",
-      "dev": true
-    },
     "forever-agent": {
       "version": "0.6.1",
       "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
@@ -3503,7 +3694,7 @@
       "requires": {
         "graceful-fs": "4.1.11",
         "jsonfile": "4.0.0",
-        "universalify": "0.1.2"
+        "universalify": "0.1.1"
       }
     },
     "fs-write-stream-atomic": {
@@ -4065,46 +4256,6 @@
         "rimraf": "2.6.2"
       }
     },
-    "ftp": {
-      "version": "0.3.10",
-      "resolved": "https://registry.npmjs.org/ftp/-/ftp-0.3.10.tgz",
-      "integrity": "sha1-kZfYYa2BQvPmPVqDv+TFn3MwiF0=",
-      "dev": true,
-      "optional": true,
-      "requires": {
-        "readable-stream": "1.1.14",
-        "xregexp": "2.0.0"
-      },
-      "dependencies": {
-        "isarray": {
-          "version": "0.0.1",
-          "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
-          "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=",
-          "dev": true,
-          "optional": true
-        },
-        "readable-stream": {
-          "version": "1.1.14",
-          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz",
-          "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=",
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "core-util-is": "1.0.2",
-            "inherits": "2.0.3",
-            "isarray": "0.0.1",
-            "string_decoder": "0.10.31"
-          }
-        },
-        "string_decoder": {
-          "version": "0.10.31",
-          "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
-          "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=",
-          "dev": true,
-          "optional": true
-        }
-      }
-    },
     "function-bind": {
       "version": "1.1.1",
       "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
@@ -4124,7 +4275,7 @@
         "signal-exit": "3.0.2",
         "string-width": "1.0.2",
         "strip-ansi": "3.0.1",
-        "wide-align": "1.1.3"
+        "wide-align": "1.1.2"
       }
     },
     "gaze": {
@@ -4134,22 +4285,20 @@
       "dev": true,
       "optional": true,
       "requires": {
-        "globule": "1.2.1"
+        "globule": "1.2.0"
       }
     },
     "generate-function": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.0.0.tgz",
       "integrity": "sha1-aFj+fAlpt9TpCTM3ZHrHn2DfvnQ=",
-      "dev": true,
-      "optional": true
+      "dev": true
     },
     "generate-object-property": {
       "version": "1.2.0",
       "resolved": "https://registry.npmjs.org/generate-object-property/-/generate-object-property-1.2.0.tgz",
       "integrity": "sha1-nA4cQDCM6AT0eDYYuTf6iPmdUNA=",
       "dev": true,
-      "optional": true,
       "requires": {
         "is-property": "1.0.2"
       }
@@ -4157,8 +4306,7 @@
     "get-caller-file": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.2.tgz",
-      "integrity": "sha1-9wLmMSfn4jHBYKgMFVSstw1QR+U=",
-      "dev": true
+      "integrity": "sha1-9wLmMSfn4jHBYKgMFVSstw1QR+U="
     },
     "get-stdin": {
       "version": "4.0.1",
@@ -4172,21 +4320,6 @@
       "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=",
       "dev": true
     },
-    "get-uri": {
-      "version": "2.0.2",
-      "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-2.0.2.tgz",
-      "integrity": "sha512-ZD325dMZOgerGqF/rF6vZXyFGTAay62svjQIT+X/oU2PtxYpFxvSkbsdi+oxIrsNxlZVd4y8wUDqkaExWTI/Cw==",
-      "dev": true,
-      "optional": true,
-      "requires": {
-        "data-uri-to-buffer": "1.2.0",
-        "debug": "2.6.9",
-        "extend": "3.0.1",
-        "file-uri-to-path": "1.0.0",
-        "ftp": "0.3.10",
-        "readable-stream": "2.3.6"
-      }
-    },
     "get-value": {
       "version": "2.0.6",
       "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz",
@@ -4232,15 +4365,53 @@
       "requires": {
         "glob-parent": "2.0.0",
         "is-glob": "2.0.1"
+      },
+      "dependencies": {
+        "glob-parent": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz",
+          "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=",
+          "dev": true,
+          "requires": {
+            "is-glob": "2.0.1"
+          }
+        },
+        "is-extglob": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz",
+          "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=",
+          "dev": true
+        },
+        "is-glob": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz",
+          "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=",
+          "dev": true,
+          "requires": {
+            "is-extglob": "1.0.0"
+          }
+        }
       }
     },
     "glob-parent": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz",
-      "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=",
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz",
+      "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=",
       "dev": true,
       "requires": {
-        "is-glob": "2.0.1"
+        "is-glob": "3.1.0",
+        "path-dirname": "1.0.2"
+      },
+      "dependencies": {
+        "is-glob": {
+          "version": "3.1.0",
+          "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz",
+          "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=",
+          "dev": true,
+          "requires": {
+            "is-extglob": "2.1.1"
+          }
+        }
       }
     },
     "globals": {
@@ -4258,15 +4429,23 @@
         "array-union": "1.0.2",
         "dir-glob": "2.0.0",
         "glob": "7.1.2",
-        "ignore": "3.3.10",
+        "ignore": "3.3.8",
         "pify": "3.0.0",
         "slash": "1.0.0"
+      },
+      "dependencies": {
+        "pify": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
+          "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=",
+          "dev": true
+        }
       }
     },
     "globule": {
-      "version": "1.2.1",
-      "resolved": "https://registry.npmjs.org/globule/-/globule-1.2.1.tgz",
-      "integrity": "sha512-g7QtgWF4uYSL5/dn71WxubOrS7JVGCnFPEnoeChJmBnyR9Mw8nGoEwOgJL/RC2Te0WhbsEUCejfH8SZNJ+adYQ==",
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/globule/-/globule-1.2.0.tgz",
+      "integrity": "sha1-HcScaCLdnoovoAuiopUAboZkvQk=",
       "dev": true,
       "optional": true,
       "requires": {
@@ -4278,8 +4457,25 @@
     "graceful-fs": {
       "version": "4.1.11",
       "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz",
-      "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=",
-      "dev": true
+      "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg="
+    },
+    "gzip-size": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-4.1.0.tgz",
+      "integrity": "sha1-iuCWJX6r59acRb4rZ8RIEk/7UXw=",
+      "dev": true,
+      "requires": {
+        "duplexer": "0.1.1",
+        "pify": "3.0.0"
+      },
+      "dependencies": {
+        "pify": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
+          "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=",
+          "dev": true
+        }
+      }
     },
     "hammerjs": {
       "version": "2.0.8",
@@ -4293,83 +4489,42 @@
       "dev": true
     },
     "handlebars": {
-      "version": "4.0.11",
-      "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.0.11.tgz",
-      "integrity": "sha1-Ywo13+ApS8KB7a5v/F0yn8eYLcw=",
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-1.3.0.tgz",
+      "integrity": "sha1-npsTCpPjiUkTItl1zz7BgYw3zjQ=",
       "dev": true,
       "requires": {
-        "async": "1.5.2",
-        "optimist": "0.6.1",
-        "source-map": "0.4.4",
-        "uglify-js": "2.8.29"
+        "optimist": "0.3.7",
+        "uglify-js": "2.3.6"
       },
       "dependencies": {
         "async": {
-          "version": "1.5.2",
-          "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz",
-          "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=",
-          "dev": true
-        },
-        "camelcase": {
-          "version": "1.2.1",
-          "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz",
-          "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=",
+          "version": "0.2.10",
+          "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz",
+          "integrity": "sha1-trvgsGdLnXGXCMo43owjfLUmw9E=",
           "dev": true,
           "optional": true
         },
-        "cliui": {
-          "version": "2.1.0",
-          "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz",
-          "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=",
+        "source-map": {
+          "version": "0.1.43",
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.43.tgz",
+          "integrity": "sha1-wkvBRspRfBRx9drL4lcbK3+eM0Y=",
           "dev": true,
           "optional": true,
           "requires": {
-            "center-align": "0.1.3",
-            "right-align": "0.1.3",
-            "wordwrap": "0.0.2"
-          }
-        },
-        "source-map": {
-          "version": "0.4.4",
-          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz",
-          "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=",
-          "dev": true,
-          "requires": {
             "amdefine": "1.0.1"
           }
         },
         "uglify-js": {
-          "version": "2.8.29",
-          "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz",
-          "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=",
+          "version": "2.3.6",
+          "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.3.6.tgz",
+          "integrity": "sha1-+gmEdwtCi3qbKoBY9GNV0U/vIRo=",
           "dev": true,
           "optional": true,
           "requires": {
-            "source-map": "0.5.7",
-            "uglify-to-browserify": "1.0.2",
-            "yargs": "3.10.0"
-          },
-          "dependencies": {
-            "source-map": {
-              "version": "0.5.7",
-              "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
-              "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
-              "dev": true,
-              "optional": true
-            }
-          }
-        },
-        "yargs": {
-          "version": "3.10.0",
-          "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz",
-          "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=",
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "camelcase": "1.2.1",
-            "cliui": "2.1.0",
-            "decamelize": "1.2.0",
-            "window-size": "0.1.0"
+            "async": "0.2.10",
+            "optimist": "0.3.7",
+            "source-map": "0.1.43"
           }
         }
       }
@@ -4403,9 +4558,9 @@
       }
     },
     "has": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
-      "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/has/-/has-1.0.1.tgz",
+      "integrity": "sha1-hGFzP1OLCDfJNh45qauelwTcLyg=",
       "dev": true,
       "requires": {
         "function-bind": "1.1.1"
@@ -4420,19 +4575,19 @@
         "ansi-regex": "2.1.1"
       }
     },
-    "has-binary2": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/has-binary2/-/has-binary2-1.0.3.tgz",
-      "integrity": "sha512-G1LWKhDSvhGeAQ8mPVQlqNcOB2sJdwATtZKl2pDKKHfpf/rYj24lkinxf69blJbnsvtqqNU+L3SL50vzZhXOnw==",
+    "has-binary": {
+      "version": "0.1.7",
+      "resolved": "https://registry.npmjs.org/has-binary/-/has-binary-0.1.7.tgz",
+      "integrity": "sha1-aOYesWIQyVRaClzOBqhzkS/h5ow=",
       "dev": true,
       "requires": {
-        "isarray": "2.0.1"
+        "isarray": "0.0.1"
       },
       "dependencies": {
         "isarray": {
-          "version": "2.0.1",
-          "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz",
-          "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=",
+          "version": "0.0.1",
+          "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
+          "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=",
           "dev": true
         }
       }
@@ -4444,9 +4599,9 @@
       "dev": true
     },
     "has-flag": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz",
-      "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=",
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz",
+      "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=",
       "dev": true
     },
     "has-unicode": {
@@ -4464,14 +4619,6 @@
         "get-value": "2.0.6",
         "has-values": "1.0.0",
         "isobject": "3.0.1"
-      },
-      "dependencies": {
-        "isobject": {
-          "version": "3.0.1",
-          "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
-          "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
-          "dev": true
-        }
       }
     },
     "has-values": {
@@ -4484,26 +4631,6 @@
         "kind-of": "4.0.0"
       },
       "dependencies": {
-        "is-number": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
-          "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=",
-          "dev": true,
-          "requires": {
-            "kind-of": "3.2.2"
-          },
-          "dependencies": {
-            "kind-of": {
-              "version": "3.2.2",
-              "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
-              "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
-              "dev": true,
-              "requires": {
-                "is-buffer": "1.1.6"
-              }
-            }
-          }
-        },
         "kind-of": {
           "version": "4.0.0",
           "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz",
@@ -4526,15 +4653,25 @@
       }
     },
     "hash.js": {
-      "version": "1.1.4",
-      "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.4.tgz",
-      "integrity": "sha512-A6RlQvvZEtFS5fLU43IDu0QUmBy+fDO9VMdTXvufKwIkt/rFfvICAViCax5fbDO4zdNzaC3/27ZhKUok5bAJyw==",
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.3.tgz",
+      "integrity": "sha512-/UETyP0W22QILqS+6HowevwhEFJ3MBJnwTf75Qob9Wz9t0DPuisL8kW8YZMK62dHAKE1c1p+gY1TtOLY+USEHA==",
       "dev": true,
       "requires": {
         "inherits": "2.0.3",
         "minimalistic-assert": "1.0.1"
       }
     },
+    "hasha": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/hasha/-/hasha-2.2.0.tgz",
+      "integrity": "sha1-eNfL/B5tZjA/55g3NlmEUXsvbuE=",
+      "dev": true,
+      "requires": {
+        "is-stream": "1.1.0",
+        "pinkie-promise": "2.0.1"
+      }
+    },
     "hawk": {
       "version": "3.1.3",
       "resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz",
@@ -4553,16 +4690,10 @@
       "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=",
       "dev": true
     },
-    "hipchat-notifier": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/hipchat-notifier/-/hipchat-notifier-1.1.0.tgz",
-      "integrity": "sha1-ttJJdVQ3wZEII2d5nTupoPI7Ix4=",
-      "dev": true,
-      "optional": true,
-      "requires": {
-        "lodash": "4.17.10",
-        "request": "2.81.0"
-      }
+    "highlight.js": {
+      "version": "9.10.0",
+      "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-9.10.0.tgz",
+      "integrity": "sha1-+fCxTAvgDw5PseV3t0n+2eb1L1U="
     },
     "hmac-drbg": {
       "version": "1.0.1",
@@ -4570,7 +4701,7 @@
       "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=",
       "dev": true,
       "requires": {
-        "hash.js": "1.1.4",
+        "hash.js": "1.1.3",
         "minimalistic-assert": "1.0.1",
         "minimalistic-crypto-utils": "1.0.1"
       }
@@ -4581,20 +4712,10 @@
       "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=",
       "dev": true
     },
-    "homedir-polyfill": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.1.tgz",
-      "integrity": "sha1-TCu8inWJmP7r9e1oWA921GdotLw=",
-      "dev": true,
-      "requires": {
-        "parse-passwd": "1.0.0"
-      }
-    },
     "hosted-git-info": {
       "version": "2.6.0",
       "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.6.0.tgz",
-      "integrity": "sha512-lIbgIIQA3lz5XaB6vxakj6sDHADJiZadYEJB+FgA+C4nubM1NwcuvUr9EJPmnH1skZqpqUzWborWo8EIUi0Sdw==",
-      "dev": true
+      "integrity": "sha512-lIbgIIQA3lz5XaB6vxakj6sDHADJiZadYEJB+FgA+C4nubM1NwcuvUr9EJPmnH1skZqpqUzWborWo8EIUi0Sdw=="
     },
     "hpack.js": {
       "version": "2.1.6",
@@ -4608,6 +4729,12 @@
         "wbuf": "1.7.3"
       }
     },
+    "html-comment-regex": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/html-comment-regex/-/html-comment-regex-1.1.1.tgz",
+      "integrity": "sha1-ZouTd26q5V696POtRkswekljYl4=",
+      "dev": true
+    },
     "html-entities": {
       "version": "1.2.1",
       "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-1.2.1.tgz",
@@ -4626,7 +4753,7 @@
         "he": "1.1.1",
         "param-case": "2.1.1",
         "relateurl": "0.2.7",
-        "uglify-js": "3.3.28"
+        "uglify-js": "3.3.27"
       }
     },
     "html-webpack-plugin": {
@@ -4739,27 +4866,6 @@
         "requires-port": "1.0.0"
       }
     },
-    "http-proxy-agent": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz",
-      "integrity": "sha512-qwHbBLV7WviBl0rQsOzH6o5lwyOIvwp/BdFnvVxXORldu5TmjFfjzBcWUWS5kWAZhmv+JtiDhSuQCp4sBfbIgg==",
-      "dev": true,
-      "requires": {
-        "agent-base": "4.2.0",
-        "debug": "3.1.0"
-      },
-      "dependencies": {
-        "debug": {
-          "version": "3.1.0",
-          "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
-          "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
-          "dev": true,
-          "requires": {
-            "ms": "2.0.0"
-          }
-        }
-      }
-    },
     "http-proxy-middleware": {
       "version": "0.17.4",
       "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.17.4.tgz",
@@ -4772,12 +4878,58 @@
         "micromatch": "2.3.11"
       },
       "dependencies": {
-        "is-extglob": {
-          "version": "2.1.1",
-          "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
-          "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
+        "arr-diff": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz",
+          "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=",
+          "dev": true,
+          "requires": {
+            "arr-flatten": "1.1.0"
+          }
+        },
+        "array-unique": {
+          "version": "0.2.1",
+          "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz",
+          "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=",
           "dev": true
         },
+        "braces": {
+          "version": "1.8.5",
+          "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz",
+          "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=",
+          "dev": true,
+          "requires": {
+            "expand-range": "1.8.2",
+            "preserve": "0.2.0",
+            "repeat-element": "1.1.2"
+          }
+        },
+        "expand-brackets": {
+          "version": "0.1.5",
+          "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz",
+          "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=",
+          "dev": true,
+          "requires": {
+            "is-posix-bracket": "0.1.1"
+          }
+        },
+        "extglob": {
+          "version": "0.3.2",
+          "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz",
+          "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=",
+          "dev": true,
+          "requires": {
+            "is-extglob": "1.0.0"
+          },
+          "dependencies": {
+            "is-extglob": {
+              "version": "1.0.0",
+              "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz",
+              "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=",
+              "dev": true
+            }
+          }
+        },
         "is-glob": {
           "version": "3.1.0",
           "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz",
@@ -4786,6 +4938,53 @@
           "requires": {
             "is-extglob": "2.1.1"
           }
+        },
+        "kind-of": {
+          "version": "3.2.2",
+          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+          "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+          "dev": true,
+          "requires": {
+            "is-buffer": "1.1.6"
+          }
+        },
+        "micromatch": {
+          "version": "2.3.11",
+          "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz",
+          "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=",
+          "dev": true,
+          "requires": {
+            "arr-diff": "2.0.0",
+            "array-unique": "0.2.1",
+            "braces": "1.8.5",
+            "expand-brackets": "0.1.5",
+            "extglob": "0.3.2",
+            "filename-regex": "2.0.1",
+            "is-extglob": "1.0.0",
+            "is-glob": "2.0.1",
+            "kind-of": "3.2.2",
+            "normalize-path": "2.1.1",
+            "object.omit": "2.0.1",
+            "parse-glob": "3.0.4",
+            "regex-cache": "0.4.4"
+          },
+          "dependencies": {
+            "is-extglob": {
+              "version": "1.0.0",
+              "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz",
+              "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=",
+              "dev": true
+            },
+            "is-glob": {
+              "version": "2.0.1",
+              "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz",
+              "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=",
+              "dev": true,
+              "requires": {
+                "is-extglob": "1.0.0"
+              }
+            }
+          }
         }
       }
     },
@@ -4797,25 +4996,9 @@
       "requires": {
         "assert-plus": "0.2.0",
         "jsprim": "1.4.1",
-        "sshpk": "1.14.2"
+        "sshpk": "1.14.1"
       }
     },
-    "httpntlm": {
-      "version": "1.6.1",
-      "resolved": "https://registry.npmjs.org/httpntlm/-/httpntlm-1.6.1.tgz",
-      "integrity": "sha1-rQFScUOi6Hc8+uapb1hla7UqNLI=",
-      "dev": true,
-      "requires": {
-        "httpreq": "0.4.24",
-        "underscore": "1.7.0"
-      }
-    },
-    "httpreq": {
-      "version": "0.4.24",
-      "resolved": "https://registry.npmjs.org/httpreq/-/httpreq-0.4.24.tgz",
-      "integrity": "sha1-QzX/2CzZaWaKOUZckprGHWOTYn8=",
-      "dev": true
-    },
     "https-browserify": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz",
@@ -4823,24 +5006,14 @@
       "dev": true
     },
     "https-proxy-agent": {
-      "version": "2.2.1",
-      "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.1.tgz",
-      "integrity": "sha512-HPCTS1LW51bcyMYbxUIOO4HEOlQ1/1qRaFWcyxvwaqUS9TY88aoEuHUY33kuAh1YhVVaDQhLZsnPd+XNARWZlQ==",
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-1.0.0.tgz",
+      "integrity": "sha1-NffabEjOTdv6JkiRrFk+5f+GceY=",
       "dev": true,
       "requires": {
-        "agent-base": "4.2.0",
-        "debug": "3.1.0"
-      },
-      "dependencies": {
-        "debug": {
-          "version": "3.1.0",
-          "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
-          "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
-          "dev": true,
-          "requires": {
-            "ms": "2.0.0"
-          }
-        }
+        "agent-base": "2.1.1",
+        "debug": "2.6.9",
+        "extend": "3.0.1"
       }
     },
     "iconv-lite": {
@@ -4849,10 +5022,59 @@
       "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ==",
       "dev": true
     },
+    "icss-replace-symbols": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz",
+      "integrity": "sha1-Bupvg2ead0njhs/h/oEq5dsiPe0=",
+      "dev": true
+    },
+    "icss-utils": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-2.1.0.tgz",
+      "integrity": "sha1-g/Cg7DeL8yRheLbCrZE28TWxyWI=",
+      "dev": true,
+      "requires": {
+        "postcss": "6.0.22"
+      },
+      "dependencies": {
+        "has-flag": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+          "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
+          "dev": true
+        },
+        "postcss": {
+          "version": "6.0.22",
+          "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.22.tgz",
+          "integrity": "sha512-Toc9lLoUASwGqxBSJGTVcOQiDqjK+Z2XlWBg+IgYwQMY9vA2f7iMpXVc1GpPcfTSyM5lkxNo0oDwDRO+wm7XHA==",
+          "dev": true,
+          "requires": {
+            "chalk": "2.4.1",
+            "source-map": "0.6.1",
+            "supports-color": "5.4.0"
+          }
+        },
+        "source-map": {
+          "version": "0.6.1",
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+          "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+          "dev": true
+        },
+        "supports-color": {
+          "version": "5.4.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz",
+          "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==",
+          "dev": true,
+          "requires": {
+            "has-flag": "3.0.0"
+          }
+        }
+      }
+    },
     "ieee754": {
-      "version": "1.1.12",
-      "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.12.tgz",
-      "integrity": "sha512-GguP+DRY+pJ3soyIiGPTvdiVXjZ+DbXOxGpXn3eMvNW4x4irjqXm4wHKscC+TfxSJ0yw/S1F24tqdMNsMZTiLA==",
+      "version": "1.1.11",
+      "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.11.tgz",
+      "integrity": "sha512-VhDzCKN7K8ufStx/CLj5/PDTMgph+qwN5Pkd5i0sGnVwk56zJ0lkT8Qzi1xqWLS0Wp29DgDtNeS7v8/wMoZeHg==",
       "dev": true
     },
     "iferr": {
@@ -4862,9 +5084,9 @@
       "dev": true
     },
     "ignore": {
-      "version": "3.3.10",
-      "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.10.tgz",
-      "integrity": "sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug==",
+      "version": "3.3.8",
+      "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.8.tgz",
+      "integrity": "sha512-pUh+xUQQhQzevjRHHFqqcTy0/dP/kS9I8HSrUydhihjuD09W6ldVWFtIrwhXdUJHis3i2rZNqEHpZH/cbinFbg==",
       "dev": true
     },
     "image-size": {
@@ -4874,14 +5096,13 @@
       "dev": true,
       "optional": true
     },
-    "import-local": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/import-local/-/import-local-1.0.0.tgz",
-      "integrity": "sha512-vAaZHieK9qjGo58agRBg+bhHX3hoTZU/Oa3GESWLz7t1U62fk63aHuDJJEteXoDeTCcPmUT+z38gkHPZkkmpmQ==",
+    "img-stats": {
+      "version": "0.5.2",
+      "resolved": "https://registry.npmjs.org/img-stats/-/img-stats-0.5.2.tgz",
+      "integrity": "sha1-wgNJbELy2esuWrgjL6dWurMsnis=",
       "dev": true,
       "requires": {
-        "pkg-dir": "2.0.0",
-        "resolve-cwd": "2.0.0"
+        "xmldom": "0.1.27"
       }
     },
     "imurmurhash": {
@@ -4906,19 +5127,18 @@
         "repeating": "2.0.1"
       }
     },
+    "indexes-of": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz",
+      "integrity": "sha1-8w9xbI4r00bHtn0985FVZqfAVgc=",
+      "dev": true
+    },
     "indexof": {
       "version": "0.0.1",
       "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz",
       "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=",
       "dev": true
     },
-    "inflection": {
-      "version": "1.12.0",
-      "resolved": "https://registry.npmjs.org/inflection/-/inflection-1.12.0.tgz",
-      "integrity": "sha1-ogCTVlbW9fa8TcdQLhrstwMihBY=",
-      "dev": true,
-      "optional": true
-    },
     "inflight": {
       "version": "1.0.6",
       "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
@@ -4968,8 +5188,7 @@
     "invert-kv": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz",
-      "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=",
-      "dev": true
+      "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY="
     },
     "ip": {
       "version": "1.1.5",
@@ -4983,6 +5202,12 @@
       "integrity": "sha1-4/o1e3c9phnybpXwSdBVxyeW+Gs=",
       "dev": true
     },
+    "is-absolute-url": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-2.1.0.tgz",
+      "integrity": "sha1-UFMN+4T8yap9vnhS6Do3uTufKqY=",
+      "dev": true
+    },
     "is-accessor-descriptor": {
       "version": "0.1.6",
       "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
@@ -4990,13 +5215,23 @@
       "dev": true,
       "requires": {
         "kind-of": "3.2.2"
+      },
+      "dependencies": {
+        "kind-of": {
+          "version": "3.2.2",
+          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+          "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+          "dev": true,
+          "requires": {
+            "is-buffer": "1.1.6"
+          }
+        }
       }
     },
     "is-arrayish": {
       "version": "0.2.1",
       "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
-      "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=",
-      "dev": true
+      "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0="
     },
     "is-binary-path": {
       "version": "1.0.1",
@@ -5017,17 +5252,10 @@
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz",
       "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=",
-      "dev": true,
       "requires": {
         "builtin-modules": "1.1.1"
       }
     },
-    "is-callable": {
-      "version": "1.1.3",
-      "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.3.tgz",
-      "integrity": "sha1-hut1OSgF3cM69xySoO7fdO52BLI=",
-      "dev": true
-    },
     "is-data-descriptor": {
       "version": "0.1.4",
       "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
@@ -5035,14 +5263,19 @@
       "dev": true,
       "requires": {
         "kind-of": "3.2.2"
+      },
+      "dependencies": {
+        "kind-of": {
+          "version": "3.2.2",
+          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+          "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+          "dev": true,
+          "requires": {
+            "is-buffer": "1.1.6"
+          }
+        }
       }
     },
-    "is-date-object": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz",
-      "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=",
-      "dev": true
-    },
     "is-descriptor": {
       "version": "0.1.6",
       "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
@@ -5090,9 +5323,9 @@
       "dev": true
     },
     "is-extglob": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz",
-      "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=",
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+      "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
       "dev": true
     },
     "is-finite": {
@@ -5108,33 +5341,30 @@
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
       "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
-      "dev": true,
       "requires": {
         "number-is-nan": "1.0.1"
       }
     },
     "is-glob": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz",
-      "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=",
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.0.tgz",
+      "integrity": "sha1-lSHHaEXMJhCoUgPd8ICpWML/q8A=",
       "dev": true,
       "requires": {
-        "is-extglob": "1.0.0"
+        "is-extglob": "2.1.1"
       }
     },
     "is-my-ip-valid": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/is-my-ip-valid/-/is-my-ip-valid-1.0.0.tgz",
       "integrity": "sha512-gmh/eWXROncUzRnIa1Ubrt5b8ep/MGSnfAUI3aRp+sqTCs1tv1Isl8d8F6JmkN3dXKc3ehZMrtiPN9eL03NuaQ==",
-      "dev": true,
-      "optional": true
+      "dev": true
     },
     "is-my-json-valid": {
       "version": "2.17.2",
       "resolved": "https://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.17.2.tgz",
       "integrity": "sha512-IBhBslgngMQN8DDSppmgDv7RNrlFotuuDsKcrCP3+HbFaVivIBU7u9oiiErw8sH4ynx3+gOGQ3q2otkgiSi6kg==",
       "dev": true,
-      "optional": true,
       "requires": {
         "generate-function": "2.0.0",
         "generate-object-property": "1.2.0",
@@ -5144,12 +5374,23 @@
       }
     },
     "is-number": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz",
-      "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=",
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
+      "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=",
       "dev": true,
       "requires": {
         "kind-of": "3.2.2"
+      },
+      "dependencies": {
+        "kind-of": {
+          "version": "3.2.2",
+          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+          "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+          "dev": true,
+          "requires": {
+            "is-buffer": "1.1.6"
+          }
+        }
       }
     },
     "is-odd": {
@@ -5193,6 +5434,12 @@
         "path-is-inside": "1.0.2"
       }
     },
+    "is-plain-obj": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz",
+      "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=",
+      "dev": true
+    },
     "is-plain-object": {
       "version": "2.0.4",
       "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
@@ -5200,14 +5447,6 @@
       "dev": true,
       "requires": {
         "isobject": "3.0.1"
-      },
-      "dependencies": {
-        "isobject": {
-          "version": "3.0.1",
-          "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
-          "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
-          "dev": true
-        }
       }
     },
     "is-posix-bracket": {
@@ -5226,17 +5465,7 @@
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz",
       "integrity": "sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ=",
-      "dev": true,
-      "optional": true
-    },
-    "is-regex": {
-      "version": "1.0.4",
-      "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz",
-      "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=",
-      "dev": true,
-      "requires": {
-        "has": "1.0.3"
-      }
+      "dev": true
     },
     "is-stream": {
       "version": "1.1.0",
@@ -5244,11 +5473,14 @@
       "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=",
       "dev": true
     },
-    "is-symbol": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.1.tgz",
-      "integrity": "sha1-PMWfAAJRlLarLjjbrmaJJWtmBXI=",
-      "dev": true
+    "is-svg": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/is-svg/-/is-svg-2.1.0.tgz",
+      "integrity": "sha1-z2EJDaDZ77yrhyLeum8DIgjbsOk=",
+      "dev": true,
+      "requires": {
+        "html-comment-regex": "1.1.1"
+      }
     },
     "is-typedarray": {
       "version": "1.0.0",
@@ -5259,8 +5491,7 @@
     "is-utf8": {
       "version": "0.2.1",
       "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz",
-      "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=",
-      "dev": true
+      "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI="
     },
     "is-windows": {
       "version": "1.0.2",
@@ -5293,13 +5524,10 @@
       "dev": true
     },
     "isobject": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz",
-      "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=",
-      "dev": true,
-      "requires": {
-        "isarray": "1.0.0"
-      }
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
+      "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
+      "dev": true
     },
     "isstream": {
       "version": "0.1.2",
@@ -5314,50 +5542,41 @@
       "dev": true,
       "requires": {
         "async": "2.6.1",
-        "compare-versions": "3.3.0",
+        "compare-versions": "3.2.1",
         "fileset": "2.0.3",
         "istanbul-lib-coverage": "1.2.0",
-        "istanbul-lib-hook": "1.2.1",
+        "istanbul-lib-hook": "1.2.0",
         "istanbul-lib-instrument": "1.10.1",
         "istanbul-lib-report": "1.1.4",
-        "istanbul-lib-source-maps": "1.2.5",
+        "istanbul-lib-source-maps": "1.2.4",
         "istanbul-reports": "1.3.0",
-        "js-yaml": "3.12.0",
+        "js-yaml": "3.7.0",
         "mkdirp": "0.5.1",
         "once": "1.4.0"
       }
     },
     "istanbul-instrumenter-loader": {
-      "version": "3.0.1",
-      "resolved": "https://registry.npmjs.org/istanbul-instrumenter-loader/-/istanbul-instrumenter-loader-3.0.1.tgz",
-      "integrity": "sha512-a5SPObZgS0jB/ixaKSMdn6n/gXSrK2S6q/UfRJBT3e6gQmVjwZROTODQsYW5ZNwOu78hG62Y3fWlebaVOL0C+w==",
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/istanbul-instrumenter-loader/-/istanbul-instrumenter-loader-2.0.0.tgz",
+      "integrity": "sha1-5UkpAKsLuoNe+oAkywC+mz7qJwA=",
       "dev": true,
       "requires": {
         "convert-source-map": "1.5.1",
         "istanbul-lib-instrument": "1.10.1",
-        "loader-utils": "1.1.0",
-        "schema-utils": "0.3.0"
+        "loader-utils": "0.2.17",
+        "object-assign": "4.1.1"
       },
       "dependencies": {
-        "ajv": {
-          "version": "5.5.2",
-          "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz",
-          "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=",
+        "loader-utils": {
+          "version": "0.2.17",
+          "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-0.2.17.tgz",
+          "integrity": "sha1-+G5jdNQyBabmxg6RlvF8Apm/s0g=",
           "dev": true,
           "requires": {
-            "co": "4.6.0",
-            "fast-deep-equal": "1.1.0",
-            "fast-json-stable-stringify": "2.0.0",
-            "json-schema-traverse": "0.3.1"
-          }
-        },
-        "schema-utils": {
-          "version": "0.3.0",
-          "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.3.0.tgz",
-          "integrity": "sha1-9YdyIs4+kx7a4DnxfrNxbnE3+M8=",
-          "dev": true,
-          "requires": {
-            "ajv": "5.5.2"
+            "big.js": "3.2.0",
+            "emojis-list": "2.1.0",
+            "json5": "0.5.1",
+            "object-assign": "4.1.1"
           }
         }
       }
@@ -5369,12 +5588,12 @@
       "dev": true
     },
     "istanbul-lib-hook": {
-      "version": "1.2.1",
-      "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-1.2.1.tgz",
-      "integrity": "sha512-eLAMkPG9FU0v5L02lIkcj/2/Zlz9OuluaXikdr5iStk8FDbSwAixTK9TkYxbF0eNnzAJTwM2fkV2A1tpsIp4Jg==",
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-1.2.0.tgz",
+      "integrity": "sha512-p3En6/oGkFQV55Up8ZPC2oLxvgSxD8CzA0yBrhRZSh3pfv3OFj9aSGVC0yoerAi/O4u7jUVnOGVX1eVFM+0tmQ==",
       "dev": true,
       "requires": {
-        "append-transform": "1.0.0"
+        "append-transform": "0.4.0"
       }
     },
     "istanbul-lib-instrument": {
@@ -5402,29 +5621,12 @@
         "mkdirp": "0.5.1",
         "path-parse": "1.0.5",
         "supports-color": "3.2.3"
-      },
-      "dependencies": {
-        "has-flag": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz",
-          "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=",
-          "dev": true
-        },
-        "supports-color": {
-          "version": "3.2.3",
-          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz",
-          "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=",
-          "dev": true,
-          "requires": {
-            "has-flag": "1.0.0"
-          }
-        }
       }
     },
     "istanbul-lib-source-maps": {
-      "version": "1.2.5",
-      "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-1.2.5.tgz",
-      "integrity": "sha512-8O2T/3VhrQHn0XcJbP1/GN7kXMiRAlPi+fj3uEHrjBD8Oz7Py0prSC25C09NuAZS6bgW1NNKAvCSHZXB0irSGA==",
+      "version": "1.2.4",
+      "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-1.2.4.tgz",
+      "integrity": "sha512-UzuK0g1wyQijiaYQxj/CdNycFhAd2TLtO2obKQMTZrZ1jzEMRY3rvpASEKkaxbRR6brvdovfA03znPa/pXcejg==",
       "dev": true,
       "requires": {
         "debug": "3.1.0",
@@ -5452,6 +5654,107 @@
       "dev": true,
       "requires": {
         "handlebars": "4.0.11"
+      },
+      "dependencies": {
+        "async": {
+          "version": "1.5.2",
+          "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz",
+          "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=",
+          "dev": true
+        },
+        "camelcase": {
+          "version": "1.2.1",
+          "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz",
+          "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=",
+          "dev": true,
+          "optional": true
+        },
+        "cliui": {
+          "version": "2.1.0",
+          "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz",
+          "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=",
+          "dev": true,
+          "optional": true,
+          "requires": {
+            "center-align": "0.1.3",
+            "right-align": "0.1.3",
+            "wordwrap": "0.0.2"
+          },
+          "dependencies": {
+            "wordwrap": {
+              "version": "0.0.2",
+              "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz",
+              "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=",
+              "dev": true,
+              "optional": true
+            }
+          }
+        },
+        "handlebars": {
+          "version": "4.0.11",
+          "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.0.11.tgz",
+          "integrity": "sha1-Ywo13+ApS8KB7a5v/F0yn8eYLcw=",
+          "dev": true,
+          "requires": {
+            "async": "1.5.2",
+            "optimist": "0.6.1",
+            "source-map": "0.4.4",
+            "uglify-js": "2.8.29"
+          }
+        },
+        "optimist": {
+          "version": "0.6.1",
+          "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz",
+          "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=",
+          "dev": true,
+          "requires": {
+            "minimist": "0.0.8",
+            "wordwrap": "0.0.3"
+          }
+        },
+        "source-map": {
+          "version": "0.4.4",
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz",
+          "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=",
+          "dev": true,
+          "requires": {
+            "amdefine": "1.0.1"
+          }
+        },
+        "uglify-js": {
+          "version": "2.8.29",
+          "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz",
+          "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=",
+          "dev": true,
+          "optional": true,
+          "requires": {
+            "source-map": "0.5.7",
+            "uglify-to-browserify": "1.0.2",
+            "yargs": "3.10.0"
+          },
+          "dependencies": {
+            "source-map": {
+              "version": "0.5.7",
+              "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+              "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
+              "dev": true,
+              "optional": true
+            }
+          }
+        },
+        "yargs": {
+          "version": "3.10.0",
+          "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz",
+          "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=",
+          "dev": true,
+          "optional": true,
+          "requires": {
+            "camelcase": "1.2.1",
+            "cliui": "2.1.0",
+            "decamelize": "1.2.0",
+            "window-size": "0.1.0"
+          }
+        }
       }
     },
     "jasmine": {
@@ -5474,15 +5777,15 @@
       }
     },
     "jasmine-core": {
-      "version": "2.8.0",
-      "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-2.8.0.tgz",
-      "integrity": "sha1-vMl5rh+f0FcB5F5S5l06XWPxok4=",
+      "version": "2.5.2",
+      "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-2.5.2.tgz",
+      "integrity": "sha1-b2G9eQYeJ/Q+b5NV5Es8bKtv8pc=",
       "dev": true
     },
     "jasmine-spec-reporter": {
-      "version": "4.2.1",
-      "resolved": "https://registry.npmjs.org/jasmine-spec-reporter/-/jasmine-spec-reporter-4.2.1.tgz",
-      "integrity": "sha512-FZBoZu7VE5nR7Nilzy+Np8KuVIOxF4oXDPDknehCYBDE080EnlPu0afdZNmpGDBRCUBv3mj5qgqCRmk6W/K8vg==",
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/jasmine-spec-reporter/-/jasmine-spec-reporter-3.2.0.tgz",
+      "integrity": "sha1-/b6FqAzN07J2dGvHf96Dwc53Pv8=",
       "dev": true,
       "requires": {
         "colors": "1.1.2"
@@ -5498,8 +5801,7 @@
       "version": "2.4.5",
       "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.4.5.tgz",
       "integrity": "sha512-aUnNwqMOXw3yvErjMPSQu6qIIzUmT1e5KcU1OZxRDU1g/am6mzBvcrmLAYwzmB59BHPrh5/tKaiF4OPhqRWESQ==",
-      "dev": true,
-      "optional": true
+      "dev": true
     },
     "js-tokens": {
       "version": "3.0.2",
@@ -5508,13 +5810,13 @@
       "dev": true
     },
     "js-yaml": {
-      "version": "3.12.0",
-      "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.0.tgz",
-      "integrity": "sha512-PIt2cnwmPfL4hKNwqeiuz4bKfnzHTBv6HyVgjahA6mPLwPDzjDWrplJBMjHUFxku/N3FlmrbyPclad+I+4mJ3A==",
+      "version": "3.7.0",
+      "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.7.0.tgz",
+      "integrity": "sha1-XJZ93YN6m/3KXy3oQlOr6KHAO4A=",
       "dev": true,
       "requires": {
         "argparse": "1.0.10",
-        "esprima": "4.0.0"
+        "esprima": "2.7.3"
       }
     },
     "jsbn": {
@@ -5525,9 +5827,9 @@
       "optional": true
     },
     "jsesc": {
-      "version": "1.3.0",
-      "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz",
-      "integrity": "sha1-RsP+yMGJKxKwgz25vHYiF226s0s=",
+      "version": "0.5.0",
+      "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz",
+      "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=",
       "dev": true
     },
     "json-loader": {
@@ -5536,6 +5838,12 @@
       "integrity": "sha512-QLPs8Dj7lnf3e3QYS1zkCo+4ZwqOiF9d/nZnYozTISxXWCfNs9yuky5rJw4/W34s7POaNlbZmQGaB5NiXCbP4w==",
       "dev": true
     },
+    "json-parse-better-errors": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz",
+      "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==",
+      "dev": true
+    },
     "json-schema": {
       "version": "0.2.3",
       "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz",
@@ -5594,8 +5902,7 @@
       "version": "4.0.1",
       "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-4.0.1.tgz",
       "integrity": "sha1-T9kss04OnbPInIYi7PUfm5eMbLk=",
-      "dev": true,
-      "optional": true
+      "dev": true
     },
     "jsprim": {
       "version": "1.4.1",
@@ -5618,18 +5925,18 @@
       }
     },
     "karma": {
-      "version": "2.0.3",
-      "resolved": "https://registry.npmjs.org/karma/-/karma-2.0.3.tgz",
-      "integrity": "sha512-7bVCQs8+DCLWj5TIUBIgPa95/o8X9pBhyF+E2hX51Z6Ttq2biYWQlynBmunKZGRyNOIyg89TnVtC58q9eGBFFw==",
+      "version": "1.4.1",
+      "resolved": "https://registry.npmjs.org/karma/-/karma-1.4.1.tgz",
+      "integrity": "sha1-QZgacdVCN2BrCj6oxYyQdz9BZQ4=",
       "dev": true,
       "requires": {
         "bluebird": "3.5.1",
         "body-parser": "1.18.2",
-        "chokidar": "2.0.4",
+        "chokidar": "1.7.0",
         "colors": "1.1.2",
         "combine-lists": "1.0.1",
         "connect": "3.6.6",
-        "core-js": "2.5.7",
+        "core-js": "2.4.1",
         "di": "0.0.1",
         "dom-serialize": "2.2.1",
         "expand-braces": "0.1.2",
@@ -5637,8 +5944,8 @@
         "graceful-fs": "4.1.11",
         "http-proxy": "1.17.0",
         "isbinaryfile": "3.0.2",
-        "lodash": "4.17.10",
-        "log4js": "2.9.0",
+        "lodash": "3.10.1",
+        "log4js": "0.6.38",
         "mime": "1.6.0",
         "minimatch": "3.0.4",
         "optimist": "0.6.1",
@@ -5646,385 +5953,193 @@
         "range-parser": "1.2.0",
         "rimraf": "2.6.2",
         "safe-buffer": "5.1.2",
-        "socket.io": "2.0.4",
-        "source-map": "0.6.1",
-        "tmp": "0.0.33",
-        "useragent": "2.2.1"
+        "socket.io": "1.7.2",
+        "source-map": "0.5.7",
+        "tmp": "0.0.28",
+        "useragent": "2.3.0"
       },
       "dependencies": {
         "anymatch": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz",
-          "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==",
+          "version": "1.3.2",
+          "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-1.3.2.tgz",
+          "integrity": "sha512-0XNayC8lTHQ2OI8aljNCN3sSx6hsr/1+rlcDAotXJR7C1oZZHCNsfpbKwMjRA3Uqb5tF1Rae2oloTr4xpq+WjA==",
           "dev": true,
           "requires": {
-            "micromatch": "3.1.10",
+            "micromatch": "2.3.11",
             "normalize-path": "2.1.1"
           }
         },
         "arr-diff": {
-          "version": "4.0.0",
-          "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz",
-          "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=",
-          "dev": true
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz",
+          "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=",
+          "dev": true,
+          "requires": {
+            "arr-flatten": "1.1.0"
+          }
         },
         "array-unique": {
-          "version": "0.3.2",
-          "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz",
-          "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=",
+          "version": "0.2.1",
+          "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz",
+          "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=",
           "dev": true
         },
         "braces": {
-          "version": "2.3.2",
-          "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz",
-          "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==",
+          "version": "1.8.5",
+          "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz",
+          "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=",
           "dev": true,
           "requires": {
-            "arr-flatten": "1.1.0",
-            "array-unique": "0.3.2",
-            "extend-shallow": "2.0.1",
-            "fill-range": "4.0.0",
-            "isobject": "3.0.1",
-            "repeat-element": "1.1.2",
-            "snapdragon": "0.8.2",
-            "snapdragon-node": "2.1.1",
-            "split-string": "3.1.0",
-            "to-regex": "3.0.2"
-          },
-          "dependencies": {
-            "extend-shallow": {
-              "version": "2.0.1",
-              "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
-              "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
-              "dev": true,
-              "requires": {
-                "is-extendable": "0.1.1"
-              }
-            }
+            "expand-range": "1.8.2",
+            "preserve": "0.2.0",
+            "repeat-element": "1.1.2"
           }
         },
         "chokidar": {
-          "version": "2.0.4",
-          "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.0.4.tgz",
-          "integrity": "sha512-z9n7yt9rOvIJrMhvDtDictKrkFHeihkNl6uWMmZlmL6tJtX9Cs+87oK+teBx+JIgzvbX3yZHT3eF8vpbDxHJXQ==",
+          "version": "1.7.0",
+          "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-1.7.0.tgz",
+          "integrity": "sha1-eY5ol3gVHIB2tLNg5e3SjNortGg=",
           "dev": true,
           "requires": {
-            "anymatch": "2.0.0",
+            "anymatch": "1.3.2",
             "async-each": "1.0.1",
-            "braces": "2.3.2",
             "fsevents": "1.2.4",
-            "glob-parent": "3.1.0",
+            "glob-parent": "2.0.0",
             "inherits": "2.0.3",
             "is-binary-path": "1.0.1",
-            "is-glob": "4.0.0",
-            "lodash.debounce": "4.0.8",
-            "normalize-path": "2.1.1",
+            "is-glob": "2.0.1",
             "path-is-absolute": "1.0.1",
-            "readdirp": "2.1.0",
-            "upath": "1.1.0"
+            "readdirp": "2.1.0"
           }
         },
         "expand-brackets": {
-          "version": "2.1.4",
-          "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz",
-          "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=",
+          "version": "0.1.5",
+          "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz",
+          "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=",
           "dev": true,
           "requires": {
-            "debug": "2.6.9",
-            "define-property": "0.2.5",
-            "extend-shallow": "2.0.1",
-            "posix-character-classes": "0.1.1",
-            "regex-not": "1.0.2",
-            "snapdragon": "0.8.2",
-            "to-regex": "3.0.2"
-          },
-          "dependencies": {
-            "define-property": {
-              "version": "0.2.5",
-              "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
-              "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
-              "dev": true,
-              "requires": {
-                "is-descriptor": "0.1.6"
-              }
-            },
-            "extend-shallow": {
-              "version": "2.0.1",
-              "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
-              "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
-              "dev": true,
-              "requires": {
-                "is-extendable": "0.1.1"
-              }
-            },
-            "is-accessor-descriptor": {
-              "version": "0.1.6",
-              "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
-              "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=",
-              "dev": true,
-              "requires": {
-                "kind-of": "3.2.2"
-              },
-              "dependencies": {
-                "kind-of": {
-                  "version": "3.2.2",
-                  "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
-                  "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
-                  "dev": true,
-                  "requires": {
-                    "is-buffer": "1.1.6"
-                  }
-                }
-              }
-            },
-            "is-data-descriptor": {
-              "version": "0.1.4",
-              "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
-              "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=",
-              "dev": true,
-              "requires": {
-                "kind-of": "3.2.2"
-              },
-              "dependencies": {
-                "kind-of": {
-                  "version": "3.2.2",
-                  "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
-                  "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
-                  "dev": true,
-                  "requires": {
-                    "is-buffer": "1.1.6"
-                  }
-                }
-              }
-            },
-            "is-descriptor": {
-              "version": "0.1.6",
-              "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
-              "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
-              "dev": true,
-              "requires": {
-                "is-accessor-descriptor": "0.1.6",
-                "is-data-descriptor": "0.1.4",
-                "kind-of": "5.1.0"
-              }
-            },
-            "kind-of": {
-              "version": "5.1.0",
-              "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
-              "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==",
-              "dev": true
-            }
+            "is-posix-bracket": "0.1.1"
           }
         },
         "extglob": {
-          "version": "2.0.4",
-          "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz",
-          "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==",
+          "version": "0.3.2",
+          "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz",
+          "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=",
           "dev": true,
           "requires": {
-            "array-unique": "0.3.2",
-            "define-property": "1.0.0",
-            "expand-brackets": "2.1.4",
-            "extend-shallow": "2.0.1",
-            "fragment-cache": "0.2.1",
-            "regex-not": "1.0.2",
-            "snapdragon": "0.8.2",
-            "to-regex": "3.0.2"
-          },
-          "dependencies": {
-            "define-property": {
-              "version": "1.0.0",
-              "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
-              "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
-              "dev": true,
-              "requires": {
-                "is-descriptor": "1.0.2"
-              }
-            },
-            "extend-shallow": {
-              "version": "2.0.1",
-              "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
-              "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
-              "dev": true,
-              "requires": {
-                "is-extendable": "0.1.1"
-              }
-            }
-          }
-        },
-        "fill-range": {
-          "version": "4.0.0",
-          "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
-          "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=",
-          "dev": true,
-          "requires": {
-            "extend-shallow": "2.0.1",
-            "is-number": "3.0.0",
-            "repeat-string": "1.6.1",
-            "to-regex-range": "2.1.1"
-          },
-          "dependencies": {
-            "extend-shallow": {
-              "version": "2.0.1",
-              "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
-              "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
-              "dev": true,
-              "requires": {
-                "is-extendable": "0.1.1"
-              }
-            }
+            "is-extglob": "1.0.0"
           }
         },
         "glob-parent": {
-          "version": "3.1.0",
-          "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz",
-          "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=",
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz",
+          "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=",
           "dev": true,
           "requires": {
-            "is-glob": "3.1.0",
-            "path-dirname": "1.0.2"
-          },
-          "dependencies": {
-            "is-glob": {
-              "version": "3.1.0",
-              "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz",
-              "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=",
-              "dev": true,
-              "requires": {
-                "is-extglob": "2.1.1"
-              }
-            }
-          }
-        },
-        "is-accessor-descriptor": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
-          "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
-          "dev": true,
-          "requires": {
-            "kind-of": "6.0.2"
-          }
-        },
-        "is-data-descriptor": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
-          "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
-          "dev": true,
-          "requires": {
-            "kind-of": "6.0.2"
-          }
-        },
-        "is-descriptor": {
-          "version": "1.0.2",
-          "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
-          "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
-          "dev": true,
-          "requires": {
-            "is-accessor-descriptor": "1.0.0",
-            "is-data-descriptor": "1.0.0",
-            "kind-of": "6.0.2"
+            "is-glob": "2.0.1"
           }
         },
         "is-extglob": {
-          "version": "2.1.1",
-          "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
-          "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz",
+          "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=",
           "dev": true
         },
         "is-glob": {
-          "version": "4.0.0",
-          "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.0.tgz",
-          "integrity": "sha1-lSHHaEXMJhCoUgPd8ICpWML/q8A=",
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz",
+          "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=",
           "dev": true,
           "requires": {
-            "is-extglob": "2.1.1"
+            "is-extglob": "1.0.0"
           }
         },
-        "is-number": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
-          "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=",
-          "dev": true,
-          "requires": {
-            "kind-of": "3.2.2"
-          },
-          "dependencies": {
-            "kind-of": {
-              "version": "3.2.2",
-              "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
-              "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
-              "dev": true,
-              "requires": {
-                "is-buffer": "1.1.6"
-              }
-            }
-          }
-        },
-        "isobject": {
-          "version": "3.0.1",
-          "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
-          "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
-          "dev": true
-        },
         "kind-of": {
-          "version": "6.0.2",
-          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
-          "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==",
+          "version": "3.2.2",
+          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+          "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+          "dev": true,
+          "requires": {
+            "is-buffer": "1.1.6"
+          }
+        },
+        "lodash": {
+          "version": "3.10.1",
+          "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz",
+          "integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=",
           "dev": true
         },
         "micromatch": {
-          "version": "3.1.10",
-          "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz",
-          "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==",
+          "version": "2.3.11",
+          "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz",
+          "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=",
           "dev": true,
           "requires": {
-            "arr-diff": "4.0.0",
-            "array-unique": "0.3.2",
-            "braces": "2.3.2",
-            "define-property": "2.0.2",
-            "extend-shallow": "3.0.2",
-            "extglob": "2.0.4",
-            "fragment-cache": "0.2.1",
-            "kind-of": "6.0.2",
-            "nanomatch": "1.2.9",
-            "object.pick": "1.3.0",
-            "regex-not": "1.0.2",
-            "snapdragon": "0.8.2",
-            "to-regex": "3.0.2"
+            "arr-diff": "2.0.0",
+            "array-unique": "0.2.1",
+            "braces": "1.8.5",
+            "expand-brackets": "0.1.5",
+            "extglob": "0.3.2",
+            "filename-regex": "2.0.1",
+            "is-extglob": "1.0.0",
+            "is-glob": "2.0.1",
+            "kind-of": "3.2.2",
+            "normalize-path": "2.1.1",
+            "object.omit": "2.0.1",
+            "parse-glob": "3.0.4",
+            "regex-cache": "0.4.4"
           }
         },
-        "source-map": {
+        "optimist": {
           "version": "0.6.1",
-          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
-          "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
-          "dev": true
+          "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz",
+          "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=",
+          "dev": true,
+          "requires": {
+            "minimist": "0.0.8",
+            "wordwrap": "0.0.3"
+          }
         }
       }
     },
     "karma-chrome-launcher": {
-      "version": "2.2.0",
-      "resolved": "https://registry.npmjs.org/karma-chrome-launcher/-/karma-chrome-launcher-2.2.0.tgz",
-      "integrity": "sha512-uf/ZVpAabDBPvdPdveyk1EPgbnloPvFFGgmRhYLTDH7gEB4nZdSBk8yTU47w1g/drLSx5uMOkjKk7IWKfWg/+w==",
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/karma-chrome-launcher/-/karma-chrome-launcher-2.0.0.tgz",
+      "integrity": "sha1-wnkMWjKxVXfQ//Wk1aJwOztDnCU=",
       "dev": true,
       "requires": {
         "fs-access": "1.0.1",
-        "which": "1.3.1"
+        "which": "1.3.0"
+      }
+    },
+    "karma-cli": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/karma-cli/-/karma-cli-1.0.1.tgz",
+      "integrity": "sha1-rmw8WKMTodALRRZMRVubhs4X+WA=",
+      "dev": true,
+      "requires": {
+        "resolve": "1.7.1"
       }
     },
     "karma-coverage-istanbul-reporter": {
-      "version": "1.4.3",
-      "resolved": "https://registry.npmjs.org/karma-coverage-istanbul-reporter/-/karma-coverage-istanbul-reporter-1.4.3.tgz",
-      "integrity": "sha1-O13/RmT6W41RlrmInj9hwforgNk=",
+      "version": "0.2.2",
+      "resolved": "https://registry.npmjs.org/karma-coverage-istanbul-reporter/-/karma-coverage-istanbul-reporter-0.2.2.tgz",
+      "integrity": "sha1-0Xu4AttCh9O2qO9PENecMYU24MQ=",
       "dev": true,
       "requires": {
-        "istanbul-api": "1.3.1",
-        "minimatch": "3.0.4"
+        "istanbul-api": "1.3.1"
       }
     },
+    "karma-firefox-launcher": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/karma-firefox-launcher/-/karma-firefox-launcher-1.0.0.tgz",
+      "integrity": "sha1-4IrzzkLjmGDClS6nt+qmTWNQi9w=",
+      "dev": true
+    },
     "karma-jasmine": {
-      "version": "1.1.2",
-      "resolved": "https://registry.npmjs.org/karma-jasmine/-/karma-jasmine-1.1.2.tgz",
-      "integrity": "sha1-OU8rJf+0pkS5rabyLUQ+L9CIhsM=",
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/karma-jasmine/-/karma-jasmine-1.1.0.tgz",
+      "integrity": "sha1-IuTAa/mhguUpTR9wXjczgRuBCs8=",
       "dev": true
     },
     "karma-jasmine-html-reporter": {
@@ -6033,7 +6148,7 @@
       "integrity": "sha1-SKjl7xiAdhfuK14zwRlMNbQ5Ukw=",
       "dev": true,
       "requires": {
-        "karma-jasmine": "1.1.2"
+        "karma-jasmine": "1.1.0"
       }
     },
     "karma-source-map-support": {
@@ -6057,25 +6172,31 @@
           "integrity": "sha512-N4KXEz7jcKqPf2b2vZF11lQIz9W5ZMuUcIOGj243lduidkf2fjkVKJS9vNxVWn3u/uxX38AcE8U9nnH9FPcq+g==",
           "dev": true,
           "requires": {
-            "buffer-from": "1.1.0",
+            "buffer-from": "1.0.0",
             "source-map": "0.6.1"
           }
         }
       }
     },
-    "killable": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/killable/-/killable-1.0.0.tgz",
-      "integrity": "sha1-2ouEvUfeU5WHj5XWTQLyRJ/gXms=",
+    "kew": {
+      "version": "0.7.0",
+      "resolved": "https://registry.npmjs.org/kew/-/kew-0.7.0.tgz",
+      "integrity": "sha1-edk9LTM2PW/dKXCzNdkUGtWR15s=",
       "dev": true
     },
     "kind-of": {
-      "version": "3.2.2",
-      "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
-      "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+      "version": "6.0.2",
+      "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
+      "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==",
+      "dev": true
+    },
+    "klaw": {
+      "version": "1.3.1",
+      "resolved": "https://registry.npmjs.org/klaw/-/klaw-1.3.1.tgz",
+      "integrity": "sha1-QIhDO0azsbolnXh4XY6W9zugJDk=",
       "dev": true,
       "requires": {
-        "is-buffer": "1.1.6"
+        "graceful-fs": "4.1.11"
       }
     },
     "lazy-cache": {
@@ -6088,7 +6209,6 @@
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz",
       "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=",
-      "dev": true,
       "requires": {
         "invert-kv": "1.0.0"
       }
@@ -6118,49 +6238,110 @@
         "clone": "2.1.1",
         "loader-utils": "1.1.0",
         "pify": "3.0.0"
-      }
-    },
-    "levn": {
-      "version": "0.3.0",
-      "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz",
-      "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=",
-      "dev": true,
-      "optional": true,
-      "requires": {
-        "prelude-ls": "1.1.2",
-        "type-check": "0.3.2"
-      }
-    },
-    "libbase64": {
-      "version": "0.1.0",
-      "resolved": "https://registry.npmjs.org/libbase64/-/libbase64-0.1.0.tgz",
-      "integrity": "sha1-YjUag5VjrF/1vSbxL2Dpgwu3UeY=",
-      "dev": true
-    },
-    "libmime": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/libmime/-/libmime-3.0.0.tgz",
-      "integrity": "sha1-UaGp50SOy9Ms2lRCFnW7IbwJPaY=",
-      "dev": true,
-      "requires": {
-        "iconv-lite": "0.4.15",
-        "libbase64": "0.1.0",
-        "libqp": "1.1.0"
       },
       "dependencies": {
-        "iconv-lite": {
-          "version": "0.4.15",
-          "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.15.tgz",
-          "integrity": "sha1-/iZaIYrGpXz+hUkn6dBMGYJe3es=",
+        "clone": {
+          "version": "2.1.1",
+          "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.1.tgz",
+          "integrity": "sha1-0hfR6WERjjrJpLi7oyhVU79kfNs=",
+          "dev": true
+        },
+        "pify": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
+          "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=",
           "dev": true
         }
       }
     },
-    "libqp": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/libqp/-/libqp-1.1.0.tgz",
-      "integrity": "sha1-9ebgatdLeU+1tbZpiL9yjvHe2+g=",
-      "dev": true
+    "license-checker": {
+      "version": "7.1.1",
+      "resolved": "https://registry.npmjs.org/license-checker/-/license-checker-7.1.1.tgz",
+      "integrity": "sha1-sKPsR/JGn+OmOeUqEVHNJvwsKQU=",
+      "dev": true,
+      "requires": {
+        "chalk": "0.5.1",
+        "debug": "2.6.9",
+        "mkdirp": "0.3.5",
+        "nopt": "2.2.1",
+        "read-installed": "4.0.3",
+        "treeify": "1.1.0"
+      },
+      "dependencies": {
+        "ansi-regex": {
+          "version": "0.2.1",
+          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-0.2.1.tgz",
+          "integrity": "sha1-DY6UaWej2BQ/k+JOKYUl/BsiNfk=",
+          "dev": true
+        },
+        "ansi-styles": {
+          "version": "1.1.0",
+          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-1.1.0.tgz",
+          "integrity": "sha1-6uy/Zs1waIJ2Cy9GkVgrj1XXp94=",
+          "dev": true
+        },
+        "chalk": {
+          "version": "0.5.1",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-0.5.1.tgz",
+          "integrity": "sha1-Zjs6ZItotV0EaQ1JFnqoN4WPIXQ=",
+          "dev": true,
+          "requires": {
+            "ansi-styles": "1.1.0",
+            "escape-string-regexp": "1.0.5",
+            "has-ansi": "0.1.0",
+            "strip-ansi": "0.3.0",
+            "supports-color": "0.2.0"
+          }
+        },
+        "has-ansi": {
+          "version": "0.1.0",
+          "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-0.1.0.tgz",
+          "integrity": "sha1-hPJlqujA5qiKEtcCKJS3VoiUxi4=",
+          "dev": true,
+          "requires": {
+            "ansi-regex": "0.2.1"
+          }
+        },
+        "mkdirp": {
+          "version": "0.3.5",
+          "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz",
+          "integrity": "sha1-3j5fiWHIjHh+4TaN+EmsRBPsqNc=",
+          "dev": true
+        },
+        "nopt": {
+          "version": "2.2.1",
+          "resolved": "https://registry.npmjs.org/nopt/-/nopt-2.2.1.tgz",
+          "integrity": "sha1-KqCbfRdoSHs7ianFqlIzW/8Lrqc=",
+          "dev": true,
+          "requires": {
+            "abbrev": "1.1.1"
+          }
+        },
+        "strip-ansi": {
+          "version": "0.3.0",
+          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-0.3.0.tgz",
+          "integrity": "sha1-JfSOoiynkYfzF0pNuHWTR7sSYiA=",
+          "dev": true,
+          "requires": {
+            "ansi-regex": "0.2.1"
+          }
+        },
+        "supports-color": {
+          "version": "0.2.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-0.2.0.tgz",
+          "integrity": "sha1-2S3iaU6z9nMjlz1649i1W0wiGQo=",
+          "dev": true
+        }
+      }
+    },
+    "license-to-fail": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/license-to-fail/-/license-to-fail-2.2.0.tgz",
+      "integrity": "sha1-bVkp87Ht+whm4F6yKRml4d4y/zc=",
+      "dev": true,
+      "requires": {
+        "license-checker": "7.1.1"
+      }
     },
     "license-webpack-plugin": {
       "version": "1.3.1",
@@ -6175,21 +6356,12 @@
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz",
       "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=",
-      "dev": true,
       "requires": {
         "graceful-fs": "4.1.11",
         "parse-json": "2.2.0",
         "pify": "2.3.0",
         "pinkie-promise": "2.0.1",
         "strip-bom": "2.0.0"
-      },
-      "dependencies": {
-        "pify": {
-          "version": "2.3.0",
-          "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
-          "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
-          "dev": true
-        }
       }
     },
     "loader-runner": {
@@ -6217,6 +6389,14 @@
       "requires": {
         "p-locate": "2.0.0",
         "path-exists": "3.0.0"
+      },
+      "dependencies": {
+        "path-exists": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
+          "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
+          "dev": true
+        }
       }
     },
     "lodash": {
@@ -6232,16 +6412,22 @@
       "dev": true,
       "optional": true
     },
+    "lodash.camelcase": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz",
+      "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY=",
+      "dev": true
+    },
     "lodash.clonedeep": {
       "version": "4.5.0",
       "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz",
       "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=",
       "dev": true
     },
-    "lodash.debounce": {
-      "version": "4.0.8",
-      "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
-      "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=",
+    "lodash.memoize": {
+      "version": "4.1.2",
+      "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
+      "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=",
       "dev": true
     },
     "lodash.mergewith": {
@@ -6257,160 +6443,51 @@
       "integrity": "sha1-0jM6NtnncXyK0vfKyv7HwytERmQ=",
       "dev": true
     },
-    "log4js": {
-      "version": "2.9.0",
-      "resolved": "https://registry.npmjs.org/log4js/-/log4js-2.9.0.tgz",
-      "integrity": "sha512-pptn4+5Q3ysOW6Jgm9lzhDUCFEYv7FLrazEzPQQlxgSP+IVl5HMPgT8hm2DyRqGY4GUiVjZz4XXRvTZ9BELQyw==",
-      "dev": true,
-      "requires": {
-        "amqplib": "0.5.2",
-        "axios": "0.15.3",
-        "circular-json": "0.5.4",
-        "date-format": "1.2.0",
-        "debug": "3.1.0",
-        "hipchat-notifier": "1.1.0",
-        "loggly": "1.1.1",
-        "mailgun-js": "0.18.1",
-        "nodemailer": "2.7.2",
-        "redis": "2.8.0",
-        "semver": "5.5.0",
-        "slack-node": "0.2.0",
-        "streamroller": "0.7.0"
-      },
-      "dependencies": {
-        "debug": {
-          "version": "3.1.0",
-          "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
-          "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
-          "dev": true,
-          "requires": {
-            "ms": "2.0.0"
-          }
-        }
-      }
+    "lodash.uniq": {
+      "version": "4.5.0",
+      "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz",
+      "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=",
+      "dev": true
     },
-    "loggly": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/loggly/-/loggly-1.1.1.tgz",
-      "integrity": "sha1-Cg/B0/o6XsRP3HuJe+uipGlc6+4=",
+    "log4js": {
+      "version": "0.6.38",
+      "resolved": "https://registry.npmjs.org/log4js/-/log4js-0.6.38.tgz",
+      "integrity": "sha1-LElBFmldb7JUgJQ9P8hy5mKlIv0=",
       "dev": true,
-      "optional": true,
       "requires": {
-        "json-stringify-safe": "5.0.1",
-        "request": "2.75.0",
-        "timespan": "2.3.0"
+        "readable-stream": "1.0.34",
+        "semver": "4.3.6"
       },
       "dependencies": {
-        "ansi-styles": {
-          "version": "2.2.1",
-          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
-          "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
-          "dev": true,
-          "optional": true
+        "isarray": {
+          "version": "0.0.1",
+          "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
+          "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=",
+          "dev": true
         },
-        "caseless": {
-          "version": "0.11.0",
-          "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.11.0.tgz",
-          "integrity": "sha1-cVuW6phBWTzDMGeSP17GDr2k99c=",
+        "readable-stream": {
+          "version": "1.0.34",
+          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz",
+          "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=",
           "dev": true,
-          "optional": true
-        },
-        "chalk": {
-          "version": "1.1.3",
-          "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
-          "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
-          "dev": true,
-          "optional": true,
           "requires": {
-            "ansi-styles": "2.2.1",
-            "escape-string-regexp": "1.0.5",
-            "has-ansi": "2.0.0",
-            "strip-ansi": "3.0.1",
-            "supports-color": "2.0.0"
+            "core-util-is": "1.0.2",
+            "inherits": "2.0.3",
+            "isarray": "0.0.1",
+            "string_decoder": "0.10.31"
           }
         },
-        "form-data": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.0.0.tgz",
-          "integrity": "sha1-bwrrrcxdoWwT4ezBETfYX5uIOyU=",
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "asynckit": "0.4.0",
-            "combined-stream": "1.0.6",
-            "mime-types": "2.1.18"
-          }
+        "semver": {
+          "version": "4.3.6",
+          "resolved": "https://registry.npmjs.org/semver/-/semver-4.3.6.tgz",
+          "integrity": "sha1-MAvG4OhjdPe6YQaLWx7NV/xlMto=",
+          "dev": true
         },
-        "har-validator": {
-          "version": "2.0.6",
-          "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-2.0.6.tgz",
-          "integrity": "sha1-zcvAgYgmWtEZtqWnyKtw7s+10n0=",
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "chalk": "1.1.3",
-            "commander": "2.15.1",
-            "is-my-json-valid": "2.17.2",
-            "pinkie-promise": "2.0.1"
-          }
-        },
-        "node-uuid": {
-          "version": "1.4.8",
-          "resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.8.tgz",
-          "integrity": "sha1-sEDrCSOWivq/jTL7HxfxFn/auQc=",
-          "dev": true,
-          "optional": true
-        },
-        "qs": {
-          "version": "6.2.3",
-          "resolved": "https://registry.npmjs.org/qs/-/qs-6.2.3.tgz",
-          "integrity": "sha1-HPyyXBCpsrSDBT/zn138kjOQjP4=",
-          "dev": true,
-          "optional": true
-        },
-        "request": {
-          "version": "2.75.0",
-          "resolved": "https://registry.npmjs.org/request/-/request-2.75.0.tgz",
-          "integrity": "sha1-0rgmiihtoT6qXQGt9dGMyQ9lfZM=",
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "aws-sign2": "0.6.0",
-            "aws4": "1.7.0",
-            "bl": "1.1.2",
-            "caseless": "0.11.0",
-            "combined-stream": "1.0.6",
-            "extend": "3.0.1",
-            "forever-agent": "0.6.1",
-            "form-data": "2.0.0",
-            "har-validator": "2.0.6",
-            "hawk": "3.1.3",
-            "http-signature": "1.1.1",
-            "is-typedarray": "1.0.0",
-            "isstream": "0.1.2",
-            "json-stringify-safe": "5.0.1",
-            "mime-types": "2.1.18",
-            "node-uuid": "1.4.8",
-            "oauth-sign": "0.8.2",
-            "qs": "6.2.3",
-            "stringstream": "0.0.6",
-            "tough-cookie": "2.3.4",
-            "tunnel-agent": "0.4.3"
-          }
-        },
-        "supports-color": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
-          "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
-          "dev": true,
-          "optional": true
-        },
-        "tunnel-agent": {
-          "version": "0.4.3",
-          "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.4.3.tgz",
-          "integrity": "sha1-Y3PbdpCf5XDgjXNYM2Xtgop07us=",
-          "dev": true,
-          "optional": true
+        "string_decoder": {
+          "version": "0.10.31",
+          "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
+          "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=",
+          "dev": true
         }
       }
     },
@@ -6470,59 +6547,6 @@
         "vlq": "0.2.3"
       }
     },
-    "mailcomposer": {
-      "version": "4.0.1",
-      "resolved": "https://registry.npmjs.org/mailcomposer/-/mailcomposer-4.0.1.tgz",
-      "integrity": "sha1-DhxEsqB890DuF9wUm6AJ8Zyt/rQ=",
-      "dev": true,
-      "optional": true,
-      "requires": {
-        "buildmail": "4.0.1",
-        "libmime": "3.0.0"
-      }
-    },
-    "mailgun-js": {
-      "version": "0.18.1",
-      "resolved": "https://registry.npmjs.org/mailgun-js/-/mailgun-js-0.18.1.tgz",
-      "integrity": "sha512-lvuMP14u24HS2uBsJEnzSyPMxzU2b99tQsIx1o6QNjqxjk8b3WvR+vq5oG1mjqz/IBYo+5gF+uSoDS0RkMVHmg==",
-      "dev": true,
-      "optional": true,
-      "requires": {
-        "async": "2.6.1",
-        "debug": "3.1.0",
-        "form-data": "2.3.2",
-        "inflection": "1.12.0",
-        "is-stream": "1.1.0",
-        "path-proxy": "1.0.0",
-        "promisify-call": "2.0.4",
-        "proxy-agent": "3.0.0",
-        "tsscmp": "1.0.5"
-      },
-      "dependencies": {
-        "debug": {
-          "version": "3.1.0",
-          "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
-          "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "ms": "2.0.0"
-          }
-        },
-        "form-data": {
-          "version": "2.3.2",
-          "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.2.tgz",
-          "integrity": "sha1-SXBJi+YEwgwAXU9cI67NIda0kJk=",
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "asynckit": "0.4.0",
-            "combined-stream": "1.0.6",
-            "mime-types": "2.1.18"
-          }
-        }
-      }
-    },
     "make-dir": {
       "version": "1.3.0",
       "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz",
@@ -6530,6 +6554,14 @@
       "dev": true,
       "requires": {
         "pify": "3.0.0"
+      },
+      "dependencies": {
+        "pify": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
+          "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=",
+          "dev": true
+        }
       }
     },
     "make-error": {
@@ -6559,10 +6591,11 @@
         "object-visit": "1.0.1"
       }
     },
-    "material-design-icons": {
-      "version": "3.0.1",
-      "resolved": "https://registry.npmjs.org/material-design-icons/-/material-design-icons-3.0.1.tgz",
-      "integrity": "sha1-mnHEh0chjrylHlGmbaaCA4zct78="
+    "math-expression-evaluator": {
+      "version": "1.2.17",
+      "resolved": "https://registry.npmjs.org/math-expression-evaluator/-/math-expression-evaluator-1.2.17.tgz",
+      "integrity": "sha1-3oGf282E3M2PrlnGrreWFbnSZqw=",
+      "dev": true
     },
     "math-random": {
       "version": "1.0.1",
@@ -6570,6 +6603,17 @@
       "integrity": "sha1-izqsWIuKZuSXXjzepn97sylgH6w=",
       "dev": true
     },
+    "md5": {
+      "version": "2.2.1",
+      "resolved": "https://registry.npmjs.org/md5/-/md5-2.2.1.tgz",
+      "integrity": "sha1-U6s41f48iJG6RlMp6iP6wFQBJvk=",
+      "dev": true,
+      "requires": {
+        "charenc": "0.0.2",
+        "crypt": "0.0.2",
+        "is-buffer": "1.1.6"
+      }
+    },
     "md5.js": {
       "version": "1.3.4",
       "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.4.tgz",
@@ -6644,24 +6688,24 @@
       "dev": true
     },
     "micromatch": {
-      "version": "2.3.11",
-      "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz",
-      "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=",
+      "version": "3.1.10",
+      "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz",
+      "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==",
       "dev": true,
       "requires": {
-        "arr-diff": "2.0.0",
-        "array-unique": "0.2.1",
-        "braces": "1.8.5",
-        "expand-brackets": "0.1.5",
-        "extglob": "0.3.2",
-        "filename-regex": "2.0.1",
-        "is-extglob": "1.0.0",
-        "is-glob": "2.0.1",
-        "kind-of": "3.2.2",
-        "normalize-path": "2.1.1",
-        "object.omit": "2.0.1",
-        "parse-glob": "3.0.4",
-        "regex-cache": "0.4.4"
+        "arr-diff": "4.0.0",
+        "array-unique": "0.3.2",
+        "braces": "2.3.2",
+        "define-property": "2.0.2",
+        "extend-shallow": "3.0.2",
+        "extglob": "2.0.4",
+        "fragment-cache": "0.2.1",
+        "kind-of": "6.0.2",
+        "nanomatch": "1.2.9",
+        "object.pick": "1.3.0",
+        "regex-not": "1.0.2",
+        "snapdragon": "0.8.2",
+        "to-regex": "3.0.2"
       }
     },
     "miller-rabin": {
@@ -6855,26 +6899,6 @@
         "regex-not": "1.0.2",
         "snapdragon": "0.8.2",
         "to-regex": "3.0.2"
-      },
-      "dependencies": {
-        "arr-diff": {
-          "version": "4.0.0",
-          "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz",
-          "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=",
-          "dev": true
-        },
-        "array-unique": {
-          "version": "0.3.2",
-          "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz",
-          "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=",
-          "dev": true
-        },
-        "kind-of": {
-          "version": "6.0.2",
-          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
-          "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==",
-          "dev": true
-        }
       }
     },
     "negotiator": {
@@ -6889,13 +6913,6 @@
       "integrity": "sha512-3KL3fvuRkZ7s4IFOMfztb7zJp3QaVWnBeGoJlgB38XnCRPj/0tLzzLG5IB8NYOHbJ8g8UGrgZv44GLDk6CxTxA==",
       "dev": true
     },
-    "netmask": {
-      "version": "1.0.6",
-      "resolved": "https://registry.npmjs.org/netmask/-/netmask-1.0.6.tgz",
-      "integrity": "sha1-ICl+idhvb2QA8lDZ9Pa0wZRfzTU=",
-      "dev": true,
-      "optional": true
-    },
     "next-tick": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz",
@@ -6903,9 +6920,9 @@
       "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="
+      "version": "0.1.8",
+      "resolved": "https://registry.npmjs.org/ngrx-store-localstorage/-/ngrx-store-localstorage-0.1.8.tgz",
+      "integrity": "sha1-IaROxmHEpH+RN8tcRRqSi6olA8o="
     },
     "no-case": {
       "version": "2.3.2",
@@ -6923,15 +6940,16 @@
       "dev": true
     },
     "node-gyp": {
-      "version": "3.7.0",
-      "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-3.7.0.tgz",
-      "integrity": "sha512-qDQE/Ft9xXP6zphwx4sD0t+VhwV7yFaloMpfbL2QnnDZcyaiakWlLdtFGGQfTAwpFHdpbRhRxVhIHN1OKAjgbg==",
+      "version": "3.6.2",
+      "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-3.6.2.tgz",
+      "integrity": "sha1-m/vlRWIoYoSDjnUOrAUpWFP6HGA=",
       "dev": true,
       "optional": true,
       "requires": {
         "fstream": "1.0.11",
         "glob": "7.1.2",
         "graceful-fs": "4.1.11",
+        "minimatch": "3.0.4",
         "mkdirp": "0.5.1",
         "nopt": "3.0.6",
         "npmlog": "4.1.2",
@@ -6940,7 +6958,7 @@
         "rimraf": "2.6.2",
         "semver": "5.3.0",
         "tar": "2.2.1",
-        "which": "1.3.1"
+        "which": "1.3.0"
       },
       "dependencies": {
         "nopt": {
@@ -6984,21 +7002,13 @@
         "querystring-es3": "0.2.1",
         "readable-stream": "2.3.6",
         "stream-browserify": "2.0.1",
-        "stream-http": "2.8.3",
+        "stream-http": "2.8.2",
         "string_decoder": "1.1.1",
         "timers-browserify": "2.0.10",
         "tty-browserify": "0.0.0",
         "url": "0.11.0",
-        "util": "0.10.4",
+        "util": "0.10.3",
         "vm-browserify": "0.0.4"
-      },
-      "dependencies": {
-        "punycode": {
-          "version": "1.4.1",
-          "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
-          "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=",
-          "dev": true
-        }
       }
     },
     "node-modules-path": {
@@ -7027,7 +7037,7 @@
         "meow": "3.7.0",
         "mkdirp": "0.5.1",
         "nan": "2.10.0",
-        "node-gyp": "3.7.0",
+        "node-gyp": "3.6.2",
         "npmlog": "4.1.2",
         "request": "2.79.0",
         "sass-graph": "2.2.4",
@@ -7035,12 +7045,6 @@
         "true-case-path": "1.0.2"
       },
       "dependencies": {
-        "ansi-styles": {
-          "version": "2.2.1",
-          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
-          "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
-          "dev": true
-        },
         "caseless": {
           "version": "0.11.0",
           "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.11.0.tgz",
@@ -7125,91 +7129,6 @@
         }
       }
     },
-    "nodemailer": {
-      "version": "2.7.2",
-      "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-2.7.2.tgz",
-      "integrity": "sha1-8kLmSa7q45tsftdA73sGHEBNMPk=",
-      "dev": true,
-      "optional": true,
-      "requires": {
-        "libmime": "3.0.0",
-        "mailcomposer": "4.0.1",
-        "nodemailer-direct-transport": "3.3.2",
-        "nodemailer-shared": "1.1.0",
-        "nodemailer-smtp-pool": "2.8.2",
-        "nodemailer-smtp-transport": "2.7.2",
-        "socks": "1.1.9"
-      },
-      "dependencies": {
-        "socks": {
-          "version": "1.1.9",
-          "resolved": "https://registry.npmjs.org/socks/-/socks-1.1.9.tgz",
-          "integrity": "sha1-Yo1+TQSRJDVEWsC25Fk3bLPm1pE=",
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "ip": "1.1.5",
-            "smart-buffer": "1.1.15"
-          }
-        }
-      }
-    },
-    "nodemailer-direct-transport": {
-      "version": "3.3.2",
-      "resolved": "https://registry.npmjs.org/nodemailer-direct-transport/-/nodemailer-direct-transport-3.3.2.tgz",
-      "integrity": "sha1-6W+vuQNYVglH5WkBfZfmBzilCoY=",
-      "dev": true,
-      "optional": true,
-      "requires": {
-        "nodemailer-shared": "1.1.0",
-        "smtp-connection": "2.12.0"
-      }
-    },
-    "nodemailer-fetch": {
-      "version": "1.6.0",
-      "resolved": "https://registry.npmjs.org/nodemailer-fetch/-/nodemailer-fetch-1.6.0.tgz",
-      "integrity": "sha1-ecSQihwPXzdbc/6IjamCj23JY6Q=",
-      "dev": true
-    },
-    "nodemailer-shared": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/nodemailer-shared/-/nodemailer-shared-1.1.0.tgz",
-      "integrity": "sha1-z1mU4v0mjQD1zw+nZ6CBae2wfsA=",
-      "dev": true,
-      "requires": {
-        "nodemailer-fetch": "1.6.0"
-      }
-    },
-    "nodemailer-smtp-pool": {
-      "version": "2.8.2",
-      "resolved": "https://registry.npmjs.org/nodemailer-smtp-pool/-/nodemailer-smtp-pool-2.8.2.tgz",
-      "integrity": "sha1-LrlNbPhXgLG0clzoU7nL1ejajHI=",
-      "dev": true,
-      "optional": true,
-      "requires": {
-        "nodemailer-shared": "1.1.0",
-        "nodemailer-wellknown": "0.1.10",
-        "smtp-connection": "2.12.0"
-      }
-    },
-    "nodemailer-smtp-transport": {
-      "version": "2.7.2",
-      "resolved": "https://registry.npmjs.org/nodemailer-smtp-transport/-/nodemailer-smtp-transport-2.7.2.tgz",
-      "integrity": "sha1-A9ccdjFPFKx9vHvwM6am0W1n+3c=",
-      "dev": true,
-      "optional": true,
-      "requires": {
-        "nodemailer-shared": "1.1.0",
-        "nodemailer-wellknown": "0.1.10",
-        "smtp-connection": "2.12.0"
-      }
-    },
-    "nodemailer-wellknown": {
-      "version": "0.1.10",
-      "resolved": "https://registry.npmjs.org/nodemailer-wellknown/-/nodemailer-wellknown-0.1.10.tgz",
-      "integrity": "sha1-WG24EB2zDLRDjrVGc3pBqtDPE9U=",
-      "dev": true
-    },
     "nopt": {
       "version": "4.0.1",
       "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz",
@@ -7224,7 +7143,6 @@
       "version": "2.4.0",
       "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz",
       "integrity": "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==",
-      "dev": true,
       "requires": {
         "hosted-git-info": "2.6.0",
         "is-builtin-module": "1.0.0",
@@ -7247,6 +7165,18 @@
       "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=",
       "dev": true
     },
+    "normalize-url": {
+      "version": "1.9.1",
+      "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-1.9.1.tgz",
+      "integrity": "sha1-LMDWazHqIwNkWENuNiDYWVTGbDw=",
+      "dev": true,
+      "requires": {
+        "object-assign": "4.1.1",
+        "prepend-http": "1.0.4",
+        "query-string": "4.3.4",
+        "sort-keys": "1.1.2"
+      }
+    },
     "npm-run-path": {
       "version": "2.0.2",
       "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz",
@@ -7262,7 +7192,7 @@
       "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==",
       "dev": true,
       "requires": {
-        "are-we-there-yet": "1.1.5",
+        "are-we-there-yet": "1.1.4",
         "console-control-strings": "1.1.0",
         "gauge": "2.7.4",
         "set-blocking": "2.0.0"
@@ -7292,8 +7222,7 @@
     "number-is-nan": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
-      "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=",
-      "dev": true
+      "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0="
     },
     "oauth-sign": {
       "version": "0.8.2",
@@ -7332,15 +7261,18 @@
           "requires": {
             "is-descriptor": "0.1.6"
           }
+        },
+        "kind-of": {
+          "version": "3.2.2",
+          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+          "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+          "dev": true,
+          "requires": {
+            "is-buffer": "1.1.6"
+          }
         }
       }
     },
-    "object-keys": {
-      "version": "1.0.12",
-      "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.12.tgz",
-      "integrity": "sha512-FTMyFUm2wBcGHnH2eXmz7tC6IwlqQZ6mVZ+6dm6vZ4IQIHjs6FdNsQBuKGPuUUUY6NfJw2PshC08Tn6LzLDOag==",
-      "dev": true
-    },
     "object-visit": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz",
@@ -7348,14 +7280,6 @@
       "dev": true,
       "requires": {
         "isobject": "3.0.1"
-      },
-      "dependencies": {
-        "isobject": {
-          "version": "3.0.1",
-          "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
-          "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
-          "dev": true
-        }
       }
     },
     "object.omit": {
@@ -7366,6 +7290,17 @@
       "requires": {
         "for-own": "0.1.5",
         "is-extendable": "0.1.1"
+      },
+      "dependencies": {
+        "for-own": {
+          "version": "0.1.5",
+          "resolved": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz",
+          "integrity": "sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4=",
+          "dev": true,
+          "requires": {
+            "for-in": "1.0.2"
+          }
+        }
       }
     },
     "object.pick": {
@@ -7375,14 +7310,6 @@
       "dev": true,
       "requires": {
         "isobject": "3.0.1"
-      },
-      "dependencies": {
-        "isobject": {
-          "version": "3.0.1",
-          "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
-          "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
-          "dev": true
-        }
       }
     },
     "obuf": {
@@ -7415,6 +7342,12 @@
         "wrappy": "1.0.2"
       }
     },
+    "opener": {
+      "version": "1.4.3",
+      "resolved": "https://registry.npmjs.org/opener/-/opener-1.4.3.tgz",
+      "integrity": "sha1-XG2ixdflgx6P+jlklQ+NZnSskLg=",
+      "dev": true
+    },
     "opn": {
       "version": "5.1.0",
       "resolved": "https://registry.npmjs.org/opn/-/opn-5.1.0.tgz",
@@ -7425,37 +7358,12 @@
       }
     },
     "optimist": {
-      "version": "0.6.1",
-      "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz",
-      "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=",
+      "version": "0.3.7",
+      "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.3.7.tgz",
+      "integrity": "sha1-yQlBrVnkJzMokjB00s8ufLxuwNk=",
       "dev": true,
       "requires": {
-        "minimist": "0.0.8",
-        "wordwrap": "0.0.2"
-      }
-    },
-    "optionator": {
-      "version": "0.8.2",
-      "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz",
-      "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=",
-      "dev": true,
-      "optional": true,
-      "requires": {
-        "deep-is": "0.1.3",
-        "fast-levenshtein": "2.0.6",
-        "levn": "0.3.0",
-        "prelude-ls": "1.1.2",
-        "type-check": "0.3.2",
-        "wordwrap": "1.0.0"
-      },
-      "dependencies": {
-        "wordwrap": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz",
-          "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=",
-          "dev": true,
-          "optional": true
-        }
+        "wordwrap": "0.0.3"
       }
     },
     "options": {
@@ -7470,7 +7378,7 @@
       "integrity": "sha512-IEvtB5vM5ULvwnqMxWBLxkS13JIEXbakizMSo3yoPNPCIWzg8TG3Usn/UhXoZFM/m+FuEA20KdzPSFq/0rS+UA==",
       "dev": true,
       "requires": {
-        "url-parse": "1.4.1"
+        "url-parse": "1.4.0"
       }
     },
     "os-browserify": {
@@ -7489,7 +7397,6 @@
       "version": "1.4.0",
       "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz",
       "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=",
-      "dev": true,
       "requires": {
         "lcid": "1.0.0"
       }
@@ -7517,9 +7424,9 @@
       "dev": true
     },
     "p-limit": {
-      "version": "1.3.0",
-      "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz",
-      "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==",
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.2.0.tgz",
+      "integrity": "sha512-Y/OtIaXtUPr4/YpMv1pCL5L5ed0rumAaAeBSj12F+bSlMdys7i8oQF/GUJmfpTS/QoaRrS/k6pma29haJpsMng==",
       "dev": true,
       "requires": {
         "p-try": "1.0.0"
@@ -7531,7 +7438,7 @@
       "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=",
       "dev": true,
       "requires": {
-        "p-limit": "1.3.0"
+        "p-limit": "1.2.0"
       }
     },
     "p-map": {
@@ -7546,49 +7453,6 @@
       "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=",
       "dev": true
     },
-    "pac-proxy-agent": {
-      "version": "2.0.2",
-      "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-2.0.2.tgz",
-      "integrity": "sha512-cDNAN1Ehjbf5EHkNY5qnRhGPUCp6SnpyVof5fRzN800QV1Y2OkzbH9rmjZkbBRa8igof903yOnjIl6z0SlAhxA==",
-      "dev": true,
-      "optional": true,
-      "requires": {
-        "agent-base": "4.2.0",
-        "debug": "3.1.0",
-        "get-uri": "2.0.2",
-        "http-proxy-agent": "2.1.0",
-        "https-proxy-agent": "2.2.1",
-        "pac-resolver": "3.0.0",
-        "raw-body": "2.3.2",
-        "socks-proxy-agent": "3.0.1"
-      },
-      "dependencies": {
-        "debug": {
-          "version": "3.1.0",
-          "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
-          "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "ms": "2.0.0"
-          }
-        }
-      }
-    },
-    "pac-resolver": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-3.0.0.tgz",
-      "integrity": "sha512-tcc38bsjuE3XZ5+4vP96OfhOugrX+JcnpUbhfuc4LuXBLQhoTthOstZeoQJBDnQUDYzYmdImKsbz0xSl1/9qeA==",
-      "dev": true,
-      "optional": true,
-      "requires": {
-        "co": "4.6.0",
-        "degenerator": "1.0.4",
-        "ip": "1.1.5",
-        "netmask": "1.0.6",
-        "thunkify": "2.1.2"
-      }
-    },
     "pako": {
       "version": "1.0.6",
       "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.6.tgz",
@@ -7638,22 +7502,56 @@
         "is-dotfile": "1.0.3",
         "is-extglob": "1.0.0",
         "is-glob": "2.0.1"
+      },
+      "dependencies": {
+        "is-extglob": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz",
+          "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=",
+          "dev": true
+        },
+        "is-glob": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz",
+          "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=",
+          "dev": true,
+          "requires": {
+            "is-extglob": "1.0.0"
+          }
+        }
       }
     },
     "parse-json": {
       "version": "2.2.0",
       "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz",
       "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=",
-      "dev": true,
       "requires": {
-        "error-ex": "1.3.2"
+        "error-ex": "1.3.1"
       }
     },
-    "parse-passwd": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz",
-      "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=",
-      "dev": true
+    "parse5": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/parse5/-/parse5-3.0.3.tgz",
+      "integrity": "sha512-rgO9Zg5LLLkfJF9E6CCmXlSE4UVceloys8JrFqCcHloC3usd/kJCyPDwH2SOlzix2j3xaP9sUX3e8+kvkuleAA==",
+      "requires": {
+        "@types/node": "10.1.2"
+      },
+      "dependencies": {
+        "@types/node": {
+          "version": "10.1.2",
+          "resolved": "https://registry.npmjs.org/@types/node/-/node-10.1.2.tgz",
+          "integrity": "sha512-bjk1RIeZBCe/WukrFToIVegOf91Pebr8cXYBwLBIsfiGWVQ+ifwWsT59H3RxrWzWrzd1l/Amk1/ioY5Fq3/bpA=="
+        }
+      }
+    },
+    "parsejson": {
+      "version": "0.0.3",
+      "resolved": "https://registry.npmjs.org/parsejson/-/parsejson-0.0.3.tgz",
+      "integrity": "sha1-q343WfIJ7OmUN5c/fQ8fZK4OZKs=",
+      "dev": true,
+      "requires": {
+        "better-assert": "1.0.2"
+      }
     },
     "parseqs": {
       "version": "0.0.5",
@@ -7698,10 +7596,12 @@
       "dev": true
     },
     "path-exists": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
-      "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
-      "dev": true
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz",
+      "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=",
+      "requires": {
+        "pinkie-promise": "2.0.1"
+      }
     },
     "path-is-absolute": {
       "version": "1.0.1",
@@ -7727,25 +7627,6 @@
       "integrity": "sha1-PBrfhx6pzWyUMbbqK9dKD/BVxME=",
       "dev": true
     },
-    "path-proxy": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/path-proxy/-/path-proxy-1.0.0.tgz",
-      "integrity": "sha1-GOijaFn8nS8aU7SN7hOFQ8Ag3l4=",
-      "dev": true,
-      "optional": true,
-      "requires": {
-        "inflection": "1.3.8"
-      },
-      "dependencies": {
-        "inflection": {
-          "version": "1.3.8",
-          "resolved": "https://registry.npmjs.org/inflection/-/inflection-1.3.8.tgz",
-          "integrity": "sha1-y9Fg2p91sUw8xjV41POWeEvzAU4=",
-          "dev": true,
-          "optional": true
-        }
-      }
-    },
     "path-to-regexp": {
       "version": "0.1.7",
       "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
@@ -7753,12 +7634,13 @@
       "dev": true
     },
     "path-type": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz",
-      "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==",
-      "dev": true,
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz",
+      "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=",
       "requires": {
-        "pify": "3.0.0"
+        "graceful-fs": "4.1.11",
+        "pify": "2.3.0",
+        "pinkie-promise": "2.0.1"
       }
     },
     "pbkdf2": {
@@ -7774,29 +7656,181 @@
         "sha.js": "2.4.11"
       }
     },
+    "pend": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz",
+      "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=",
+      "dev": true
+    },
     "performance-now": {
       "version": "0.2.0",
       "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-0.2.0.tgz",
       "integrity": "sha1-M+8wxcd9TqIcWlOGnZG1bY8lVeU=",
       "dev": true
     },
+    "phantomjs-prebuilt": {
+      "version": "2.1.7",
+      "resolved": "https://registry.npmjs.org/phantomjs-prebuilt/-/phantomjs-prebuilt-2.1.7.tgz",
+      "integrity": "sha1-yQvxuXcvoZeZQzH88/ZwmaloU8o=",
+      "dev": true,
+      "requires": {
+        "extract-zip": "1.5.0",
+        "fs-extra": "0.26.7",
+        "hasha": "2.2.0",
+        "kew": "0.7.0",
+        "progress": "1.1.8",
+        "request": "2.67.0",
+        "request-progress": "2.0.1",
+        "which": "1.2.14"
+      },
+      "dependencies": {
+        "caseless": {
+          "version": "0.11.0",
+          "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.11.0.tgz",
+          "integrity": "sha1-cVuW6phBWTzDMGeSP17GDr2k99c=",
+          "dev": true
+        },
+        "chalk": {
+          "version": "1.1.3",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+          "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
+          "dev": true,
+          "requires": {
+            "ansi-styles": "2.2.1",
+            "escape-string-regexp": "1.0.5",
+            "has-ansi": "2.0.0",
+            "strip-ansi": "3.0.1",
+            "supports-color": "2.0.0"
+          }
+        },
+        "form-data": {
+          "version": "1.0.1",
+          "resolved": "https://registry.npmjs.org/form-data/-/form-data-1.0.1.tgz",
+          "integrity": "sha1-rjFduaSQf6BlUCMEpm13M0de43w=",
+          "dev": true,
+          "requires": {
+            "async": "2.6.1",
+            "combined-stream": "1.0.6",
+            "mime-types": "2.1.18"
+          }
+        },
+        "fs-extra": {
+          "version": "0.26.7",
+          "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.26.7.tgz",
+          "integrity": "sha1-muH92UiXeY7at20JGM9C0MMYT6k=",
+          "dev": true,
+          "requires": {
+            "graceful-fs": "4.1.11",
+            "jsonfile": "2.4.0",
+            "klaw": "1.3.1",
+            "path-is-absolute": "1.0.1",
+            "rimraf": "2.6.2"
+          }
+        },
+        "har-validator": {
+          "version": "2.0.6",
+          "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-2.0.6.tgz",
+          "integrity": "sha1-zcvAgYgmWtEZtqWnyKtw7s+10n0=",
+          "dev": true,
+          "requires": {
+            "chalk": "1.1.3",
+            "commander": "2.15.1",
+            "is-my-json-valid": "2.17.2",
+            "pinkie-promise": "2.0.1"
+          }
+        },
+        "jsonfile": {
+          "version": "2.4.0",
+          "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz",
+          "integrity": "sha1-NzaitCi4e72gzIO1P6PWM6NcKug=",
+          "dev": true,
+          "requires": {
+            "graceful-fs": "4.1.11"
+          }
+        },
+        "node-uuid": {
+          "version": "1.4.8",
+          "resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.8.tgz",
+          "integrity": "sha1-sEDrCSOWivq/jTL7HxfxFn/auQc=",
+          "dev": true
+        },
+        "qs": {
+          "version": "5.2.1",
+          "resolved": "https://registry.npmjs.org/qs/-/qs-5.2.1.tgz",
+          "integrity": "sha1-gB/uAw4LlFDWOFrcSKTMVbRK7fw=",
+          "dev": true
+        },
+        "request": {
+          "version": "2.67.0",
+          "resolved": "https://registry.npmjs.org/request/-/request-2.67.0.tgz",
+          "integrity": "sha1-ivdHgOK/EeoK6aqWXBHxGv0nJ0I=",
+          "dev": true,
+          "requires": {
+            "aws-sign2": "0.6.0",
+            "bl": "1.0.3",
+            "caseless": "0.11.0",
+            "combined-stream": "1.0.6",
+            "extend": "3.0.1",
+            "forever-agent": "0.6.1",
+            "form-data": "1.0.1",
+            "har-validator": "2.0.6",
+            "hawk": "3.1.3",
+            "http-signature": "1.1.1",
+            "is-typedarray": "1.0.0",
+            "isstream": "0.1.2",
+            "json-stringify-safe": "5.0.1",
+            "mime-types": "2.1.18",
+            "node-uuid": "1.4.8",
+            "oauth-sign": "0.8.2",
+            "qs": "5.2.1",
+            "stringstream": "0.0.6",
+            "tough-cookie": "2.2.2",
+            "tunnel-agent": "0.4.3"
+          }
+        },
+        "supports-color": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
+          "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
+          "dev": true
+        },
+        "tough-cookie": {
+          "version": "2.2.2",
+          "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.2.2.tgz",
+          "integrity": "sha1-yDoYMPTl7wuT7yo0iOck+N4Basc=",
+          "dev": true
+        },
+        "tunnel-agent": {
+          "version": "0.4.3",
+          "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.4.3.tgz",
+          "integrity": "sha1-Y3PbdpCf5XDgjXNYM2Xtgop07us=",
+          "dev": true
+        },
+        "which": {
+          "version": "1.2.14",
+          "resolved": "https://registry.npmjs.org/which/-/which-1.2.14.tgz",
+          "integrity": "sha1-mofEN48D6CfOyvGs31bHNsAcFOU=",
+          "dev": true,
+          "requires": {
+            "isexe": "2.0.0"
+          }
+        }
+      }
+    },
     "pify": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
-      "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=",
-      "dev": true
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+      "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw="
     },
     "pinkie": {
       "version": "2.0.4",
       "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz",
-      "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=",
-      "dev": true
+      "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA="
     },
     "pinkie-promise": {
       "version": "2.0.1",
       "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz",
       "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=",
-      "dev": true,
       "requires": {
         "pinkie": "2.0.4"
       }
@@ -7808,6 +7842,17 @@
       "dev": true,
       "requires": {
         "find-up": "2.1.0"
+      },
+      "dependencies": {
+        "find-up": {
+          "version": "2.1.0",
+          "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz",
+          "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=",
+          "dev": true,
+          "requires": {
+            "locate-path": "2.0.0"
+          }
+        }
       }
     },
     "portfinder": {
@@ -7836,60 +7881,125 @@
       "dev": true
     },
     "postcss": {
-      "version": "6.0.22",
-      "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.22.tgz",
-      "integrity": "sha512-Toc9lLoUASwGqxBSJGTVcOQiDqjK+Z2XlWBg+IgYwQMY9vA2f7iMpXVc1GpPcfTSyM5lkxNo0oDwDRO+wm7XHA==",
+      "version": "5.2.18",
+      "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz",
+      "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==",
       "dev": true,
       "requires": {
-        "chalk": "2.4.1",
-        "source-map": "0.6.1",
-        "supports-color": "5.4.0"
+        "chalk": "1.1.3",
+        "js-base64": "2.4.5",
+        "source-map": "0.5.7",
+        "supports-color": "3.2.3"
       },
       "dependencies": {
         "chalk": {
-          "version": "2.4.1",
-          "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz",
-          "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==",
+          "version": "1.1.3",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+          "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
           "dev": true,
           "requires": {
-            "ansi-styles": "3.2.1",
+            "ansi-styles": "2.2.1",
             "escape-string-regexp": "1.0.5",
-            "supports-color": "5.4.0"
-          }
-        },
-        "has-flag": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
-          "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
-          "dev": true
-        },
-        "source-map": {
-          "version": "0.6.1",
-          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
-          "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
-          "dev": true
-        },
-        "supports-color": {
-          "version": "5.4.0",
-          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz",
-          "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==",
-          "dev": true,
-          "requires": {
-            "has-flag": "3.0.0"
+            "has-ansi": "2.0.0",
+            "strip-ansi": "3.0.1",
+            "supports-color": "2.0.0"
+          },
+          "dependencies": {
+            "supports-color": {
+              "version": "2.0.0",
+              "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
+              "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
+              "dev": true
+            }
           }
         }
       }
     },
-    "postcss-import": {
-      "version": "11.1.0",
-      "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-11.1.0.tgz",
-      "integrity": "sha512-5l327iI75POonjxkXgdRCUS+AlzAdBx4pOvMEhTKTCjb1p8IEeVR9yx3cPbmN7LIWJLbfnIXxAhoB4jpD0c/Cw==",
+    "postcss-calc": {
+      "version": "5.3.1",
+      "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-5.3.1.tgz",
+      "integrity": "sha1-d7rnypKK2FcW4v2kLyYb98HWW14=",
       "dev": true,
       "requires": {
-        "postcss": "6.0.22",
-        "postcss-value-parser": "3.3.0",
-        "read-cache": "1.0.0",
-        "resolve": "1.8.1"
+        "postcss": "5.2.18",
+        "postcss-message-helpers": "2.0.0",
+        "reduce-css-calc": "1.3.0"
+      }
+    },
+    "postcss-colormin": {
+      "version": "2.2.2",
+      "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-2.2.2.tgz",
+      "integrity": "sha1-ZjFBfV8OkJo9fsJrJMio0eT5bks=",
+      "dev": true,
+      "requires": {
+        "colormin": "1.1.2",
+        "postcss": "5.2.18",
+        "postcss-value-parser": "3.3.0"
+      }
+    },
+    "postcss-convert-values": {
+      "version": "2.6.1",
+      "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-2.6.1.tgz",
+      "integrity": "sha1-u9hZPFwf0uPRwyK7kl3K6Nrk1i0=",
+      "dev": true,
+      "requires": {
+        "postcss": "5.2.18",
+        "postcss-value-parser": "3.3.0"
+      }
+    },
+    "postcss-discard-comments": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-2.0.4.tgz",
+      "integrity": "sha1-vv6J+v1bPazlzM5Rt2uBUUvgDj0=",
+      "dev": true,
+      "requires": {
+        "postcss": "5.2.18"
+      }
+    },
+    "postcss-discard-duplicates": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-2.1.0.tgz",
+      "integrity": "sha1-uavye4isGIFYpesSq8riAmO5GTI=",
+      "dev": true,
+      "requires": {
+        "postcss": "5.2.18"
+      }
+    },
+    "postcss-discard-empty": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-2.1.0.tgz",
+      "integrity": "sha1-0rS9nVztXr2Nyt52QMfXzX9PkrU=",
+      "dev": true,
+      "requires": {
+        "postcss": "5.2.18"
+      }
+    },
+    "postcss-discard-overridden": {
+      "version": "0.1.1",
+      "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-0.1.1.tgz",
+      "integrity": "sha1-ix6vVU9ob7KIzYdMVWZ7CqNmjVg=",
+      "dev": true,
+      "requires": {
+        "postcss": "5.2.18"
+      }
+    },
+    "postcss-discard-unused": {
+      "version": "2.2.3",
+      "resolved": "https://registry.npmjs.org/postcss-discard-unused/-/postcss-discard-unused-2.2.3.tgz",
+      "integrity": "sha1-vOMLLMWR/8Y0Mitfs0ZLbZNPRDM=",
+      "dev": true,
+      "requires": {
+        "postcss": "5.2.18",
+        "uniqs": "2.0.0"
+      }
+    },
+    "postcss-filter-plugins": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/postcss-filter-plugins/-/postcss-filter-plugins-2.0.3.tgz",
+      "integrity": "sha512-T53GVFsdinJhgwm7rg1BzbeBRomOg9y5MBVhGcsV0CxurUdVj1UlPdKtn7aqYA/c/QVkzKMjq2bSV5dKG5+AwQ==",
+      "dev": true,
+      "requires": {
+        "postcss": "5.2.18"
       }
     },
     "postcss-load-config": {
@@ -7925,28 +8035,384 @@
       }
     },
     "postcss-loader": {
-      "version": "2.1.5",
-      "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-2.1.5.tgz",
-      "integrity": "sha512-pV7kB5neJ0/1tZ8L1uGOBNTVBCSCXQoIsZMsrwvO8V2rKGa2tBl/f80GGVxow2jJnRJ2w1ocx693EKhZAb9Isg==",
+      "version": "1.3.3",
+      "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-1.3.3.tgz",
+      "integrity": "sha1-piHqH6KQYqg5cqRvVEhncTAZFus=",
       "dev": true,
       "requires": {
         "loader-utils": "1.1.0",
-        "postcss": "6.0.22",
-        "postcss-load-config": "1.2.0",
-        "schema-utils": "0.4.5"
+        "object-assign": "4.1.1",
+        "postcss": "5.2.18",
+        "postcss-load-config": "1.2.0"
+      }
+    },
+    "postcss-merge-idents": {
+      "version": "2.1.7",
+      "resolved": "https://registry.npmjs.org/postcss-merge-idents/-/postcss-merge-idents-2.1.7.tgz",
+      "integrity": "sha1-TFUwMTwI4dWzu/PSu8dH4njuonA=",
+      "dev": true,
+      "requires": {
+        "has": "1.0.1",
+        "postcss": "5.2.18",
+        "postcss-value-parser": "3.3.0"
+      }
+    },
+    "postcss-merge-longhand": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-2.0.2.tgz",
+      "integrity": "sha1-I9kM0Sewp3mUkVMyc5A0oaTz1lg=",
+      "dev": true,
+      "requires": {
+        "postcss": "5.2.18"
+      }
+    },
+    "postcss-merge-rules": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-2.1.2.tgz",
+      "integrity": "sha1-0d9d+qexrMO+VT8OnhDofGG19yE=",
+      "dev": true,
+      "requires": {
+        "browserslist": "1.7.7",
+        "caniuse-api": "1.6.1",
+        "postcss": "5.2.18",
+        "postcss-selector-parser": "2.2.3",
+        "vendors": "1.0.2"
+      }
+    },
+    "postcss-message-helpers": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/postcss-message-helpers/-/postcss-message-helpers-2.0.0.tgz",
+      "integrity": "sha1-pPL0+rbk/gAvCu0ABHjN9S+bpg4=",
+      "dev": true
+    },
+    "postcss-minify-font-values": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-1.0.5.tgz",
+      "integrity": "sha1-S1jttWZB66fIR0qzUmyv17vey2k=",
+      "dev": true,
+      "requires": {
+        "object-assign": "4.1.1",
+        "postcss": "5.2.18",
+        "postcss-value-parser": "3.3.0"
+      }
+    },
+    "postcss-minify-gradients": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-1.0.5.tgz",
+      "integrity": "sha1-Xb2hE3NwP4PPtKPqOIHY11/15uE=",
+      "dev": true,
+      "requires": {
+        "postcss": "5.2.18",
+        "postcss-value-parser": "3.3.0"
+      }
+    },
+    "postcss-minify-params": {
+      "version": "1.2.2",
+      "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-1.2.2.tgz",
+      "integrity": "sha1-rSzgcTc7lDs9kwo/pZo1jCjW8fM=",
+      "dev": true,
+      "requires": {
+        "alphanum-sort": "1.0.2",
+        "postcss": "5.2.18",
+        "postcss-value-parser": "3.3.0",
+        "uniqs": "2.0.0"
+      }
+    },
+    "postcss-minify-selectors": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-2.1.1.tgz",
+      "integrity": "sha1-ssapjAByz5G5MtGkllCBFDEXNb8=",
+      "dev": true,
+      "requires": {
+        "alphanum-sort": "1.0.2",
+        "has": "1.0.1",
+        "postcss": "5.2.18",
+        "postcss-selector-parser": "2.2.3"
+      }
+    },
+    "postcss-modules-extract-imports": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-1.2.0.tgz",
+      "integrity": "sha1-ZhQOzs447wa/DT41XWm/WdFB6oU=",
+      "dev": true,
+      "requires": {
+        "postcss": "6.0.22"
+      },
+      "dependencies": {
+        "has-flag": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+          "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
+          "dev": true
+        },
+        "postcss": {
+          "version": "6.0.22",
+          "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.22.tgz",
+          "integrity": "sha512-Toc9lLoUASwGqxBSJGTVcOQiDqjK+Z2XlWBg+IgYwQMY9vA2f7iMpXVc1GpPcfTSyM5lkxNo0oDwDRO+wm7XHA==",
+          "dev": true,
+          "requires": {
+            "chalk": "2.4.1",
+            "source-map": "0.6.1",
+            "supports-color": "5.4.0"
+          }
+        },
+        "source-map": {
+          "version": "0.6.1",
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+          "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+          "dev": true
+        },
+        "supports-color": {
+          "version": "5.4.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz",
+          "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==",
+          "dev": true,
+          "requires": {
+            "has-flag": "3.0.0"
+          }
+        }
+      }
+    },
+    "postcss-modules-local-by-default": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-1.2.0.tgz",
+      "integrity": "sha1-99gMOYxaOT+nlkRmvRlQCn1hwGk=",
+      "dev": true,
+      "requires": {
+        "css-selector-tokenizer": "0.7.0",
+        "postcss": "6.0.22"
+      },
+      "dependencies": {
+        "has-flag": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+          "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
+          "dev": true
+        },
+        "postcss": {
+          "version": "6.0.22",
+          "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.22.tgz",
+          "integrity": "sha512-Toc9lLoUASwGqxBSJGTVcOQiDqjK+Z2XlWBg+IgYwQMY9vA2f7iMpXVc1GpPcfTSyM5lkxNo0oDwDRO+wm7XHA==",
+          "dev": true,
+          "requires": {
+            "chalk": "2.4.1",
+            "source-map": "0.6.1",
+            "supports-color": "5.4.0"
+          }
+        },
+        "source-map": {
+          "version": "0.6.1",
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+          "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+          "dev": true
+        },
+        "supports-color": {
+          "version": "5.4.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz",
+          "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==",
+          "dev": true,
+          "requires": {
+            "has-flag": "3.0.0"
+          }
+        }
+      }
+    },
+    "postcss-modules-scope": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-1.1.0.tgz",
+      "integrity": "sha1-1upkmUx5+XtipytCb75gVqGUu5A=",
+      "dev": true,
+      "requires": {
+        "css-selector-tokenizer": "0.7.0",
+        "postcss": "6.0.22"
+      },
+      "dependencies": {
+        "has-flag": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+          "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
+          "dev": true
+        },
+        "postcss": {
+          "version": "6.0.22",
+          "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.22.tgz",
+          "integrity": "sha512-Toc9lLoUASwGqxBSJGTVcOQiDqjK+Z2XlWBg+IgYwQMY9vA2f7iMpXVc1GpPcfTSyM5lkxNo0oDwDRO+wm7XHA==",
+          "dev": true,
+          "requires": {
+            "chalk": "2.4.1",
+            "source-map": "0.6.1",
+            "supports-color": "5.4.0"
+          }
+        },
+        "source-map": {
+          "version": "0.6.1",
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+          "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+          "dev": true
+        },
+        "supports-color": {
+          "version": "5.4.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz",
+          "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==",
+          "dev": true,
+          "requires": {
+            "has-flag": "3.0.0"
+          }
+        }
+      }
+    },
+    "postcss-modules-values": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-1.3.0.tgz",
+      "integrity": "sha1-7P+p1+GSUYOJ9CrQ6D9yrsRW6iA=",
+      "dev": true,
+      "requires": {
+        "icss-replace-symbols": "1.1.0",
+        "postcss": "6.0.22"
+      },
+      "dependencies": {
+        "has-flag": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+          "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
+          "dev": true
+        },
+        "postcss": {
+          "version": "6.0.22",
+          "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.22.tgz",
+          "integrity": "sha512-Toc9lLoUASwGqxBSJGTVcOQiDqjK+Z2XlWBg+IgYwQMY9vA2f7iMpXVc1GpPcfTSyM5lkxNo0oDwDRO+wm7XHA==",
+          "dev": true,
+          "requires": {
+            "chalk": "2.4.1",
+            "source-map": "0.6.1",
+            "supports-color": "5.4.0"
+          }
+        },
+        "source-map": {
+          "version": "0.6.1",
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+          "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+          "dev": true
+        },
+        "supports-color": {
+          "version": "5.4.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz",
+          "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==",
+          "dev": true,
+          "requires": {
+            "has-flag": "3.0.0"
+          }
+        }
+      }
+    },
+    "postcss-normalize-charset": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-1.1.1.tgz",
+      "integrity": "sha1-757nEhLX/nWceO0WL2HtYrXLk/E=",
+      "dev": true,
+      "requires": {
+        "postcss": "5.2.18"
+      }
+    },
+    "postcss-normalize-url": {
+      "version": "3.0.8",
+      "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-3.0.8.tgz",
+      "integrity": "sha1-EI90s/L82viRov+j6kWSJ5/HgiI=",
+      "dev": true,
+      "requires": {
+        "is-absolute-url": "2.1.0",
+        "normalize-url": "1.9.1",
+        "postcss": "5.2.18",
+        "postcss-value-parser": "3.3.0"
+      }
+    },
+    "postcss-ordered-values": {
+      "version": "2.2.3",
+      "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-2.2.3.tgz",
+      "integrity": "sha1-7sbCpntsQSqNsgQud/6NpD+VwR0=",
+      "dev": true,
+      "requires": {
+        "postcss": "5.2.18",
+        "postcss-value-parser": "3.3.0"
+      }
+    },
+    "postcss-reduce-idents": {
+      "version": "2.4.0",
+      "resolved": "https://registry.npmjs.org/postcss-reduce-idents/-/postcss-reduce-idents-2.4.0.tgz",
+      "integrity": "sha1-wsbSDMlYKE9qv75j92Cb9AkFmtM=",
+      "dev": true,
+      "requires": {
+        "postcss": "5.2.18",
+        "postcss-value-parser": "3.3.0"
+      }
+    },
+    "postcss-reduce-initial": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-1.0.1.tgz",
+      "integrity": "sha1-aPgGlfBF0IJjqHmtJA343WT2ROo=",
+      "dev": true,
+      "requires": {
+        "postcss": "5.2.18"
+      }
+    },
+    "postcss-reduce-transforms": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-1.0.4.tgz",
+      "integrity": "sha1-/3b02CEkN7McKYpC0uFEQCV3GuE=",
+      "dev": true,
+      "requires": {
+        "has": "1.0.1",
+        "postcss": "5.2.18",
+        "postcss-value-parser": "3.3.0"
+      }
+    },
+    "postcss-selector-parser": {
+      "version": "2.2.3",
+      "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-2.2.3.tgz",
+      "integrity": "sha1-+UN3iGBsPJrO4W/+jYsWKX8nu5A=",
+      "dev": true,
+      "requires": {
+        "flatten": "1.0.2",
+        "indexes-of": "1.0.1",
+        "uniq": "1.0.1"
+      }
+    },
+    "postcss-svgo": {
+      "version": "2.1.6",
+      "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-2.1.6.tgz",
+      "integrity": "sha1-tt8YqmE7Zm4TPwittSGcJoSsEI0=",
+      "dev": true,
+      "requires": {
+        "is-svg": "2.1.0",
+        "postcss": "5.2.18",
+        "postcss-value-parser": "3.3.0",
+        "svgo": "0.7.2"
+      }
+    },
+    "postcss-unique-selectors": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-2.0.2.tgz",
+      "integrity": "sha1-mB1X0p3csz57Hf4f1DuGSfkzyh0=",
+      "dev": true,
+      "requires": {
+        "alphanum-sort": "1.0.2",
+        "postcss": "5.2.18",
+        "uniqs": "2.0.0"
       }
     },
     "postcss-url": {
-      "version": "7.3.2",
-      "resolved": "https://registry.npmjs.org/postcss-url/-/postcss-url-7.3.2.tgz",
-      "integrity": "sha512-QMV5mA+pCYZQcUEPQkmor9vcPQ2MT+Ipuu8qdi1gVxbNiIiErEGft+eny1ak19qALoBkccS5AHaCaCDzh7b9MA==",
+      "version": "5.1.2",
+      "resolved": "https://registry.npmjs.org/postcss-url/-/postcss-url-5.1.2.tgz",
+      "integrity": "sha1-mLMWW+jVkkccsMqt3iwNH4MvEz4=",
       "dev": true,
       "requires": {
+        "directory-encoder": "0.7.2",
+        "js-base64": "2.4.5",
         "mime": "1.6.0",
         "minimatch": "3.0.4",
         "mkdirp": "0.5.1",
-        "postcss": "6.0.22",
-        "xxhashjs": "0.2.2"
+        "path-is-absolute": "1.0.1",
+        "postcss": "5.2.18"
       }
     },
     "postcss-value-parser": {
@@ -7955,10 +8421,21 @@
       "integrity": "sha1-h/OPnxj3dKSrTIojL1xc6IcqnRU=",
       "dev": true
     },
-    "prelude-ls": {
-      "version": "1.1.2",
-      "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz",
-      "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=",
+    "postcss-zindex": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/postcss-zindex/-/postcss-zindex-2.2.0.tgz",
+      "integrity": "sha1-0hCd3AVbka9n/EyzsCWUZjnSryI=",
+      "dev": true,
+      "requires": {
+        "has": "1.0.1",
+        "postcss": "5.2.18",
+        "uniqs": "2.0.0"
+      }
+    },
+    "prepend-http": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz",
+      "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=",
       "dev": true
     },
     "preserve": {
@@ -7989,6 +8466,12 @@
       "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==",
       "dev": true
     },
+    "progress": {
+      "version": "1.1.8",
+      "resolved": "https://registry.npmjs.org/progress/-/progress-1.1.8.tgz",
+      "integrity": "sha1-4mDHj2Fhzdmw5WzD4Khd4Xx6V74=",
+      "dev": true
+    },
     "promise": {
       "version": "7.3.1",
       "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz",
@@ -8005,26 +8488,16 @@
       "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=",
       "dev": true
     },
-    "promisify-call": {
-      "version": "2.0.4",
-      "resolved": "https://registry.npmjs.org/promisify-call/-/promisify-call-2.0.4.tgz",
-      "integrity": "sha1-1IwtRWUszM1SgB3ey9UzptS9X7o=",
-      "dev": true,
-      "optional": true,
-      "requires": {
-        "with-callback": "1.0.2"
-      }
-    },
     "protractor": {
-      "version": "5.1.2",
-      "resolved": "https://registry.npmjs.org/protractor/-/protractor-5.1.2.tgz",
-      "integrity": "sha1-myIXQXCaTGLVzVPGqt1UpxE36V8=",
+      "version": "5.1.0",
+      "resolved": "https://registry.npmjs.org/protractor/-/protractor-5.1.0.tgz",
+      "integrity": "sha1-0mUPLx/mkDGq01KE7sHveaUGJaE=",
       "dev": true,
       "requires": {
-        "@types/node": "6.0.113",
+        "@types/node": "6.0.78",
         "@types/q": "0.0.32",
         "@types/selenium-webdriver": "2.53.43",
-        "blocking-proxy": "0.0.5",
+        "blocking-proxy": "0.0.4",
         "chalk": "1.1.3",
         "glob": "7.1.2",
         "jasmine": "2.99.0",
@@ -8038,10 +8511,10 @@
         "webdriver-manager": "12.0.6"
       },
       "dependencies": {
-        "ansi-styles": {
-          "version": "2.2.1",
-          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
-          "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
+        "@types/selenium-webdriver": {
+          "version": "2.53.43",
+          "resolved": "https://registry.npmjs.org/@types/selenium-webdriver/-/selenium-webdriver-2.53.43.tgz",
+          "integrity": "sha512-UBYHWph6P3tutkbXpW6XYg9ZPbTKjw/YC2hGG1/GEvWwTbvezBUv3h+mmUFw79T3RFPnmedpiXdOBbXX+4l0jg==",
           "dev": true
         },
         "chalk": {
@@ -8086,16 +8559,20 @@
             "pinkie-promise": "2.0.1"
           }
         },
-        "minimist": {
-          "version": "1.2.0",
-          "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
-          "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
-          "dev": true
+        "optimist": {
+          "version": "0.6.1",
+          "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz",
+          "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=",
+          "dev": true,
+          "requires": {
+            "minimist": "0.0.8",
+            "wordwrap": "0.0.3"
+          }
         },
-        "pify": {
-          "version": "2.3.0",
-          "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
-          "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
+        "q": {
+          "version": "1.4.1",
+          "resolved": "https://registry.npmjs.org/q/-/q-1.4.1.tgz",
+          "integrity": "sha1-VXBbzZPF82c1MMLCy8DCs63cKG4=",
           "dev": true
         },
         "supports-color": {
@@ -8121,6 +8598,14 @@
             "rimraf": "2.6.2",
             "semver": "5.5.0",
             "xml2js": "0.4.19"
+          },
+          "dependencies": {
+            "minimist": {
+              "version": "1.2.0",
+              "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
+              "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
+              "dev": true
+            }
           }
         }
       }
@@ -8135,42 +8620,6 @@
         "ipaddr.js": "1.6.0"
       }
     },
-    "proxy-agent": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-3.0.0.tgz",
-      "integrity": "sha512-g6n6vnk8fRf705ShN+FEXFG/SDJaW++lSs0d9KaJh4uBWW/wi7en4Cpo5VYQW3SZzAE121lhB/KLQrbURoubZw==",
-      "dev": true,
-      "optional": true,
-      "requires": {
-        "agent-base": "4.2.0",
-        "debug": "3.1.0",
-        "http-proxy-agent": "2.1.0",
-        "https-proxy-agent": "2.2.1",
-        "lru-cache": "4.1.3",
-        "pac-proxy-agent": "2.0.2",
-        "proxy-from-env": "1.0.0",
-        "socks-proxy-agent": "3.0.1"
-      },
-      "dependencies": {
-        "debug": {
-          "version": "3.1.0",
-          "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
-          "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "ms": "2.0.0"
-          }
-        }
-      }
-    },
-    "proxy-from-env": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.0.0.tgz",
-      "integrity": "sha1-M8UDmPcOp+uW0h97gXYwpVeRx+4=",
-      "dev": true,
-      "optional": true
-    },
     "prr": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz",
@@ -8218,15 +8667,15 @@
       }
     },
     "punycode": {
-      "version": "2.1.1",
-      "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
-      "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
+      "version": "1.4.1",
+      "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
+      "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=",
       "dev": true
     },
     "q": {
-      "version": "1.4.1",
-      "resolved": "https://registry.npmjs.org/q/-/q-1.4.1.tgz",
-      "integrity": "sha1-VXBbzZPF82c1MMLCy8DCs63cKG4=",
+      "version": "1.5.1",
+      "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz",
+      "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=",
       "dev": true
     },
     "qjobs": {
@@ -8241,6 +8690,16 @@
       "integrity": "sha1-E+JtKK1rD/qpExLNO/cI7TUecjM=",
       "dev": true
     },
+    "query-string": {
+      "version": "4.3.4",
+      "resolved": "https://registry.npmjs.org/query-string/-/query-string-4.3.4.tgz",
+      "integrity": "sha1-u7aTucqRXCMlFbIosaArYJBD2+s=",
+      "dev": true,
+      "requires": {
+        "object-assign": "4.1.1",
+        "strict-uri-encode": "1.1.0"
+      }
+    },
     "querystring": {
       "version": "0.2.0",
       "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz",
@@ -8275,12 +8734,6 @@
           "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz",
           "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==",
           "dev": true
-        },
-        "kind-of": {
-          "version": "6.0.2",
-          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
-          "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==",
-          "dev": true
         }
       }
     },
@@ -8353,82 +8806,51 @@
       "integrity": "sha1-DD0L6u2KAclm2Xh793goElKpeao=",
       "dev": true
     },
-    "read-cache": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
-      "integrity": "sha1-5mTvMRYRZsl1HNvo28+GtftY93Q=",
+    "read-installed": {
+      "version": "4.0.3",
+      "resolved": "https://registry.npmjs.org/read-installed/-/read-installed-4.0.3.tgz",
+      "integrity": "sha1-/5uLZ/GH0eTCm5/rMfayI6zRkGc=",
       "dev": true,
       "requires": {
-        "pify": "2.3.0"
-      },
-      "dependencies": {
-        "pify": {
-          "version": "2.3.0",
-          "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
-          "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
-          "dev": true
-        }
+        "debuglog": "1.0.1",
+        "graceful-fs": "4.1.11",
+        "read-package-json": "2.0.13",
+        "readdir-scoped-modules": "1.0.2",
+        "semver": "5.5.0",
+        "slide": "1.1.6",
+        "util-extend": "1.0.3"
+      }
+    },
+    "read-package-json": {
+      "version": "2.0.13",
+      "resolved": "https://registry.npmjs.org/read-package-json/-/read-package-json-2.0.13.tgz",
+      "integrity": "sha512-/1dZ7TRZvGrYqE0UAfN6qQb5GYBsNcqS1C0tNK601CFOJmtHI7NIGXwetEPU/OtoFHZL3hDxm4rolFFVE9Bnmg==",
+      "dev": true,
+      "requires": {
+        "glob": "7.1.2",
+        "graceful-fs": "4.1.11",
+        "json-parse-better-errors": "1.0.2",
+        "normalize-package-data": "2.4.0",
+        "slash": "1.0.0"
       }
     },
     "read-pkg": {
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz",
       "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=",
-      "dev": true,
       "requires": {
         "load-json-file": "1.1.0",
         "normalize-package-data": "2.4.0",
         "path-type": "1.1.0"
-      },
-      "dependencies": {
-        "path-type": {
-          "version": "1.1.0",
-          "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz",
-          "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=",
-          "dev": true,
-          "requires": {
-            "graceful-fs": "4.1.11",
-            "pify": "2.3.0",
-            "pinkie-promise": "2.0.1"
-          }
-        },
-        "pify": {
-          "version": "2.3.0",
-          "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
-          "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
-          "dev": true
-        }
       }
     },
     "read-pkg-up": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz",
       "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=",
-      "dev": true,
       "requires": {
         "find-up": "1.1.2",
         "read-pkg": "1.1.0"
-      },
-      "dependencies": {
-        "find-up": {
-          "version": "1.1.2",
-          "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz",
-          "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=",
-          "dev": true,
-          "requires": {
-            "path-exists": "2.1.0",
-            "pinkie-promise": "2.0.1"
-          }
-        },
-        "path-exists": {
-          "version": "2.1.0",
-          "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz",
-          "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=",
-          "dev": true,
-          "requires": {
-            "pinkie-promise": "2.0.1"
-          }
-        }
       }
     },
     "readable-stream": {
@@ -8446,6 +8868,18 @@
         "util-deprecate": "1.0.2"
       }
     },
+    "readdir-scoped-modules": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/readdir-scoped-modules/-/readdir-scoped-modules-1.0.2.tgz",
+      "integrity": "sha1-n6+jfShr5dksuuve4DDcm19AZ0c=",
+      "dev": true,
+      "requires": {
+        "debuglog": "1.0.1",
+        "dezalgo": "1.0.3",
+        "graceful-fs": "4.1.11",
+        "once": "1.4.0"
+      }
+    },
     "readdirp": {
       "version": "2.1.0",
       "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.1.0.tgz",
@@ -8468,31 +8902,41 @@
         "strip-indent": "1.0.1"
       }
     },
-    "redis": {
-      "version": "2.8.0",
-      "resolved": "https://registry.npmjs.org/redis/-/redis-2.8.0.tgz",
-      "integrity": "sha512-M1OkonEQwtRmZv4tEWF2VgpG0JWJ8Fv1PhlgT5+B+uNq2cA3Rt1Yt/ryoR+vQNOQcIEgdCdfH0jr3bDpihAw1A==",
+    "reduce-css-calc": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/reduce-css-calc/-/reduce-css-calc-1.3.0.tgz",
+      "integrity": "sha1-dHyRTgSWFKTJz7umKYca0dKSdxY=",
       "dev": true,
-      "optional": true,
       "requires": {
-        "double-ended-queue": "2.1.0-0",
-        "redis-commands": "1.3.5",
-        "redis-parser": "2.6.0"
+        "balanced-match": "0.4.2",
+        "math-expression-evaluator": "1.2.17",
+        "reduce-function-call": "1.0.2"
+      },
+      "dependencies": {
+        "balanced-match": {
+          "version": "0.4.2",
+          "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz",
+          "integrity": "sha1-yz8+PHMtwPAe5wtAPzAuYddwmDg=",
+          "dev": true
+        }
       }
     },
-    "redis-commands": {
-      "version": "1.3.5",
-      "resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.3.5.tgz",
-      "integrity": "sha512-foGF8u6MXGFF++1TZVC6icGXuMYPftKXt1FBT2vrfU9ZATNtZJ8duRC5d1lEfE8hyVe3jhelHGB91oB7I6qLsA==",
+    "reduce-function-call": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/reduce-function-call/-/reduce-function-call-1.0.2.tgz",
+      "integrity": "sha1-WiAL+S4ON3UXUv5FsKszD9S2vpk=",
       "dev": true,
-      "optional": true
-    },
-    "redis-parser": {
-      "version": "2.6.0",
-      "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-2.6.0.tgz",
-      "integrity": "sha1-Uu0J2srBCPGmMcB+m2mUHnoZUEs=",
-      "dev": true,
-      "optional": true
+      "requires": {
+        "balanced-match": "0.4.2"
+      },
+      "dependencies": {
+        "balanced-match": {
+          "version": "0.4.2",
+          "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz",
+          "integrity": "sha1-yz8+PHMtwPAe5wtAPzAuYddwmDg=",
+          "dev": true
+        }
+      }
     },
     "reflect-metadata": {
       "version": "0.1.12",
@@ -8555,14 +8999,6 @@
       "dev": true,
       "requires": {
         "jsesc": "0.5.0"
-      },
-      "dependencies": {
-        "jsesc": {
-          "version": "0.5.0",
-          "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz",
-          "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=",
-          "dev": true
-        }
       }
     },
     "relateurl": {
@@ -8649,33 +9085,19 @@
         "uuid": "3.2.1"
       }
     },
-    "requestretry": {
-      "version": "1.13.0",
-      "resolved": "https://registry.npmjs.org/requestretry/-/requestretry-1.13.0.tgz",
-      "integrity": "sha512-Lmh9qMvnQXADGAQxsXHP4rbgO6pffCfuR8XUBdP9aitJcLQJxhp7YZK4xAVYXnPJ5E52mwrfiKQtKonPL8xsmg==",
+    "request-progress": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/request-progress/-/request-progress-2.0.1.tgz",
+      "integrity": "sha1-XTa7V5YcZzqlt4jbyBQf3yO0Tgg=",
       "dev": true,
-      "optional": true,
       "requires": {
-        "extend": "3.0.1",
-        "lodash": "4.17.10",
-        "request": "2.81.0",
-        "when": "3.7.8"
-      },
-      "dependencies": {
-        "when": {
-          "version": "3.7.8",
-          "resolved": "https://registry.npmjs.org/when/-/when-3.7.8.tgz",
-          "integrity": "sha1-xxMLan6gRpPoQs3J56Hyqjmjn4I=",
-          "dev": true,
-          "optional": true
-        }
+        "throttleit": "1.0.0"
       }
     },
     "require-directory": {
       "version": "2.1.1",
       "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
-      "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=",
-      "dev": true
+      "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I="
     },
     "require-from-string": {
       "version": "1.2.1",
@@ -8686,8 +9108,7 @@
     "require-main-filename": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz",
-      "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=",
-      "dev": true
+      "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE="
     },
     "requires-port": {
       "version": "1.0.0",
@@ -8696,34 +9117,19 @@
       "dev": true
     },
     "reselect": {
-      "version": "3.0.1",
-      "resolved": "https://registry.npmjs.org/reselect/-/reselect-3.0.1.tgz",
-      "integrity": "sha1-79qpjqdFEyTQkrKyFjpqHXqaIUc="
+      "version": "2.5.4",
+      "resolved": "https://registry.npmjs.org/reselect/-/reselect-2.5.4.tgz",
+      "integrity": "sha1-t9I/3wC4P6etAnlUb427vXZccEc="
     },
     "resolve": {
-      "version": "1.8.1",
-      "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.8.1.tgz",
-      "integrity": "sha512-AicPrAC7Qu1JxPCZ9ZgCZlY35QgFnNqc+0LtbRNxnVw4TXvjQ72wnuL9JQcEBgXkI9JM8MsT9kaQoHcpCRJOYA==",
+      "version": "1.7.1",
+      "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.7.1.tgz",
+      "integrity": "sha512-c7rwLofp8g1U+h1KNyHL/jicrKg1Ek4q+Lr33AL65uZTinUZHe30D5HlyN5V9NW0JX1D5dXQ4jqW5l7Sy/kGfw==",
       "dev": true,
       "requires": {
         "path-parse": "1.0.5"
       }
     },
-    "resolve-cwd": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz",
-      "integrity": "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=",
-      "dev": true,
-      "requires": {
-        "resolve-from": "3.0.0"
-      }
-    },
-    "resolve-from": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz",
-      "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=",
-      "dev": true
-    },
     "resolve-url": {
       "version": "0.2.1",
       "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz",
@@ -8774,11 +9180,11 @@
       }
     },
     "rxjs": {
-      "version": "5.5.11",
-      "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-5.5.11.tgz",
-      "integrity": "sha512-3bjO7UwWfA2CV7lmwYMBzj4fQ6Cq+ftHc2MvUe+WMS7wcdJ1LosDWmdjPQanYp2dBRj572p7PeU81JUxHKOcBA==",
+      "version": "5.4.1",
+      "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-5.4.1.tgz",
+      "integrity": "sha1-ti91fyeURdJloYpY+wpw3JDpFiY=",
       "requires": {
-        "symbol-observable": "1.0.1"
+        "symbol-observable": "1.2.0"
       }
     },
     "safe-buffer": {
@@ -8796,12 +9202,6 @@
         "ret": "0.1.15"
       }
     },
-    "safer-buffer": {
-      "version": "2.1.2",
-      "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
-      "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
-      "dev": true
-    },
     "sass-graph": {
       "version": "2.2.4",
       "resolved": "https://registry.npmjs.org/sass-graph/-/sass-graph-2.2.4.tgz",
@@ -8813,6 +9213,40 @@
         "lodash": "4.17.10",
         "scss-tokenizer": "0.2.3",
         "yargs": "7.1.0"
+      },
+      "dependencies": {
+        "yargs": {
+          "version": "7.1.0",
+          "resolved": "https://registry.npmjs.org/yargs/-/yargs-7.1.0.tgz",
+          "integrity": "sha1-a6MY6xaWFyf10oT46gA+jWFU0Mg=",
+          "dev": true,
+          "optional": true,
+          "requires": {
+            "camelcase": "3.0.0",
+            "cliui": "3.2.0",
+            "decamelize": "1.2.0",
+            "get-caller-file": "1.0.2",
+            "os-locale": "1.4.0",
+            "read-pkg-up": "1.0.1",
+            "require-directory": "2.1.1",
+            "require-main-filename": "1.0.1",
+            "set-blocking": "2.0.0",
+            "string-width": "1.0.2",
+            "which-module": "1.0.0",
+            "y18n": "3.2.1",
+            "yargs-parser": "5.0.0"
+          }
+        },
+        "yargs-parser": {
+          "version": "5.0.0",
+          "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-5.0.0.tgz",
+          "integrity": "sha1-J17PDX/+Bcd+ZOfIbkzZS/DhIoo=",
+          "dev": true,
+          "optional": true,
+          "requires": {
+            "camelcase": "3.0.0"
+          }
+        }
       }
     },
     "sass-loader": {
@@ -8826,6 +9260,14 @@
         "lodash.tail": "4.1.1",
         "neo-async": "2.5.1",
         "pify": "3.0.0"
+      },
+      "dependencies": {
+        "pify": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
+          "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=",
+          "dev": true
+        }
       }
     },
     "saucelabs": {
@@ -8835,51 +9277,21 @@
       "dev": true,
       "requires": {
         "https-proxy-agent": "1.0.0"
-      },
-      "dependencies": {
-        "agent-base": {
-          "version": "2.1.1",
-          "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-2.1.1.tgz",
-          "integrity": "sha1-1t4Q1a9hMtW9aSQn1G/FOFOQlMc=",
-          "dev": true,
-          "requires": {
-            "extend": "3.0.1",
-            "semver": "5.0.3"
-          }
-        },
-        "https-proxy-agent": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-1.0.0.tgz",
-          "integrity": "sha1-NffabEjOTdv6JkiRrFk+5f+GceY=",
-          "dev": true,
-          "requires": {
-            "agent-base": "2.1.1",
-            "debug": "2.6.9",
-            "extend": "3.0.1"
-          }
-        },
-        "semver": {
-          "version": "5.0.3",
-          "resolved": "https://registry.npmjs.org/semver/-/semver-5.0.3.tgz",
-          "integrity": "sha1-d0Zt5YnNXTyV8TiqeLxWmjy10no=",
-          "dev": true
-        }
       }
     },
     "sax": {
-      "version": "0.5.8",
-      "resolved": "https://registry.npmjs.org/sax/-/sax-0.5.8.tgz",
-      "integrity": "sha1-1HLbIo6zMcJQaw6MFVJK25OdEsE=",
+      "version": "1.2.4",
+      "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
+      "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==",
       "dev": true
     },
     "schema-utils": {
-      "version": "0.4.5",
-      "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.4.5.tgz",
-      "integrity": "sha512-yYrjb9TX2k/J1Y5UNy3KYdZq10xhYcF8nMpAW6o3hy6Q8WSIEf9lJHG/ePnOBfziPM3fvQwfOwa13U/Fh8qTfA==",
+      "version": "0.3.0",
+      "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.3.0.tgz",
+      "integrity": "sha1-9YdyIs4+kx7a4DnxfrNxbnE3+M8=",
       "dev": true,
       "requires": {
-        "ajv": "6.5.1",
-        "ajv-keywords": "3.2.0"
+        "ajv": "5.5.2"
       }
     },
     "scss-tokenizer": {
@@ -8946,8 +9358,7 @@
     "semver": {
       "version": "5.5.0",
       "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz",
-      "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==",
-      "dev": true
+      "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA=="
     },
     "semver-dsl": {
       "version": "1.0.1",
@@ -8958,15 +9369,6 @@
         "semver": "5.5.0"
       }
     },
-    "semver-intersect": {
-      "version": "1.3.1",
-      "resolved": "https://registry.npmjs.org/semver-intersect/-/semver-intersect-1.3.1.tgz",
-      "integrity": "sha1-j6hKnhAovSOeRTDRo+GB5pjYhLo=",
-      "dev": true,
-      "requires": {
-        "semver": "5.5.0"
-      }
-    },
     "send": {
       "version": "0.16.2",
       "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz",
@@ -9032,8 +9434,7 @@
     "set-blocking": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
-      "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=",
-      "dev": true
+      "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc="
     },
     "set-immediate-shim": {
       "version": "1.0.1",
@@ -9120,6 +9521,14 @@
       "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=",
       "dev": true
     },
+    "showdown": {
+      "version": "1.6.4",
+      "resolved": "https://registry.npmjs.org/showdown/-/showdown-1.6.4.tgz",
+      "integrity": "sha1-BWu7ZU7NuNhkOuEtbVl4k8yvRsY=",
+      "requires": {
+        "yargs": "6.6.0"
+      }
+    },
     "signal-exit": {
       "version": "3.0.2",
       "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz",
@@ -9135,38 +9544,18 @@
         "debug": "2.6.9"
       }
     },
-    "slack-node": {
-      "version": "0.2.0",
-      "resolved": "https://registry.npmjs.org/slack-node/-/slack-node-0.2.0.tgz",
-      "integrity": "sha1-3kuN3aqLeT9h29KTgQT9q/N9+jA=",
-      "dev": true,
-      "optional": true,
-      "requires": {
-        "requestretry": "1.13.0"
-      }
-    },
     "slash": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz",
       "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=",
       "dev": true
     },
-    "smart-buffer": {
-      "version": "1.1.15",
-      "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-1.1.15.tgz",
-      "integrity": "sha1-fxFLW2X6s+KjWqd1uxLw0cZJvxY=",
+    "slide": {
+      "version": "1.1.6",
+      "resolved": "https://registry.npmjs.org/slide/-/slide-1.1.6.tgz",
+      "integrity": "sha1-VusCfWW00tzmyy4tMsTUr8nh1wc=",
       "dev": true
     },
-    "smtp-connection": {
-      "version": "2.12.0",
-      "resolved": "https://registry.npmjs.org/smtp-connection/-/smtp-connection-2.12.0.tgz",
-      "integrity": "sha1-1275EnyyPCJZ7bHoNJwujV4tdME=",
-      "dev": true,
-      "requires": {
-        "httpntlm": "1.6.1",
-        "nodemailer-shared": "1.1.0"
-      }
-    },
     "snapdragon": {
       "version": "0.8.2",
       "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz",
@@ -9251,18 +9640,6 @@
             "is-data-descriptor": "1.0.0",
             "kind-of": "6.0.2"
           }
-        },
-        "isobject": {
-          "version": "3.0.1",
-          "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
-          "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
-          "dev": true
-        },
-        "kind-of": {
-          "version": "6.0.2",
-          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
-          "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==",
-          "dev": true
         }
       }
     },
@@ -9273,6 +9650,17 @@
       "dev": true,
       "requires": {
         "kind-of": "3.2.2"
+      },
+      "dependencies": {
+        "kind-of": {
+          "version": "3.2.2",
+          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+          "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+          "dev": true,
+          "requires": {
+            "is-buffer": "1.1.6"
+          }
+        }
       }
     },
     "sntp": {
@@ -9285,82 +9673,163 @@
       }
     },
     "socket.io": {
-      "version": "2.0.4",
-      "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-2.0.4.tgz",
-      "integrity": "sha1-waRZDO/4fs8TxyZS8Eb3FrKeYBQ=",
+      "version": "1.7.2",
+      "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-1.7.2.tgz",
+      "integrity": "sha1-g7u98ueSY7N4kA2kA+eEPgXcO3E=",
       "dev": true,
       "requires": {
-        "debug": "2.6.9",
-        "engine.io": "3.1.5",
-        "socket.io-adapter": "1.1.1",
-        "socket.io-client": "2.0.4",
-        "socket.io-parser": "3.1.3"
-      }
-    },
-    "socket.io-adapter": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-1.1.1.tgz",
-      "integrity": "sha1-KoBeihTWNyEk3ZFZrUUC+MsH8Gs=",
-      "dev": true
-    },
-    "socket.io-client": {
-      "version": "2.0.4",
-      "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.0.4.tgz",
-      "integrity": "sha1-CRilUkBtxeVAs4Dc2Xr8SmQzL44=",
-      "dev": true,
-      "requires": {
-        "backo2": "1.0.2",
-        "base64-arraybuffer": "0.1.5",
-        "component-bind": "1.0.0",
-        "component-emitter": "1.2.1",
-        "debug": "2.6.9",
-        "engine.io-client": "3.1.6",
-        "has-cors": "1.1.0",
-        "indexof": "0.0.1",
-        "object-component": "0.0.3",
-        "parseqs": "0.0.5",
-        "parseuri": "0.0.5",
-        "socket.io-parser": "3.1.3",
-        "to-array": "0.1.4"
-      }
-    },
-    "socket.io-parser": {
-      "version": "3.1.3",
-      "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.1.3.tgz",
-      "integrity": "sha512-g0a2HPqLguqAczs3dMECuA1RgoGFPyvDqcbaDEdCWY9g59kdUAz3YRmaJBNKXflrHNwB7Q12Gkf/0CZXfdHR7g==",
-      "dev": true,
-      "requires": {
-        "component-emitter": "1.2.1",
-        "debug": "3.1.0",
-        "has-binary2": "1.0.3",
-        "isarray": "2.0.1"
+        "debug": "2.3.3",
+        "engine.io": "1.8.2",
+        "has-binary": "0.1.7",
+        "object-assign": "4.1.0",
+        "socket.io-adapter": "0.5.0",
+        "socket.io-client": "1.7.2",
+        "socket.io-parser": "2.3.1"
       },
       "dependencies": {
         "debug": {
-          "version": "3.1.0",
-          "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
-          "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
+          "version": "2.3.3",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-2.3.3.tgz",
+          "integrity": "sha1-QMRT5n5uE8kB3ewxeviYbNqe/4w=",
           "dev": true,
           "requires": {
-            "ms": "2.0.0"
+            "ms": "0.7.2"
+          }
+        },
+        "ms": {
+          "version": "0.7.2",
+          "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz",
+          "integrity": "sha1-riXPJRKziFodldfwN4aNhDESR2U=",
+          "dev": true
+        },
+        "object-assign": {
+          "version": "4.1.0",
+          "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.0.tgz",
+          "integrity": "sha1-ejs9DpgGPUP0wD8uiubNUahog6A=",
+          "dev": true
+        }
+      }
+    },
+    "socket.io-adapter": {
+      "version": "0.5.0",
+      "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-0.5.0.tgz",
+      "integrity": "sha1-y21LuL7IHhB4uZZ3+c7QBGBmu4s=",
+      "dev": true,
+      "requires": {
+        "debug": "2.3.3",
+        "socket.io-parser": "2.3.1"
+      },
+      "dependencies": {
+        "debug": {
+          "version": "2.3.3",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-2.3.3.tgz",
+          "integrity": "sha1-QMRT5n5uE8kB3ewxeviYbNqe/4w=",
+          "dev": true,
+          "requires": {
+            "ms": "0.7.2"
+          }
+        },
+        "ms": {
+          "version": "0.7.2",
+          "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz",
+          "integrity": "sha1-riXPJRKziFodldfwN4aNhDESR2U=",
+          "dev": true
+        }
+      }
+    },
+    "socket.io-client": {
+      "version": "1.7.2",
+      "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-1.7.2.tgz",
+      "integrity": "sha1-Of2ww91FDjIbfkDP2DYS7FM91kQ=",
+      "dev": true,
+      "requires": {
+        "backo2": "1.0.2",
+        "component-bind": "1.0.0",
+        "component-emitter": "1.2.1",
+        "debug": "2.3.3",
+        "engine.io-client": "1.8.2",
+        "has-binary": "0.1.7",
+        "indexof": "0.0.1",
+        "object-component": "0.0.3",
+        "parseuri": "0.0.5",
+        "socket.io-parser": "2.3.1",
+        "to-array": "0.1.4"
+      },
+      "dependencies": {
+        "debug": {
+          "version": "2.3.3",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-2.3.3.tgz",
+          "integrity": "sha1-QMRT5n5uE8kB3ewxeviYbNqe/4w=",
+          "dev": true,
+          "requires": {
+            "ms": "0.7.2"
+          }
+        },
+        "ms": {
+          "version": "0.7.2",
+          "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz",
+          "integrity": "sha1-riXPJRKziFodldfwN4aNhDESR2U=",
+          "dev": true
+        }
+      }
+    },
+    "socket.io-parser": {
+      "version": "2.3.1",
+      "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-2.3.1.tgz",
+      "integrity": "sha1-3VMgJRA85Clpcya+/WQAX8/ltKA=",
+      "dev": true,
+      "requires": {
+        "component-emitter": "1.1.2",
+        "debug": "2.2.0",
+        "isarray": "0.0.1",
+        "json3": "3.3.2"
+      },
+      "dependencies": {
+        "component-emitter": {
+          "version": "1.1.2",
+          "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.1.2.tgz",
+          "integrity": "sha1-KWWU8nU9qmOZbSrwjRWpURbJrsM=",
+          "dev": true
+        },
+        "debug": {
+          "version": "2.2.0",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz",
+          "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=",
+          "dev": true,
+          "requires": {
+            "ms": "0.7.1"
           }
         },
         "isarray": {
-          "version": "2.0.1",
-          "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz",
-          "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=",
+          "version": "0.0.1",
+          "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
+          "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=",
+          "dev": true
+        },
+        "ms": {
+          "version": "0.7.1",
+          "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz",
+          "integrity": "sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg=",
           "dev": true
         }
       }
     },
     "sockjs": {
-      "version": "0.3.19",
-      "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.19.tgz",
-      "integrity": "sha512-V48klKZl8T6MzatbLlzzRNhMepEys9Y4oGFpypBFFn1gLI/QQ9HtLLyWJNbPlwGLelOVOEijUbTTJeLLI59jLw==",
+      "version": "0.3.18",
+      "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.18.tgz",
+      "integrity": "sha1-2bKJMWyn33dZXvKZ4HXw+TfrQgc=",
       "dev": true,
       "requires": {
         "faye-websocket": "0.10.0",
-        "uuid": "3.2.1"
+        "uuid": "2.0.3"
+      },
+      "dependencies": {
+        "uuid": {
+          "version": "2.0.3",
+          "resolved": "https://registry.npmjs.org/uuid/-/uuid-2.0.3.tgz",
+          "integrity": "sha1-Z+LoY3lyFVMN/zGOW/nc6/1Hsho=",
+          "dev": true
+        }
       }
     },
     "sockjs-client": {
@@ -9374,7 +9843,7 @@
         "faye-websocket": "0.11.1",
         "inherits": "2.0.3",
         "json3": "3.3.2",
-        "url-parse": "1.4.1"
+        "url-parse": "1.4.0"
       },
       "dependencies": {
         "faye-websocket": {
@@ -9388,24 +9857,13 @@
         }
       }
     },
-    "socks": {
-      "version": "1.1.10",
-      "resolved": "https://registry.npmjs.org/socks/-/socks-1.1.10.tgz",
-      "integrity": "sha1-W4t/x8jzQcU+0FbpKbe/Tei6e1o=",
+    "sort-keys": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz",
+      "integrity": "sha1-RBttTTRnmPG05J6JIK37oOVD+a0=",
       "dev": true,
       "requires": {
-        "ip": "1.1.5",
-        "smart-buffer": "1.1.15"
-      }
-    },
-    "socks-proxy-agent": {
-      "version": "3.0.1",
-      "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-3.0.1.tgz",
-      "integrity": "sha512-ZwEDymm204mTzvdqyUqOdovVr2YRd2NYskrYrF2LXyZ9qDiMAoFESGK8CRphiO7rtbo2Y757k2Nia3x2hGtalA==",
-      "dev": true,
-      "requires": {
-        "agent-base": "4.2.0",
-        "socks": "1.1.10"
+        "is-plain-obj": "1.1.0"
       }
     },
     "source-list-map": {
@@ -9420,6 +9878,37 @@
       "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
       "dev": true
     },
+    "source-map-loader": {
+      "version": "0.2.3",
+      "resolved": "https://registry.npmjs.org/source-map-loader/-/source-map-loader-0.2.3.tgz",
+      "integrity": "sha512-MYbFX9DYxmTQFfy2v8FC1XZwpwHKYxg3SK8Wb7VPBKuhDjz8gi9re2819MsG4p49HDyiOSUKlmZ+nQBArW5CGw==",
+      "dev": true,
+      "requires": {
+        "async": "2.6.1",
+        "loader-utils": "0.2.17",
+        "source-map": "0.6.1"
+      },
+      "dependencies": {
+        "loader-utils": {
+          "version": "0.2.17",
+          "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-0.2.17.tgz",
+          "integrity": "sha1-+G5jdNQyBabmxg6RlvF8Apm/s0g=",
+          "dev": true,
+          "requires": {
+            "big.js": "3.2.0",
+            "emojis-list": "2.1.0",
+            "json5": "0.5.1",
+            "object-assign": "4.1.1"
+          }
+        },
+        "source-map": {
+          "version": "0.6.1",
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+          "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+          "dev": true
+        }
+      }
+    },
     "source-map-resolve": {
       "version": "0.5.2",
       "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.2.tgz",
@@ -9452,7 +9941,6 @@
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.0.0.tgz",
       "integrity": "sha512-N19o9z5cEyc8yQQPukRCZ9EUmb4HUpnrmaL/fxS2pBo2jbfcFRVuFZ/oFC+vZz0MNNk0h80iMn5/S6qGZOL5+g==",
-      "dev": true,
       "requires": {
         "spdx-expression-parse": "3.0.0",
         "spdx-license-ids": "3.0.0"
@@ -9461,14 +9949,12 @@
     "spdx-exceptions": {
       "version": "2.1.0",
       "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.1.0.tgz",
-      "integrity": "sha512-4K1NsmrlCU1JJgUrtgEeTVyfx8VaYea9J9LvARxhbHtVtohPs/gFGG5yy49beySjlIMhhXZ4QqujIZEfS4l6Cg==",
-      "dev": true
+      "integrity": "sha512-4K1NsmrlCU1JJgUrtgEeTVyfx8VaYea9J9LvARxhbHtVtohPs/gFGG5yy49beySjlIMhhXZ4QqujIZEfS4l6Cg=="
     },
     "spdx-expression-parse": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz",
       "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==",
-      "dev": true,
       "requires": {
         "spdx-exceptions": "2.1.0",
         "spdx-license-ids": "3.0.0"
@@ -9477,8 +9963,7 @@
     "spdx-license-ids": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.0.tgz",
-      "integrity": "sha512-2+EPwgbnmOIl8HjGBXXMd9NAu02vLjOO1nWw4kmeRDFyHn+M/ETfHxQUK0oXg8ctgVnl9t3rosNVsZ1jG61nDA==",
-      "dev": true
+      "integrity": "sha512-2+EPwgbnmOIl8HjGBXXMd9NAu02vLjOO1nWw4kmeRDFyHn+M/ETfHxQUK0oXg8ctgVnl9t3rosNVsZ1jG61nDA=="
     },
     "spdy": {
       "version": "3.4.7",
@@ -9525,9 +10010,9 @@
       "dev": true
     },
     "sshpk": {
-      "version": "1.14.2",
-      "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.14.2.tgz",
-      "integrity": "sha1-xvxhZIo9nE52T9P8306hBeSSupg=",
+      "version": "1.14.1",
+      "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.14.1.tgz",
+      "integrity": "sha1-Ew9Zde3a2WPx1W+SuaxsUfqfg+s=",
       "dev": true,
       "requires": {
         "asn1": "0.2.3",
@@ -9537,7 +10022,6 @@
         "ecc-jsbn": "0.1.1",
         "getpass": "0.1.7",
         "jsbn": "0.1.1",
-        "safer-buffer": "2.1.2",
         "tweetnacl": "0.14.5"
       },
       "dependencies": {
@@ -9616,9 +10100,9 @@
       }
     },
     "stream-http": {
-      "version": "2.8.3",
-      "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.3.tgz",
-      "integrity": "sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==",
+      "version": "2.8.2",
+      "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.2.tgz",
+      "integrity": "sha512-QllfrBhqF1DPcz46WxKTs6Mz1Bpc+8Qm6vbqOpVav5odAXwbyzwnEczoWqtxrsmlO+cJqtPrp/8gWKWjaKLLlA==",
       "dev": true,
       "requires": {
         "builtin-status-codes": "3.0.0",
@@ -9634,34 +10118,16 @@
       "integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=",
       "dev": true
     },
-    "streamroller": {
-      "version": "0.7.0",
-      "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-0.7.0.tgz",
-      "integrity": "sha512-WREzfy0r0zUqp3lGO096wRuUp7ho1X6uo/7DJfTlEi0Iv/4gT7YHqXDjKC2ioVGBZtE8QzsQD9nx1nIuoZ57jQ==",
-      "dev": true,
-      "requires": {
-        "date-format": "1.2.0",
-        "debug": "3.1.0",
-        "mkdirp": "0.5.1",
-        "readable-stream": "2.3.6"
-      },
-      "dependencies": {
-        "debug": {
-          "version": "3.1.0",
-          "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
-          "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
-          "dev": true,
-          "requires": {
-            "ms": "2.0.0"
-          }
-        }
-      }
+    "strict-uri-encode": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz",
+      "integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=",
+      "dev": true
     },
     "string-width": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
       "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
-      "dev": true,
       "requires": {
         "code-point-at": "1.1.0",
         "is-fullwidth-code-point": "1.0.0",
@@ -9687,7 +10153,6 @@
       "version": "3.0.1",
       "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
       "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
-      "dev": true,
       "requires": {
         "ansi-regex": "2.1.1"
       }
@@ -9696,7 +10161,6 @@
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz",
       "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=",
-      "dev": true,
       "requires": {
         "is-utf8": "0.2.1"
       }
@@ -9723,36 +10187,12 @@
       "dev": true
     },
     "style-loader": {
-      "version": "0.19.1",
-      "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-0.19.1.tgz",
-      "integrity": "sha512-IRE+ijgojrygQi3rsqT0U4dd+UcPCqcVvauZpCnQrGAlEe+FUIyrK93bUDScamesjP08JlQNsFJU+KmPedP5Og==",
+      "version": "0.13.2",
+      "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-0.13.2.tgz",
+      "integrity": "sha1-dFMzhM9pjHEEx5URULSXF63C87s=",
       "dev": true,
       "requires": {
-        "loader-utils": "1.1.0",
-        "schema-utils": "0.3.0"
-      },
-      "dependencies": {
-        "ajv": {
-          "version": "5.5.2",
-          "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz",
-          "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=",
-          "dev": true,
-          "requires": {
-            "co": "4.6.0",
-            "fast-deep-equal": "1.1.0",
-            "fast-json-stable-stringify": "2.0.0",
-            "json-schema-traverse": "0.3.1"
-          }
-        },
-        "schema-utils": {
-          "version": "0.3.0",
-          "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.3.0.tgz",
-          "integrity": "sha1-9YdyIs4+kx7a4DnxfrNxbnE3+M8=",
-          "dev": true,
-          "requires": {
-            "ajv": "5.5.2"
-          }
-        }
+        "loader-utils": "1.1.0"
       }
     },
     "stylus": {
@@ -9783,6 +10223,12 @@
             "path-is-absolute": "1.0.1"
           }
         },
+        "sax": {
+          "version": "0.5.8",
+          "resolved": "https://registry.npmjs.org/sax/-/sax-0.5.8.tgz",
+          "integrity": "sha1-1HLbIo6zMcJQaw6MFVJK25OdEsE=",
+          "dev": true
+        },
         "source-map": {
           "version": "0.1.43",
           "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.43.tgz",
@@ -9806,18 +10252,33 @@
       }
     },
     "supports-color": {
-      "version": "4.5.0",
-      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz",
-      "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=",
+      "version": "3.2.3",
+      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz",
+      "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=",
       "dev": true,
       "requires": {
-        "has-flag": "2.0.0"
+        "has-flag": "1.0.0"
+      }
+    },
+    "svgo": {
+      "version": "0.7.2",
+      "resolved": "https://registry.npmjs.org/svgo/-/svgo-0.7.2.tgz",
+      "integrity": "sha1-n1dyQTlSE1xv779Ar+ak+qiLS7U=",
+      "dev": true,
+      "requires": {
+        "coa": "1.0.4",
+        "colors": "1.1.2",
+        "csso": "2.3.2",
+        "js-yaml": "3.7.0",
+        "mkdirp": "0.5.1",
+        "sax": "1.2.4",
+        "whet.extend": "0.9.9"
       }
     },
     "symbol-observable": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.0.1.tgz",
-      "integrity": "sha1-g0D8RwLDEi310iKI+IKD9RPT/dQ="
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz",
+      "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ=="
     },
     "tapable": {
       "version": "0.2.8",
@@ -9837,6 +10298,22 @@
         "inherits": "2.0.3"
       }
     },
+    "text-mask-addons": {
+      "version": "3.7.2",
+      "resolved": "https://registry.npmjs.org/text-mask-addons/-/text-mask-addons-3.7.2.tgz",
+      "integrity": "sha512-uezfAuWcDJ/B/lu5ooz38DEdI8NfIVX2xKwshLs40Er8MBGSppklOqOYw32wEmCUP7XPVX2u3u2TdQcIRuBLXA=="
+    },
+    "text-mask-core": {
+      "version": "5.1.1",
+      "resolved": "https://registry.npmjs.org/text-mask-core/-/text-mask-core-5.1.1.tgz",
+      "integrity": "sha512-tcHJSs0jlHpIed2flCuZZhUIGN1KfDsN/qCW7lkYxyXkKt4OLd3GXCHPFsY9rOLhmyC43ZRTM+tCxizBzluWpw=="
+    },
+    "throttleit": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/throttleit/-/throttleit-1.0.0.tgz",
+      "integrity": "sha1-nnhYNtr0Z0MUWlmEtiaNgoUorGw=",
+      "dev": true
+    },
     "through": {
       "version": "2.3.8",
       "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
@@ -9853,13 +10330,6 @@
         "xtend": "4.0.1"
       }
     },
-    "thunkify": {
-      "version": "2.1.2",
-      "resolved": "https://registry.npmjs.org/thunkify/-/thunkify-2.1.2.tgz",
-      "integrity": "sha1-+qDp0jDFGsyVyhOjYawFyn4EVT0=",
-      "dev": true,
-      "optional": true
-    },
     "thunky": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.0.2.tgz",
@@ -9881,17 +10351,10 @@
         "setimmediate": "1.0.5"
       }
     },
-    "timespan": {
-      "version": "2.3.0",
-      "resolved": "https://registry.npmjs.org/timespan/-/timespan-2.3.0.tgz",
-      "integrity": "sha1-SQLOBAvRPYRcj1myfp1ZutbzmSk=",
-      "dev": true,
-      "optional": true
-    },
     "tmp": {
-      "version": "0.0.33",
-      "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
-      "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==",
+      "version": "0.0.28",
+      "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.28.tgz",
+      "integrity": "sha1-Fyc1t/YU6nrzlmT6hM8N5OUV0SA=",
       "dev": true,
       "requires": {
         "os-tmpdir": "1.0.2"
@@ -9922,6 +10385,17 @@
       "dev": true,
       "requires": {
         "kind-of": "3.2.2"
+      },
+      "dependencies": {
+        "kind-of": {
+          "version": "3.2.2",
+          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+          "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+          "dev": true,
+          "requires": {
+            "is-buffer": "1.1.6"
+          }
+        }
       }
     },
     "to-regex": {
@@ -9944,17 +10418,6 @@
       "requires": {
         "is-number": "3.0.0",
         "repeat-string": "1.6.1"
-      },
-      "dependencies": {
-        "is-number": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
-          "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=",
-          "dev": true,
-          "requires": {
-            "kind-of": "3.2.2"
-          }
-        }
       }
     },
     "toposort": {
@@ -9970,20 +10433,12 @@
       "dev": true,
       "requires": {
         "punycode": "1.4.1"
-      },
-      "dependencies": {
-        "punycode": {
-          "version": "1.4.1",
-          "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
-          "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=",
-          "dev": true
-        }
       }
     },
-    "tree-kill": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.0.tgz",
-      "integrity": "sha512-DlX6dR0lOIRDFxI0mjL9IYg6OTncLm/Zt+JiBhE5OlFcAR8yc9S7FFXU9so0oda47frdM/JFsk7UjNt9vscKcg==",
+    "treeify": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/treeify/-/treeify-1.1.0.tgz",
+      "integrity": "sha512-1m4RA7xVAJrSGrrXGs0L3YTwyvBs2S8PbRHaLZAkFw7JR8oIFwYtysxlBZhYIa7xSyiYJKZ3iGrrk55cGA3i9A==",
       "dev": true
     },
     "trim-newlines": {
@@ -10024,82 +10479,63 @@
         }
       }
     },
+    "tryer": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/tryer/-/tryer-1.0.0.tgz",
+      "integrity": "sha1-Antp+oIyJeVRys4+8DsR9qs3wdc=",
+      "dev": true
+    },
     "ts-node": {
-      "version": "4.1.0",
-      "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-4.1.0.tgz",
-      "integrity": "sha512-xcZH12oVg9PShKhy3UHyDmuDLV3y7iKwX25aMVPt1SIXSuAfWkFiGPEkg+th8R4YKW/QCxDoW7lJdb15lx6QWg==",
+      "version": "3.0.6",
+      "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-3.0.6.tgz",
+      "integrity": "sha1-VRJ/95DH7r9rpowebd6UsJqqIeA=",
       "dev": true,
       "requires": {
         "arrify": "1.0.1",
-        "chalk": "2.4.1",
+        "chalk": "1.1.3",
         "diff": "3.5.0",
         "make-error": "1.3.4",
         "minimist": "1.2.0",
         "mkdirp": "0.5.1",
-        "source-map-support": "0.5.6",
-        "tsconfig": "7.0.0",
-        "v8flags": "3.1.1",
+        "source-map-support": "0.4.18",
+        "tsconfig": "6.0.0",
+        "v8flags": "2.1.1",
         "yn": "2.0.0"
       },
       "dependencies": {
         "chalk": {
-          "version": "2.4.1",
-          "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz",
-          "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==",
+          "version": "1.1.3",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+          "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
           "dev": true,
           "requires": {
-            "ansi-styles": "3.2.1",
+            "ansi-styles": "2.2.1",
             "escape-string-regexp": "1.0.5",
-            "supports-color": "5.4.0"
+            "has-ansi": "2.0.0",
+            "strip-ansi": "3.0.1",
+            "supports-color": "2.0.0"
           }
         },
-        "has-flag": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
-          "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
-          "dev": true
-        },
         "minimist": {
           "version": "1.2.0",
           "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
           "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
           "dev": true
         },
-        "source-map": {
-          "version": "0.6.1",
-          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
-          "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
-          "dev": true
-        },
-        "source-map-support": {
-          "version": "0.5.6",
-          "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.6.tgz",
-          "integrity": "sha512-N4KXEz7jcKqPf2b2vZF11lQIz9W5ZMuUcIOGj243lduidkf2fjkVKJS9vNxVWn3u/uxX38AcE8U9nnH9FPcq+g==",
-          "dev": true,
-          "requires": {
-            "buffer-from": "1.1.0",
-            "source-map": "0.6.1"
-          }
-        },
         "supports-color": {
-          "version": "5.4.0",
-          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz",
-          "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==",
-          "dev": true,
-          "requires": {
-            "has-flag": "3.0.0"
-          }
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
+          "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
+          "dev": true
         }
       }
     },
     "tsconfig": {
-      "version": "7.0.0",
-      "resolved": "https://registry.npmjs.org/tsconfig/-/tsconfig-7.0.0.tgz",
-      "integrity": "sha512-vZXmzPrL+EmC4T/4rVlT2jNVMWCi/O4DIiSj3UHg1OE5kCKbk4mfrXc6dZksLgRM/TZlKnousKH9bbTazUWRRw==",
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/tsconfig/-/tsconfig-6.0.0.tgz",
+      "integrity": "sha1-aw6DdgA9evGGT434+J3QBZ/80DI=",
       "dev": true,
       "requires": {
-        "@types/strip-bom": "3.0.0",
-        "@types/strip-json-comments": "0.0.30",
         "strip-bom": "3.0.0",
         "strip-json-comments": "2.0.1"
       },
@@ -10113,15 +10549,15 @@
       }
     },
     "tsickle": {
-      "version": "0.27.5",
-      "resolved": "https://registry.npmjs.org/tsickle/-/tsickle-0.27.5.tgz",
-      "integrity": "sha512-NP+CjM1EXza/M8mOXBLH3vkFEJiu1zfEAlC5WdJxHPn8l96QPz5eooP6uAgYtw1CcKfuSyIiheNUdKxtDWCNeg==",
+      "version": "0.21.6",
+      "resolved": "https://registry.npmjs.org/tsickle/-/tsickle-0.21.6.tgz",
+      "integrity": "sha1-U7Abl5xcE/2xOvs/uVgXflmRWI0=",
       "dev": true,
       "requires": {
         "minimist": "1.2.0",
         "mkdirp": "0.5.1",
-        "source-map": "0.6.1",
-        "source-map-support": "0.5.6"
+        "source-map": "0.5.7",
+        "source-map-support": "0.4.18"
       },
       "dependencies": {
         "minimist": {
@@ -10129,93 +10565,49 @@
           "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
           "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
           "dev": true
-        },
-        "source-map": {
-          "version": "0.6.1",
-          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
-          "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
-          "dev": true
-        },
-        "source-map-support": {
-          "version": "0.5.6",
-          "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.6.tgz",
-          "integrity": "sha512-N4KXEz7jcKqPf2b2vZF11lQIz9W5ZMuUcIOGj243lduidkf2fjkVKJS9vNxVWn3u/uxX38AcE8U9nnH9FPcq+g==",
-          "dev": true,
-          "requires": {
-            "buffer-from": "1.1.0",
-            "source-map": "0.6.1"
-          }
         }
       }
     },
     "tslib": {
-      "version": "1.9.2",
-      "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.2.tgz",
-      "integrity": "sha512-AVP5Xol3WivEr7hnssHDsaM+lVrVXWUvd1cfXTRkTj80b//6g2wIFEH6hZG0muGZRnHGrfttpdzRk3YlBkWjKw=="
+      "version": "1.9.1",
+      "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.1.tgz",
+      "integrity": "sha512-avfPS28HmGLLc2o4elcc2EIq2FcH++Yo5YxpBZi9Yw93BCTGFthI4HPE4Rpep6vSYQaK8e69PelM44tPj+RaQg=="
     },
     "tslint": {
-      "version": "5.9.1",
-      "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.9.1.tgz",
-      "integrity": "sha1-ElX4ej/1frCw4fDmEKi0dIBGya4=",
+      "version": "5.2.0",
+      "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.2.0.tgz",
+      "integrity": "sha1-FqKt3yDLdIOF9UTpoO2rCGvDQRQ=",
       "dev": true,
       "requires": {
         "babel-code-frame": "6.26.0",
-        "builtin-modules": "1.1.1",
-        "chalk": "2.4.1",
-        "commander": "2.15.1",
+        "colors": "1.1.2",
         "diff": "3.5.0",
+        "findup-sync": "0.3.0",
         "glob": "7.1.2",
-        "js-yaml": "3.12.0",
-        "minimatch": "3.0.4",
-        "resolve": "1.8.1",
+        "optimist": "0.6.1",
+        "resolve": "1.7.1",
         "semver": "5.5.0",
-        "tslib": "1.9.2",
-        "tsutils": "2.27.1"
+        "tslib": "1.9.1",
+        "tsutils": "1.9.1"
       },
       "dependencies": {
-        "chalk": {
-          "version": "2.4.1",
-          "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz",
-          "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==",
+        "optimist": {
+          "version": "0.6.1",
+          "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz",
+          "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=",
           "dev": true,
           "requires": {
-            "ansi-styles": "3.2.1",
-            "escape-string-regexp": "1.0.5",
-            "supports-color": "5.4.0"
-          }
-        },
-        "has-flag": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
-          "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
-          "dev": true
-        },
-        "supports-color": {
-          "version": "5.4.0",
-          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz",
-          "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==",
-          "dev": true,
-          "requires": {
-            "has-flag": "3.0.0"
+            "minimist": "0.0.8",
+            "wordwrap": "0.0.3"
           }
         }
       }
     },
-    "tsscmp": {
-      "version": "1.0.5",
-      "resolved": "https://registry.npmjs.org/tsscmp/-/tsscmp-1.0.5.tgz",
-      "integrity": "sha1-fcSjOvcVgatDN9qR2FylQn69mpc=",
-      "dev": true,
-      "optional": true
-    },
     "tsutils": {
-      "version": "2.27.1",
-      "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.27.1.tgz",
-      "integrity": "sha512-AE/7uzp32MmaHvNNFES85hhUDHFdFZp6OAiZcd6y4ZKKIg6orJTm8keYWBhIhrJQH3a4LzNKat7ZPXZt5aTf6w==",
-      "dev": true,
-      "requires": {
-        "tslib": "1.9.2"
-      }
+      "version": "1.9.1",
+      "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-1.9.1.tgz",
+      "integrity": "sha1-ufmrROVa+WgYMdXyjQrur1x1DLA=",
+      "dev": true
     },
     "tty-browserify": {
       "version": "0.0.0",
@@ -10239,15 +10631,6 @@
       "dev": true,
       "optional": true
     },
-    "type-check": {
-      "version": "0.3.2",
-      "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz",
-      "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=",
-      "dev": true,
-      "requires": {
-        "prelude-ls": "1.1.2"
-      }
-    },
     "type-is": {
       "version": "1.6.16",
       "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz",
@@ -10265,15 +10648,15 @@
       "dev": true
     },
     "typescript": {
-      "version": "2.5.3",
-      "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.5.3.tgz",
-      "integrity": "sha512-ptLSQs2S4QuS6/OD1eAKG+S5G8QQtrU5RT32JULdZQtM1L3WTi34Wsu48Yndzi8xsObRAB9RPt/KhA9wlpEF6w==",
+      "version": "2.3.4",
+      "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.3.4.tgz",
+      "integrity": "sha1-PTgyGCgjHkNPKHUUlZw3qCtin0I=",
       "dev": true
     },
     "uglify-js": {
-      "version": "3.3.28",
-      "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.3.28.tgz",
-      "integrity": "sha512-68Rc/aA6cswiaQ5SrE979UJcXX+ADA1z33/ZsPd+fbAiVdjZ16OXdbtGO+rJUUBgK6qdf3SOPhQf3K/ybF5Miw==",
+      "version": "3.3.27",
+      "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.3.27.tgz",
+      "integrity": "sha512-O94wxMSb3td/TlofkITYvYIlvIVdldvNXDVRekzK13CQZuL37ua4nrdXX0Ro7MapfUVzglRHs0/+imPRUdOghg==",
       "dev": true,
       "requires": {
         "commander": "2.15.1",
@@ -10296,55 +10679,68 @@
       "optional": true
     },
     "uglifyjs-webpack-plugin": {
-      "version": "1.2.6",
-      "resolved": "https://registry.npmjs.org/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-1.2.6.tgz",
-      "integrity": "sha512-NDP94ahjW7ZH+qzdjxjIV04n5YGnrYD2jeHgKgnpUKmdAfcXEO5DbVo21fXAm/KPMyX9k21zWFBMYm9m9R2ptg==",
+      "version": "0.4.6",
+      "resolved": "https://registry.npmjs.org/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-0.4.6.tgz",
+      "integrity": "sha1-uVH0q7a9YX5m9j64kUmOORdj4wk=",
       "dev": true,
       "requires": {
-        "cacache": "10.0.4",
-        "find-cache-dir": "1.0.0",
-        "schema-utils": "0.4.5",
-        "serialize-javascript": "1.5.0",
-        "source-map": "0.6.1",
-        "uglify-es": "3.3.9",
-        "webpack-sources": "1.1.0",
-        "worker-farm": "1.6.0"
+        "source-map": "0.5.7",
+        "uglify-js": "2.8.29",
+        "webpack-sources": "1.1.0"
       },
       "dependencies": {
-        "commander": {
-          "version": "2.13.0",
-          "resolved": "https://registry.npmjs.org/commander/-/commander-2.13.0.tgz",
-          "integrity": "sha512-MVuS359B+YzaWqjCL/c+22gfryv+mCBPHAv3zyVI2GN8EY6IRP8VwtasXn8jyyhvvq84R4ImN1OKRtcbIasjYA==",
+        "camelcase": {
+          "version": "1.2.1",
+          "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz",
+          "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=",
           "dev": true
         },
-        "source-map": {
-          "version": "0.6.1",
-          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
-          "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
-          "dev": true
-        },
-        "uglify-es": {
-          "version": "3.3.9",
-          "resolved": "https://registry.npmjs.org/uglify-es/-/uglify-es-3.3.9.tgz",
-          "integrity": "sha512-r+MU0rfv4L/0eeW3xZrd16t4NZfK8Ld4SWVglYBb7ez5uXFWHuVRs6xCTrf1yirs9a4j4Y27nn7SRfO6v67XsQ==",
+        "cliui": {
+          "version": "2.1.0",
+          "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz",
+          "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=",
           "dev": true,
           "requires": {
-            "commander": "2.13.0",
-            "source-map": "0.6.1"
+            "center-align": "0.1.3",
+            "right-align": "0.1.3",
+            "wordwrap": "0.0.2"
+          }
+        },
+        "uglify-js": {
+          "version": "2.8.29",
+          "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz",
+          "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=",
+          "dev": true,
+          "requires": {
+            "source-map": "0.5.7",
+            "uglify-to-browserify": "1.0.2",
+            "yargs": "3.10.0"
+          }
+        },
+        "wordwrap": {
+          "version": "0.0.2",
+          "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz",
+          "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=",
+          "dev": true
+        },
+        "yargs": {
+          "version": "3.10.0",
+          "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz",
+          "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=",
+          "dev": true,
+          "requires": {
+            "camelcase": "1.2.1",
+            "cliui": "2.1.0",
+            "decamelize": "1.2.0",
+            "window-size": "0.1.0"
           }
         }
       }
     },
     "ultron": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz",
-      "integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og==",
-      "dev": true
-    },
-    "underscore": {
-      "version": "1.7.0",
-      "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.7.0.tgz",
-      "integrity": "sha1-a7rwh3UA02vjTsqlhODbn+8DUgk=",
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.0.2.tgz",
+      "integrity": "sha1-rOEWq1V80Zc4ak6I9GhTeMiy5Po=",
       "dev": true
     },
     "union-value": {
@@ -10382,6 +10778,18 @@
         }
       }
     },
+    "uniq": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz",
+      "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=",
+      "dev": true
+    },
+    "uniqs": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/uniqs/-/uniqs-2.0.0.tgz",
+      "integrity": "sha1-/+3ks2slKQaW5uFl1KWe25mOawI=",
+      "dev": true
+    },
     "unique-filename": {
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.0.tgz",
@@ -10401,9 +10809,9 @@
       }
     },
     "universalify": {
-      "version": "0.1.2",
-      "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
-      "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==",
+      "version": "0.1.1",
+      "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.1.tgz",
+      "integrity": "sha1-+nG63UQ3r0wUiEHjs7Fl+enlkLc=",
       "dev": true
     },
     "unpipe": {
@@ -10449,12 +10857,6 @@
           "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz",
           "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=",
           "dev": true
-        },
-        "isobject": {
-          "version": "3.0.1",
-          "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
-          "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
-          "dev": true
         }
       }
     },
@@ -10470,15 +10872,6 @@
       "integrity": "sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg=",
       "dev": true
     },
-    "uri-js": {
-      "version": "4.2.2",
-      "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz",
-      "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==",
-      "dev": true,
-      "requires": {
-        "punycode": "2.1.1"
-      }
-    },
     "urix": {
       "version": "0.1.0",
       "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz",
@@ -10512,35 +10905,12 @@
         "loader-utils": "1.1.0",
         "mime": "1.6.0",
         "schema-utils": "0.3.0"
-      },
-      "dependencies": {
-        "ajv": {
-          "version": "5.5.2",
-          "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz",
-          "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=",
-          "dev": true,
-          "requires": {
-            "co": "4.6.0",
-            "fast-deep-equal": "1.1.0",
-            "fast-json-stable-stringify": "2.0.0",
-            "json-schema-traverse": "0.3.1"
-          }
-        },
-        "schema-utils": {
-          "version": "0.3.0",
-          "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.3.0.tgz",
-          "integrity": "sha1-9YdyIs4+kx7a4DnxfrNxbnE3+M8=",
-          "dev": true,
-          "requires": {
-            "ajv": "5.5.2"
-          }
-        }
       }
     },
     "url-parse": {
-      "version": "1.4.1",
-      "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.4.1.tgz",
-      "integrity": "sha512-x95Td74QcvICAA0+qERaVkRpTGKyBHHYdwL2LXZm5t/gBtCB9KQSO/0zQgSTYEV1p0WcvSg79TLNPSvd5IDJMQ==",
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.4.0.tgz",
+      "integrity": "sha512-ERuGxDiQ6Xw/agN4tuoCRbmwRuZP0cJ1lJxJubXr5Q/5cDa78+Dc4wfvtxzhzhkm5VvmW6Mf8EVj9SPGN4l8Lg==",
       "dev": true,
       "requires": {
         "querystringify": "2.0.0",
@@ -10554,41 +10924,39 @@
       "dev": true,
       "requires": {
         "kind-of": "6.0.2"
-      },
-      "dependencies": {
-        "kind-of": {
-          "version": "6.0.2",
-          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
-          "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==",
-          "dev": true
-        }
       }
     },
+    "user-home": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/user-home/-/user-home-1.1.1.tgz",
+      "integrity": "sha1-K1viOjK2Onyd640PKNSFcko98ZA=",
+      "dev": true
+    },
     "useragent": {
-      "version": "2.2.1",
-      "resolved": "https://registry.npmjs.org/useragent/-/useragent-2.2.1.tgz",
-      "integrity": "sha1-z1k+9PLRdYdei7ZY6pLhik/QbY4=",
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/useragent/-/useragent-2.3.0.tgz",
+      "integrity": "sha512-4AoH4pxuSvHCjqLO04sU6U/uE65BYza8l/KKBS0b0hnUPWi+cQ2BpeTEwejCSx9SPV5/U03nniDTrWx5NrmKdw==",
       "dev": true,
       "requires": {
-        "lru-cache": "2.2.4",
-        "tmp": "0.0.33"
-      },
-      "dependencies": {
-        "lru-cache": {
-          "version": "2.2.4",
-          "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.2.4.tgz",
-          "integrity": "sha1-bGWGGb7PFAMdDQtZSxYELOTcBj0=",
-          "dev": true
-        }
+        "lru-cache": "4.1.3",
+        "tmp": "0.0.28"
       }
     },
     "util": {
-      "version": "0.10.4",
-      "resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz",
-      "integrity": "sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==",
+      "version": "0.10.3",
+      "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz",
+      "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=",
       "dev": true,
       "requires": {
-        "inherits": "2.0.3"
+        "inherits": "2.0.1"
+      },
+      "dependencies": {
+        "inherits": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz",
+          "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=",
+          "dev": true
+        }
       }
     },
     "util-deprecate": {
@@ -10597,6 +10965,12 @@
       "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
       "dev": true
     },
+    "util-extend": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/util-extend/-/util-extend-1.0.3.tgz",
+      "integrity": "sha1-p8IW0mdUUWljeztu3GypEZ4v+T8=",
+      "dev": true
+    },
     "utila": {
       "version": "0.4.0",
       "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz",
@@ -10615,27 +10989,19 @@
       "integrity": "sha512-jZnMwlb9Iku/O3smGWvZhauCf6cvvpKi4BKRiliS3cxnI+Gz9j5MEpTz2UFuXiKPJocb7gnsLHwiS05ige5BEA==",
       "dev": true
     },
-    "uws": {
-      "version": "9.14.0",
-      "resolved": "https://registry.npmjs.org/uws/-/uws-9.14.0.tgz",
-      "integrity": "sha512-HNMztPP5A1sKuVFmdZ6BPVpBQd5bUjNC8EFMFiICK+oho/OQsAJy5hnIx4btMHiOk8j04f/DbIlqnEZ9d72dqg==",
-      "dev": true,
-      "optional": true
-    },
     "v8flags": {
-      "version": "3.1.1",
-      "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-3.1.1.tgz",
-      "integrity": "sha512-iw/1ViSEaff8NJ3HLyEjawk/8hjJib3E7pvG4pddVXfUg1983s3VGsiClDjhK64MQVDGqc1Q8r18S4VKQZS9EQ==",
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-2.1.1.tgz",
+      "integrity": "sha1-qrGh+jDUX4jdMhFIh1rALAtV5bQ=",
       "dev": true,
       "requires": {
-        "homedir-polyfill": "1.0.1"
+        "user-home": "1.1.1"
       }
     },
     "validate-npm-package-license": {
       "version": "3.0.3",
       "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.3.tgz",
       "integrity": "sha512-63ZOUnL4SIXj4L0NixR3L1lcjO38crAbgrTpl28t8jjrfuiOBL5Iygm+60qPs/KsZGzPNg6Smnc/oY16QTjF0g==",
-      "dev": true,
       "requires": {
         "spdx-correct": "3.0.0",
         "spdx-expression-parse": "3.0.0"
@@ -10647,6 +11013,12 @@
       "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=",
       "dev": true
     },
+    "vendors": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/vendors/-/vendors-1.0.2.tgz",
+      "integrity": "sha512-w/hry/368nO21AN9QljsaIhb9ZiZtZARoVH5f3CsFbawdLdayCgKRPup7CggujvySMxx0I91NOyxdVENohprLQ==",
+      "dev": true
+    },
     "verror": {
       "version": "1.10.0",
       "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
@@ -10693,352 +11065,9 @@
       "integrity": "sha512-i6dHe3EyLjMmDlU1/bGQpEw25XSjkJULPuAVKCbNRefQVq48yXKUpwg538F7AZTf9kyr57zj++pQFltUa5H7yA==",
       "dev": true,
       "requires": {
-        "chokidar": "2.0.4",
+        "chokidar": "2.0.3",
         "graceful-fs": "4.1.11",
         "neo-async": "2.5.1"
-      },
-      "dependencies": {
-        "anymatch": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz",
-          "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==",
-          "dev": true,
-          "requires": {
-            "micromatch": "3.1.10",
-            "normalize-path": "2.1.1"
-          }
-        },
-        "arr-diff": {
-          "version": "4.0.0",
-          "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz",
-          "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=",
-          "dev": true
-        },
-        "array-unique": {
-          "version": "0.3.2",
-          "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz",
-          "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=",
-          "dev": true
-        },
-        "braces": {
-          "version": "2.3.2",
-          "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz",
-          "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==",
-          "dev": true,
-          "requires": {
-            "arr-flatten": "1.1.0",
-            "array-unique": "0.3.2",
-            "extend-shallow": "2.0.1",
-            "fill-range": "4.0.0",
-            "isobject": "3.0.1",
-            "repeat-element": "1.1.2",
-            "snapdragon": "0.8.2",
-            "snapdragon-node": "2.1.1",
-            "split-string": "3.1.0",
-            "to-regex": "3.0.2"
-          },
-          "dependencies": {
-            "extend-shallow": {
-              "version": "2.0.1",
-              "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
-              "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
-              "dev": true,
-              "requires": {
-                "is-extendable": "0.1.1"
-              }
-            }
-          }
-        },
-        "chokidar": {
-          "version": "2.0.4",
-          "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.0.4.tgz",
-          "integrity": "sha512-z9n7yt9rOvIJrMhvDtDictKrkFHeihkNl6uWMmZlmL6tJtX9Cs+87oK+teBx+JIgzvbX3yZHT3eF8vpbDxHJXQ==",
-          "dev": true,
-          "requires": {
-            "anymatch": "2.0.0",
-            "async-each": "1.0.1",
-            "braces": "2.3.2",
-            "fsevents": "1.2.4",
-            "glob-parent": "3.1.0",
-            "inherits": "2.0.3",
-            "is-binary-path": "1.0.1",
-            "is-glob": "4.0.0",
-            "lodash.debounce": "4.0.8",
-            "normalize-path": "2.1.1",
-            "path-is-absolute": "1.0.1",
-            "readdirp": "2.1.0",
-            "upath": "1.1.0"
-          }
-        },
-        "expand-brackets": {
-          "version": "2.1.4",
-          "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz",
-          "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=",
-          "dev": true,
-          "requires": {
-            "debug": "2.6.9",
-            "define-property": "0.2.5",
-            "extend-shallow": "2.0.1",
-            "posix-character-classes": "0.1.1",
-            "regex-not": "1.0.2",
-            "snapdragon": "0.8.2",
-            "to-regex": "3.0.2"
-          },
-          "dependencies": {
-            "define-property": {
-              "version": "0.2.5",
-              "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
-              "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
-              "dev": true,
-              "requires": {
-                "is-descriptor": "0.1.6"
-              }
-            },
-            "extend-shallow": {
-              "version": "2.0.1",
-              "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
-              "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
-              "dev": true,
-              "requires": {
-                "is-extendable": "0.1.1"
-              }
-            },
-            "is-accessor-descriptor": {
-              "version": "0.1.6",
-              "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
-              "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=",
-              "dev": true,
-              "requires": {
-                "kind-of": "3.2.2"
-              },
-              "dependencies": {
-                "kind-of": {
-                  "version": "3.2.2",
-                  "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
-                  "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
-                  "dev": true,
-                  "requires": {
-                    "is-buffer": "1.1.6"
-                  }
-                }
-              }
-            },
-            "is-data-descriptor": {
-              "version": "0.1.4",
-              "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
-              "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=",
-              "dev": true,
-              "requires": {
-                "kind-of": "3.2.2"
-              },
-              "dependencies": {
-                "kind-of": {
-                  "version": "3.2.2",
-                  "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
-                  "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
-                  "dev": true,
-                  "requires": {
-                    "is-buffer": "1.1.6"
-                  }
-                }
-              }
-            },
-            "is-descriptor": {
-              "version": "0.1.6",
-              "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
-              "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
-              "dev": true,
-              "requires": {
-                "is-accessor-descriptor": "0.1.6",
-                "is-data-descriptor": "0.1.4",
-                "kind-of": "5.1.0"
-              }
-            },
-            "kind-of": {
-              "version": "5.1.0",
-              "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
-              "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==",
-              "dev": true
-            }
-          }
-        },
-        "extglob": {
-          "version": "2.0.4",
-          "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz",
-          "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==",
-          "dev": true,
-          "requires": {
-            "array-unique": "0.3.2",
-            "define-property": "1.0.0",
-            "expand-brackets": "2.1.4",
-            "extend-shallow": "2.0.1",
-            "fragment-cache": "0.2.1",
-            "regex-not": "1.0.2",
-            "snapdragon": "0.8.2",
-            "to-regex": "3.0.2"
-          },
-          "dependencies": {
-            "define-property": {
-              "version": "1.0.0",
-              "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
-              "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
-              "dev": true,
-              "requires": {
-                "is-descriptor": "1.0.2"
-              }
-            },
-            "extend-shallow": {
-              "version": "2.0.1",
-              "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
-              "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
-              "dev": true,
-              "requires": {
-                "is-extendable": "0.1.1"
-              }
-            }
-          }
-        },
-        "fill-range": {
-          "version": "4.0.0",
-          "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
-          "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=",
-          "dev": true,
-          "requires": {
-            "extend-shallow": "2.0.1",
-            "is-number": "3.0.0",
-            "repeat-string": "1.6.1",
-            "to-regex-range": "2.1.1"
-          },
-          "dependencies": {
-            "extend-shallow": {
-              "version": "2.0.1",
-              "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
-              "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
-              "dev": true,
-              "requires": {
-                "is-extendable": "0.1.1"
-              }
-            }
-          }
-        },
-        "glob-parent": {
-          "version": "3.1.0",
-          "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz",
-          "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=",
-          "dev": true,
-          "requires": {
-            "is-glob": "3.1.0",
-            "path-dirname": "1.0.2"
-          },
-          "dependencies": {
-            "is-glob": {
-              "version": "3.1.0",
-              "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz",
-              "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=",
-              "dev": true,
-              "requires": {
-                "is-extglob": "2.1.1"
-              }
-            }
-          }
-        },
-        "is-accessor-descriptor": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
-          "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
-          "dev": true,
-          "requires": {
-            "kind-of": "6.0.2"
-          }
-        },
-        "is-data-descriptor": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
-          "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
-          "dev": true,
-          "requires": {
-            "kind-of": "6.0.2"
-          }
-        },
-        "is-descriptor": {
-          "version": "1.0.2",
-          "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
-          "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
-          "dev": true,
-          "requires": {
-            "is-accessor-descriptor": "1.0.0",
-            "is-data-descriptor": "1.0.0",
-            "kind-of": "6.0.2"
-          }
-        },
-        "is-extglob": {
-          "version": "2.1.1",
-          "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
-          "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
-          "dev": true
-        },
-        "is-glob": {
-          "version": "4.0.0",
-          "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.0.tgz",
-          "integrity": "sha1-lSHHaEXMJhCoUgPd8ICpWML/q8A=",
-          "dev": true,
-          "requires": {
-            "is-extglob": "2.1.1"
-          }
-        },
-        "is-number": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
-          "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=",
-          "dev": true,
-          "requires": {
-            "kind-of": "3.2.2"
-          },
-          "dependencies": {
-            "kind-of": {
-              "version": "3.2.2",
-              "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
-              "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
-              "dev": true,
-              "requires": {
-                "is-buffer": "1.1.6"
-              }
-            }
-          }
-        },
-        "isobject": {
-          "version": "3.0.1",
-          "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
-          "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
-          "dev": true
-        },
-        "kind-of": {
-          "version": "6.0.2",
-          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
-          "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==",
-          "dev": true
-        },
-        "micromatch": {
-          "version": "3.1.10",
-          "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz",
-          "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==",
-          "dev": true,
-          "requires": {
-            "arr-diff": "4.0.0",
-            "array-unique": "0.3.2",
-            "braces": "2.3.2",
-            "define-property": "2.0.2",
-            "extend-shallow": "3.0.2",
-            "extglob": "2.0.4",
-            "fragment-cache": "0.2.1",
-            "kind-of": "6.0.2",
-            "nanomatch": "1.2.9",
-            "object.pick": "1.3.0",
-            "regex-not": "1.0.2",
-            "snapdragon": "0.8.2",
-            "to-regex": "3.0.2"
-          }
-        }
       }
     },
     "wbuf": {
@@ -11056,7 +11085,7 @@
       "integrity": "sha1-gcUzqeM9W/tZe05j4s2yW1R3dRU=",
       "dev": true,
       "requires": {
-        "@types/selenium-webdriver": "2.53.43",
+        "@types/selenium-webdriver": "2.53.36",
         "selenium-webdriver": "2.53.3"
       },
       "dependencies": {
@@ -11081,7 +11110,7 @@
             "adm-zip": "0.4.4",
             "rimraf": "2.6.2",
             "tmp": "0.0.24",
-            "ws": "1.1.5",
+            "ws": "1.1.1",
             "xml2js": "0.4.4"
           }
         },
@@ -11091,22 +11120,6 @@
           "integrity": "sha1-1qXhmNFKmDXMby18PZ4wJCjIzxI=",
           "dev": true
         },
-        "ultron": {
-          "version": "1.0.2",
-          "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.0.2.tgz",
-          "integrity": "sha1-rOEWq1V80Zc4ak6I9GhTeMiy5Po=",
-          "dev": true
-        },
-        "ws": {
-          "version": "1.1.5",
-          "resolved": "https://registry.npmjs.org/ws/-/ws-1.1.5.tgz",
-          "integrity": "sha512-o3KqipXNUdS7wpQzBHSe180lBGO60SoK0yVo3CYJgb2MkobuWuBX6dhkYP5ORCLd55y+SaflMOV5fqAB53ux4w==",
-          "dev": true,
-          "requires": {
-            "options": "0.0.6",
-            "ultron": "1.0.2"
-          }
-        },
         "xml2js": {
           "version": "0.4.4",
           "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.4.tgz",
@@ -11120,15 +11133,15 @@
       }
     },
     "webpack": {
-      "version": "3.11.0",
-      "resolved": "https://registry.npmjs.org/webpack/-/webpack-3.11.0.tgz",
-      "integrity": "sha512-3kOFejWqj5ISpJk4Qj/V7w98h9Vl52wak3CLiw/cDOfbVTq7FeoZ0SdoHHY9PYlHr50ZS42OfvzE2vB4nncKQg==",
+      "version": "3.6.0",
+      "resolved": "https://registry.npmjs.org/webpack/-/webpack-3.6.0.tgz",
+      "integrity": "sha512-OsHT3D0W0KmPPh60tC7asNnOmST6bKTiR90UyEdT9QYoaJ4OYN4Gg7WK1k3VxHK07ZoiYWPsKvlS/gAjwL/vRA==",
       "dev": true,
       "requires": {
-        "acorn": "5.7.1",
+        "acorn": "5.5.3",
         "acorn-dynamic-import": "2.0.2",
-        "ajv": "6.5.1",
-        "ajv-keywords": "3.2.0",
+        "ajv": "5.5.2",
+        "ajv-keywords": "2.1.1",
         "async": "2.6.1",
         "enhanced-resolve": "3.4.1",
         "escope": "3.6.0",
@@ -11156,22 +11169,32 @@
           "dev": true
         },
         "camelcase": {
-          "version": "1.2.1",
-          "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz",
-          "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=",
+          "version": "4.1.0",
+          "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz",
+          "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=",
           "dev": true
         },
-        "cliui": {
+        "find-up": {
           "version": "2.1.0",
-          "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz",
-          "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=",
+          "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz",
+          "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=",
           "dev": true,
           "requires": {
-            "center-align": "0.1.3",
-            "right-align": "0.1.3",
-            "wordwrap": "0.0.2"
+            "locate-path": "2.0.0"
           }
         },
+        "has-flag": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz",
+          "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=",
+          "dev": true
+        },
+        "is-fullwidth-code-point": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
+          "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
+          "dev": true
+        },
         "load-json-file": {
           "version": "2.0.0",
           "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz",
@@ -11204,12 +11227,6 @@
             "pify": "2.3.0"
           }
         },
-        "pify": {
-          "version": "2.3.0",
-          "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
-          "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
-          "dev": true
-        },
         "read-pkg": {
           "version": "2.0.0",
           "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz",
@@ -11239,23 +11256,15 @@
           "requires": {
             "is-fullwidth-code-point": "2.0.0",
             "strip-ansi": "4.0.0"
-          },
-          "dependencies": {
-            "is-fullwidth-code-point": {
-              "version": "2.0.0",
-              "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
-              "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
-              "dev": true
-            },
-            "strip-ansi": {
-              "version": "4.0.0",
-              "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
-              "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
-              "dev": true,
-              "requires": {
-                "ansi-regex": "3.0.0"
-              }
-            }
+          }
+        },
+        "strip-ansi": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
+          "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
+          "dev": true,
+          "requires": {
+            "ansi-regex": "3.0.0"
           }
         },
         "strip-bom": {
@@ -11264,40 +11273,13 @@
           "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=",
           "dev": true
         },
-        "uglify-js": {
-          "version": "2.8.29",
-          "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz",
-          "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=",
+        "supports-color": {
+          "version": "4.5.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz",
+          "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=",
           "dev": true,
           "requires": {
-            "source-map": "0.5.7",
-            "uglify-to-browserify": "1.0.2",
-            "yargs": "3.10.0"
-          },
-          "dependencies": {
-            "yargs": {
-              "version": "3.10.0",
-              "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz",
-              "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=",
-              "dev": true,
-              "requires": {
-                "camelcase": "1.2.1",
-                "cliui": "2.1.0",
-                "decamelize": "1.2.0",
-                "window-size": "0.1.0"
-              }
-            }
-          }
-        },
-        "uglifyjs-webpack-plugin": {
-          "version": "0.4.6",
-          "resolved": "https://registry.npmjs.org/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-0.4.6.tgz",
-          "integrity": "sha1-uVH0q7a9YX5m9j64kUmOORdj4wk=",
-          "dev": true,
-          "requires": {
-            "source-map": "0.5.7",
-            "uglify-js": "2.8.29",
-            "webpack-sources": "1.1.0"
+            "has-flag": "2.0.0"
           }
         },
         "which-module": {
@@ -11306,12 +11288,6 @@
           "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=",
           "dev": true
         },
-        "y18n": {
-          "version": "3.2.1",
-          "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz",
-          "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=",
-          "dev": true
-        },
         "yargs": {
           "version": "8.0.2",
           "resolved": "https://registry.npmjs.org/yargs/-/yargs-8.0.2.tgz",
@@ -11331,38 +11307,6 @@
             "which-module": "2.0.0",
             "y18n": "3.2.1",
             "yargs-parser": "7.0.0"
-          },
-          "dependencies": {
-            "camelcase": {
-              "version": "4.1.0",
-              "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz",
-              "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=",
-              "dev": true
-            },
-            "cliui": {
-              "version": "3.2.0",
-              "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz",
-              "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=",
-              "dev": true,
-              "requires": {
-                "string-width": "1.0.2",
-                "strip-ansi": "3.0.1",
-                "wrap-ansi": "2.1.0"
-              },
-              "dependencies": {
-                "string-width": {
-                  "version": "1.0.2",
-                  "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
-                  "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
-                  "dev": true,
-                  "requires": {
-                    "code-point-at": "1.1.0",
-                    "is-fullwidth-code-point": "1.0.0",
-                    "strip-ansi": "3.0.1"
-                  }
-                }
-              }
-            }
           }
         },
         "yargs-parser": {
@@ -11372,41 +11316,96 @@
           "dev": true,
           "requires": {
             "camelcase": "4.1.0"
-          },
-          "dependencies": {
-            "camelcase": {
-              "version": "4.1.0",
-              "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz",
-              "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=",
-              "dev": true
-            }
           }
         }
       }
     },
-    "webpack-core": {
-      "version": "0.6.9",
-      "resolved": "https://registry.npmjs.org/webpack-core/-/webpack-core-0.6.9.tgz",
-      "integrity": "sha1-/FcViMhVjad76e+23r3Fo7FyvcI=",
+    "webpack-bundle-analyzer": {
+      "version": "2.13.1",
+      "resolved": "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-2.13.1.tgz",
+      "integrity": "sha512-rwxyfecTAxoarCC9VlHlIpfQCmmJ/qWD5bpbjkof+7HrNhTNZIwZITxN6CdlYL2axGmwNUQ+tFgcSOiNXMf/sQ==",
       "dev": true,
       "requires": {
-        "source-list-map": "0.1.8",
-        "source-map": "0.4.4"
+        "acorn": "5.5.3",
+        "bfj-node4": "5.3.1",
+        "chalk": "2.4.1",
+        "commander": "2.15.1",
+        "ejs": "2.6.1",
+        "express": "4.16.3",
+        "filesize": "3.6.1",
+        "gzip-size": "4.1.0",
+        "lodash": "4.17.10",
+        "mkdirp": "0.5.1",
+        "opener": "1.4.3",
+        "ws": "4.1.0"
       },
       "dependencies": {
-        "source-list-map": {
-          "version": "0.1.8",
-          "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-0.1.8.tgz",
-          "integrity": "sha1-xVCyq1Qn9rPyH1r+rYjE9Vh7IQY=",
-          "dev": true
-        },
-        "source-map": {
-          "version": "0.4.4",
-          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz",
-          "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=",
+        "ws": {
+          "version": "4.1.0",
+          "resolved": "https://registry.npmjs.org/ws/-/ws-4.1.0.tgz",
+          "integrity": "sha512-ZGh/8kF9rrRNffkLFV4AzhvooEclrOH0xaugmqGsIfFgOE/pIz4fMc4Ef+5HSQqTEug2S9JZIWDR47duDSLfaA==",
           "dev": true,
           "requires": {
-            "amdefine": "1.0.1"
+            "async-limiter": "1.0.0",
+            "safe-buffer": "5.1.2"
+          }
+        }
+      }
+    },
+    "webpack-concat-plugin": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/webpack-concat-plugin/-/webpack-concat-plugin-1.4.0.tgz",
+      "integrity": "sha512-Ym9Qm5Sw9oXJYChNJk09I/yaXDaV3UDxsa07wcCvILzIeSJTnSUZjhS4y2YkULzgE8VHOv9X04KtlJPZGwXqMg==",
+      "dev": true,
+      "requires": {
+        "md5": "2.2.1",
+        "uglify-js": "2.8.29"
+      },
+      "dependencies": {
+        "camelcase": {
+          "version": "1.2.1",
+          "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz",
+          "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=",
+          "dev": true
+        },
+        "cliui": {
+          "version": "2.1.0",
+          "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz",
+          "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=",
+          "dev": true,
+          "requires": {
+            "center-align": "0.1.3",
+            "right-align": "0.1.3",
+            "wordwrap": "0.0.2"
+          }
+        },
+        "uglify-js": {
+          "version": "2.8.29",
+          "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz",
+          "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=",
+          "dev": true,
+          "requires": {
+            "source-map": "0.5.7",
+            "uglify-to-browserify": "1.0.2",
+            "yargs": "3.10.0"
+          }
+        },
+        "wordwrap": {
+          "version": "0.0.2",
+          "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz",
+          "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=",
+          "dev": true
+        },
+        "yargs": {
+          "version": "3.10.0",
+          "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz",
+          "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=",
+          "dev": true,
+          "requires": {
+            "camelcase": "1.2.1",
+            "cliui": "2.1.0",
+            "decamelize": "1.2.0",
+            "window-size": "0.1.0"
           }
         }
       }
@@ -11425,462 +11424,177 @@
       }
     },
     "webpack-dev-server": {
-      "version": "2.11.2",
-      "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-2.11.2.tgz",
-      "integrity": "sha512-zrPoX97bx47vZiAXfDrkw8pe9QjJ+lunQl3dypojyWwWr1M5I2h0VSrMPfTjopHQPRNn+NqfjcMmhoLcUJe2gA==",
+      "version": "2.7.1",
+      "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-2.7.1.tgz",
+      "integrity": "sha1-IVgPWgjNBlxxFEz29hw0W8pZqLg=",
       "dev": true,
       "requires": {
         "ansi-html": "0.0.7",
-        "array-includes": "3.0.3",
         "bonjour": "3.5.0",
-        "chokidar": "2.0.4",
+        "chokidar": "1.7.0",
         "compression": "1.7.2",
         "connect-history-api-fallback": "1.5.0",
-        "debug": "3.1.0",
         "del": "3.0.0",
         "express": "4.16.3",
         "html-entities": "1.2.1",
         "http-proxy-middleware": "0.17.4",
-        "import-local": "1.0.0",
         "internal-ip": "1.2.0",
         "ip": "1.1.5",
-        "killable": "1.0.0",
         "loglevel": "1.6.1",
-        "opn": "5.1.0",
+        "opn": "4.0.2",
         "portfinder": "1.0.13",
         "selfsigned": "1.10.3",
         "serve-index": "1.9.1",
-        "sockjs": "0.3.19",
+        "sockjs": "0.3.18",
         "sockjs-client": "1.1.4",
         "spdy": "3.4.7",
         "strip-ansi": "3.0.1",
-        "supports-color": "5.4.0",
+        "supports-color": "3.2.3",
         "webpack-dev-middleware": "1.12.2",
         "yargs": "6.6.0"
       },
       "dependencies": {
         "anymatch": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz",
-          "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==",
+          "version": "1.3.2",
+          "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-1.3.2.tgz",
+          "integrity": "sha512-0XNayC8lTHQ2OI8aljNCN3sSx6hsr/1+rlcDAotXJR7C1oZZHCNsfpbKwMjRA3Uqb5tF1Rae2oloTr4xpq+WjA==",
           "dev": true,
           "requires": {
-            "micromatch": "3.1.10",
+            "micromatch": "2.3.11",
             "normalize-path": "2.1.1"
           }
         },
         "arr-diff": {
-          "version": "4.0.0",
-          "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz",
-          "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=",
-          "dev": true
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz",
+          "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=",
+          "dev": true,
+          "requires": {
+            "arr-flatten": "1.1.0"
+          }
         },
         "array-unique": {
-          "version": "0.3.2",
-          "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz",
-          "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=",
+          "version": "0.2.1",
+          "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz",
+          "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=",
           "dev": true
         },
         "braces": {
-          "version": "2.3.2",
-          "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz",
-          "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==",
+          "version": "1.8.5",
+          "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz",
+          "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=",
           "dev": true,
           "requires": {
-            "arr-flatten": "1.1.0",
-            "array-unique": "0.3.2",
-            "extend-shallow": "2.0.1",
-            "fill-range": "4.0.0",
-            "isobject": "3.0.1",
-            "repeat-element": "1.1.2",
-            "snapdragon": "0.8.2",
-            "snapdragon-node": "2.1.1",
-            "split-string": "3.1.0",
-            "to-regex": "3.0.2"
-          },
-          "dependencies": {
-            "extend-shallow": {
-              "version": "2.0.1",
-              "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
-              "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
-              "dev": true,
-              "requires": {
-                "is-extendable": "0.1.1"
-              }
-            }
+            "expand-range": "1.8.2",
+            "preserve": "0.2.0",
+            "repeat-element": "1.1.2"
           }
         },
-        "camelcase": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz",
-          "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=",
-          "dev": true
-        },
         "chokidar": {
-          "version": "2.0.4",
-          "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.0.4.tgz",
-          "integrity": "sha512-z9n7yt9rOvIJrMhvDtDictKrkFHeihkNl6uWMmZlmL6tJtX9Cs+87oK+teBx+JIgzvbX3yZHT3eF8vpbDxHJXQ==",
+          "version": "1.7.0",
+          "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-1.7.0.tgz",
+          "integrity": "sha1-eY5ol3gVHIB2tLNg5e3SjNortGg=",
           "dev": true,
           "requires": {
-            "anymatch": "2.0.0",
+            "anymatch": "1.3.2",
             "async-each": "1.0.1",
-            "braces": "2.3.2",
             "fsevents": "1.2.4",
-            "glob-parent": "3.1.0",
+            "glob-parent": "2.0.0",
             "inherits": "2.0.3",
             "is-binary-path": "1.0.1",
-            "is-glob": "4.0.0",
-            "lodash.debounce": "4.0.8",
-            "normalize-path": "2.1.1",
+            "is-glob": "2.0.1",
             "path-is-absolute": "1.0.1",
-            "readdirp": "2.1.0",
-            "upath": "1.1.0"
-          }
-        },
-        "debug": {
-          "version": "3.1.0",
-          "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
-          "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
-          "dev": true,
-          "requires": {
-            "ms": "2.0.0"
+            "readdirp": "2.1.0"
           }
         },
         "expand-brackets": {
-          "version": "2.1.4",
-          "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz",
-          "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=",
+          "version": "0.1.5",
+          "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz",
+          "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=",
           "dev": true,
           "requires": {
-            "debug": "2.6.9",
-            "define-property": "0.2.5",
-            "extend-shallow": "2.0.1",
-            "posix-character-classes": "0.1.1",
-            "regex-not": "1.0.2",
-            "snapdragon": "0.8.2",
-            "to-regex": "3.0.2"
-          },
-          "dependencies": {
-            "debug": {
-              "version": "2.6.9",
-              "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
-              "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
-              "dev": true,
-              "requires": {
-                "ms": "2.0.0"
-              }
-            },
-            "define-property": {
-              "version": "0.2.5",
-              "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
-              "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
-              "dev": true,
-              "requires": {
-                "is-descriptor": "0.1.6"
-              }
-            },
-            "extend-shallow": {
-              "version": "2.0.1",
-              "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
-              "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
-              "dev": true,
-              "requires": {
-                "is-extendable": "0.1.1"
-              }
-            },
-            "is-accessor-descriptor": {
-              "version": "0.1.6",
-              "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
-              "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=",
-              "dev": true,
-              "requires": {
-                "kind-of": "3.2.2"
-              },
-              "dependencies": {
-                "kind-of": {
-                  "version": "3.2.2",
-                  "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
-                  "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
-                  "dev": true,
-                  "requires": {
-                    "is-buffer": "1.1.6"
-                  }
-                }
-              }
-            },
-            "is-data-descriptor": {
-              "version": "0.1.4",
-              "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
-              "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=",
-              "dev": true,
-              "requires": {
-                "kind-of": "3.2.2"
-              },
-              "dependencies": {
-                "kind-of": {
-                  "version": "3.2.2",
-                  "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
-                  "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
-                  "dev": true,
-                  "requires": {
-                    "is-buffer": "1.1.6"
-                  }
-                }
-              }
-            },
-            "is-descriptor": {
-              "version": "0.1.6",
-              "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
-              "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
-              "dev": true,
-              "requires": {
-                "is-accessor-descriptor": "0.1.6",
-                "is-data-descriptor": "0.1.4",
-                "kind-of": "5.1.0"
-              }
-            },
-            "kind-of": {
-              "version": "5.1.0",
-              "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
-              "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==",
-              "dev": true
-            }
+            "is-posix-bracket": "0.1.1"
           }
         },
         "extglob": {
-          "version": "2.0.4",
-          "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz",
-          "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==",
+          "version": "0.3.2",
+          "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz",
+          "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=",
           "dev": true,
           "requires": {
-            "array-unique": "0.3.2",
-            "define-property": "1.0.0",
-            "expand-brackets": "2.1.4",
-            "extend-shallow": "2.0.1",
-            "fragment-cache": "0.2.1",
-            "regex-not": "1.0.2",
-            "snapdragon": "0.8.2",
-            "to-regex": "3.0.2"
-          },
-          "dependencies": {
-            "define-property": {
-              "version": "1.0.0",
-              "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
-              "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
-              "dev": true,
-              "requires": {
-                "is-descriptor": "1.0.2"
-              }
-            },
-            "extend-shallow": {
-              "version": "2.0.1",
-              "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
-              "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
-              "dev": true,
-              "requires": {
-                "is-extendable": "0.1.1"
-              }
-            }
-          }
-        },
-        "fill-range": {
-          "version": "4.0.0",
-          "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
-          "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=",
-          "dev": true,
-          "requires": {
-            "extend-shallow": "2.0.1",
-            "is-number": "3.0.0",
-            "repeat-string": "1.6.1",
-            "to-regex-range": "2.1.1"
-          },
-          "dependencies": {
-            "extend-shallow": {
-              "version": "2.0.1",
-              "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
-              "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
-              "dev": true,
-              "requires": {
-                "is-extendable": "0.1.1"
-              }
-            }
+            "is-extglob": "1.0.0"
           }
         },
         "glob-parent": {
-          "version": "3.1.0",
-          "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz",
-          "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=",
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz",
+          "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=",
           "dev": true,
           "requires": {
-            "is-glob": "3.1.0",
-            "path-dirname": "1.0.2"
-          },
-          "dependencies": {
-            "is-glob": {
-              "version": "3.1.0",
-              "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz",
-              "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=",
-              "dev": true,
-              "requires": {
-                "is-extglob": "2.1.1"
-              }
-            }
-          }
-        },
-        "has-flag": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
-          "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
-          "dev": true
-        },
-        "is-accessor-descriptor": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
-          "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
-          "dev": true,
-          "requires": {
-            "kind-of": "6.0.2"
-          }
-        },
-        "is-data-descriptor": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
-          "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
-          "dev": true,
-          "requires": {
-            "kind-of": "6.0.2"
-          }
-        },
-        "is-descriptor": {
-          "version": "1.0.2",
-          "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
-          "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
-          "dev": true,
-          "requires": {
-            "is-accessor-descriptor": "1.0.0",
-            "is-data-descriptor": "1.0.0",
-            "kind-of": "6.0.2"
+            "is-glob": "2.0.1"
           }
         },
         "is-extglob": {
-          "version": "2.1.1",
-          "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
-          "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz",
+          "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=",
           "dev": true
         },
         "is-glob": {
-          "version": "4.0.0",
-          "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.0.tgz",
-          "integrity": "sha1-lSHHaEXMJhCoUgPd8ICpWML/q8A=",
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz",
+          "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=",
           "dev": true,
           "requires": {
-            "is-extglob": "2.1.1"
+            "is-extglob": "1.0.0"
           }
         },
-        "is-number": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
-          "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=",
-          "dev": true,
-          "requires": {
-            "kind-of": "3.2.2"
-          },
-          "dependencies": {
-            "kind-of": {
-              "version": "3.2.2",
-              "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
-              "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
-              "dev": true,
-              "requires": {
-                "is-buffer": "1.1.6"
-              }
-            }
-          }
-        },
-        "isobject": {
-          "version": "3.0.1",
-          "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
-          "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
-          "dev": true
-        },
         "kind-of": {
-          "version": "6.0.2",
-          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
-          "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==",
-          "dev": true
+          "version": "3.2.2",
+          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+          "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+          "dev": true,
+          "requires": {
+            "is-buffer": "1.1.6"
+          }
         },
         "micromatch": {
-          "version": "3.1.10",
-          "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz",
-          "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==",
+          "version": "2.3.11",
+          "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz",
+          "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=",
           "dev": true,
           "requires": {
-            "arr-diff": "4.0.0",
-            "array-unique": "0.3.2",
-            "braces": "2.3.2",
-            "define-property": "2.0.2",
-            "extend-shallow": "3.0.2",
-            "extglob": "2.0.4",
-            "fragment-cache": "0.2.1",
-            "kind-of": "6.0.2",
-            "nanomatch": "1.2.9",
-            "object.pick": "1.3.0",
-            "regex-not": "1.0.2",
-            "snapdragon": "0.8.2",
-            "to-regex": "3.0.2"
+            "arr-diff": "2.0.0",
+            "array-unique": "0.2.1",
+            "braces": "1.8.5",
+            "expand-brackets": "0.1.5",
+            "extglob": "0.3.2",
+            "filename-regex": "2.0.1",
+            "is-extglob": "1.0.0",
+            "is-glob": "2.0.1",
+            "kind-of": "3.2.2",
+            "normalize-path": "2.1.1",
+            "object.omit": "2.0.1",
+            "parse-glob": "3.0.4",
+            "regex-cache": "0.4.4"
           }
         },
-        "supports-color": {
-          "version": "5.4.0",
-          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz",
-          "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==",
+        "opn": {
+          "version": "4.0.2",
+          "resolved": "https://registry.npmjs.org/opn/-/opn-4.0.2.tgz",
+          "integrity": "sha1-erwi5kTf9jsKltWrfyeQwPAavJU=",
           "dev": true,
           "requires": {
-            "has-flag": "3.0.0"
-          }
-        },
-        "y18n": {
-          "version": "3.2.1",
-          "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz",
-          "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=",
-          "dev": true
-        },
-        "yargs": {
-          "version": "6.6.0",
-          "resolved": "https://registry.npmjs.org/yargs/-/yargs-6.6.0.tgz",
-          "integrity": "sha1-eC7CHvQDNF+DCoCMo9UTr1YGUgg=",
-          "dev": true,
-          "requires": {
-            "camelcase": "3.0.0",
-            "cliui": "3.2.0",
-            "decamelize": "1.2.0",
-            "get-caller-file": "1.0.2",
-            "os-locale": "1.4.0",
-            "read-pkg-up": "1.0.1",
-            "require-directory": "2.1.1",
-            "require-main-filename": "1.0.1",
-            "set-blocking": "2.0.0",
-            "string-width": "1.0.2",
-            "which-module": "1.0.0",
-            "y18n": "3.2.1",
-            "yargs-parser": "4.2.1"
-          }
-        },
-        "yargs-parser": {
-          "version": "4.2.1",
-          "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-4.2.1.tgz",
-          "integrity": "sha1-KczqwNxPA8bIe0qfIX3RjJ90hxw=",
-          "dev": true,
-          "requires": {
-            "camelcase": "3.0.0"
+            "object-assign": "4.1.1",
+            "pinkie-promise": "2.0.1"
           }
         }
       }
     },
     "webpack-merge": {
-      "version": "4.1.3",
-      "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-4.1.3.tgz",
-      "integrity": "sha512-zxwAIGK7nKdu5CIZL0BjTQoq3elV0t0MfB7rUC1zj668geid52abs6hN/ACwZdK6LeMS8dC9B6WmtF978zH5mg==",
+      "version": "4.1.2",
+      "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-4.1.2.tgz",
+      "integrity": "sha512-/0QYwW/H1N/CdXYA2PNPVbsxO3u2Fpz34vs72xm03SRfg6bMNGfMJIQEpQjKRvkG2JvT6oRJFpDtSrwbX8Jzvw==",
       "dev": true,
       "requires": {
         "lodash": "4.17.10"
@@ -11904,15 +11618,6 @@
         }
       }
     },
-    "webpack-subresource-integrity": {
-      "version": "1.0.4",
-      "resolved": "https://registry.npmjs.org/webpack-subresource-integrity/-/webpack-subresource-integrity-1.0.4.tgz",
-      "integrity": "sha1-j6yKfo61n8ahZ2ioXJ2U7n+dDts=",
-      "dev": true,
-      "requires": {
-        "webpack-core": "0.6.9"
-      }
-    },
     "websocket-driver": {
       "version": "0.7.0",
       "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.0.tgz",
@@ -11935,10 +11640,16 @@
       "integrity": "sha1-RztRfsFZ4rhQBUl6E5g/CVQS404=",
       "dev": true
     },
+    "whet.extend": {
+      "version": "0.9.9",
+      "resolved": "https://registry.npmjs.org/whet.extend/-/whet.extend-0.9.9.tgz",
+      "integrity": "sha1-+HfVv2SMl+WqVC+twW1qJZucEaE=",
+      "dev": true
+    },
     "which": {
-      "version": "1.3.1",
-      "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
-      "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/which/-/which-1.3.0.tgz",
+      "integrity": "sha512-xcJpopdamTuY5duC/KnTTNBraPK54YwpenP4lzxU8H91GudWpFv38u0CKjclE1Wi2EH2EDz5LRcHcKbCIzqGyg==",
       "dev": true,
       "requires": {
         "isexe": "2.0.0"
@@ -11947,13 +11658,12 @@
     "which-module": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz",
-      "integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=",
-      "dev": true
+      "integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8="
     },
     "wide-align": {
-      "version": "1.1.3",
-      "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz",
-      "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==",
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.2.tgz",
+      "integrity": "sha512-ijDLlyQ7s6x1JgCLur53osjm/UXUYD9+0PbYKrBsYisYXzCxN+HC3mYDNy/dWdmf3AwqwU3CXwDCvsNgGK1S0w==",
       "dev": true,
       "requires": {
         "string-width": "1.0.2"
@@ -11965,33 +11675,16 @@
       "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=",
       "dev": true
     },
-    "with-callback": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/with-callback/-/with-callback-1.0.2.tgz",
-      "integrity": "sha1-oJYpuakgAo1yFAT7Q1vc/1yRvCE=",
-      "dev": true,
-      "optional": true
-    },
     "wordwrap": {
-      "version": "0.0.2",
-      "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz",
-      "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=",
+      "version": "0.0.3",
+      "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz",
+      "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=",
       "dev": true
     },
-    "worker-farm": {
-      "version": "1.6.0",
-      "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.6.0.tgz",
-      "integrity": "sha512-6w+3tHbM87WnSWnENBUvA2pxJPLhQUg5LKwUQHq3r+XPhIM+Gh2R5ycbwPCyuGbNg+lPgdcnQUhuC02kJCvffQ==",
-      "dev": true,
-      "requires": {
-        "errno": "0.1.7"
-      }
-    },
     "wrap-ansi": {
       "version": "2.1.0",
       "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz",
       "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=",
-      "dev": true,
       "requires": {
         "string-width": "1.0.2",
         "strip-ansi": "3.0.1"
@@ -12004,16 +11697,26 @@
       "dev": true
     },
     "ws": {
-      "version": "3.3.3",
-      "resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz",
-      "integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==",
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/ws/-/ws-1.1.1.tgz",
+      "integrity": "sha1-CC3bbGQehdS7RR8D1S8G6r2x8Bg=",
       "dev": true,
       "requires": {
-        "async-limiter": "1.0.0",
-        "safe-buffer": "5.1.2",
-        "ultron": "1.1.1"
+        "options": "0.0.6",
+        "ultron": "1.0.2"
       }
     },
+    "wtf-8": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/wtf-8/-/wtf-8-1.0.0.tgz",
+      "integrity": "sha1-OS2LotDxw00e4tYw8V0O+2jhBIo=",
+      "dev": true
+    },
+    "xhr2": {
+      "version": "0.1.4",
+      "resolved": "https://registry.npmjs.org/xhr2/-/xhr2-0.1.4.tgz",
+      "integrity": "sha1-f4dliEdxbbUCYyOBL4GMras4el8="
+    },
     "xml2js": {
       "version": "0.4.19",
       "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz",
@@ -12022,14 +11725,6 @@
       "requires": {
         "sax": "1.2.4",
         "xmlbuilder": "9.0.7"
-      },
-      "dependencies": {
-        "sax": {
-          "version": "1.2.4",
-          "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
-          "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==",
-          "dev": true
-        }
       }
     },
     "xmlbuilder": {
@@ -12038,18 +11733,17 @@
       "integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=",
       "dev": true
     },
-    "xmlhttprequest-ssl": {
-      "version": "1.5.5",
-      "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz",
-      "integrity": "sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4=",
+    "xmldom": {
+      "version": "0.1.27",
+      "resolved": "https://registry.npmjs.org/xmldom/-/xmldom-0.1.27.tgz",
+      "integrity": "sha1-1QH5ezvbQDr4757MIFcxh6rawOk=",
       "dev": true
     },
-    "xregexp": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/xregexp/-/xregexp-2.0.0.tgz",
-      "integrity": "sha1-UqY+VsoLhKfzpfPWGHLxJq16WUM=",
-      "dev": true,
-      "optional": true
+    "xmlhttprequest-ssl": {
+      "version": "1.5.3",
+      "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.3.tgz",
+      "integrity": "sha1-GFqIjATspGw+QHDZn3tJ3jUomS0=",
+      "dev": true
     },
     "xtend": {
       "version": "4.0.1",
@@ -12057,20 +11751,10 @@
       "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=",
       "dev": true
     },
-    "xxhashjs": {
-      "version": "0.2.2",
-      "resolved": "https://registry.npmjs.org/xxhashjs/-/xxhashjs-0.2.2.tgz",
-      "integrity": "sha512-AkTuIuVTET12tpsVIQo+ZU6f/qDmKuRUcjaqR+OIvm+aCBsZ95i7UVY5WJ9TMsSaZ0DA2WxoZ4acu0sPH+OKAw==",
-      "dev": true,
-      "requires": {
-        "cuint": "0.2.2"
-      }
-    },
     "y18n": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz",
-      "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==",
-      "dev": true
+      "version": "3.2.1",
+      "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz",
+      "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE="
     },
     "yallist": {
       "version": "2.1.2",
@@ -12079,11 +11763,9 @@
       "dev": true
     },
     "yargs": {
-      "version": "7.1.0",
-      "resolved": "https://registry.npmjs.org/yargs/-/yargs-7.1.0.tgz",
-      "integrity": "sha1-a6MY6xaWFyf10oT46gA+jWFU0Mg=",
-      "dev": true,
-      "optional": true,
+      "version": "6.6.0",
+      "resolved": "https://registry.npmjs.org/yargs/-/yargs-6.6.0.tgz",
+      "integrity": "sha1-eC7CHvQDNF+DCoCMo9UTr1YGUgg=",
       "requires": {
         "camelcase": "3.0.0",
         "cliui": "3.2.0",
@@ -12097,42 +11779,24 @@
         "string-width": "1.0.2",
         "which-module": "1.0.0",
         "y18n": "3.2.1",
-        "yargs-parser": "5.0.0"
-      },
-      "dependencies": {
-        "camelcase": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz",
-          "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=",
-          "dev": true,
-          "optional": true
-        },
-        "y18n": {
-          "version": "3.2.1",
-          "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz",
-          "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=",
-          "dev": true,
-          "optional": true
-        }
+        "yargs-parser": "4.2.1"
       }
     },
     "yargs-parser": {
-      "version": "5.0.0",
-      "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-5.0.0.tgz",
-      "integrity": "sha1-J17PDX/+Bcd+ZOfIbkzZS/DhIoo=",
-      "dev": true,
-      "optional": true,
+      "version": "4.2.1",
+      "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-4.2.1.tgz",
+      "integrity": "sha1-KczqwNxPA8bIe0qfIX3RjJ90hxw=",
       "requires": {
         "camelcase": "3.0.0"
-      },
-      "dependencies": {
-        "camelcase": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz",
-          "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=",
-          "dev": true,
-          "optional": true
-        }
+      }
+    },
+    "yauzl": {
+      "version": "2.4.1",
+      "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.4.1.tgz",
+      "integrity": "sha1-lSj0QtqxsihOWLQ3m7GU4i4MQAU=",
+      "dev": true,
+      "requires": {
+        "fd-slicer": "1.0.1"
       }
     },
     "yeast": {
@@ -12148,9 +11812,9 @@
       "dev": true
     },
     "zone.js": {
-      "version": "0.8.26",
-      "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.8.26.tgz",
-      "integrity": "sha512-W9Nj+UmBJG251wkCacIkETgra4QgBo/vgoEkb4a2uoLzpQG7qF9nzwoLXWU5xj3Fg2mxGvEDh47mg24vXccYjA=="
+      "version": "0.8.17",
+      "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.8.17.tgz",
+      "integrity": "sha1-TF5RhahX2o2nk9rzkZNxxaNrKgs="
     }
   }
 }
diff --git a/package.json b/package.json
index 96fc2b0..5b9a040 100644
--- a/package.json
+++ b/package.json
@@ -1,59 +1,92 @@
 {
-  "name": "fineract-cn-web-app",
-  "version": "0.0.0",
-  "license": "MIT",
-  "scripts": {
-    "ng": "ng",
-    "start": "ng serve",
-    "build": "ng build --prod",
-    "test": "ng test",
-    "lint": "ng lint",
-    "e2e": "ng e2e"
-  },
+  "name": "fims",
+  "version": "0.1.0",
   "private": true,
+  "description": "fims backoffice",
+  "keywords": [],
+  "scripts": {
+    "e2e": "protractor",
+    "e2e-test": "protractor ./protractor.conf.js",
+    "tslint": "tslint -c ./tslint.json \"./src/**/*.ts\" -e \"./src/**/typings.d.ts\" -e \"./src/environments/**\"",
+    "postinstall": "webdriver-manager update",
+    "test": "ng test --single-run",
+    "karma": "karma start ./karma.conf.js --single-run",
+    "devTest": "ng test",
+    "dev": "ng serve --verbose --proxy-config proxy.conf.json",
+    "runProd": "ng serve --proxy-config proxy.conf.json --prod",
+    "build": "ng build --prod",
+    "checkLicenses": "license-to-fail ./license.config.js",
+    "bundle-report": "webpack-bundle-analyzer dist/stats.json"
+  },
+  "engines": {
+    "node": ">4.4 < 7",
+    "npm": ">3"
+  },
+  "repository": {
+    "type": "git",
+    "url": "https://github.com/apache/fineract-cn-fims-web-app.git"
+  },
+  "license": "Apache-2.0",
+  "author": "Mark van Veen",
+  "contributors": [
+    "Mark van Veen <mvanveen@mifos.org>",
+    "Myrle Krantz <myrle@apache.org>",
+    "Pembe Miriam <pembemiriam007@gmail.com"
+  ],
   "dependencies": {
-    "@angular/animations": "^5.2.9",
-    "@angular/cdk": "^5.2.4",
-    "@angular/common": "^5.2.0",
-    "@angular/compiler": "^5.2.0",
-    "@angular/core": "^5.2.0",
-    "@angular/forms": "^5.2.0",
-    "@angular/http": "^5.2.0",
-    "@angular/material": "^5.2.4",
-    "@angular/platform-browser": "^5.2.0",
-    "@angular/platform-browser-dynamic": "^5.2.0",
-    "@angular/router": "^5.2.0",
-    "@covalent/core": "^1.0.1",
-    "@ngrx/effects": "^5.2.0",
-    "@ngrx/store": "^5.2.0",
-    "@ngx-translate/core": "^10.0.2",
+    "@angular/animations": "4.4.5",
+    "@angular/cdk": "2.0.0-beta.12",
+    "@angular/common": "4.4.5",
+    "@angular/compiler": "4.4.5",
+    "@angular/core": "4.4.5",
+    "@angular/forms": "4.4.5",
+    "@angular/http": "4.4.5",
+    "@angular/material": "2.0.0-beta.12",
+    "@angular/platform-browser": "4.4.5",
+    "@angular/platform-browser-dynamic": "4.4.5",
+    "@angular/platform-server": "4.4.5",
+    "@angular/router": "4.4.5",
+    "@covalent/core": "1.0.0-beta.8-1",
+    "@ngrx/core": "1.2.0",
+    "@ngrx/effects": "2.0.3",
+    "@ngrx/store": "2.2.2",
+    "@ngrx/store-devtools": "3.2.4",
+    "@ngx-translate/core": "7.0.0",
     "@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"
+    "angular2-text-mask": "^8.0.2",
+    "core-js": "2.4.1",
+    "hammerjs": "2.0.8",
+    "highlight.js": "9.10.0",
+    "ngrx-store-localstorage": "0.1.8",
+    "reselect": "2.5.4",
+    "rxjs": "5.4.1",
+    "showdown": "1.6.4",
+    "text-mask-addons": "^3.6.0",
+    "zone.js": "0.8.17"
   },
   "devDependencies": {
-    "@angular/cli": "~1.7.2",
-    "@angular/compiler-cli": "^5.2.0",
-    "@angular/language-service": "^5.2.0",
-    "@types/jasmine": "~2.8.3",
-    "@types/jasminewd2": "~2.0.2",
-    "@types/node": "~6.0.60",
-    "codelyzer": "^4.0.1",
-    "jasmine-core": "~2.8.0",
-    "jasmine-spec-reporter": "~4.2.1",
-    "karma": "~2.0.0",
-    "karma-chrome-launcher": "~2.2.0",
-    "karma-coverage-istanbul-reporter": "^1.2.1",
-    "karma-jasmine": "~1.1.0",
-    "karma-jasmine-html-reporter": "^0.2.2",
-    "protractor": "~5.1.2",
-    "ts-node": "~4.1.0",
-    "tslint": "~5.9.1",
-    "typescript": "~2.5.3"
+    "@angular/cli": "1.4.7",
+    "@angular/compiler-cli": "4.4.5",
+    "@types/hammerjs": "2.0.30",
+    "@types/jasmine": "2.5.38",
+    "@types/node": "6.0.78",
+    "@types/selenium-webdriver": "2.53.36",
+    "codelyzer": "3.0.0",
+    "jasmine-core": "2.5.2",
+    "jasmine-spec-reporter": "3.2.0",
+    "karma": "1.4.1",
+    "karma-chrome-launcher": "2.0.0",
+    "karma-cli": "1.0.1",
+    "karma-coverage-istanbul-reporter": "0.2.2",
+    "karma-firefox-launcher": "1.0.0",
+    "karma-jasmine": "1.1.0",
+    "karma-jasmine-html-reporter": "0.2.2",
+    "license-to-fail": "2.2.0",
+    "phantomjs-prebuilt": "2.1.7",
+    "protractor": "5.1.0",
+    "ts-node": "3.0.6",
+    "tslint": "5.2.0",
+    "typescript": "2.3.4",
+    "webpack-bundle-analyzer": "^2.3.1"
   }
 }
diff --git a/protractor.conf.js b/protractor.conf.js
index 584292a..3a3ffdb 100644
--- a/protractor.conf.js
+++ b/protractor.conf.js
@@ -1,28 +1,49 @@
-// Protractor configuration file, see link for more information
-// https://github.com/angular/protractor/blob/master/lib/config.ts
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */// Protractor configuration file, see link for more information
+// https://github.com/angular/protractor/blob/master/docs/referenceConf.js
 
-const { SpecReporter } = require('jasmine-spec-reporter');
+/*global jasmine */
+var SpecReporter = require('jasmine-spec-reporter');
 
 exports.config = {
   allScriptsTimeout: 11000,
   specs: [
-    './e2e/**/*.e2e-spec.ts'
+    './e2e/**/*.e2e.ts'
   ],
   capabilities: {
     'browserName': 'chrome'
   },
   directConnect: true,
-  baseUrl: 'http://163.172.130.175:8888/',
+  baseUrl: 'http://localhost:4200/',
   framework: 'jasmine',
   jasmineNodeOpts: {
     showColors: true,
     defaultTimeoutInterval: 30000,
     print: function() {}
   },
-  onPrepare() {
+  useAllAngular2AppRoots: true,
+  beforeLaunch: function() {
     require('ts-node').register({
-      project: 'e2e/tsconfig.e2e.json'
+      project: 'e2e'
     });
-    jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
+  },
+  onPrepare: function() {
+    jasmine.getEnv().addReporter(new SpecReporter());
   }
 };
diff --git a/proxy.conf.json b/proxy.conf.json
index a5b96e0..45b82ad 100644
--- a/proxy.conf.json
+++ b/proxy.conf.json
@@ -1,69 +1,69 @@
 {
   "/identity": {
-    "target": "http://163.172.130.175:2021",
+    "target": "http://51.15.140.45:2021",
     "secure": false,
     "pathRewrite": {
       "/identity": "/identity"
     }
   },
   "/api/office": {
-    "target": "http://163.172.130.175:2023",
+    "target": "http://51.15.140.45:2023",
     "secure": false,
     "pathRewrite": {
       "^/api/office": "/office"
     }
   },
   "/api/customer": {
-    "target": "http://163.172.130.175:2024",
+    "target": "http://51.15.140.45:2024",
     "secure": false,
     "pathRewrite": {
       "^/api/customer": "/customer"
     }
   },
   "/api/accounting": {
-    "target": "http://163.172.130.175:2025",
+    "target": "http://51.15.140.45:2025",
     "secure": false,
     "pathRewrite": {
       "^/api/accounting": "/accounting"
     }
   },
   "/api/portfolio": {
-    "target": "http://163.172.130.175:2026",
+    "target": "http://51.15.140.45:2026",
     "secure": false,
     "pathRewrite": {
       "^/api/portfolio": "/portfolio"
     }
   },
   "/api/deposit": {
-    "target": "http://163.172.130.175:2027",
+    "target": "http://51.15.140.45:2027",
     "secure": false,
     "pathRewrite": {
       "^/api/deposit": "/deposit"
     }
   },
   "/api/teller": {
-    "target": "http://163.172.130.175:2028",
+    "target": "http://51.15.140.45:2028",
     "secure": false,
     "pathRewrite": {
       "^/api/teller": "/teller"
     }
   },
   "/api/reporting": {
-    "target": "http://163.172.130.175:2029",
+    "target": "http://51.15.140.45:2029",
     "secure": false,
     "pathRewrite": {
       "^/api/reporting": "/reporting"
     }
   },
   "/api/cheques": {
-    "target": "http://163.172.130.175:2030",
+    "target": "http://51.15.140.45:2030",
     "secure": false,
     "pathRewrite": {
       "^/api/cheques": "/cheques"
     }
   },
   "/api/payroll": {
-    "target": "http://163.172.130.175:2031",
+    "target": "http://51.15.140.45:2031",
     "secure": false,
     "pathRewrite": {
       "^/api/payroll": "/payroll"
diff --git a/scripts/ghpages-deploy b/scripts/ghpages-deploy
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/scripts/ghpages-deploy
diff --git a/scripts/license/HEADER_HTML b/scripts/license/HEADER_HTML
new file mode 100644
index 0000000..96b8abc
--- /dev/null
+++ b/scripts/license/HEADER_HTML
@@ -0,0 +1,16 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
diff --git a/scripts/license/HEADER_SCSS b/scripts/license/HEADER_SCSS
new file mode 100644
index 0000000..7243427
--- /dev/null
+++ b/scripts/license/HEADER_SCSS
@@ -0,0 +1,18 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
diff --git a/scripts/license/HEADER_TS b/scripts/license/HEADER_TS
new file mode 100644
index 0000000..51da6c0
--- /dev/null
+++ b/scripts/license/HEADER_TS
@@ -0,0 +1,18 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
diff --git a/scripts/license/README.md b/scripts/license/README.md
new file mode 100644
index 0000000..953717d
--- /dev/null
+++ b/scripts/license/README.md
@@ -0,0 +1,9 @@
+# License header generation
+
+This script prepends the Apache License Header to all ts, html and scss files in the src folder.
+
+## Setup
+
+* Install gulp `npm i -g gulp`
+* Install gulp-header-license `npm i gulp-header-license`
+* Run default task `gulp`
diff --git a/scripts/license/gulpfile.js b/scripts/license/gulpfile.js
new file mode 100644
index 0000000..3b2071a
--- /dev/null
+++ b/scripts/license/gulpfile.js
@@ -0,0 +1,24 @@
+var gulp = require('gulp');
+var license = require('gulp-header-license');
+var fs = require('fs');
+
+gulp.task('licenseTS', function () {
+  gulp.src('../../src/**/*.ts')
+    .pipe(license(fs.readFileSync('HEADER_TS', 'utf8')))
+    .pipe(gulp.dest('../../src/'));
+});
+
+gulp.task('licenseHTML', function () {
+  gulp.src('../../src/**/*.html')
+    .pipe(license(fs.readFileSync('HEADER_HTML', 'utf8')))
+    .pipe(gulp.dest('../../src/'));
+});
+
+gulp.task('licenseSCSS', function () {
+  gulp.src('../../src/**/*.scss')
+    .pipe(license(fs.readFileSync('HEADER_SCSS', 'utf8')))
+    .pipe(gulp.dest('../../src/'));
+});
+
+gulp.task('default', ['licenseTS', 'licenseHTML', 'licenseSCSS']);
+
diff --git a/scripts/nginx/README.md b/scripts/nginx/README.md
new file mode 100644
index 0000000..403edf5
--- /dev/null
+++ b/scripts/nginx/README.md
@@ -0,0 +1,15 @@
+# nginx configuration
+
+Config to use nginx for hosting fims.
+
+This config assumes that all needed services are started.
+
+## Setup
+
+* Install nginx
+* Run `npm run build` from root folder
+* Copy all contents of /dist into your nginx root folder under /fims
+* Copy/Overwrite nginx.conf into /conf of your nginx root folder
+* Change proxy_pass configuration of each service to point to the right url and port
+* Start nginx with `nginx`
+* Navigate with your browser to `http://localhost:8888`
diff --git a/scripts/nginx/nginx.conf b/scripts/nginx/nginx.conf
new file mode 100644
index 0000000..c348ce5
--- /dev/null
+++ b/scripts/nginx/nginx.conf
@@ -0,0 +1,124 @@
+events {
+  worker_connections  4096;  ## Default: 1024
+}
+
+http {
+
+  server {
+    listen       8888;
+    server_name  localhost;
+    large_client_header_buffers 4 32k;
+
+    root   fims;
+    index  index.html index.htm;
+    include mime.types;
+
+    location / {
+      try_files $uri /index.html;
+    }
+
+    # /api will server your proxied API that is running on same machine different port
+    # or another machine. So you can protect your API endpoint not get hit by public directly
+    location /identity/ {
+      proxy_pass http://localhost:2021;
+      proxy_http_version 1.1;
+      proxy_set_header Upgrade $http_upgrade;
+      proxy_set_header Connection 'upgrade';
+      proxy_set_header Host $host;
+      proxy_cache_bypass $http_upgrade;
+      proxy_set_header X-Real-IP $remote_addr;
+      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+    }
+
+	location /api/office/ {
+      proxy_pass http://localhost:2022/office/;
+      proxy_http_version 1.1;
+      proxy_set_header Upgrade $http_upgrade;
+      proxy_set_header Connection 'upgrade';
+      proxy_set_header Host $host;
+      proxy_cache_bypass $http_upgrade;
+      proxy_set_header X-Real-IP $remote_addr;
+      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+    }
+
+	location /api/customer/ {
+      proxy_pass http://localhost:2023/customer/;
+      proxy_http_version 1.1;
+      proxy_set_header Upgrade $http_upgrade;
+      proxy_set_header Connection 'upgrade';
+      proxy_set_header Host $host;
+      proxy_cache_bypass $http_upgrade;
+      proxy_set_header X-Real-IP $remote_addr;
+      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+    }
+
+	location /api/accounting/ {
+      proxy_pass http://localhost:2024/accounting/;
+      proxy_http_version 1.1;
+      proxy_set_header Upgrade $http_upgrade;
+      proxy_set_header Connection 'upgrade';
+      proxy_set_header Host $host;
+      proxy_cache_bypass $http_upgrade;
+      proxy_set_header X-Real-IP $remote_addr;
+      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+    }
+
+	location /api/portfolio/ {
+      proxy_pass http://localhost:2025/portfolio/;
+      proxy_http_version 1.1;
+      proxy_set_header Upgrade $http_upgrade;
+      proxy_set_header Connection 'upgrade';
+      proxy_set_header Host $host;
+      proxy_cache_bypass $http_upgrade;
+      proxy_set_header X-Real-IP $remote_addr;
+      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+    }
+
+  location /api/deposit/ {
+      proxy_pass http://localhost:2026/deposit/;
+      proxy_http_version 1.1;
+      proxy_set_header Upgrade $http_upgrade;
+      proxy_set_header Connection 'upgrade';
+      proxy_set_header Host $host;
+      proxy_cache_bypass $http_upgrade;
+      proxy_set_header X-Real-IP $remote_addr;
+      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+    }
+
+  location /api/teller/ {
+      proxy_pass http://localhost:2027/teller/;
+      proxy_http_version 1.1;
+      proxy_set_header Upgrade $http_upgrade;
+      proxy_set_header Connection 'upgrade';
+      proxy_set_header Host $host;
+      proxy_cache_bypass $http_upgrade;
+      proxy_set_header X-Real-IP $remote_addr;
+      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+    }
+
+  location /api/reporting/ {
+      proxy_pass http://localhost:2028/reporting/;
+      proxy_http_version 1.1;
+      proxy_set_header Upgrade $http_upgrade;
+      proxy_set_header Connection 'upgrade';
+      proxy_set_header Host $host;
+      proxy_cache_bypass $http_upgrade;
+      proxy_set_header X-Real-IP $remote_addr;
+      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+    }
+
+    #Static File Caching. All static files with the following extension will be cached for 1 day
+    location ~* .(jpg|jpeg|png|gif|ico|css|js)$ {
+      expires 1d;
+    }
+
+    gzip on;
+    gzip_http_version 1.1;
+    gzip_disable      "MSIE [1-6]\.";
+    gzip_min_length   1100;
+    gzip_vary         on;
+    gzip_proxied      expired no-cache no-store private auth;
+    gzip_types        text/plain text/css application/json application/javascript application/x-javascript text/xml application/xml application/xml+rss text/javascript;
+    gzip_comp_level   9;
+  }
+}
diff --git a/src/app/accounting/account-payable/account-payable.component.html b/src/app/accounting/account-payable/account-payable.component.html
deleted file mode 100644
index a4bed29..0000000
--- a/src/app/accounting/account-payable/account-payable.component.html
+++ /dev/null
@@ -1,60 +0,0 @@
-<div class="main-div mat-elevation-z2">
-  <h3 class="heading">Account Payable</h3>
-  <mat-divider></mat-divider>
-  <div class="fineract-button">
-    <a mat-raised-button  color="primary" [routerLink]="['/navbar/add_cheque']">
-      <mat-icon>add</mat-icon>Add cheque</a>
-      </div>
-
-  <mat-form-field>
-    <input matInput (keyup)="applyFilter($event.target.value)" placeholder="Filter by identifier">
-  </mat-form-field>
-  
-<mat-table #table [dataSource]="dataSource">
-
-  <!-- Position Column -->
-  <ng-container matColumnDef="name">
-    <mat-header-cell *matHeaderCellDef>Name </mat-header-cell>
-    <mat-cell *matCellDef="let element">
-      <a routerLink="{{element.name}}">{{element.name}}</a>
-    </mat-cell>
-  </ng-container>
-
-  <!-- Name Column -->
-  <ng-container matColumnDef="branch">
-    <mat-header-cell *matHeaderCellDef>Branch</mat-header-cell>
-    <mat-cell *matCellDef="let element"> {{element.branch}} </mat-cell>
-  </ng-container>
-
-  <ng-container matColumnDef="bankAccount">
-      <mat-header-cell *matHeaderCellDef>Bank Account</mat-header-cell>
-      <mat-cell *matCellDef="let element"> {{element.bankAccount}} </mat-cell>
-    </ng-container>
-
-    <ng-container matColumnDef="payableAccount">
-        <mat-header-cell *matHeaderCellDef>Payable Account</mat-header-cell>
-        <mat-cell *matCellDef="let element"> {{element.payableAccount} </mat-cell>
-      </ng-container>
-
-      <ng-container matColumnDef="payee">
-        <mat-header-cell *matHeaderCellDef>Payee</mat-header-cell>
-        <mat-cell *matCellDef="let element"> {{element.payee} </mat-cell>
-      </ng-container>
-
-      <ng-container matColumnDef="currency">
-        <mat-header-cell *matHeaderCellDef>Currency</mat-header-cell>
-        <mat-cell *matCellDef="let element"> {{element.currency} </mat-cell>
-      </ng-container>
-
-      <ng-container matColumnDef="amount">
-        <mat-header-cell *matHeaderCellDef>amount</mat-header-cell>
-        <mat-cell *matCellDef="let element"> {{element.amount} </mat-cell>
-      </ng-container>
-
-  <mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
-  <mat-row *matRowDef="let row; columns: displayedColumns;"></mat-row>
-</mat-table>
-  
-  
-</div>
-  
\ No newline at end of file
diff --git a/src/app/accounting/account-payable/account-payable.component.scss b/src/app/accounting/account-payable/account-payable.component.scss
deleted file mode 100644
index 0961a8e..0000000
--- a/src/app/accounting/account-payable/account-payable.component.scss
+++ /dev/null
@@ -1,18 +0,0 @@
-.my-div{
-    margin-left: 2%;
-    margin-right:2%;
-    border-radius: 5px 5px 5px 5px;
-    background-color: #e6e6ff;
-    min-height: 100%;
-   
-  }
-  table {
-      width: 100%;
-    }
-    
-    .mat-form-field {
-      font-size: 14px;
-      width: 100%;
-    }
-    
-    
\ No newline at end of file
diff --git a/src/app/accounting/account-payable/account-payable.component.spec.ts b/src/app/accounting/account-payable/account-payable.component.spec.ts
deleted file mode 100644
index 6e951ab..0000000
--- a/src/app/accounting/account-payable/account-payable.component.spec.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-import { async, ComponentFixture, TestBed } from '@angular/core/testing';
-
-import { AccountPayableComponent } from './account-payable.component';
-
-describe('AccountPayableComponent', () => {
-  let component: AccountPayableComponent;
-  let fixture: ComponentFixture<AccountPayableComponent>;
-
-  beforeEach(async(() => {
-    TestBed.configureTestingModule({
-      declarations: [ AccountPayableComponent ]
-    })
-    .compileComponents();
-  }));
-
-  beforeEach(() => {
-    fixture = TestBed.createComponent(AccountPayableComponent);
-    component = fixture.componentInstance;
-    fixture.detectChanges();
-  });
-
-  it('should create', () => {
-    expect(component).toBeTruthy();
-  });
-});
diff --git a/src/app/accounting/account-payable/account-payable.component.ts b/src/app/accounting/account-payable/account-payable.component.ts
deleted file mode 100644
index 7e4bbc8..0000000
--- a/src/app/accounting/account-payable/account-payable.component.ts
+++ /dev/null
@@ -1,40 +0,0 @@
-import { Component, OnInit } from '@angular/core';
-import {MatTableDataSource} from '@angular/material';
-
-
-@Component({
-  selector: 'app-account-payable',
-  templateUrl: './account-payable.component.html',
-  styleUrls: ['./account-payable.component.scss']
-})
-export class AccountPayableComponent implements OnInit {
-
-  displayedColumns = ['name','branch','bankAccount','payableAccount','payee','currency','amount'];
-  dataSource = new MatTableDataSource(ELEMENT_DATA);
-
-  applyFilter(filterValue: string) {
-    filterValue = filterValue.trim(); // Remove whitespace
-    filterValue = filterValue.toLowerCase(); // MatTableDataSource defaults to lowercase matches
-    this.dataSource.filter = filterValue;
-  }
-
-  constructor() { }
-
-  ngOnInit() {
-  }
-
-}
-export interface Element {
-  name: string;
-  branch:string;
-  bankAccount: string;
-  payableAccount:string;
-  payee: number;
-  currency:Date;
-  amount:string;
-  
-}
-
-const ELEMENT_DATA: Element[] = [
-  
-];
diff --git a/src/app/accounting/account-types.model.ts b/src/app/accounting/account-types.model.ts
new file mode 100644
index 0000000..973cabf
--- /dev/null
+++ b/src/app/accounting/account-types.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 {AccountType} from '../services/accounting/domain/account-type.model';
+
+export interface AccountTypeOption {
+  type: AccountType;
+  label: string;
+}
+
+export const accountTypes: AccountTypeOption[] = [
+  { type: 'ASSET', label: 'Asset' },
+  { type: 'LIABILITY', label: 'Liability' },
+  { type: 'EQUITY', label: 'Equity' },
+  { type: 'EXPENSE', label: 'Expense' },
+  { type: 'REVENUE', label: 'Revenue' }
+];
diff --git a/src/app/accounting/accounting.component.html b/src/app/accounting/accounting.component.html
deleted file mode 100644
index 46ee77c..0000000
--- a/src/app/accounting/accounting.component.html
+++ /dev/null
@@ -1,69 +0,0 @@
-<div class="my-div mat-elevation-z2">
-  <h3 class="heading">Accounting</h3>
-  <hr>
-  <mat-card class="card">
-    <mat-list>
-      <mat-list-item>
-        <a [routerLink]="['/navbar/chart_of_accounts']">
-          <mat-icon>domain</mat-icon>Chart of accounts
-          <br> List of the accounts used by the organisation
-        </a>
-      </mat-list-item>
-      <mat-divider></mat-divider>
-      <mat-list-item>
-        <a [routerLink]="['/navbar/journal_entry']">
-          <mat-icon>add</mat-icon>Journal Entries
-          <br> Manual journal entry transactions recorded in a journal
-        </a>
-      </mat-list-item>
-      <mat-divider></mat-divider>
-      <mat-list-item>
-        <a [routerLink]="['/navbar/transaction_type']">
-          <mat-icon>search</mat-icon>Transaction Types
-          <br> Advance search option for journal entries
-        </a>
-      </mat-list-item>
-      <mat-divider></mat-divider>
-      <mat-list-item>
-        <a [routerLink]="['/navbar/cheque_clearing']">
-          <mat-icon>shuffle</mat-icon>Cheque Clearing
-          <br> List of Financial Activity and GL Accounnt Mappings
-        </a>
-      </mat-list-item>
-  
-    </mat-list>
-  </mat-card>
-
-  <mat-card class="card1">
-    <mat-list>
-      <mat-list-item>
-        <a [routerLink]="['/navbar/trial_balance']">
-          <mat-icon>domain</mat-icon>Trial Balance
-          <br> List of the accounts used by the organisation
-        </a>
-      </mat-list-item>
-      <mat-divider></mat-divider>
-      <mat-list-item>
-        <a [routerLink]="['/navbar/']">
-          <mat-icon>folder</mat-icon>Income Statement
-          <br> Journal Entries made at the end of the accounting period
-        </a>
-      </mat-list-item>
-      <mat-divider></mat-divider>
-      <mat-list-item>
-        <a [routerLink]="['/navbar/']">
-          <mat-icon>lock</mat-icon>Financial Condition
-          <br> Lists all Accounting rules
-        </a>
-      </mat-list-item>
-      <mat-divider></mat-divider>
-      <mat-list-item>
-        <a [routerLink]="['/navbar/payroll']">
-          <mat-icon>settings</mat-icon>Payroll
-          <br> Accrues income,expenses and liabilities as on the provided date
-        </a>
-      </mat-list-item>
-      
-    </mat-list>
-  </mat-card>
-</div>
\ No newline at end of file
diff --git a/src/app/accounting/accounting.component.scss b/src/app/accounting/accounting.component.scss
deleted file mode 100644
index 83a38bb..0000000
--- a/src/app/accounting/accounting.component.scss
+++ /dev/null
@@ -1,30 +0,0 @@
-.my-div{
-    margin-left: 2%;
-    margin-right:2%;
-    border-radius: 5px 5px 5px 5px;
-    background-color: white;
-    min-height: 100%;
-   
-}
-
-.card1{
-    margin-right:20px;
-    margin-left:20px;
-}
-
-mat-list-item {
-    padding-top: 10px;
-    font-size: 14px;
-    padding-bottom: 10px;
-}
-.card,.card1{
-    
-    display:inline-block;
-    
-    vertical-align: top;
-    width:40%;
-   
-}
-.card{
-    margin-left:2%; 
-}
diff --git a/src/app/accounting/accounting.component.spec.ts b/src/app/accounting/accounting.component.spec.ts
deleted file mode 100644
index 2a38b7f..0000000
--- a/src/app/accounting/accounting.component.spec.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-import { async, ComponentFixture, TestBed } from '@angular/core/testing';
-
-import { AccountingComponent } from './accounting.component';
-
-describe('AccountingComponent', () => {
-  let component: AccountingComponent;
-  let fixture: ComponentFixture<AccountingComponent>;
-
-  beforeEach(async(() => {
-    TestBed.configureTestingModule({
-      declarations: [ AccountingComponent ]
-    })
-    .compileComponents();
-  }));
-
-  beforeEach(() => {
-    fixture = TestBed.createComponent(AccountingComponent);
-    component = fixture.componentInstance;
-    fixture.detectChanges();
-  });
-
-  it('should create', () => {
-    expect(component).toBeTruthy();
-  });
-});
diff --git a/src/app/accounting/accounting.component.ts b/src/app/accounting/accounting.component.ts
deleted file mode 100644
index 7a5c834..0000000
--- a/src/app/accounting/accounting.component.ts
+++ /dev/null
@@ -1,16 +0,0 @@
-import { Component, OnInit } from '@angular/core';
-
-
-@Component({
-  selector: 'app-accounting',
-  templateUrl: './accounting.component.html',
-  styleUrls: ['./accounting.component.scss']
-})
-export class AccountingComponent implements OnInit {
-
-  constructor() { }
-
-  ngOnInit() {
-  }
-
-}
diff --git a/src/app/accounting/accounting.module.ts b/src/app/accounting/accounting.module.ts
new file mode 100644
index 0000000..6a9ad10
--- /dev/null
+++ b/src/app/accounting/accounting.module.ts
@@ -0,0 +1,188 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 {FimsSharedModule} from '../common/common.module';
+import {AccountingRoutes} from './accounting.routing';
+import {RouterModule} from '@angular/router';
+import {NgModule} from '@angular/core';
+import {GeneralLedgerComponent} from './general-ledger.component';
+import {TrailBalanceComponent} from './trailBalance/trial-balance.component';
+import {SubLedgerDetailComponent} from './subLedger/sub-ledger.detail.component';
+import {AccountDetailComponent} from './accounts/account.detail.component';
+import {AccountStatusComponent} from './status/status.component';
+import {AccountActivityComponent} from './activity/activity.component';
+import {CommandsResolver} from './activity/commands.resolver';
+import {AccountFormComponent} from './accounts/form/form.component';
+import {CreateAccountFormComponent} from './accounts/form/create/create.form.component';
+import {SubLedgerComponent} from './subLedger/sub-ledger.component';
+import {EditAccountFormComponent} from './accounts/form/edit/edit.form.component';
+import {JournalEntryListComponent} from './journalEntries/journal-entry.list.component';
+import {JournalEntryFormComponent} from './journalEntries/form/form.component';
+import {AccountEntryListComponent} from './accounts/entries/account-entry.list.component';
+import {LedgerFormComponent} from './form/form.component';
+import {EditLedgerFormComponent} from './form/edit/edit.form.component';
+import {CreateLedgerFormComponent} from './form/create/create.form.component';
+import {LedgerExistsGuard} from './ledger-exists.guard';
+import {AccountExistsGuard} from './accounts/account-exists.guard';
+import {AccountingStore, accountingStoreFactory} from './store/index';
+import {Store} from '@ngrx/store';
+import {AccountCommandNotificationEffects} from './store/account/task/effects/notification.effects';
+import {EffectsModule} from '@ngrx/effects';
+import {AccountCommandApiEffects} from './store/account/task/effects/service.effects';
+import {AccountNotificationEffects} from './store/account/effects/notification.effects';
+import {AccountEntryApiEffects} from './store/account/entries/effects/service.effect';
+import {AccountRouteEffects} from './store/account/effects/route.effects';
+import {AccountApiEffects} from './store/account/effects/service.effects';
+import {JournalEntryNotificationEffects} from './store/ledger/journal-entry/effects/notification.effects';
+import {JournalEntryRouteEffects} from './store/ledger/journal-entry/effects/route.effects';
+import {JournalEntryApiEffects} from './store/ledger/journal-entry/effects/service.effects';
+import {LedgerNotificationEffects} from './store/ledger/effects/notification.effects';
+import {LedgerRouteEffects} from './store/ledger/effects/route.effects';
+import {LedgerApiEffects} from './store/ledger/effects/service.effects';
+import {AccountCommandRouteEffects} from './store/account/task/effects/route.effects';
+import {ChartOfAccountComponent} from './chartOfAccounts/chart-of-accounts.component';
+import {ChartOfAccountTableComponent} from './chartOfAccounts/chart-of-account-table.component';
+import {SubLedgerListComponent} from './subLedger/sub-ledger.list.component';
+import {TranslateModule} from '@ngx-translate/core';
+import {
+  MatAutocompleteModule,
+  MatButtonModule,
+  MatCardModule,
+  MatCheckboxModule,
+  MatIconModule,
+  MatInputModule,
+  MatListModule,
+  MatOptionModule,
+  MatRadioModule,
+  MatToolbarModule
+} from '@angular/material';
+import {CommonModule} from '@angular/common';
+import {CovalentDataTableModule, CovalentStepsModule} from '@covalent/core';
+import {FormsModule, ReactiveFormsModule} from '@angular/forms';
+import {TransactionTypeListComponent} from './transactionTypes/transaction-types.list.component';
+import {TransactionTypeApiEffects} from './store/ledger/transaction-type/effects/service.effects';
+import {TransactionTypeRouteEffects} from './store/ledger/transaction-type/effects/route.effects';
+import {TransactionTypeNotificationEffects} from './store/ledger/transaction-type/effects/notification.effects';
+import {TransactionTypeFormComponent} from './transactionTypes/form/transaction-type-form.component';
+import {CreateTransactionTypeFormComponent} from './transactionTypes/form/create/create.form.component';
+import {EditTransactionTypeFormComponent} from './transactionTypes/form/edit/edit.form.component';
+import {TransactionTypeExistsGuard} from './transactionTypes/transaction-type-exists.guard';
+import {TransactionTypeSelectComponent} from './journalEntries/form/transaction-type-select/transaction-type-select.component';
+import {ChequeApiEffects} from './store/cheques/effects/service.effects';
+import {ChequesListComponent} from './cheques/cheques.list.component';
+import {PayrollCollectionApiEffects} from './store/payroll/effects/service.effects';
+import {PayrollListComponent} from './payroll/payroll.list.component';
+import {CreatePayrollFormComponent} from './payroll/form/create.form.component';
+import {PayrollFormComponent} from './payroll/form/form.component';
+import {PayrollCollectionRouteEffects} from './store/payroll/effects/route.effects';
+import {PayrollCollectionNotificationEffects} from './store/payroll/effects/notification.effects';
+import {PaymentsListComponent} from './payroll/payments.list.component';
+import {CreateJournalEntryFormComponent} from './journalEntries/form/create.form.component';
+import {IncomeStatementComponent} from './incomeStatement/income-statement.component';
+import {FinancialConditionComponent} from './financialCondition/financial-condition.component';
+
+@NgModule({
+  imports: [
+    RouterModule.forChild(AccountingRoutes),
+    FimsSharedModule,
+    TranslateModule,
+    CommonModule,
+    FormsModule,
+    ReactiveFormsModule,
+    MatCardModule,
+    MatIconModule,
+    MatListModule,
+    MatToolbarModule,
+    MatInputModule,
+    MatButtonModule,
+    MatRadioModule,
+    MatCheckboxModule,
+    MatAutocompleteModule,
+    MatOptionModule,
+    CovalentDataTableModule,
+    CovalentStepsModule,
+
+    EffectsModule.run(LedgerApiEffects),
+    EffectsModule.run(LedgerRouteEffects),
+    EffectsModule.run(LedgerNotificationEffects),
+
+    EffectsModule.run(JournalEntryApiEffects),
+    EffectsModule.run(JournalEntryRouteEffects),
+    EffectsModule.run(JournalEntryNotificationEffects),
+
+    EffectsModule.run(TransactionTypeApiEffects),
+    EffectsModule.run(TransactionTypeRouteEffects),
+    EffectsModule.run(TransactionTypeNotificationEffects),
+
+    EffectsModule.run(AccountApiEffects),
+    EffectsModule.run(AccountRouteEffects),
+    EffectsModule.run(AccountNotificationEffects),
+    EffectsModule.run(AccountEntryApiEffects),
+    EffectsModule.run(AccountCommandApiEffects),
+    EffectsModule.run(AccountCommandRouteEffects),
+    EffectsModule.run(AccountCommandNotificationEffects),
+
+    EffectsModule.run(ChequeApiEffects),
+
+    EffectsModule.run(PayrollCollectionApiEffects),
+    EffectsModule.run(PayrollCollectionRouteEffects),
+    EffectsModule.run(PayrollCollectionNotificationEffects)
+  ],
+  declarations: [
+    GeneralLedgerComponent,
+    SubLedgerComponent,
+    SubLedgerListComponent,
+    SubLedgerDetailComponent,
+    LedgerFormComponent,
+    CreateLedgerFormComponent,
+    EditLedgerFormComponent,
+    TrailBalanceComponent,
+    ChartOfAccountComponent,
+    ChartOfAccountTableComponent,
+    AccountEntryListComponent,
+    AccountDetailComponent,
+    AccountStatusComponent,
+    AccountActivityComponent,
+    AccountFormComponent,
+    CreateAccountFormComponent,
+    EditAccountFormComponent,
+    JournalEntryListComponent,
+    CreateJournalEntryFormComponent,
+    JournalEntryFormComponent,
+    TransactionTypeListComponent,
+    TransactionTypeFormComponent,
+    CreateTransactionTypeFormComponent,
+    EditTransactionTypeFormComponent,
+    TransactionTypeSelectComponent,
+    ChequesListComponent,
+    PayrollListComponent,
+    CreatePayrollFormComponent,
+    PayrollFormComponent,
+    PaymentsListComponent,
+    IncomeStatementComponent,
+    FinancialConditionComponent
+  ],
+  providers: [
+    CommandsResolver,
+    LedgerExistsGuard,
+    AccountExistsGuard,
+    TransactionTypeExistsGuard,
+    { provide: AccountingStore, useFactory: accountingStoreFactory, deps: [Store]}
+  ]
+})
+export class AccountingModule {}
diff --git a/src/app/accounting/accounting.routing.ts b/src/app/accounting/accounting.routing.ts
new file mode 100644
index 0000000..1ada363
--- /dev/null
+++ b/src/app/accounting/accounting.routing.ts
@@ -0,0 +1,175 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 {Routes} from '@angular/router';
+import {GeneralLedgerComponent} from './general-ledger.component';
+import {TrailBalanceComponent} from './trailBalance/trial-balance.component';
+import {SubLedgerDetailComponent} from './subLedger/sub-ledger.detail.component';
+import {AccountDetailComponent} from './accounts/account.detail.component';
+import {AccountStatusComponent} from './status/status.component';
+import {CommandsResolver} from './activity/commands.resolver';
+import {AccountActivityComponent} from './activity/activity.component';
+import {CreateAccountFormComponent} from './accounts/form/create/create.form.component';
+import {SubLedgerComponent} from './subLedger/sub-ledger.component';
+import {EditAccountFormComponent} from './accounts/form/edit/edit.form.component';
+import {JournalEntryListComponent} from './journalEntries/journal-entry.list.component';
+import {AccountEntryListComponent} from './accounts/entries/account-entry.list.component';
+import {CreateLedgerFormComponent} from './form/create/create.form.component';
+import {EditLedgerFormComponent} from './form/edit/edit.form.component';
+import {LedgerExistsGuard} from './ledger-exists.guard';
+import {AccountExistsGuard} from './accounts/account-exists.guard';
+import {ChartOfAccountComponent} from './chartOfAccounts/chart-of-accounts.component';
+import {SubLedgerListComponent} from './subLedger/sub-ledger.list.component';
+import {TransactionTypeListComponent} from './transactionTypes/transaction-types.list.component';
+import {EditTransactionTypeFormComponent} from './transactionTypes/form/edit/edit.form.component';
+import {CreateTransactionTypeFormComponent} from './transactionTypes/form/create/create.form.component';
+import {TransactionTypeExistsGuard} from './transactionTypes/transaction-type-exists.guard';
+import {ChequesListComponent} from './cheques/cheques.list.component';
+import {PayrollListComponent} from './payroll/payroll.list.component';
+import {CreatePayrollFormComponent} from './payroll/form/create.form.component';
+import {PaymentsListComponent} from './payroll/payments.list.component';
+import {CreateJournalEntryFormComponent} from './journalEntries/form/create.form.component';
+import {IncomeStatementComponent} from './incomeStatement/income-statement.component';
+import {FinancialConditionComponent} from './financialCondition/financial-condition.component';
+
+export const AccountingRoutes: Routes = [
+  {path: '', component: GeneralLedgerComponent},
+  {
+    path: 'ledgers/detail/:id',
+    component: SubLedgerComponent,
+    canActivate: [LedgerExistsGuard],
+    data: {
+      hasPermission: { id: 'accounting_ledgers', accessLevel: 'READ' }
+    },
+    children: [
+      {
+        path: '',
+        component: SubLedgerDetailComponent,
+      },
+      {
+        path: 'edit',
+        component: EditLedgerFormComponent,
+        data: { hasPermission: { id: 'accounting_ledgers', accessLevel: 'CHANGE' }}
+      },
+      {
+        path: 'ledgers',
+        component: SubLedgerListComponent,
+      },
+      {
+        path: 'ledgers/edit',
+        component: EditLedgerFormComponent,
+        data: { hasPermission: { id: 'accounting_ledgers', accessLevel: 'CHANGE' }}
+      },
+      {
+        path: 'ledgers/create',
+        component: CreateLedgerFormComponent,
+        data: { hasPermission: { id: 'accounting_ledgers', accessLevel: 'CHANGE' }}
+      },
+      {
+        path: 'accounts/create',
+        component: CreateAccountFormComponent,
+        data: {
+          hasPermission: {id: 'accounting_accounts', accessLevel: 'CHANGE'}
+        }
+      }
+    ]
+  },
+  {
+    path: 'create',
+    component: CreateLedgerFormComponent,
+    data: {hasPermission: {id: 'accounting_ledgers', accessLevel: 'CHANGE'}}
+  },
+  {
+    path: 'accounts/detail/:id',
+    component: AccountDetailComponent,
+    canActivate: [AccountExistsGuard],
+    data: {hasPermission: {id: 'accounting_accounts', accessLevel: 'READ'}}
+  },
+  {
+    path: 'accounts/detail/:id/edit',
+    component: EditAccountFormComponent,
+    canActivate: [AccountExistsGuard],
+    data: {hasPermission: {id: 'accounting_accounts', accessLevel: 'CHANGE'}}
+  },
+  {
+    path: 'accounts/detail/:id/tasks',
+    component: AccountStatusComponent,
+    canActivate: [AccountExistsGuard],
+    data: {hasPermission: {id: 'accounting_accounts', accessLevel: 'READ'}}
+  },
+  {
+    path: 'accounts/detail/:id/activities',
+    component: AccountActivityComponent,
+    canActivate: [AccountExistsGuard],
+    resolve: {commands: CommandsResolver},
+    data: {hasPermission: {id: 'accounting_accounts', accessLevel: 'READ'}}
+  },
+  {
+    path: 'accounts/detail/:id/entries',
+    component: AccountEntryListComponent,
+    canActivate: [AccountExistsGuard],
+    data: {hasPermission: {id: 'accounting_accounts', accessLevel: 'READ'}}
+  },
+
+  {path: 'trialBalance', component: TrailBalanceComponent, data: {hasPermission: {id: 'accounting_ledgers', accessLevel: 'READ'}}},
+  {
+    path: 'incomeStatement',
+    component: IncomeStatementComponent,
+    data: {hasPermission: {id: 'accounting_income_statement', accessLevel: 'READ'}}
+  },
+  {
+    path: 'financialCondition',
+    component: FinancialConditionComponent,
+    data: {hasPermission: {id: 'accounting_fin_condition', accessLevel: 'READ'}}
+  },
+  {
+    path: 'transactiontypes',
+    component: TransactionTypeListComponent,
+    data: {hasPermission: {id: 'accounting_tx_types', accessLevel: 'READ'}}
+  },
+  {
+    path: 'transactiontypes/create',
+    component: CreateTransactionTypeFormComponent,
+    data: {hasPermission: {id: 'accounting_tx_types', accessLevel: 'CHANGE'}}
+  },
+  {
+    path: 'transactiontypes/edit/:code',
+    component: EditTransactionTypeFormComponent,
+    canActivate: [TransactionTypeExistsGuard],
+    data: {hasPermission: {id: 'accounting_tx_types', accessLevel: 'CHANGE'}}
+  },
+  {path: 'chartOfAccounts', component: ChartOfAccountComponent, data: {hasPermission: {id: 'accounting_ledgers', accessLevel: 'READ'}}},
+  {path: 'journalEntries', component: JournalEntryListComponent, data: {hasPermission: {id: 'accounting_journals', accessLevel: 'READ'}}},
+  {
+    path: 'journalEntries/create',
+    component: CreateJournalEntryFormComponent,
+    data: {hasPermission: {id: 'accounting_journals', accessLevel: 'CHANGE'}}
+  },
+  {path: 'cheques', component: ChequesListComponent, data: {hasPermission: {id: 'cheque_management', accessLevel: 'READ'}}},
+  {path: 'payrolls', component: PayrollListComponent, data: {hasPermission: {id: 'payroll_distribution', accessLevel: 'READ'}}},
+  {
+    path: 'payrolls/create',
+    component: CreatePayrollFormComponent,
+    data: {hasPermission: {id: 'payroll_distribution', accessLevel: 'CHANGE'}}
+  },
+  {
+    path: 'payrolls/payments/:id',
+    component: PaymentsListComponent,
+    data: {hasPermission: {id: 'payroll_distribution', accessLevel: 'READ'}}
+  }
+];
diff --git a/src/app/accounting/accounts/account-exists.guard.ts b/src/app/accounting/accounts/account-exists.guard.ts
new file mode 100644
index 0000000..c8d48d0
--- /dev/null
+++ b/src/app/accounting/accounts/account-exists.guard.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 {ActivatedRouteSnapshot, CanActivate, RouterStateSnapshot} from '@angular/router';
+import {Injectable} from '@angular/core';
+import * as fromAccounting from '../store';
+import {Observable} from 'rxjs/Observable';
+import {of} from 'rxjs/observable/of';
+import {AccountingService} from '../../services/accounting/accounting.service';
+import {LoadAction} from '../store/account/account.actions';
+import {AccountingStore} from '../store/index';
+import {ExistsGuardService} from '../../common/guards/exists-guard';
+
+@Injectable()
+export class AccountExistsGuard implements CanActivate {
+
+  constructor(private store: AccountingStore,
+              private accountingService: AccountingService,
+              private existsGuardService: ExistsGuardService) {}
+
+  hasAccountInStore(id: string): Observable<boolean> {
+    const timestamp$: Observable<number> = this.store.select(fromAccounting.getAccountsLoadedAt)
+      .map(loadedAt => loadedAt[id]);
+
+    return this.existsGuardService.isWithinExpiry(timestamp$);
+  }
+
+  hasAccountInApi(id: string): Observable<boolean> {
+    const getAccount$ = this.accountingService.findAccount(id)
+      .map(accountEntity => new LoadAction({
+        resource: accountEntity
+      }))
+      .do((action: LoadAction) => this.store.dispatch(action))
+      .map(employee => !!employee);
+
+    return this.existsGuardService.routeTo404OnError(getAccount$);
+  }
+
+  hasAccount(id: string): Observable<boolean> {
+    return this.hasAccountInStore(id)
+      .switchMap(inStore => {
+        if (inStore) {
+          return of(inStore);
+        }
+
+        return this.hasAccountInApi(id);
+      });
+  }
+
+  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
+    return this.hasAccount(route.params['id']);
+  }
+}
diff --git a/src/app/accounting/accounts/account.detail.component.html b/src/app/accounting/accounts/account.detail.component.html
new file mode 100644
index 0000000..9237ae9
--- /dev/null
+++ b/src/app/accounting/accounts/account.detail.component.html
@@ -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.
+-->
+
+<fims-layout-card-over title="{{'Account with value' | translate:{ value: account.identifier } }}" [subTitle]="account.name" [navigateBackTo]="['../../../ledgers/detail', account.ledger]">
+  <fims-layout-card-over-header-menu>
+    <button mat-icon-button (click)="deleteAccount()" title="{{'Delete this account' | translate}}" *ngIf="canDelete$ | async"><mat-icon>delete</mat-icon></button>
+  </fims-layout-card-over-header-menu>
+  <fims-two-column-layout>
+    <mat-nav-list left>
+      <h3 mat-subheader translate>Management</h3>
+      <a mat-list-item [routerLink]="['./entries']">
+        <mat-icon matListAvatar>assignment</mat-icon>
+        <h3 matLine translate>Account entries</h3>
+        <p matLine translate>Account entries</p>
+      </a>
+      <a mat-list-item [routerLink]="['./tasks']">
+        <mat-icon matListAvatar>playlist_add_check</mat-icon>
+        <h3 matLine translate>Tasks</h3>
+        <p matLine translate>Change the status of this account</p>
+      </a>
+      <a mat-list-item [routerLink]="['./activities']">
+        <mat-icon matListAvatar>event</mat-icon>
+        <h3 matLine translate>Activities</h3>
+        <p matLine translate>Recent activities</p>
+      </a>
+      <a *ngIf="account.referenceAccount" mat-list-item [routerLink]="['../', account.referenceAccount]">
+        <mat-icon matListAvatar>view_module</mat-icon>
+        <h3 matLine translate>Reference account</h3>
+        <p matLine translate>Navigate to reference account</p>
+      </a>
+    </mat-nav-list>
+    <mat-list right>
+      <h3 mat-subheader translate>Current status</h3>
+      <fims-state-display [state]="account.state"></fims-state-display>
+      <mat-list-item>
+        <mat-icon matListAvatar>account_balance</mat-icon>
+        <h3 matLine translate>Type</h3>
+        <p matLine>{{account.type}}</p>
+      </mat-list-item>
+      <mat-list-item>
+        <mat-icon matListAvatar>payment</mat-icon>
+        <h3 matLine translate>Balance</h3>
+        <p matLine>{{account.balance | number}}</p>
+      </mat-list-item>
+    </mat-list>
+  </fims-two-column-layout>
+</fims-layout-card-over>
+<fims-fab-button title="{{'Edit account' | translate}}" icon="mode_edit" [link]="['edit']" [permission]="{ id: 'accounting_accounts', accessLevel: 'CHANGE'}"></fims-fab-button>
diff --git a/src/app/accounting/accounts/account.detail.component.ts b/src/app/accounting/accounts/account.detail.component.ts
new file mode 100644
index 0000000..693d509
--- /dev/null
+++ b/src/app/accounting/accounts/account.detail.component.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 {Component, OnDestroy, OnInit} from '@angular/core';
+import {Account} from '../../services/accounting/domain/account.model';
+import {ActivatedRoute} from '@angular/router';
+import * as fromAccounting from '../store';
+import * as fromRoot from '../../store';
+import {Subscription} from 'rxjs/Subscription';
+import {AccountingStore} from '../store/index';
+import {DELETE, SelectAction} from '../store/account/account.actions';
+import {Observable} from 'rxjs/Observable';
+import {FimsPermission} from '../../services/security/authz/fims-permission.model';
+import {TranslateService} from '@ngx-translate/core';
+import {TdDialogService} from '@covalent/core';
+
+@Component({
+  templateUrl: './account.detail.component.html'
+})
+export class AccountDetailComponent implements OnInit, OnDestroy {
+
+  private actionsSubscription: Subscription;
+
+  private accountSubscription: Subscription;
+
+  canDelete$: Observable<boolean>;
+
+  account: Account;
+
+  constructor(private route: ActivatedRoute, private dialogService: TdDialogService, private translate: TranslateService,
+              private store: AccountingStore) {
+  }
+
+  ngOnInit(): void {
+    this.actionsSubscription = this.route.params
+      .map(params => new SelectAction(params['id']))
+      .subscribe(this.store);
+
+    const account$: Observable<Account> = this.store.select(fromAccounting.getSelectedAccount)
+      .filter(account => !!account);
+
+    this.canDelete$ = Observable.combineLatest(
+      this.store.select(fromRoot.getPermissions),
+      account$,
+      (permissions, account: Account) => ({
+        hasPermission: this.hasDeletePermission(permissions),
+        isAccountClosed: account.state === 'CLOSED'
+      }))
+      .map(result => result.hasPermission && result.isAccountClosed);
+
+    this.accountSubscription = account$
+      .subscribe(account => this.account = account);
+  }
+
+  private hasDeletePermission(permissions: FimsPermission[]): boolean {
+    return permissions.filter(permission =>
+      permission.id === 'accounting_accounts' &&
+      permission.accessLevel === 'DELETE'
+    ).length > 0;
+  }
+
+  ngOnDestroy(): void {
+    this.actionsSubscription.unsubscribe();
+    this.accountSubscription.unsubscribe();
+  }
+
+  confirmDeletion(): Observable<boolean> {
+    const message = 'Do you want to delete this account?';
+    const title = 'Confirm deletion';
+    const button = 'DELETE ACCOUNT';
+
+    return this.translate.get([title, message, button])
+      .flatMap(result =>
+        this.dialogService.openConfirm({
+          message: result[message],
+          title: result[title],
+          acceptButton: result[button]
+        }).afterClosed()
+      );
+  }
+
+  deleteAccount(): void {
+    this.confirmDeletion()
+      .filter(accept => accept)
+      .subscribe(() => {
+        this.store.dispatch({
+          type: DELETE, payload: {
+            account: this.account,
+            activatedRoute: this.route
+          }
+        });
+      });
+  }
+
+}
diff --git a/src/app/accounting/accounts/entries/account-entry.list.component.html b/src/app/accounting/accounts/entries/account-entry.list.component.html
new file mode 100644
index 0000000..af2046e
--- /dev/null
+++ b/src/app/accounting/accounts/entries/account-entry.list.component.html
@@ -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.
+-->
+
+<fims-layout-card-over
+  title="{{'Account entries for account' | translate:{ value: account.identifier} }}"
+  [navigateBackTo]="['../']"
+  *ngIf="account$ | async as account">
+  <form [formGroup]="form">
+    <mat-form-field layout-margin>
+      <input matInput type="date" placeholder="{{'Start date' | translate}}" formControlName="startDate">
+    </mat-form-field>
+    <mat-form-field layout-margin>
+      <input matInput type="date" placeholder="{{'End date' | translate}}" formControlName="endDate">
+      <mat-error *ngIf="form.hasError('rangeInvalid')" class="tc-red-600" translate>Invalid date range</mat-error>
+    </mat-form-field>
+    <button layout-margin mat-button mat-icon-button (click)="fetchAccountsEntries(account.identifier)" [disabled]="!form.valid">
+      <mat-icon>search</mat-icon>
+    </button>
+  </form>
+  <fims-data-table flex
+                   (onFetch)="fetchAccountsEntries(account.identifier, $event)"
+                   [columns]="columns"
+                   [data]="accountEntryData$ | async"
+                   [actionColumn]="false"
+                   [sortable]="true"
+                   [sortBy]="'transactionDate'"
+                   [pageable]="true">
+  </fims-data-table>
+</fims-layout-card-over>
diff --git a/src/app/accounting/accounts/entries/account-entry.list.component.ts b/src/app/accounting/accounts/entries/account-entry.list.component.ts
new file mode 100644
index 0000000..da896d6
--- /dev/null
+++ b/src/app/accounting/accounts/entries/account-entry.list.component.ts
@@ -0,0 +1,111 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, OnDestroy, OnInit} from '@angular/core';
+import {TableData, TableFetchRequest} from '../../../common/data-table/data-table.component';
+import {Account} from '../../../services/accounting/domain/account.model';
+import {ActivatedRoute} from '@angular/router';
+import {FormBuilder, FormGroup, Validators} from '@angular/forms';
+import {todayAsISOString, toShortISOString} from '../../../services/domain/date.converter';
+import {FimsValidators} from '../../../common/validator/validators';
+import * as fromAccounting from '../../store';
+import {Observable} from 'rxjs/Observable';
+import {Subscription} from 'rxjs/Subscription';
+import {AccountingStore} from '../../store/index';
+import {SEARCH} from '../../store/account/entries/entries.actions';
+import {SelectAction} from '../../store/account/account.actions';
+import {DatePipe} from '@angular/common';
+import {FetchRequest} from '../../../services/domain/paging/fetch-request.model';
+
+@Component({
+  templateUrl: './account-entry.list.component.html',
+  providers: [DatePipe]
+})
+export class AccountEntryListComponent implements OnInit, OnDestroy {
+
+  private actionsSubscription: Subscription;
+
+  private lastFetchRequest: FetchRequest = {};
+
+  form: FormGroup;
+
+  account$: Observable<Account>;
+
+  accountEntryData$: Observable<TableData>;
+
+  columns: any[] = [
+    {
+      name: 'transactionDate', label: 'Transaction date', tooltip: 'Transaction date', format: (v: any) => {
+        return this.datePipe.transform(v, 'short');
+    }
+    },
+    {name: 'type', label: 'Type', tooltip: 'Type'},
+    {name: 'message', label: 'Message', tooltip: 'Message'},
+    {name: 'amount', label: 'Amount', tooltip: 'Amount'},
+    {name: 'balance', label: 'Balance', tooltip: 'Balance'}
+  ];
+
+  constructor(private route: ActivatedRoute, private formBuilder: FormBuilder, private store: AccountingStore, private datePipe: DatePipe) {
+  }
+
+  ngOnInit(): void {
+    const today = todayAsISOString();
+    this.form = this.formBuilder.group({
+      'startDate': [today, [Validators.required]],
+      'endDate': [today, [Validators.required]],
+    }, {validator: FimsValidators.matchRange('startDate', 'endDate')});
+
+    this.actionsSubscription = this.route.params
+      .map(params => new SelectAction(params['id']))
+      .subscribe(this.store);
+
+    this.account$ = this.store.select(fromAccounting.getSelectedAccount)
+      .filter(account => !!account)
+      .do(account => this.fetchAccountsEntries(account.identifier));
+
+    this.accountEntryData$ = this.store.select(fromAccounting.getAccountEntrySearchResults)
+      .map(accountEntryPage => ({
+        totalElements: accountEntryPage.totalElements,
+        totalPages: accountEntryPage.totalPages,
+        data: accountEntryPage.entries
+      }));
+  }
+
+  ngOnDestroy(): void {
+    this.actionsSubscription.unsubscribe();
+  }
+
+  fetchAccountsEntries(accountId: string, fetchRequest?: TableFetchRequest): void {
+    if (fetchRequest) {
+      this.lastFetchRequest = fetchRequest;
+    }
+
+    const startDate = toShortISOString(this.form.get('startDate').value);
+    const endDate = toShortISOString(this.form.get('endDate').value);
+
+    this.store.dispatch({
+      type: SEARCH, payload: {
+        accountId,
+        startDate,
+        endDate,
+        fetchRequest: this.lastFetchRequest
+      }
+    });
+
+  }
+}
diff --git a/src/app/accounting/accounts/form/create/create.form.component.html b/src/app/accounting/accounts/form/create/create.form.component.html
new file mode 100644
index 0000000..a9ff595
--- /dev/null
+++ b/src/app/accounting/accounts/form/create/create.form.component.html
@@ -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.
+-->
+
+<fims-layout-card-over title="{{'Create new account' | translate}}">
+  <fims-account-form-component #form
+                                  (onSave)="onSave($event)"
+                                  (onCancel)="onCancel()"
+                                  [account]="account">
+  </fims-account-form-component>
+</fims-layout-card-over>
diff --git a/src/app/accounting/accounts/form/create/create.form.component.ts b/src/app/accounting/accounts/form/create/create.form.component.ts
new file mode 100644
index 0000000..80deed1
--- /dev/null
+++ b/src/app/accounting/accounts/form/create/create.form.component.ts
@@ -0,0 +1,86 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, OnDestroy, OnInit, ViewChild} from '@angular/core';
+import {AccountFormComponent} from '../form.component';
+import {Account} from '../../../../services/accounting/domain/account.model';
+import {ActivatedRoute, Router} from '@angular/router';
+import {Ledger} from '../../../../services/accounting/domain/ledger.model';
+import * as fromAccounting from '../../../store';
+import {Store} from '@ngrx/store';
+import {Subscription} from 'rxjs/Subscription';
+import {Error} from '../../../../services/domain/error.model';
+import {CREATE, RESET_FORM} from '../../../store/account/account.actions';
+
+@Component({
+  templateUrl: './create.form.component.html'
+})
+export class CreateAccountFormComponent implements OnInit, OnDestroy {
+
+  private formStateSubscription: Subscription;
+  private selectedLedgerSubscription: Subscription;
+
+  ledger: Ledger;
+
+  @ViewChild('form') formComponent: AccountFormComponent;
+
+  account: Account = {
+    identifier: '',
+    name: '',
+    ledger: '',
+    balance: 0
+  };
+
+  constructor(private router: Router, private route: ActivatedRoute, private store: Store<fromAccounting.State>) {}
+
+  ngOnInit() {
+    this.formStateSubscription = this.store.select(fromAccounting.getAccountFormError)
+      .filter((error: Error) => !!error)
+      .subscribe((error: Error) => this.formComponent.showIdentifierValidationError());
+
+    this.selectedLedgerSubscription = this.store.select(fromAccounting.getSelectedLedger)
+      .filter(ledger => !!ledger)
+      .subscribe(ledger => {
+        this.ledger = ledger;
+        this.account.ledger = this.ledger.identifier;
+        this.account.type = this.ledger.type;
+      });
+  }
+
+  ngOnDestroy(): void {
+    this.formStateSubscription.unsubscribe();
+    this.selectedLedgerSubscription.unsubscribe();
+
+    this.store.dispatch({ type: RESET_FORM });
+  }
+
+  onSave(account: Account) {
+    this.store.dispatch({ type: CREATE, payload: {
+      account,
+      activatedRoute: this.route
+    }});
+  }
+
+  onCancel() {
+    this.navigateAway();
+  }
+
+  navigateAway(): void {
+    this.router.navigate(['../../'], { relativeTo: this.route });
+  }
+}
diff --git a/src/app/accounting/accounts/form/edit/edit.form.component.html b/src/app/accounting/accounts/form/edit/edit.form.component.html
new file mode 100644
index 0000000..09009a0
--- /dev/null
+++ b/src/app/accounting/accounts/form/edit/edit.form.component.html
@@ -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.
+-->
+
+<fims-layout-card-over title="{{'Edit account' | translate}}">
+  <fims-account-form-component #form
+                                 (onSave)="onSave($event)"
+                                 (onCancel)="onCancel()"
+                                 [account]="account"
+                                 [editMode]="true">
+  </fims-account-form-component>
+</fims-layout-card-over>
diff --git a/src/app/accounting/accounts/form/edit/edit.form.component.ts b/src/app/accounting/accounts/form/edit/edit.form.component.ts
new file mode 100644
index 0000000..76bb0aa
--- /dev/null
+++ b/src/app/accounting/accounts/form/edit/edit.form.component.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 {Account} from '../../../../services/accounting/domain/account.model';
+import {AccountFormComponent} from '../form.component';
+import {Component, OnDestroy, OnInit, ViewChild} from '@angular/core';
+import {ActivatedRoute, Router} from '@angular/router';
+import {Subscription} from 'rxjs/Subscription';
+import * as fromAccounting from '../../../store';
+import {AccountingStore} from '../../../store/index';
+import {SelectAction, UPDATE} from '../../../store/account/account.actions';
+
+@Component({
+  templateUrl: './edit.form.component.html'
+})
+export class EditAccountFormComponent implements OnInit, OnDestroy {
+
+  private actionsSubscription: Subscription;
+
+  private accountSubscription: Subscription;
+
+  account: Account;
+
+  @ViewChild('form') formComponent: AccountFormComponent;
+
+  constructor(private router: Router, private route: ActivatedRoute, private store: AccountingStore) {}
+
+  ngOnInit() {
+    this.actionsSubscription = this.route.params
+      .map(params => new SelectAction(params['id']))
+      .subscribe(this.store);
+
+    this.accountSubscription = this.store.select(fromAccounting.getSelectedAccount)
+      .filter(account => !!account)
+      .subscribe(account => this.account = account);
+  }
+
+  ngOnDestroy(): void {
+    this.actionsSubscription.unsubscribe();
+    this.accountSubscription.unsubscribe();
+  }
+
+  onSave(account: Account) {
+    this.store.dispatch({ type: UPDATE, payload: {
+      account,
+      activatedRoute: this.route
+    }});
+  }
+
+  onCancel() {
+    this.navigateAway();
+  }
+
+  navigateAway(): void {
+    this.router.navigate(['../'], { relativeTo: this.route });
+  }
+}
diff --git a/src/app/accounting/accounts/form/form.component.html b/src/app/accounting/accounts/form/form.component.html
new file mode 100644
index 0000000..c43e3da
--- /dev/null
+++ b/src/app/accounting/accounts/form/form.component.html
@@ -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.
+-->
+
+<td-steps mode="'vertical'">
+  <td-step #detailsStep label="{{'Account details' | translate}}" [state]="">
+    <form [formGroup]="form" layout="column">
+      <mat-radio-group formControlName="type">
+        <mat-radio-button *ngFor="let type of accountTypeOptions" [value]="type.type" layout-margin>
+          {{type.label}}
+        </mat-radio-button>
+      </mat-radio-group>
+      <fims-id-input flex [form]="form" controlName="identifier" [readonly]="editMode"></fims-id-input>
+      <fims-text-input [form]="form" controlName="name" placeholder="{{'Name' | translate}}"></fims-text-input>
+      <fims-text-input type="number" [form]="form" controlName="balance" placeholder="{{'Balance' | translate}}"></fims-text-input>
+    </form>
+    <ng-template td-step-actions>
+      <fims-form-final-action
+        [resourceName]="'ACCOUNT'"
+        [editMode]="editMode"
+        [disabled]="!valid"
+        (onCancel)="cancel()"
+        (onSave)="save()">
+      </fims-form-final-action>
+    </ng-template>
+  </td-step>
+</td-steps>
diff --git a/src/app/accounting/accounts/form/form.component.spec.ts b/src/app/accounting/accounts/form/form.component.spec.ts
new file mode 100644
index 0000000..25d1f56
--- /dev/null
+++ b/src/app/accounting/accounts/form/form.component.spec.ts
@@ -0,0 +1,111 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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} from '@angular/core';
+import {async, ComponentFixture, TestBed} from '@angular/core/testing';
+import {AccountFormComponent} from './form.component';
+import {By} from '@angular/platform-browser';
+import {Account} from '../../../services/accounting/domain/account.model';
+import {TranslateModule} from '@ngx-translate/core';
+import {ReactiveFormsModule} from '@angular/forms';
+import {FimsSharedModule} from '../../../common/common.module';
+import {MatCheckboxModule, MatInputModule, MatRadioModule} from '@angular/material';
+import {CovalentStepsModule} from '@covalent/core';
+import {NoopAnimationsModule} from '@angular/platform-browser/animations';
+
+describe('Test account form', () => {
+
+  let fixture: ComponentFixture<TestComponent>;
+
+  let testComponent: TestComponent;
+
+  beforeEach( async(() => {
+    TestBed.configureTestingModule({
+      imports: [
+        TranslateModule.forRoot(),
+        MatCheckboxModule,
+        MatRadioModule,
+        MatInputModule,
+        CovalentStepsModule,
+        FimsSharedModule,
+        ReactiveFormsModule,
+        NoopAnimationsModule
+      ],
+      declarations: [
+        AccountFormComponent,
+        TestComponent
+      ]
+    }).compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture  = TestBed.createComponent(TestComponent);
+    testComponent = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should trigger save event', () => {
+    const button: DebugElement = fixture.debugElement.query(By.css('button[mat-raised-button]'));
+
+    button.nativeElement.click();
+
+    expect(testComponent.savedAccount).toEqual(testComponent.account);
+  });
+
+  it('should trigger cancel event', () => {
+    const button: DebugElement = fixture.debugElement.query(By.css('button[mat-button]'));
+
+    button.nativeElement.click();
+
+    expect(testComponent.canceled).toBeTruthy();
+  });
+
+});
+
+@Component({
+  template: `
+    <fims-account-form-component
+      (onSave)="onSave($event)"
+      (onCancel)="onCancel($event)"
+      [account]="account"
+      [editMode]="true">
+    </fims-account-form-component>`
+})
+class TestComponent {
+
+  account: Account = {
+    identifier: 'test',
+    type: 'ASSET',
+    name: 'test',
+    ledger: 'test',
+    balance: 10
+  };
+
+  savedAccount: Account;
+
+  canceled: boolean;
+
+  onSave(account: Account): void {
+    this.savedAccount = account;
+  }
+
+  onCancel(): void {
+    this.canceled = true;
+  }
+
+}
diff --git a/src/app/accounting/accounts/form/form.component.ts b/src/app/accounting/accounts/form/form.component.ts
new file mode 100644
index 0000000..fcff1fa
--- /dev/null
+++ b/src/app/accounting/accounts/form/form.component.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 {Component, EventEmitter, Input, OnInit, Output, ViewChild} from '@angular/core';
+import {Account} from '../../../services/accounting/domain/account.model';
+import {FormBuilder, Validators} from '@angular/forms';
+import {FormComponent} from '../../../common/forms/form.component';
+import {TdStepComponent} from '@covalent/core';
+import {Observable} from 'rxjs/Observable';
+import {AccountTypeOption, accountTypes} from '../../account-types.model';
+import {FimsValidators} from '../../../common/validator/validators';
+
+@Component({
+  selector: 'fims-account-form-component',
+  templateUrl: './form.component.html'
+})
+export class AccountFormComponent extends FormComponent<Account> implements OnInit {
+
+  @ViewChild('detailsStep') step: TdStepComponent;
+
+  accounts: Observable<Account[]>;
+
+  @Input() account: Account;
+
+  @Input() editMode: boolean;
+
+  @Output('onSave') onSave = new EventEmitter<Account>();
+
+  @Output('onCancel') onCancel = new EventEmitter<void>();
+
+  accountTypeOptions: AccountTypeOption[] = accountTypes;
+
+  constructor(private formBuilder: FormBuilder) {
+    super();
+  }
+
+  ngOnInit() {
+    this.openDetailStep();
+    this.form = this.formBuilder.group({
+      'identifier': [this.account.identifier, [Validators.required, Validators.minLength(3),
+        Validators.maxLength(34), FimsValidators.urlSafe]],
+      'name': [this.account.name, [Validators.required, Validators.maxLength(256)]],
+      'type': [{value: this.account.type, disabled: true}, [Validators.required]],
+      'ledger': [this.account.ledger, [Validators.required]],
+      'balance': [{value: this.account.balance, disabled: this.editMode}, [Validators.required]],
+    });
+  }
+
+  openDetailStep(): void {
+    this.step.open();
+  }
+
+  showIdentifierValidationError(): void {
+    this.setError('identifier', 'unique', true);
+    this.openDetailStep();
+  }
+
+  get formData(): Account {
+    // Not needed
+    return;
+  }
+
+  save(): void {
+    const account: Account = {
+      identifier: this.form.get('identifier').value,
+      name: this.form.get('name').value,
+      type: this.form.get('type').value,
+      ledger: this.form.get('ledger').value,
+      balance: this.form.get('balance').value
+    };
+
+    this.onSave.emit(account);
+  }
+
+  cancel(): void {
+    this.onCancel.emit();
+  }
+
+}
diff --git a/src/app/accounting/activity/activity.component.html b/src/app/accounting/activity/activity.component.html
new file mode 100644
index 0000000..ae2f48d
--- /dev/null
+++ b/src/app/accounting/activity/activity.component.html
@@ -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.
+-->
+
+<fims-layout-card-over title="{{'Activities' | translate}}" [navigateBackTo]="['../']">
+  <mat-list>
+    <h3 mat-subheader translate>Latest activity</h3>
+    <ng-template let-item let-last="last" ngFor [ngForOf]="commands">
+      <fims-command-display [command]="item"></fims-command-display>
+      <mat-divider *ngIf="!last" mat-inset></mat-divider>
+    </ng-template>
+  </mat-list>
+</fims-layout-card-over>
diff --git a/src/app/accounting/activity/activity.component.ts b/src/app/accounting/activity/activity.component.ts
new file mode 100644
index 0000000..08d73d6
--- /dev/null
+++ b/src/app/accounting/activity/activity.component.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 {Component, OnInit} from '@angular/core';
+import {ActivatedRoute} from '@angular/router';
+import {AccountCommand} from '../../services/accounting/domain/account-command.model';
+
+@Component({
+  templateUrl: './activity.component.html'
+})
+export class AccountActivityComponent implements OnInit {
+
+  commands: AccountCommand[];
+
+  constructor(private route: ActivatedRoute) {}
+
+  ngOnInit(): void {
+    this.route.data.subscribe(( data: { commands: AccountCommand[]}) => this.commands = data.commands );
+  }
+}
diff --git a/src/app/accounting/activity/commands.resolver.ts b/src/app/accounting/activity/commands.resolver.ts
new file mode 100644
index 0000000..4d10dcb
--- /dev/null
+++ b/src/app/accounting/activity/commands.resolver.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 {Injectable} from '@angular/core';
+import {ActivatedRouteSnapshot, Resolve, RouterStateSnapshot} from '@angular/router';
+import {Observable} from 'rxjs/Observable';
+import {AccountingService} from '../../services/accounting/accounting.service';
+import {AccountCommand} from '../../services/accounting/domain/account-command.model';
+
+@Injectable()
+export class CommandsResolver implements Resolve<AccountCommand[]> {
+
+  constructor(private accountingService: AccountingService) {}
+
+  resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<AccountCommand[]> {
+    return this.accountingService.fetchAccountCommands(route.params['id']);
+  }
+}
diff --git a/src/app/accounting/add-cheque/add-cheque.component.html b/src/app/accounting/add-cheque/add-cheque.component.html
deleted file mode 100644
index 5d5da0e..0000000
--- a/src/app/accounting/add-cheque/add-cheque.component.html
+++ /dev/null
@@ -1,56 +0,0 @@
-<div class="main-div mat-elevation-z2">
-  <h3 class="heading">Add Cheque</h3>
-  <div class="fin-button">
-    <a mat-raised-button  class="button1" color="primary" [routerLink]="['/navbar/add_cheque']">
-      Process cheque</a>
-        <a mat-raised-button  class="button2" color="primary" [routerLink]="['/navbar/add_cheque']">
-          Print cheque</a> 
-      </div>
-  <mat-divider></mat-divider>
-<form class="fineract-form">
-  <mat-form-field>
-    <mat-select placeholder="Template">
-      <mat-option *ngFor="let template of templates" [value]="template.value">
-        {{ template.viewValue }}
-      </mat-option>
-    </mat-select>
-  </mat-form-field>
-  <br/>
-  <mat-form-field>
-    <input matInput placeholder="Branch">
-  </mat-form-field>
-<br/>
-  <mat-form-field>
-    <input matInput placeholder="Bank account">
-  </mat-form-field>
-  <br/>
-  <mat-form-field>
-    <input matInput placeholder="Payable account">
-  </mat-form-field>
-  <br/>
-  <mat-form-field>
-    <input matInput placeholder="Payee">
-  </mat-form-field>
-  <br/>
-  <mat-form-field>
-    <input matInput placeholder="Amount">
-  </mat-form-field>
-  <br/>
-  <mat-form-field>
-    <mat-select placeholder="Currency">
-      <mat-option *ngFor="let currency of currencies" [value]="currency.value">
-        {{ currency.viewValue }}
-      </mat-option>
-    </mat-select>
-  </mat-form-field>
-  <br/>
-  <mat-form-field>
-    <input matInput placeholder="Cheque number">
-  </mat-form-field>
-  <br/>
-  </form>
-  
-  <div>
-  <button mat-raised-button class="button1" color="warn">Cancel</button>
-  </div>
-</div>
\ No newline at end of file
diff --git a/src/app/accounting/add-cheque/add-cheque.component.scss b/src/app/accounting/add-cheque/add-cheque.component.scss
deleted file mode 100644
index 3bf8f37..0000000
--- a/src/app/accounting/add-cheque/add-cheque.component.scss
+++ /dev/null
@@ -1,17 +0,0 @@
-.fineract-form {
-    min-width: 150px;
-    max-width: 500px;
-    width: 100%;
-  }
-
-  //changed
-  .button1{
-      margin-left: 60%;
-      
-      }
-  .button1, .button2{
-      width:150px;
-      display: inline-block;
-  }
-
- 
\ No newline at end of file
diff --git a/src/app/accounting/add-cheque/add-cheque.component.spec.ts b/src/app/accounting/add-cheque/add-cheque.component.spec.ts
deleted file mode 100644
index 61af568..0000000
--- a/src/app/accounting/add-cheque/add-cheque.component.spec.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-import { async, ComponentFixture, TestBed } from '@angular/core/testing';
-
-import { AddChequeComponent } from './add-cheque.component';
-
-describe('AddChequeComponent', () => {
-  let component: AddChequeComponent;
-  let fixture: ComponentFixture<AddChequeComponent>;
-
-  beforeEach(async(() => {
-    TestBed.configureTestingModule({
-      declarations: [ AddChequeComponent ]
-    })
-    .compileComponents();
-  }));
-
-  beforeEach(() => {
-    fixture = TestBed.createComponent(AddChequeComponent);
-    component = fixture.componentInstance;
-    fixture.detectChanges();
-  });
-
-  it('should create', () => {
-    expect(component).toBeTruthy();
-  });
-});
diff --git a/src/app/accounting/add-cheque/add-cheque.component.ts b/src/app/accounting/add-cheque/add-cheque.component.ts
deleted file mode 100644
index 978a8cf..0000000
--- a/src/app/accounting/add-cheque/add-cheque.component.ts
+++ /dev/null
@@ -1,26 +0,0 @@
-import { Component, OnInit } from '@angular/core';
-
-@Component({
-  selector: 'app-add-cheque',
-  templateUrl: './add-cheque.component.html',
-  styleUrls: ['./add-cheque.component.scss']
-})
-export class AddChequeComponent implements OnInit {
-
-  templates = [
-    {value: '', viewValue: ''},
-   
-  ];
-
-  currencies = [
-    {value: '', viewValue: ''},
-   
-  ];
-
-
-  constructor() { }
-
-  ngOnInit() {
-  }
-
-}
diff --git a/src/app/accounting/add-journal-entry/add-journal-entry.component.html b/src/app/accounting/add-journal-entry/add-journal-entry.component.html
deleted file mode 100644
index cb447df..0000000
--- a/src/app/accounting/add-journal-entry/add-journal-entry.component.html
+++ /dev/null
@@ -1,59 +0,0 @@
-<div class="page-space mat-elevation-z2">
-  <p>
-  <a [routerLink]="['/navbar/accounting']">Accounting</a>
-  <span class="current-page">/ Add Journal Entry</span>
-</p>
-<div class="main-div mat-elevation-z2">
-  <h3 class="heading">Add Journal Entry</h3>
-  <mat-divider></mat-divider>
-  <form class="fineract-form">
-  <mat-form-field>
-    <input matInput [matDatepicker]="picker" placeholder="Transaction date">
-    <mat-datepicker-toggle matSuffix [for]="picker"></mat-datepicker-toggle>
-    <mat-datepicker #picker></mat-datepicker>
-  </mat-form-field>
-<br/>
-  <mat-form-field>
-    <input matInput placeholder="Transaction Type">
-  </mat-form-field>
-<br/>
-  <mat-form-field>
-    <input matInput placeholder="Note(optional)">
-  </mat-form-field>
-<br/>
-  <mat-form-field class="input">
-    <textarea matInput placeholder="Leave a comment"></textarea>
-  </mat-form-field>
-  </form>
-<div>
-  <span><b>Affected Accounts</b></span>
-  <br/>
-  <mat-card class="debit-card">
-    <span>Debit</span>
-    <br/>
-    <mat-form-field>
-      <input matInput placeholder="Account">
-    </mat-form-field>
-    <mat-form-field>
-      <input matInput placeholder="Amount">
-    </mat-form-field>
-    <button mat-icon-button color="warn"><mat-icon>clear</mat-icon></button>
-    <br/>
-    <button mat-raised-button class="button" color="primary">Add Debit</button>
-  </mat-card>
-  <mat-card class="credit-card">
-    <span>Credit</span>
-    <br/>
-    <mat-form-field>
-      <input matInput placeholder="Account">
-    </mat-form-field>
-    <mat-form-field>
-      <input matInput placeholder="Amount">
-    </mat-form-field>
-    <button mat-icon-button color="warn"><mat-icon>clear</mat-icon></button>
-    <br/>
-    <button mat-raised-button class="button" color="primary">Add Credit</button>
-  </mat-card>
-</div>
-</div>
-</div>
\ No newline at end of file
diff --git a/src/app/accounting/add-journal-entry/add-journal-entry.component.scss b/src/app/accounting/add-journal-entry/add-journal-entry.component.scss
deleted file mode 100644
index 5f6cdc6..0000000
--- a/src/app/accounting/add-journal-entry/add-journal-entry.component.scss
+++ /dev/null
@@ -1,24 +0,0 @@
-
-.fineract-form {
-    min-width: 150px;
-    max-width: 500px;
-    width: 100%;
-  }
-  
-  .input {
-    width: 100%;
-  }
-  .debit-card,.credit-card{
-    
-    display: inline-block;
-    
-    width: 395px;
-    
-    
-
-  }
-
-  .button{
-    margin-left: 260px;
-  }
-  
\ No newline at end of file
diff --git a/src/app/accounting/add-journal-entry/add-journal-entry.component.spec.ts b/src/app/accounting/add-journal-entry/add-journal-entry.component.spec.ts
deleted file mode 100644
index 081bd33..0000000
--- a/src/app/accounting/add-journal-entry/add-journal-entry.component.spec.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-import { async, ComponentFixture, TestBed } from '@angular/core/testing';
-
-import { AddJournalEntryComponent } from './add-journal-entry.component';
-
-describe('AddJournalEntryComponent', () => {
-  let component: AddJournalEntryComponent;
-  let fixture: ComponentFixture<AddJournalEntryComponent>;
-
-  beforeEach(async(() => {
-    TestBed.configureTestingModule({
-      declarations: [ AddJournalEntryComponent ]
-    })
-    .compileComponents();
-  }));
-
-  beforeEach(() => {
-    fixture = TestBed.createComponent(AddJournalEntryComponent);
-    component = fixture.componentInstance;
-    fixture.detectChanges();
-  });
-
-  it('should create', () => {
-    expect(component).toBeTruthy();
-  });
-});
diff --git a/src/app/accounting/add-journal-entry/add-journal-entry.component.ts b/src/app/accounting/add-journal-entry/add-journal-entry.component.ts
deleted file mode 100644
index 37a73b0..0000000
--- a/src/app/accounting/add-journal-entry/add-journal-entry.component.ts
+++ /dev/null
@@ -1,34 +0,0 @@
-import { Component, OnInit } from '@angular/core';
-import {ReactiveFormsModule} from '@angular/forms'
-import {FormControl, Validators} from '@angular/forms';
-
-@Component({
-  selector: 'app-add-journal-entry',
-  templateUrl: './add-journal-entry.component.html',
-  styleUrls: ['./add-journal-entry.component.scss']
-})
-export class AddJournalEntryComponent implements OnInit {
-
-  startDate = new Date(1990, 0, 1);
-  show=false;
-
-  myControl: FormControl = new FormControl();
-  
-  options = [
-    {value: 'mor', viewValue: 'Morogoro branch'},
-    {value: 'kat', viewValue: 'Katuba'},
-    {value:'loan',viewValue:'Loan Officer'}
-  ];
-
-  currencies = [
-    {value:'afgh',viewValue:'Afghanistan Afghani()'},
-    {value:'alba',viewValue:'Albanian lek()'},
-    {value:'alg',viewValue:'Algerian Dinar()'}
-  ]
-
-  constructor() { }
-
-  ngOnInit() {
-  }
-
-}
diff --git a/src/app/accounting/add-ledger/add-ledger.component.html b/src/app/accounting/add-ledger/add-ledger.component.html
deleted file mode 100644
index 992015e..0000000
--- a/src/app/accounting/add-ledger/add-ledger.component.html
+++ /dev/null
@@ -1,94 +0,0 @@
-<div class="main-div mat-elevation-z2">
-  <h3 class="heading">Add Ledger</h3>
-  <mat-divider></mat-divider>
-  <br/>
-  
-<form class="fineract-form">
-  <mat-form-field>
-    <input matInput placeholder="Ledger number">
-  </mat-form-field>
-</form>
-
-<mat-tab-group>
-  <mat-tab label="Asset">
-    <form class="fineract-form">
-      <mat-form-field>
-        <input matInput placeholder="Name">
-      </mat-form-field>
-      <br/>
-      <mat-form-field>
-        <input matInput placeholder="Description">
-      </mat-form-field>
-    </form>
-    <mat-checkbox>Show account in chart</mat-checkbox>
-    <div>
-      <button mat-raised-button class="button1" color="primary">Submit</button>
-      <button mat-raised-button class="button2" color="warn">Cancel</button>
-      </div>
-  </mat-tab>
-  <mat-tab label="Liability">
-    <form class="fineract-form">
-      <mat-form-field>
-        <input matInput placeholder="Name">
-      </mat-form-field>
-      <br/>
-      <mat-form-field>
-        <input matInput placeholder="Description">
-      </mat-form-field>
-    </form>
-    <mat-checkbox>Show account in chart</mat-checkbox>
-    <div>
-      <button mat-raised-button class="button1" color="primary">Submit</button>
-      <button mat-raised-button class="button2" color="warn">Cancel</button>
-      </div>
-  </mat-tab>
-  <mat-tab label="Equity">
-    <form class="fineract-form">
-      <mat-form-field>
-        <input matInput placeholder="Name">
-      </mat-form-field>
-      <br/>
-      <mat-form-field>
-        <input matInput placeholder="Description">
-      </mat-form-field>
-    </form>
-    <mat-checkbox>Show account in chart</mat-checkbox>
-    <div>
-      <button mat-raised-button class="button1" color="primary">Submit</button>
-      <button mat-raised-button class="button2" color="warn">Cancel</button>
-      </div>
-  </mat-tab>
-  <mat-tab label="Expense">
-    <form class="fineract-form">
-      <mat-form-field>
-        <input matInput placeholder="Name">
-      </mat-form-field>
-      <br/>
-      <mat-form-field>
-        <input matInput placeholder="Description">
-      </mat-form-field>
-    </form>
-    <mat-checkbox>Show account in chart</mat-checkbox>
-    <div>
-      <button mat-raised-button class="button1" color="primary">Submit</button>
-      <button mat-raised-button class="button2" color="warn">Cancel</button>
-      </div>
-  </mat-tab>
-  <mat-tab label="Revenue">
-    <form class="fineract-form">
-      <mat-form-field>
-        <input matInput placeholder="Name">
-      </mat-form-field>
-      <br/>
-      <mat-form-field>
-        <input matInput placeholder="Description">
-      </mat-form-field>
-    </form>
-    <mat-checkbox>Show account in chart</mat-checkbox>
-    <div>
-      <button mat-raised-button class="button1" color="primary">Submit</button>
-      <button mat-raised-button class="button2" color="warn">Cancel</button>
-      </div>
-  </mat-tab>
-</mat-tab-group>
-
diff --git a/src/app/accounting/add-ledger/add-ledger.component.scss b/src/app/accounting/add-ledger/add-ledger.component.scss
deleted file mode 100644
index 7109ed8..0000000
--- a/src/app/accounting/add-ledger/add-ledger.component.scss
+++ /dev/null
@@ -1,13 +0,0 @@
-.fineract-form {
-    min-width: 150px;
-    max-width: 500px;
-    width: 100%;
-  }
-  .button1{
-      margin-left: 50%;
-      
-      }
-  .button1, .button2{
-      width:20px;
-      display: inline-block;
-  }
\ No newline at end of file
diff --git a/src/app/accounting/add-ledger/add-ledger.component.spec.ts b/src/app/accounting/add-ledger/add-ledger.component.spec.ts
deleted file mode 100644
index 0e5555b..0000000
--- a/src/app/accounting/add-ledger/add-ledger.component.spec.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-import { async, ComponentFixture, TestBed } from '@angular/core/testing';
-
-import { AddLedgerComponent } from './add-ledger.component';
-
-describe('AddLedgerComponent', () => {
-  let component: AddLedgerComponent;
-  let fixture: ComponentFixture<AddLedgerComponent>;
-
-  beforeEach(async(() => {
-    TestBed.configureTestingModule({
-      declarations: [ AddLedgerComponent ]
-    })
-    .compileComponents();
-  }));
-
-  beforeEach(() => {
-    fixture = TestBed.createComponent(AddLedgerComponent);
-    component = fixture.componentInstance;
-    fixture.detectChanges();
-  });
-
-  it('should create', () => {
-    expect(component).toBeTruthy();
-  });
-});
diff --git a/src/app/accounting/add-ledger/add-ledger.component.ts b/src/app/accounting/add-ledger/add-ledger.component.ts
deleted file mode 100644
index cc3126a..0000000
--- a/src/app/accounting/add-ledger/add-ledger.component.ts
+++ /dev/null
@@ -1,15 +0,0 @@
-import { Component, OnInit } from '@angular/core';
-
-@Component({
-  selector: 'app-add-ledger',
-  templateUrl: './add-ledger.component.html',
-  styleUrls: ['./add-ledger.component.scss']
-})
-export class AddLedgerComponent implements OnInit {
-
-  constructor() { }
-
-  ngOnInit() {
-  }
-
-}
diff --git a/src/app/accounting/add-payroll/add-payroll.component.html b/src/app/accounting/add-payroll/add-payroll.component.html
deleted file mode 100644
index f5c0137..0000000
--- a/src/app/accounting/add-payroll/add-payroll.component.html
+++ /dev/null
@@ -1,29 +0,0 @@
-<div class="main-div mat-elevation-z2">
-  <h3 class="heading">Add Payroll</h3>
-  <mat-divider></mat-divider>
-  
-<form class="fineract-form">
-  <mat-form-field>
-    <input matInput placeholder="Account">
-  </mat-form-field>
-  <br/>
-  <mat-form-field>
-    <input matInput placeholder="Member">
-  </mat-form-field>
-  <mat-form-field>
-    <input matInput placeholder="Employer">
-  </mat-form-field>
-  <mat-form-field>
-    <input matInput placeholder="Salary">
-  </mat-form-field>
-  <button mat-icon-button color="warn"><mat-icon>clear</mat-icon></button>
-</form>
-<div style="margin-left: 80%;">
-  <button mat-raised-button color="primary">Add Payment</button>
-</div>
-<div>
-  <button mat-raised-button class="button1" color="primary">Submit</button>
-  <button mat-raised-button class="button2" color="warn">Cancel</button>
-  </div>
-  </div>
-
diff --git a/src/app/accounting/add-payroll/add-payroll.component.scss b/src/app/accounting/add-payroll/add-payroll.component.scss
deleted file mode 100644
index 33ff61b..0000000
--- a/src/app/accounting/add-payroll/add-payroll.component.scss
+++ /dev/null
@@ -1,13 +0,0 @@
-.fineract-form {
-    min-width: 150px;
-    max-width: 1000px;
-    width: 100%;
-  }
-  .button1{
-      margin-left: 50%;
-      
-      }
-  .button1, .button2{
-      width:20px;
-      display: inline-block;
-  }
\ No newline at end of file
diff --git a/src/app/accounting/add-payroll/add-payroll.component.spec.ts b/src/app/accounting/add-payroll/add-payroll.component.spec.ts
deleted file mode 100644
index 1a31bd1..0000000
--- a/src/app/accounting/add-payroll/add-payroll.component.spec.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-import { async, ComponentFixture, TestBed } from '@angular/core/testing';
-
-import { AddPayrollComponent } from './add-payroll.component';
-
-describe('AddPayrollComponent', () => {
-  let component: AddPayrollComponent;
-  let fixture: ComponentFixture<AddPayrollComponent>;
-
-  beforeEach(async(() => {
-    TestBed.configureTestingModule({
-      declarations: [ AddPayrollComponent ]
-    })
-    .compileComponents();
-  }));
-
-  beforeEach(() => {
-    fixture = TestBed.createComponent(AddPayrollComponent);
-    component = fixture.componentInstance;
-    fixture.detectChanges();
-  });
-
-  it('should create', () => {
-    expect(component).toBeTruthy();
-  });
-});
diff --git a/src/app/accounting/add-payroll/add-payroll.component.ts b/src/app/accounting/add-payroll/add-payroll.component.ts
deleted file mode 100644
index bc72cb9..0000000
--- a/src/app/accounting/add-payroll/add-payroll.component.ts
+++ /dev/null
@@ -1,15 +0,0 @@
-import { Component, OnInit } from '@angular/core';
-
-@Component({
-  selector: 'app-add-payroll',
-  templateUrl: './add-payroll.component.html',
-  styleUrls: ['./add-payroll.component.scss']
-})
-export class AddPayrollComponent implements OnInit {
-
-  constructor() { }
-
-  ngOnInit() {
-  }
-
-}
diff --git a/src/app/accounting/add-transaction-type/add-transaction-type.component.html b/src/app/accounting/add-transaction-type/add-transaction-type.component.html
deleted file mode 100644
index ac3ce3c..0000000
--- a/src/app/accounting/add-transaction-type/add-transaction-type.component.html
+++ /dev/null
@@ -1,23 +0,0 @@
-<div class="main-div mat-elevation-z2">
-  <h3 class="heading">Add transaction Type</h3>
-  <mat-divider></mat-divider>
-  <br/>
-  <span>Transaction Type</span>
-<form class="fineract-form">
-  <mat-form-field>
-    <input matInput placeholder="Code">
-  </mat-form-field>
-<br/>
-  <mat-form-field>
-    <input matInput placeholder="Name">
-  </mat-form-field>
-<br/>
-  <mat-form-field>
-    <textarea matInput placeholder="Description(optional)"></textarea>
-  </mat-form-field>
-  </form>
-  <div>
-  <button mat-raised-button class="button1" color="primary">Submit</button>
-  <button mat-raised-button class="button2" color="warn">Cancel</button>
-  </div>
-</div>
\ No newline at end of file
diff --git a/src/app/accounting/add-transaction-type/add-transaction-type.component.scss b/src/app/accounting/add-transaction-type/add-transaction-type.component.scss
deleted file mode 100644
index 7109ed8..0000000
--- a/src/app/accounting/add-transaction-type/add-transaction-type.component.scss
+++ /dev/null
@@ -1,13 +0,0 @@
-.fineract-form {
-    min-width: 150px;
-    max-width: 500px;
-    width: 100%;
-  }
-  .button1{
-      margin-left: 50%;
-      
-      }
-  .button1, .button2{
-      width:20px;
-      display: inline-block;
-  }
\ No newline at end of file
diff --git a/src/app/accounting/add-transaction-type/add-transaction-type.component.spec.ts b/src/app/accounting/add-transaction-type/add-transaction-type.component.spec.ts
deleted file mode 100644
index 001163c..0000000
--- a/src/app/accounting/add-transaction-type/add-transaction-type.component.spec.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-import { async, ComponentFixture, TestBed } from '@angular/core/testing';
-
-import { AddTransactionTypeComponent } from './add-transaction-type.component';
-
-describe('AddTransactionTypeComponent', () => {
-  let component: AddTransactionTypeComponent;
-  let fixture: ComponentFixture<AddTransactionTypeComponent>;
-
-  beforeEach(async(() => {
-    TestBed.configureTestingModule({
-      declarations: [ AddTransactionTypeComponent ]
-    })
-    .compileComponents();
-  }));
-
-  beforeEach(() => {
-    fixture = TestBed.createComponent(AddTransactionTypeComponent);
-    component = fixture.componentInstance;
-    fixture.detectChanges();
-  });
-
-  it('should create', () => {
-    expect(component).toBeTruthy();
-  });
-});
diff --git a/src/app/accounting/add-transaction-type/add-transaction-type.component.ts b/src/app/accounting/add-transaction-type/add-transaction-type.component.ts
deleted file mode 100644
index ce80310..0000000
--- a/src/app/accounting/add-transaction-type/add-transaction-type.component.ts
+++ /dev/null
@@ -1,15 +0,0 @@
-import { Component, OnInit } from '@angular/core';
-
-@Component({
-  selector: 'app-add-transaction-type',
-  templateUrl: './add-transaction-type.component.html',
-  styleUrls: ['./add-transaction-type.component.scss']
-})
-export class AddTransactionTypeComponent implements OnInit {
-
-  constructor() { }
-
-  ngOnInit() {
-  }
-
-}
diff --git a/src/app/accounting/chart-of-accounts/chart-of-accounts.component.html b/src/app/accounting/chart-of-accounts/chart-of-accounts.component.html
deleted file mode 100644
index e1dc266..0000000
--- a/src/app/accounting/chart-of-accounts/chart-of-accounts.component.html
+++ /dev/null
@@ -1,47 +0,0 @@
-<div class="main-div mat-elevation-z2">
-  <h3 class="heading">Chart of Accounts</h3>
-  <mat-divider></mat-divider>
-  <div class="fineract-button">
-  <a mat-raised-button  color="primary" >
-    Download</a>
-    </div>
-  <mat-form-field >
-    <input matInput class="filter" (keyup)="applyFilter($event.target.value)" placeholder="Filter by name/code">
-  </mat-form-field>
-  
-<br>
-<mat-divider></mat-divider>
-
-<mat-table #table [dataSource]="dataSource">
-
-  <!-- Position Column -->
-  <ng-container matColumnDef="code">
-    <mat-header-cell *matHeaderCellDef>Code </mat-header-cell>
-    <mat-cell *matCellDef="let element">
-      <a routerLink="{{element.code}}">{{element.code}}</a>
-    </mat-cell>
-  </ng-container>
-
-  <!-- Name Column -->
-  <ng-container matColumnDef="name">
-    <mat-header-cell *matHeaderCellDef>Name</mat-header-cell>
-    <mat-cell *matCellDef="let element"> {{element.name}} </mat-cell>
-  </ng-container>
-
-  <ng-container matColumnDef="description">
-      <mat-header-cell *matHeaderCellDef>Description</mat-header-cell>
-      <mat-cell *matCellDef="let element"> {{element.description}} </mat-cell>
-    </ng-container>
-
-    <ng-container matColumnDef="type">
-        <mat-header-cell *matHeaderCellDef>Type</mat-header-cell>
-        <mat-cell *matCellDef="let element"> {{element.type} </mat-cell>
-      </ng-container>
-
-  <mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
-  <mat-row *matRowDef="let row; columns: displayedColumns;"></mat-row>
-</mat-table>
-  
-  
-</div>
-  
\ No newline at end of file
diff --git a/src/app/accounting/chart-of-accounts/chart-of-accounts.component.scss b/src/app/accounting/chart-of-accounts/chart-of-accounts.component.scss
deleted file mode 100644
index 345f7a3..0000000
--- a/src/app/accounting/chart-of-accounts/chart-of-accounts.component.scss
+++ /dev/null
@@ -1,11 +0,0 @@
-
-  table {
-      width: 100%;
-    }
-    
-    .mat-form-field {
-      font-size: 14px;
-      width: 100%;
-    }
-    
-    
\ No newline at end of file
diff --git a/src/app/accounting/chart-of-accounts/chart-of-accounts.component.spec.ts b/src/app/accounting/chart-of-accounts/chart-of-accounts.component.spec.ts
deleted file mode 100644
index 226cb27..0000000
--- a/src/app/accounting/chart-of-accounts/chart-of-accounts.component.spec.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-import { async, ComponentFixture, TestBed } from '@angular/core/testing';
-
-import { ChartOfAccountsComponent } from './chart-of-accounts.component';
-
-describe('ChartOfAccountsComponent', () => {
-  let component: ChartOfAccountsComponent;
-  let fixture: ComponentFixture<ChartOfAccountsComponent>;
-
-  beforeEach(async(() => {
-    TestBed.configureTestingModule({
-      declarations: [ ChartOfAccountsComponent ]
-    })
-    .compileComponents();
-  }));
-
-  beforeEach(() => {
-    fixture = TestBed.createComponent(ChartOfAccountsComponent);
-    component = fixture.componentInstance;
-    fixture.detectChanges();
-  });
-
-  it('should create', () => {
-    expect(component).toBeTruthy();
-  });
-});
diff --git a/src/app/accounting/chart-of-accounts/chart-of-accounts.component.ts b/src/app/accounting/chart-of-accounts/chart-of-accounts.component.ts
deleted file mode 100644
index bb3a3d2..0000000
--- a/src/app/accounting/chart-of-accounts/chart-of-accounts.component.ts
+++ /dev/null
@@ -1,37 +0,0 @@
-import { Component, OnInit } from '@angular/core';
-import {MatTableDataSource} from '@angular/material';
-
-
-@Component({
-  selector: 'app-chart-of-accounts',
-  templateUrl: './chart-of-accounts.component.html',
-  styleUrls: ['./chart-of-accounts.component.scss']
-})
-export class ChartOfAccountsComponent implements OnInit {
-  displayedColumns = ['code','name','description','type'];
-  dataSource = new MatTableDataSource(ELEMENT_DATA);
-
-  applyFilter(filterValue: string) {
-    filterValue = filterValue.trim(); // Remove whitespace
-    filterValue = filterValue.toLowerCase(); // MatTableDataSource defaults to lowercase matches
-    this.dataSource.filter = filterValue;
-  }
-
-  constructor() { }
-
-  ngOnInit() {
-  }
-
-}
-export interface Element {
-  code: string;
-  name: string;
-  description: string;
-  type: string;
-  
-}
-
-const ELEMENT_DATA: Element[] = [
-  
-];
- 
\ No newline at end of file
diff --git a/src/app/accounting/chartOfAccounts/chart-of-account-table.component.html b/src/app/accounting/chartOfAccounts/chart-of-account-table.component.html
new file mode 100644
index 0000000..db58506
--- /dev/null
+++ b/src/app/accounting/chartOfAccounts/chart-of-account-table.component.html
@@ -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.
+-->
+
+<table td-data-table>
+  <thead>
+    <tr td-data-table-column-row>
+      <th td-data-table-column translate>Code</th>
+      <th td-data-table-column translate>Name</th>
+      <th td-data-table-column translate>Description</th>
+      <th td-data-table-column translate>Type</th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr td-data-table-row *ngFor="let entry of chartOfAccountEntries">
+      <td td-data-table-cell [ngStyle]="{'padding-left.px': 24+(15*entry.level)}">{{entry.code}}</td>
+      <td td-data-table-cell>{{entry.name}}</td>
+      <td td-data-table-cell>{{entry.description}}</td>
+      <td td-data-table-cell>{{entry.type}}</td>
+    </tr>
+  </tbody>
+</table>
+
+<div class="mat-padding" *ngIf="!(chartOfAccountEntries.length > 0) && !loading" layout="row" layout-align="center center">
+  <h3 translate>No data for chart of accounts available.</h3>
+</div>
+
+<div class="mat-padding" *ngIf="loading" layout="row" layout-align="center center">
+  <h3 translate>Fetching data for chart of accounts...</h3>
+</div>
diff --git a/src/app/accounting/chartOfAccounts/chart-of-account-table.component.ts b/src/app/accounting/chartOfAccounts/chart-of-account-table.component.ts
new file mode 100644
index 0000000..3ae57de
--- /dev/null
+++ b/src/app/accounting/chartOfAccounts/chart-of-account-table.component.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 {Component, Input} from '@angular/core';
+import {ChartOfAccountEntry} from '../../services/accounting/domain/chart-of-account-entry.model';
+
+@Component({
+  selector: 'fims-chart-of-account-table',
+  templateUrl: './chart-of-account-table.component.html'
+})
+export class ChartOfAccountTableComponent {
+
+  @Input() chartOfAccountEntries: ChartOfAccountEntry[] = [];
+
+  @Input() loading: boolean;
+
+}
diff --git a/src/app/accounting/chartOfAccounts/chart-of-accounts.component.html b/src/app/accounting/chartOfAccounts/chart-of-accounts.component.html
new file mode 100644
index 0000000..354df54
--- /dev/null
+++ b/src/app/accounting/chartOfAccounts/chart-of-accounts.component.html
@@ -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.
+-->
+
+<fims-layout-card-over title="{{'Chart of accounts' | translate}}" [navigateBackTo]="['../']">
+  <fims-chart-of-account-table [chartOfAccountEntries]="chartOfAccountEntries$ | async" [loading]="loading$ | async"></fims-chart-of-account-table>
+</fims-layout-card-over>
diff --git a/src/app/accounting/chartOfAccounts/chart-of-accounts.component.ts b/src/app/accounting/chartOfAccounts/chart-of-accounts.component.ts
new file mode 100644
index 0000000..aa16e5a
--- /dev/null
+++ b/src/app/accounting/chartOfAccounts/chart-of-accounts.component.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 {Component, OnInit} from '@angular/core';
+import {ChartOfAccountEntry} from '../../services/accounting/domain/chart-of-account-entry.model';
+import {Observable} from 'rxjs/Observable';
+import {AccountingStore} from '../store/index';
+import * as fromAccounting from '../store';
+import {LOAD_CHART_OF_ACCOUNTS} from '../store/ledger/ledger.actions';
+
+@Component({
+  templateUrl: './chart-of-accounts.component.html'
+})
+export class ChartOfAccountComponent implements OnInit {
+
+  chartOfAccountEntries$: Observable<ChartOfAccountEntry[]>;
+
+  loading$: Observable<boolean>;
+
+  constructor(private store: AccountingStore) {}
+
+  ngOnInit(): void {
+    this.chartOfAccountEntries$ = this.store.select(fromAccounting.getChartOfAccountEntries);
+
+    this.loading$ = this.store.select(fromAccounting.getChartOfAccountLoading);
+
+    this.store.dispatch({ type: LOAD_CHART_OF_ACCOUNTS });
+  }
+
+}
diff --git a/src/app/accounting/cheque-clearing/cheque-clearing.component.html b/src/app/accounting/cheque-clearing/cheque-clearing.component.html
deleted file mode 100644
index f623c55..0000000
--- a/src/app/accounting/cheque-clearing/cheque-clearing.component.html
+++ /dev/null
@@ -1,70 +0,0 @@
-<div class="main-div mat-elevation-z2">
-  <h3 class="heading">Cheque Clearing</h3>
-  <mat-divider></mat-divider>
-  <mat-form-field>
-    <input matInput (keyup)="applyFilter($event.target.value)" placeholder="Filter by identifier">
-  </mat-form-field>
-  
-<br>
-<mat-divider></mat-divider>
-
-<mat-table #table [dataSource]="dataSource">
-
-  <!-- Position Column -->
-  <ng-container matColumnDef="identifier">
-    <mat-header-cell *matHeaderCellDef>Identifier </mat-header-cell>
-    <mat-cell *matCellDef="let element">
-      <a routerLink="{{element.identifier}}">{{element.identifier}}</a>
-    </mat-cell>
-  </ng-container>
-
-  <!-- Name Column -->
-  <ng-container matColumnDef="drawee">
-    <mat-header-cell *matHeaderCellDef>Drawee</mat-header-cell>
-    <mat-cell *matCellDef="let element"> {{element.drawee}} </mat-cell>
-  </ng-container>
-
-  <ng-container matColumnDef="drawer">
-      <mat-header-cell *matHeaderCellDef>Drawer</mat-header-cell>
-      <mat-cell *matCellDef="let element"> {{element.drawer}} </mat-cell>
-    </ng-container>
-
-    <ng-container matColumnDef="payee">
-        <mat-header-cell *matHeaderCellDef>Payee</mat-header-cell>
-        <mat-cell *matCellDef="let element"> {{element.payee} </mat-cell>
-      </ng-container>
-
-      <ng-container matColumnDef="amount">
-        <mat-header-cell *matHeaderCellDef>Amount</mat-header-cell>
-        <mat-cell *matCellDef="let element"> {{element.amount} </mat-cell>
-      </ng-container>
-
-      <ng-container matColumnDef="dateissued">
-        <mat-header-cell *matHeaderCellDef>Date Issued</mat-header-cell>
-        <mat-cell *matCellDef="let element"> {{element.dateisued} </mat-cell>
-      </ng-container>
-
-      <ng-container matColumnDef="state">
-        <mat-header-cell *matHeaderCellDef>state</mat-header-cell>
-        <mat-cell *matCellDef="let element"> {{element.state} </mat-cell>
-      </ng-container>
-
-      <ng-container matColumnDef="approve">
-        <mat-header-cell *matHeaderCellDef>Approve</mat-header-cell>
-        <mat-cell *matCellDef="let element"> {{element.approve} </mat-cell>
-      </ng-container>
-
-      <ng-container matColumnDef="cancel">
-        <mat-header-cell *matHeaderCellDef>Cancel</mat-header-cell>
-        <mat-cell *matCellDef="let element"> {{element.cancel} </mat-cell>
-      </ng-container>
-
-
-
-  <mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
-  <mat-row *matRowDef="let row; columns: displayedColumns;"></mat-row>
-</mat-table>
-  
-  
-</div>
-  
\ No newline at end of file
diff --git a/src/app/accounting/cheque-clearing/cheque-clearing.component.scss b/src/app/accounting/cheque-clearing/cheque-clearing.component.scss
deleted file mode 100644
index 0961a8e..0000000
--- a/src/app/accounting/cheque-clearing/cheque-clearing.component.scss
+++ /dev/null
@@ -1,18 +0,0 @@
-.my-div{
-    margin-left: 2%;
-    margin-right:2%;
-    border-radius: 5px 5px 5px 5px;
-    background-color: #e6e6ff;
-    min-height: 100%;
-   
-  }
-  table {
-      width: 100%;
-    }
-    
-    .mat-form-field {
-      font-size: 14px;
-      width: 100%;
-    }
-    
-    
\ No newline at end of file
diff --git a/src/app/accounting/cheque-clearing/cheque-clearing.component.spec.ts b/src/app/accounting/cheque-clearing/cheque-clearing.component.spec.ts
deleted file mode 100644
index ecb3d3f..0000000
--- a/src/app/accounting/cheque-clearing/cheque-clearing.component.spec.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-import { async, ComponentFixture, TestBed } from '@angular/core/testing';
-
-import { ChequeClearingComponent } from './cheque-clearing.component';
-
-describe('ChequeClearingComponent', () => {
-  let component: ChequeClearingComponent;
-  let fixture: ComponentFixture<ChequeClearingComponent>;
-
-  beforeEach(async(() => {
-    TestBed.configureTestingModule({
-      declarations: [ ChequeClearingComponent ]
-    })
-    .compileComponents();
-  }));
-
-  beforeEach(() => {
-    fixture = TestBed.createComponent(ChequeClearingComponent);
-    component = fixture.componentInstance;
-    fixture.detectChanges();
-  });
-
-  it('should create', () => {
-    expect(component).toBeTruthy();
-  });
-});
diff --git a/src/app/accounting/cheque-clearing/cheque-clearing.component.ts b/src/app/accounting/cheque-clearing/cheque-clearing.component.ts
deleted file mode 100644
index 6eec557..0000000
--- a/src/app/accounting/cheque-clearing/cheque-clearing.component.ts
+++ /dev/null
@@ -1,41 +0,0 @@
-import { Component, OnInit } from '@angular/core';
-import {MatTableDataSource} from '@angular/material';
-
-@Component({
-  selector: 'app-cheque-clearing',
-  templateUrl: './cheque-clearing.component.html',
-  styleUrls: ['./cheque-clearing.component.scss']
-})
-export class ChequeClearingComponent implements OnInit {
-
-  displayedColumns = ['identifier','drawer','drawee','payee','amount','dateissued','state','approve','cancel'];
-  dataSource = new MatTableDataSource(ELEMENT_DATA);
-
-  applyFilter(filterValue: string) {
-    filterValue = filterValue.trim(); // Remove whitespace
-    filterValue = filterValue.toLowerCase(); // MatTableDataSource defaults to lowercase matches
-    this.dataSource.filter = filterValue;
-  }
-
-  constructor() { }
-
-  ngOnInit() {
-  }
-
-}
-export interface Element {
-  identifier: number;
-  drawee:string;
-  drawer: string;
-  payee:string;
-  amount: number;
-  dateissued:Date;
-  state:string;
-  approve:boolean;
-  cancel:boolean;
-  
-}
-
-const ELEMENT_DATA: Element[] = [
-  
-];
diff --git a/src/app/accounting/cheques/cheques.list.component.html b/src/app/accounting/cheques/cheques.list.component.html
new file mode 100644
index 0000000..25b3d4e
--- /dev/null
+++ b/src/app/accounting/cheques/cheques.list.component.html
@@ -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.
+-->
+
+<fims-layout-card-over title="{{'Cheque clearing' | translate}}" [navigateBackTo]="['../']">
+  <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"
+              [name]="column.name">
+            {{column.label}}
+          </th>
+          <th td-data-table-column *hasPermission="{ id: 'cheque_management', accessLevel: 'CHANGE' }" translate>APPROVE</th>
+          <th td-data-table-column *hasPermission="{ id: 'cheque_management', accessLevel: 'CHANGE' }" translate>CANCEL</th>
+        </tr>
+      </thead>
+      <tbody>
+        <tr td-data-table-row *ngFor="let row of cheques$ | async">
+          <td td-data-table-cell [numeric]="column.numeric" *ngFor="let column of columns">
+            {{column.format ? column.format(row[column.name]) : row[column.name]}}
+          </td>
+          <td td-data-table-cell *hasPermission="{ id: 'cheque_management', accessLevel: 'CHANGE' }">
+            <button mat-button mat-raised-button color="primary" [disabled]="row.state !== 'PENDING'" (click)="approveCheque(row)">
+              {{ 'APPROVE' | translate}}
+            </button>
+          </td>
+          <td td-data-table-cell *hasPermission="{ id: 'cheque_management', accessLevel: 'CHANGE' }">
+            <button mat-button mat-raised-button color="warn" [disabled]="row.state !== 'PENDING'" (click)="cancelCheque(row)">
+              {{ 'CANCEL' | translate}}
+            </button>
+          </td>
+        </tr>
+      </tbody>
+    </table>
+  </div>
+</fims-layout-card-over>
diff --git a/src/app/accounting/cheques/cheques.list.component.ts b/src/app/accounting/cheques/cheques.list.component.ts
new file mode 100644
index 0000000..61db892
--- /dev/null
+++ b/src/app/accounting/cheques/cheques.list.component.ts
@@ -0,0 +1,99 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, OnInit} from '@angular/core';
+import {Observable} from 'rxjs/Observable';
+import {ITdDataTableColumn, TdDialogService} from '@covalent/core';
+import {AccountingStore} from '../store/index';
+import * as fromAccounting from '../store';
+import {ChequeCRUDActions, ProcessAction} from '../store/cheques/cheque.actions';
+import {TranslateService} from '@ngx-translate/core';
+import {FimsCheque} from '../../services/cheque/domain/fims-cheque.model';
+import {DatePipe} from '@angular/common';
+
+@Component({
+  templateUrl: './cheques.list.component.html',
+  providers: [DatePipe]
+})
+export class ChequesListComponent implements OnInit {
+
+  cheques$: Observable<FimsCheque[]>;
+
+  columns: ITdDataTableColumn[] = [
+    {name: 'identifier', label: 'Identifier'},
+    {name: 'drawee', label: 'Drawee'},
+    {name: 'drawer', label: 'Drawer'},
+    {name: 'payee', label: 'Payee'},
+    {name: 'amount', label: 'Amount'},
+    {name: 'dateIssued', label: 'Date issued', format: value => this.datePipe.transform(value, 'shortDate')},
+    {name: 'state', label: 'State'}
+  ];
+
+  constructor(private store: AccountingStore, private dialogService: TdDialogService,
+              private translate: TranslateService, private datePipe: DatePipe) {
+    this.cheques$ = store.select(fromAccounting.getAllChequeEntities);
+  }
+
+  ngOnInit(): void {
+    this.store.dispatch(ChequeCRUDActions.loadAllAction({
+      state: 'PENDING'
+    }));
+  }
+
+  confirmAction(action: string): Observable<boolean> {
+    const message = `Do you want to ${action} this cheque?`;
+    const title = 'Confirm action';
+    const button = `${action} cheque`;
+
+    return this.translate.get([title, message, button])
+      .flatMap(result =>
+        this.dialogService.openConfirm({
+          message: result[message],
+          title: result[title],
+          acceptButton: result[button]
+        }).afterClosed()
+      );
+  }
+
+  approveCheque(cheque: FimsCheque): void {
+    this.confirmAction('APPROVE')
+      .filter(accept => accept)
+      .subscribe(() => {
+        this.store.dispatch(new ProcessAction({
+          chequeIdentifier: cheque.identifier,
+          command: {
+            action: 'APPROVE'
+          }
+        }));
+      });
+  }
+
+  cancelCheque(cheque: FimsCheque): void {
+    this.confirmAction('CANCEL')
+      .filter(accept => accept)
+      .subscribe(() => {
+        this.store.dispatch(new ProcessAction({
+          chequeIdentifier: cheque.identifier,
+          command: {
+            action: 'CANCEL'
+          }
+        }));
+      });
+  }
+
+}
diff --git a/src/app/accounting/financialCondition/financial-condition.component.html b/src/app/accounting/financialCondition/financial-condition.component.html
new file mode 100644
index 0000000..8ffac70
--- /dev/null
+++ b/src/app/accounting/financialCondition/financial-condition.component.html
@@ -0,0 +1,136 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<fims-layout-card-over title="{{'Financial condition' | translate}}" [navigateBackTo]="['../']">
+  <div layout="row">
+    <table td-data-table *ngIf="financialCondition$ | async as financialCondition">
+      <ng-container *ngIf="assets$ | async as assets">
+        <tr td-data-table-row>
+          <td td-data-table-cell>
+            <b translate>Assets</b>
+          </td>
+          <td td-data-table-cell></td>
+          <td td-data-table-cell></td>
+        </tr>
+        <tr td-data-table-row *ngFor="let entry of assets.financialConditionEntries">
+          <td td-data-table-cell></td>
+          <td td-data-table-cell>
+            {{entry.description}}
+          </td>
+          <td td-data-table-cell>
+            {{entry.value | displayFimsFinancialNumber}}
+          </td>
+        </tr>
+        <tr td-data-table-row>
+          <td td-data-table-cell>
+            <i translate>Subtotal</i>
+          </td>
+          <td td-data-table-cell></td>
+          <td td-data-table-cell>
+            <i class="double-underline">
+              {{assets.subtotal | displayFimsFinancialNumber}}
+            </i>
+          </td>
+        </tr>
+        <tr td-data-table-row></tr>
+        <tr td-data-table-row>
+          <td td-data-table-cell>
+            <b translate>Total Assets</b>
+          </td>
+          <td td-data-table-cell></td>
+          <td td-data-table-cell>
+            <b class="double-underline">
+              {{financialCondition.totalAssets | displayFimsFinancialNumber}}
+            </b>
+          </td>
+        </tr>
+        <tr td-data-table-row></tr>
+      </ng-container>
+      <ng-container *ngIf="equities$ | async as equities">
+        <tr td-data-table-row>
+          <td td-data-table-cell>
+            <b translate>Equities</b>
+          </td>
+          <td td-data-table-cell></td>
+          <td td-data-table-cell></td>
+        </tr>
+        <tr td-data-table-row *ngFor="let entry of equities.financialConditionEntries">
+          <td td-data-table-cell></td>
+          <td td-data-table-cell>
+            {{entry.description}}
+          </td>
+          <td td-data-table-cell>
+            {{entry.value | displayFimsFinancialNumber}}
+          </td>
+        </tr>
+        <tr td-data-table-row>
+          <td td-data-table-cell>
+            <i translate>Subtotal</i>
+          </td>
+          <td td-data-table-cell></td>
+          <td td-data-table-cell>
+            <i class="double-underline">
+              {{equities.subtotal | displayFimsFinancialNumber}}
+            </i>
+          </td>
+        </tr>
+        <tr td-data-table-row></tr>
+      </ng-container>
+      <ng-container *ngIf="liabilities$ | async as liabilities">
+        <tr td-data-table-row>
+          <td td-data-table-cell>
+            <b translate>Liabilities</b>
+          </td>
+          <td td-data-table-cell></td>
+          <td td-data-table-cell></td>
+        </tr>
+        <tr td-data-table-row *ngFor="let entry of liabilities.financialConditionEntries">
+          <td td-data-table-cell></td>
+          <td td-data-table-cell>
+            {{entry.description}}
+          </td>
+          <td td-data-table-cell>
+            {{entry.value | displayFimsFinancialNumber}}
+          </td>
+        </tr>
+        <tr td-data-table-row>
+          <td td-data-table-cell>
+            <i translate>Subtotal</i>
+          </td>
+          <td td-data-table-cell></td>
+          <td td-data-table-cell>
+            <i class="double-underline">
+              {{liabilities.subtotal | displayFimsFinancialNumber}}
+            </i>
+          </td>
+        </tr>
+        <tr td-data-table-row></tr>
+        <tr td-data-table-row>
+          <td td-data-table-cell>
+            <b translate>Total Equities and Liabilities</b>
+          </td>
+          <td td-data-table-cell></td>
+          <td td-data-table-cell>
+            <b class="double-underline">
+              {{financialCondition.totalEquitiesAndLiabilities | displayFimsFinancialNumber}}
+            </b>
+          </td>
+        </tr>
+      </ng-container>
+    </table>
+  </div>
+</fims-layout-card-over>
diff --git a/src/app/accounting/financialCondition/financial-condition.component.scss b/src/app/accounting/financialCondition/financial-condition.component.scss
new file mode 100644
index 0000000..9580096
--- /dev/null
+++ b/src/app/accounting/financialCondition/financial-condition.component.scss
@@ -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.
+ */
+
+.double-underline {
+  border-bottom: 3px double;
+}
diff --git a/src/app/accounting/financialCondition/financial-condition.component.ts b/src/app/accounting/financialCondition/financial-condition.component.ts
new file mode 100644
index 0000000..040a286
--- /dev/null
+++ b/src/app/accounting/financialCondition/financial-condition.component.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 {Component} from '@angular/core';
+import {AccountingService} from '../../services/accounting/accounting.service';
+import {FinancialCondition} from '../../services/accounting/domain/financial-condition.model';
+import {Observable} from 'rxjs/Observable';
+import {FinancialConditionSection} from '../../services/accounting/domain/financial-condition-section.model';
+
+@Component({
+  templateUrl: './financial-condition.component.html',
+  styleUrls: ['./financial-condition.component.scss']
+})
+export class FinancialConditionComponent {
+
+  financialCondition$: Observable<FinancialCondition>;
+
+  assets$: Observable<FinancialConditionSection>;
+
+  equities$: Observable<FinancialConditionSection>;
+
+  liabilities$: Observable<FinancialConditionSection>;
+
+  constructor(private accountingService: AccountingService) {
+    this.financialCondition$ = accountingService.getFinancialCondition().share();
+
+    this.assets$ = this.financialCondition$
+      .map(statement => statement.financialConditionSections
+        .find(section => section.type === 'ASSET')
+      );
+
+    this.equities$ = this.financialCondition$
+      .map(statement => statement.financialConditionSections
+        .find(section => section.type === 'EQUITY')
+      );
+
+    this.liabilities$ = this.financialCondition$
+      .map(statement => statement.financialConditionSections
+        .find(section => section.type === 'LIABILITY')
+      );
+  }
+}
diff --git a/src/app/accounting/form/create/create.form.component.html b/src/app/accounting/form/create/create.form.component.html
new file mode 100644
index 0000000..6280e1b
--- /dev/null
+++ b/src/app/accounting/form/create/create.form.component.html
@@ -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.
+-->
+
+<fims-layout-card-over title="{{'Create new ledger' | translate}}">
+  <fims-ledger-form-component #form
+                                 (onSave)="onSave($event)"
+                                 (onCancel)="onCancel()"
+                                 [ledger]="ledger"
+                                 [parentLedger]="parentLedger">
+  </fims-ledger-form-component>
+</fims-layout-card-over>
diff --git a/src/app/accounting/form/create/create.form.component.ts b/src/app/accounting/form/create/create.form.component.ts
new file mode 100644
index 0000000..576fb94
--- /dev/null
+++ b/src/app/accounting/form/create/create.form.component.ts
@@ -0,0 +1,95 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, OnDestroy, OnInit, ViewChild} from '@angular/core';
+import {Ledger} from '../../../services/accounting/domain/ledger.model';
+import {LedgerFormComponent} from '../form.component';
+import {ActivatedRoute, Router} from '@angular/router';
+import {Error} from '../../../services/domain/error.model';
+import * as fromAccounting from '../../store';
+import {Subscription} from 'rxjs/Subscription';
+import {CREATE, CREATE_SUB_LEDGER, RESET_FORM} from '../../store/ledger/ledger.actions';
+import {AccountingStore} from '../../store/index';
+
+@Component({
+  templateUrl: './create.form.component.html'
+})
+export class CreateLedgerFormComponent implements OnInit, OnDestroy {
+
+  private formStateSubscription: Subscription;
+
+  private ledgerSubscription: Subscription;
+
+  @ViewChild('form') formComponent: LedgerFormComponent;
+
+  parentLedger: Ledger;
+
+  ledger: Ledger = {
+    identifier: '',
+    type: 'ASSET',
+    name: '',
+    showAccountsInChart: true,
+    subLedgers: [],
+  };
+
+  constructor(private router: Router, private route: ActivatedRoute, private store: AccountingStore) {
+  }
+
+  ngOnInit() {
+    this.formStateSubscription = this.store.select(fromAccounting.getLedgerFormError)
+      .filter((error: Error) => !!error)
+      .subscribe((error: Error) => this.formComponent.showIdentifierValidationError());
+
+    this.ledgerSubscription = this.store.select(fromAccounting.getSelectedLedger)
+      .subscribe(ledger => this.parentLedger = ledger);
+  }
+
+  ngOnDestroy(): void {
+    this.formStateSubscription.unsubscribe();
+    this.ledgerSubscription.unsubscribe();
+
+    this.store.dispatch({type: RESET_FORM});
+  }
+
+  onSave(ledger: Ledger) {
+    if (this.parentLedger) {
+      this.store.dispatch({
+        type: CREATE_SUB_LEDGER, payload: {
+          parentLedgerId: this.parentLedger.identifier,
+          ledger,
+          activatedRoute: this.route
+        }
+      });
+    } else {
+      this.store.dispatch({
+        type: CREATE, payload: {
+          ledger,
+          activatedRoute: this.route
+        }
+      });
+    }
+  }
+
+  onCancel() {
+    this.navigateAway();
+  }
+
+  navigateAway(): void {
+    this.router.navigate(['../'], {relativeTo: this.route});
+  }
+}
diff --git a/src/app/accounting/form/edit/edit.form.component.html b/src/app/accounting/form/edit/edit.form.component.html
new file mode 100644
index 0000000..14883bc
--- /dev/null
+++ b/src/app/accounting/form/edit/edit.form.component.html
@@ -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.
+-->
+
+<fims-layout-card-over title="{{'Edit ledger' | translate}}">
+  <fims-ledger-form-component #form
+                                 (onSave)="onSave($event)"
+                                 (onCancel)="onCancel()"
+                                 [ledger]="ledger"
+                                 [editMode]="true">
+  </fims-ledger-form-component>
+</fims-layout-card-over>
diff --git a/src/app/accounting/form/edit/edit.form.component.ts b/src/app/accounting/form/edit/edit.form.component.ts
new file mode 100644
index 0000000..5676d19
--- /dev/null
+++ b/src/app/accounting/form/edit/edit.form.component.ts
@@ -0,0 +1,66 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, OnDestroy, OnInit, ViewChild} from '@angular/core';
+import {Ledger} from '../../../services/accounting/domain/ledger.model';
+import {LedgerFormComponent} from '../form.component';
+import {ActivatedRoute, Router} from '@angular/router';
+import {UPDATE} from '../../store/ledger/ledger.actions';
+import * as fromAccounting from '../../store';
+import {Subscription} from 'rxjs/Subscription';
+import {AccountingStore} from '../../store/index';
+
+@Component({
+  templateUrl: './edit.form.component.html'
+})
+export class EditLedgerFormComponent implements OnInit, OnDestroy {
+
+  private ledgerSubscription: Subscription;
+
+  ledger: Ledger;
+
+  @ViewChild('form') formComponent: LedgerFormComponent;
+
+  constructor(private router: Router, private route: ActivatedRoute, private store: AccountingStore) {}
+
+  ngOnInit() {
+    this.ledgerSubscription = this.store.select(fromAccounting.getSelectedLedger)
+      .subscribe(ledger => this.ledger = ledger);
+  }
+
+  ngOnDestroy(): void {
+    this.ledgerSubscription.unsubscribe();
+  }
+
+  onSave(ledger: Ledger) {
+    ledger.subLedgers = this.ledger.subLedgers;
+
+    this.store.dispatch({ type: UPDATE, payload: {
+      ledger,
+      activatedRoute: this.route
+    }});
+  }
+
+  onCancel() {
+    this.navigateAway();
+  }
+
+  navigateAway(): void {
+    this.router.navigate(['../'], { relativeTo: this.route });
+  }
+}
diff --git a/src/app/accounting/form/form.component.html b/src/app/accounting/form/form.component.html
new file mode 100644
index 0000000..6b19dc4
--- /dev/null
+++ b/src/app/accounting/form/form.component.html
@@ -0,0 +1,47 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<td-steps mode="'vertical'">
+  <td-step #detailsStep label="{{'Ledger details' | translate}}"
+           [state]="form.valid ? 'complete' : form.pristine ? 'none' : 'required'">
+    <form [formGroup]="form" layout="column">
+      <fims-id-input flex [form]="form" placeholder="Ledger Number" controlName="identifier" [readonly]="editMode"></fims-id-input>
+      <mat-radio-group formControlName="type">
+        <mat-radio-button *ngFor="let type of accountTypeOptions" [value]="type.type" layout-margin>
+          {{type.label}}
+        </mat-radio-button>
+      </mat-radio-group>
+      <fims-text-input [form]="form" controlName="name" placeholder="{{'Name' | translate}}"></fims-text-input>
+      <mat-form-field layout-margin flex>
+        <textarea matInput placeholder="{{'Description(Optional)' | translate}}" formControlName="description"></textarea>
+        <mat-error *ngIf="form.get('description').hasError('maxlength')">
+          {{ 'Only characters allowed.' | translate:{ value: form.get('description').getError('maxlength')['requiredLength']} }}
+        </mat-error>
+      </mat-form-field>
+      <mat-checkbox formControlName="showAccountsInChart" layout-margin translate>Show accounts when displayed in chart?</mat-checkbox>
+    </form>
+    <ng-template td-step-actions>
+      <fims-form-final-action
+        [resourceName]="'LEDGER'"
+        [editMode]="editMode"
+        [disabled]="!form.valid"
+        (onCancel)="cancel()"
+        (onSave)="save()">
+      </fims-form-final-action>
+    </ng-template>
+  </td-step>
+</td-steps>
diff --git a/src/app/accounting/form/form.component.spec.ts b/src/app/accounting/form/form.component.spec.ts
new file mode 100644
index 0000000..895f77c
--- /dev/null
+++ b/src/app/accounting/form/form.component.spec.ts
@@ -0,0 +1,108 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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} from '@angular/core';
+import {async, ComponentFixture, TestBed} from '@angular/core/testing';
+import {By} from '@angular/platform-browser';
+import {TranslateModule} from '@ngx-translate/core';
+import {ReactiveFormsModule} from '@angular/forms';
+import {CovalentStepsModule} from '@covalent/core';
+import {NoopAnimationsModule} from '@angular/platform-browser/animations';
+import {FimsSharedModule} from '../../common/common.module';
+import {LedgerFormComponent} from './form.component';
+import {Ledger} from '../../services/accounting/domain/ledger.model';
+import {MatCheckboxModule, MatInputModule, MatRadioModule} from '@angular/material';
+
+describe('Test ledger form', () => {
+
+  let fixture: ComponentFixture<TestComponent>;
+
+  let testComponent: TestComponent;
+
+  beforeEach( async(() => {
+    TestBed.configureTestingModule({
+      imports: [
+        TranslateModule.forRoot(),
+        MatCheckboxModule,
+        MatRadioModule,
+        MatInputModule,
+        CovalentStepsModule,
+        FimsSharedModule,
+        ReactiveFormsModule,
+        NoopAnimationsModule
+      ],
+      declarations: [
+        LedgerFormComponent,
+        TestComponent
+      ]
+    }).compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture  = TestBed.createComponent(TestComponent);
+    testComponent = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should trigger save event', () => {
+    const button: DebugElement = fixture.debugElement.query(By.css('button[mat-raised-button]'));
+
+    button.nativeElement.click();
+
+    expect(testComponent.savedLedger).toEqual(testComponent.ledger);
+  });
+
+  it('should trigger cancel event', () => {
+    const button: DebugElement = fixture.debugElement.query(By.css('button[mat-button]'));
+
+    button.nativeElement.click();
+
+    expect(testComponent.canceled).toBeTruthy();
+  });
+
+});
+
+@Component({
+  template: `
+    <fims-ledger-form-component (onSave)="onSave($event)" (onCancel)="onCancel($event)" [ledger]="ledger" [editMode]="true">
+    </fims-ledger-form-component>`
+})
+class TestComponent {
+
+  ledger: Ledger = {
+    identifier: 'test',
+    type: 'ASSET',
+    name: 'test',
+    description: 'test',
+    showAccountsInChart: true,
+    subLedgers: []
+  };
+
+  savedLedger: Ledger;
+
+  canceled: boolean;
+
+  onSave(ledger: Ledger): void {
+    this.savedLedger = ledger;
+  }
+
+  onCancel(): void {
+    this.canceled = true;
+  }
+
+}
diff --git a/src/app/accounting/form/form.component.ts b/src/app/accounting/form/form.component.ts
new file mode 100644
index 0000000..881347b
--- /dev/null
+++ b/src/app/accounting/form/form.component.ts
@@ -0,0 +1,98 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 {AccountTypeOption, accountTypes} from '../account-types.model';
+import {Component, EventEmitter, Input, OnInit, Output, ViewChild} from '@angular/core';
+import {FormComponent} from '../../common/forms/form.component';
+import {Ledger} from '../../services/accounting/domain/ledger.model';
+import {TdStepComponent} from '@covalent/core';
+import {FormBuilder, Validators} from '@angular/forms';
+import {FimsValidators} from '../../common/validator/validators';
+
+@Component({
+  selector: 'fims-ledger-form-component',
+  templateUrl: './form.component.html'
+})
+export class LedgerFormComponent extends FormComponent<Ledger> implements OnInit {
+
+  @ViewChild('detailsStep') step: TdStepComponent;
+
+  @Input() parentLedger: Ledger;
+
+  @Input() ledger: Ledger;
+
+  @Input() editMode: boolean;
+
+  @Output('onSave') onSave = new EventEmitter<Ledger>();
+
+  @Output('onCancel') onCancel = new EventEmitter<void>();
+
+  accountTypeOptions: AccountTypeOption[] = accountTypes;
+
+  constructor(private formBuilder: FormBuilder) {
+    super();
+  }
+
+  get formData(): Ledger {
+    return null;
+  }
+
+  ngOnInit(): void {
+    this.openDetailStep();
+
+    const typeValue = {
+      value: this.parentLedger ? this.parentLedger.type : this.ledger.type,
+      disabled: this.parentLedger || this.editMode
+    };
+
+    this.form = this.formBuilder.group({
+      'identifier': [this.ledger.identifier, [Validators.required, Validators.minLength(3), Validators.maxLength(32),
+        FimsValidators.urlSafe]],
+      'type': [typeValue, [Validators.required]],
+      'name': [this.ledger.name, [Validators.required, Validators.maxLength(256)]],
+      'showAccountsInChart': [this.ledger.showAccountsInChart, [Validators.required]],
+      'description': [this.ledger.description, Validators.maxLength(2048)],
+    });
+  }
+
+  showIdentifierValidationError(): void {
+    this.setError('identifier', 'unique', true);
+    this.openDetailStep();
+  }
+
+  openDetailStep(): void {
+    this.step.open();
+  }
+
+  save(): void {
+    const ledger: Ledger = {
+      identifier: this.form.get('identifier').value,
+      type: this.form.get('type').value,
+      name: this.form.get('name').value,
+      showAccountsInChart: this.form.get('showAccountsInChart').value,
+      description: this.form.get('description').value,
+      subLedgers: []
+    };
+
+    this.onSave.emit(ledger);
+  }
+
+  cancel(): void {
+    this.onCancel.emit();
+  }
+}
diff --git a/src/app/accounting/general-ledger.component.html b/src/app/accounting/general-ledger.component.html
new file mode 100644
index 0000000..a226703
--- /dev/null
+++ b/src/app/accounting/general-ledger.component.html
@@ -0,0 +1,69 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<fims-layout-card-over title="{{'General Ledger' | translate}}">
+  <fims-two-column-layout>
+    <mat-nav-list left>
+      <h3 mat-subheader translate>Details</h3>
+      <a mat-list-item [routerLink]="['./chartOfAccounts']">
+        <mat-icon matListAvatar>list</mat-icon>
+        <h3 matLine translate>Chart of accounts</h3>
+        <p matLine translate>View chart of accounts</p>
+      </a>
+      <a mat-list-item [routerLink]="['./journalEntries']" *hasPermission="{ id: 'accounting_journals', accessLevel: 'READ' }">
+        <mat-icon matListAvatar>assignment</mat-icon>
+        <h3 matLine translate>Journal entries</h3>
+        <p matLine translate>View journal entries</p>
+      </a>
+      <a mat-list-item [routerLink]="['./transactiontypes']" *hasPermission="{ id: 'accounting_tx_types', accessLevel: 'READ' }">
+        <mat-icon matListAvatar>swap_horiz</mat-icon>
+        <h3 matLine translate>Transaction types</h3>
+        <p matLine translate>View transaction types</p>
+      </a>
+      <a mat-list-item [routerLink]="['./cheques']" *hasPermission="{ id: 'cheque_management', accessLevel: 'READ' }">
+        <mat-icon matListAvatar>import_contacts</mat-icon>
+        <h3 matLine translate>Cheque clearing</h3>
+        <p matLine translate>View and clear cheques</p>
+      </a>
+      <a mat-list-item [routerLink]="['./trialBalance']">
+        <mat-icon matListAvatar>account_balance</mat-icon>
+        <h3 matLine translate>Trial balance</h3>
+        <p matLine translate>View trial balance</p>
+      </a>
+      <a mat-list-item [routerLink]="['./incomeStatement']" *hasPermission="{ id: 'accounting_income_statement', accessLevel: 'READ'}">
+        <mat-icon matListAvatar>timeline</mat-icon>
+        <h3 matLine translate>Income statement</h3>
+        <p matLine translate>View income statement</p>
+      </a>
+      <a mat-list-item [routerLink]="['./financialCondition']" *hasPermission="{ id: 'accounting_fin_condition', accessLevel: 'READ'}">
+        <mat-icon matListAvatar>timeline</mat-icon>
+        <h3 matLine translate>Financial condition</h3>
+        <p matLine translate>View financial condition</p>
+      </a>
+      <a mat-list-item [routerLink]="['./payrolls']" *hasPermission="{ id: 'payroll_distribution', accessLevel: 'READ' }">
+        <mat-icon matListAvatar>receipt</mat-icon>
+        <h3 matLine translate>Payrolls</h3>
+        <p matLine translate>Manage payrolls</p>
+      </a>
+    </mat-nav-list>
+    <div layout="column" flex-gt-md="100" right>
+      <h3 translate>Ledger</h3>
+      <fims-data-table flex (onActionCellClick)="rowSelect($event)" [columns]="columns" [data]="ledgerData | async"></fims-data-table>
+    </div>
+  </fims-two-column-layout>
+</fims-layout-card-over>
+<fims-fab-button title="{{'Create ledger' | translate}}" icon="add" [link]="['create']" [permission]="{ id: 'accounting_ledgers', accessLevel: 'CHANGE'}"></fims-fab-button>
diff --git a/src/app/accounting/general-ledger.component.ts b/src/app/accounting/general-ledger.component.ts
new file mode 100644
index 0000000..d688a50
--- /dev/null
+++ b/src/app/accounting/general-ledger.component.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 {Component, OnInit} from '@angular/core';
+import {ActivatedRoute, Params, Router} from '@angular/router';
+import {Ledger} from '../services/accounting/domain/ledger.model';
+import {TableData} from '../common/data-table/data-table.component';
+import * as fromAccounting from './store';
+import {LOAD_ALL_TOP_LEVEL} from './store/ledger/ledger.actions';
+import {Observable} from 'rxjs/Observable';
+import {AccountingStore} from './store/index';
+
+@Component({
+  templateUrl: './general-ledger.component.html'
+})
+export class GeneralLedgerComponent implements OnInit {
+
+  ledgerData: Observable<TableData>;
+
+  columns: any[] = [
+    { name: 'identifier', label: 'Id', tooltip: 'Id' },
+    { name: 'name', label: 'Name', tooltip: 'Name' },
+    { name: 'description', label: 'Description', tooltip: 'Description' },
+    { name: 'totalValue', label: 'Balance', tooltip: 'Balance', format: value => value ? value.toFixed(2) : '-' }
+  ];
+
+  constructor(private router: Router, private route: ActivatedRoute, private store: AccountingStore) {}
+
+  ngOnInit(): void {
+    this.ledgerData = this.store.select(fromAccounting.getAllTopLevelLedgerEntities)
+      .map(ledgers => ({
+        data: ledgers,
+        totalElements: ledgers.length,
+        totalPages: 1
+      }));
+
+    this.route.queryParams.subscribe((params: Params) => {
+      this.store.dispatch({ type: LOAD_ALL_TOP_LEVEL });
+    });
+  }
+
+  rowSelect(ledger: Ledger): void {
+    this.router.navigate(['ledgers/detail', ledger.identifier, 'ledgers'], { relativeTo: this.route });
+  }
+
+}
diff --git a/src/app/accounting/general-ledger/general-ledger.component.html b/src/app/accounting/general-ledger/general-ledger.component.html
deleted file mode 100644
index 6d76381..0000000
--- a/src/app/accounting/general-ledger/general-ledger.component.html
+++ /dev/null
@@ -1,47 +0,0 @@
-<div class="main-div mat-elevation-z2">
-  <h3 class="heading">General Ledger</h3>
-  <mat-divider></mat-divider>
-  <div class="fineract-button">
-  <a mat-raised-button  color="primary" [routerLink]="['/navbar/add_ledger']">
-    <mat-icon>add</mat-icon>Add Ledger</a>
-    </div>
-  <mat-form-field>
-    <input matInput (keyup)="applyFilter($event.target.value)" placeholder="Filter by name">
-  </mat-form-field>
-  
-<br>
-<mat-divider></mat-divider>
-
-<mat-table #table [dataSource]="dataSource">
-
-  <!-- Position Column -->
-  <ng-container matColumnDef="id">
-    <mat-header-cell *matHeaderCellDef>Id </mat-header-cell>
-    <mat-cell *matCellDef="let element">
-      <a routerLink="{{element.id}}">{{element.id}}</a>
-    </mat-cell>
-  </ng-container>
-
-  <!-- Name Column -->
-  <ng-container matColumnDef="name">
-    <mat-header-cell *matHeaderCellDef>Name</mat-header-cell>
-    <mat-cell *matCellDef="let element"> {{element.name}} </mat-cell>
-  </ng-container>
-
-  <ng-container matColumnDef="description">
-      <mat-header-cell *matHeaderCellDef>Description</mat-header-cell>
-      <mat-cell *matCellDef="let element"> {{element.description}} </mat-cell>
-    </ng-container>
-
-    <ng-container matColumnDef="balance">
-        <mat-header-cell *matHeaderCellDef>Balance</mat-header-cell>
-        <mat-cell *matCellDef="let element"> {{element.balance} </mat-cell>
-      </ng-container>
-
-  <mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
-  <mat-row *matRowDef="let row; columns: displayedColumns;"></mat-row>
-</mat-table>
-  
-  
-</div>
-  
\ No newline at end of file
diff --git a/src/app/accounting/general-ledger/general-ledger.component.scss b/src/app/accounting/general-ledger/general-ledger.component.scss
deleted file mode 100644
index b03d650..0000000
--- a/src/app/accounting/general-ledger/general-ledger.component.scss
+++ /dev/null
@@ -1,11 +0,0 @@
-
-table {
-    width: 80%;
-  }
-  
-  .mat-form-field {
-    font-size: 14px;
-    width: 100%;
-  }
-  
-  
\ No newline at end of file
diff --git a/src/app/accounting/general-ledger/general-ledger.component.spec.ts b/src/app/accounting/general-ledger/general-ledger.component.spec.ts
deleted file mode 100644
index 8cfe52a..0000000
--- a/src/app/accounting/general-ledger/general-ledger.component.spec.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-import { async, ComponentFixture, TestBed } from '@angular/core/testing';
-
-import { GeneralLedgerComponent } from './general-ledger.component';
-
-describe('GeneralLedgerComponent', () => {
-  let component: GeneralLedgerComponent;
-  let fixture: ComponentFixture<GeneralLedgerComponent>;
-
-  beforeEach(async(() => {
-    TestBed.configureTestingModule({
-      declarations: [ GeneralLedgerComponent ]
-    })
-    .compileComponents();
-  }));
-
-  beforeEach(() => {
-    fixture = TestBed.createComponent(GeneralLedgerComponent);
-    component = fixture.componentInstance;
-    fixture.detectChanges();
-  });
-
-  it('should create', () => {
-    expect(component).toBeTruthy();
-  });
-});
diff --git a/src/app/accounting/general-ledger/general-ledger.component.ts b/src/app/accounting/general-ledger/general-ledger.component.ts
deleted file mode 100644
index 460769d..0000000
--- a/src/app/accounting/general-ledger/general-ledger.component.ts
+++ /dev/null
@@ -1,35 +0,0 @@
-import { Component, OnInit } from '@angular/core';
-import {MatTableDataSource} from '@angular/material';
-
-@Component({
-  selector: 'app-general-ledger',
-  templateUrl: './general-ledger.component.html',
-  styleUrls: ['./general-ledger.component.scss']
-})
-export class GeneralLedgerComponent implements OnInit {
-  displayedColumns = ['id','name','description','balance'];
-  dataSource = new MatTableDataSource(ELEMENT_DATA);
-
-  applyFilter(filterValue: string) {
-    filterValue = filterValue.trim(); // Remove whitespace
-    filterValue = filterValue.toLowerCase(); // MatTableDataSource defaults to lowercase matches
-    this.dataSource.filter = filterValue;
-  }
-
-  constructor() { }
-
-  ngOnInit() {
-  }
-
-}
-export interface Element {
-  id: number;
-  name:string;
-  description: string;
-  balance:string;
-  
-}
-
-const ELEMENT_DATA: Element[] = [
-  
-];
diff --git a/src/app/accounting/incomeStatement/income-statement.component.html b/src/app/accounting/incomeStatement/income-statement.component.html
new file mode 100644
index 0000000..2b008a9
--- /dev/null
+++ b/src/app/accounting/incomeStatement/income-statement.component.html
@@ -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.
+-->
+
+<fims-layout-card-over title="{{'Income statement' | translate}}" [navigateBackTo]="['../']">
+  <div layout="row">
+    <table td-data-table *ngIf="incomeStatement$ | async as incomeStatement">
+      <ng-container *ngIf="income$ | async as income">
+        <tr td-data-table-row>
+          <td td-data-table-cell>
+            <b translate>Income</b>
+          </td>
+          <td td-data-table-cell></td>
+          <td td-data-table-cell></td>
+        </tr>
+        <tr td-data-table-row *ngFor="let entry of income.incomeStatementEntries">
+          <td td-data-table-cell></td>
+          <td td-data-table-cell>
+            {{entry.description}}
+          </td>
+          <td td-data-table-cell>
+            {{entry.value | displayFimsFinancialNumber}}
+          </td>
+        </tr>
+        <tr td-data-table-row>
+          <td td-data-table-cell>
+            <i translate>Subtotal</i>
+          </td>
+          <td td-data-table-cell></td>
+          <td td-data-table-cell>
+            <i class="double-underline">
+              {{income.subtotal | displayFimsFinancialNumber}}
+            </i>
+          </td>
+        </tr>
+        <tr td-data-table-row></tr>
+        <tr td-data-table-row>
+          <td td-data-table-cell>
+            <b translate>Gross Profit</b>
+          </td>
+          <td td-data-table-cell></td>
+          <td td-data-table-cell>
+            <b class="double-underline">
+              {{incomeStatement.grossProfit | displayFimsFinancialNumber}}
+            </b>
+          </td>
+        </tr>
+        <tr td-data-table-row></tr>
+      </ng-container>
+      <ng-container *ngIf="expenses$ | async as expenses">
+        <tr td-data-table-row>
+          <td td-data-table-cell>
+            <b translate>Expenses</b>
+          </td>
+          <td td-data-table-cell></td>
+          <td td-data-table-cell></td>
+        </tr>
+        <tr td-data-table-row *ngFor="let entry of expenses.incomeStatementEntries">
+          <td td-data-table-cell></td>
+          <td td-data-table-cell>
+            {{entry.description}}
+          </td>
+          <td td-data-table-cell>
+            {{entry.value | displayFimsFinancialNumber}}
+          </td>
+        </tr>
+        <tr td-data-table-row>
+          <td td-data-table-cell>
+            <i translate>Subtotal</i>
+          </td>
+          <td td-data-table-cell></td>
+          <td td-data-table-cell>
+            <i class="double-underline">
+              {{expenses.subtotal | displayFimsFinancialNumber}}
+            </i>
+          </td>
+        </tr>
+        <tr td-data-table-row></tr>
+        <tr td-data-table-row>
+          <td td-data-table-cell>
+            <b translate>Total expenses</b>
+          </td>
+          <td td-data-table-cell></td>
+          <td td-data-table-cell>
+            <b class="double-underline">
+              {{incomeStatement.totalExpenses | displayFimsFinancialNumber}}
+            </b>
+          </td>
+        </tr>
+        <tr td-data-table-row></tr>
+        <tr td-data-table-row>
+          <td td-data-table-cell>
+            <b translate>Net Income (Loss)</b>
+          </td>
+          <td td-data-table-cell></td>
+          <td td-data-table-cell>
+            <b class="double-underline">
+              {{incomeStatement.netIncome | displayFimsFinancialNumber}}
+            </b>
+          </td>
+        </tr>
+      </ng-container>
+    </table>
+  </div>
+</fims-layout-card-over>
diff --git a/src/app/accounting/incomeStatement/income-statement.component.scss b/src/app/accounting/incomeStatement/income-statement.component.scss
new file mode 100644
index 0000000..9580096
--- /dev/null
+++ b/src/app/accounting/incomeStatement/income-statement.component.scss
@@ -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.
+ */
+
+.double-underline {
+  border-bottom: 3px double;
+}
diff --git a/src/app/accounting/incomeStatement/income-statement.component.ts b/src/app/accounting/incomeStatement/income-statement.component.ts
new file mode 100644
index 0000000..6588e2e
--- /dev/null
+++ b/src/app/accounting/incomeStatement/income-statement.component.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 {Component} from '@angular/core';
+import {AccountingService} from '../../services/accounting/accounting.service';
+import {IncomeStatement} from '../../services/accounting/domain/income-statement.model';
+import {Observable} from 'rxjs/Observable';
+import {IncomeStatementSection} from '../../services/accounting/domain/income-statement-section.model';
+
+@Component({
+  templateUrl: './income-statement.component.html',
+  styleUrls: ['./income-statement.component.scss']
+})
+export class IncomeStatementComponent {
+
+  incomeStatement$: Observable<IncomeStatement>;
+
+  income$: Observable<IncomeStatementSection>;
+
+  expenses$: Observable<IncomeStatementSection>;
+
+  constructor(private accountingService: AccountingService) {
+    this.incomeStatement$ = accountingService.getIncomeStatement().share();
+
+    this.income$ = this.incomeStatement$
+      .map(statement => statement.incomeStatementSections
+        .find(section => section.type === 'INCOME')
+      );
+
+    this.expenses$ = this.incomeStatement$
+      .map(statement => statement.incomeStatementSections
+        .find(section => section.type === 'EXPENSES')
+      );
+  }
+}
diff --git a/src/app/accounting/journalEntries/form/create.form.component.html b/src/app/accounting/journalEntries/form/create.form.component.html
new file mode 100644
index 0000000..1a2bccb
--- /dev/null
+++ b/src/app/accounting/journalEntries/form/create.form.component.html
@@ -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.
+-->
+
+<fims-layout-card-over title="{{'Create new journal entry' | translate}}">
+  <fims-journal-entry-form
+    [journalEntry]="journalEntry$ | async"
+    [error]="error$ | async"
+    (onSave)="save($event)"
+    (onCancel)="cancel()">
+  </fims-journal-entry-form>
+</fims-layout-card-over>
diff --git a/src/app/accounting/journalEntries/form/create.form.component.ts b/src/app/accounting/journalEntries/form/create.form.component.ts
new file mode 100644
index 0000000..77400c5
--- /dev/null
+++ b/src/app/accounting/journalEntries/form/create.form.component.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 {JournalEntry} from '../../../services/accounting/domain/journal-entry.model';
+import {Component, OnDestroy, OnInit, ViewChild} from '@angular/core';
+import {ActivatedRoute, Router} from '@angular/router';
+import {TdStepComponent} from '@covalent/core';
+import * as fromAccounting from '../../store';
+import * as fromRoot from '../../../store';
+import {CREATE, RESET_FORM} from '../../store/ledger/journal-entry/journal-entry.actions';
+import {Error} from '../../../services/domain/error.model';
+import {AccountingStore} from '../../store/index';
+import {Observable} from 'rxjs/Observable';
+import {todayAsISOString} from '../../../services/domain/date.converter';
+
+@Component({
+  templateUrl: './create.form.component.html'
+})
+export class CreateJournalEntryFormComponent implements OnInit, OnDestroy {
+
+  @ViewChild('detailsStep') detailsStep: TdStepComponent;
+
+  journalEntry$: Observable<JournalEntry>;
+
+  error$: Observable<Error>;
+
+  constructor(private router: Router, private route: ActivatedRoute, private store: AccountingStore) {
+  }
+
+  ngOnInit(): void {
+    this.error$ = this.store.select(fromAccounting.getJournalEntryFormError)
+      .filter((error: Error) => !!error);
+
+    this.journalEntry$ = this.store.select(fromRoot.getUsername)
+      .map(username => ({
+        transactionIdentifier: '',
+        transactionDate: todayAsISOString(),
+        transactionType: '',
+        clerk: username,
+        debtors: [
+          { accountNumber: '', amount: '0' }
+        ],
+        creditors: [
+          { accountNumber: '', amount: '0' }
+        ]
+      }));
+  }
+
+  ngOnDestroy(): void {
+    this.store.dispatch({ type: RESET_FORM });
+  }
+
+  save(journalEntry: JournalEntry): void {
+    this.store.dispatch({ type: CREATE, payload: {
+      journalEntry,
+      activatedRoute: this.route
+    } });
+  }
+
+  cancel() {
+    this.navigateAway();
+  }
+
+  navigateAway(): void {
+    this.router.navigate(['../'], {relativeTo: this.route});
+  }
+
+}
diff --git a/src/app/accounting/journalEntries/form/form.component.html b/src/app/accounting/journalEntries/form/form.component.html
new file mode 100644
index 0000000..163ed24
--- /dev/null
+++ b/src/app/accounting/journalEntries/form/form.component.html
@@ -0,0 +1,121 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+  <form [formGroup]="form">
+    <td-steps mode="'vertical'">
+      <td-step #detailsStep label="{{'Journal Entry details' | translate}}" layout="column">
+        <fims-id-input [form]="form" controlName="transactionIdentifier" [readonly]="false"></fims-id-input>
+        <fims-date-input placeholder="{{'Transaction date' | translate}}" [form]="form" controlName="transactionDate"></fims-date-input>
+        <fims-transaction-type-select title="{{'Transaction type' | translate}}" formControlName="transactionType">
+          <ng-container *ngIf="!form.get('transactionType').pristine && form.get('transactionType').hasError('required')" translate>
+            Required
+          </ng-container>
+          <ng-container *ngIf="form.get('transactionType').hasError('invalidTransactionType')" translate>
+            Invalid transaction type
+          </ng-container>
+        </fims-transaction-type-select>
+        <div layout="row">
+          <mat-form-field layout-margin flex>
+            <textarea matInput placeholder="{{'Note(Optional)' | translate}}" formControlName="note"></textarea>
+          </mat-form-field>
+        </div>
+        <div layout="row">
+          <mat-form-field layout-margin flex>
+            <textarea matInput flex placeholder="{{'Message(Optional)' | translate}}" formControlName="message"></textarea>
+          </mat-form-field>
+        </div>
+        <ng-template td-step-actions>
+          <fims-form-continue-action (onContinue)="accountsStep.open()"></fims-form-continue-action>
+        </ng-template>
+      </td-step>
+      <td-step #accountsStep label="{{'Affected Accounts' | translate}}">
+        <div layout-gt-xs="column">
+          <div layout="row">
+            <mat-card flex formArrayName="debtors">
+              <mat-card-title>Debit</mat-card-title>
+              <mat-card-content>
+                <div layout="row" layout-align="start center" [formGroupName]="i" *ngFor="let debtor of debtors; let i = index;">
+                  <fims-account-select formControlName="accountNumber" [title]="'Account' | translate" flex="50">
+                    <ng-container *ngIf="!debtor.get('accountNumber').pristine && debtor.get('accountNumber').hasError('required')"
+                                  translate>
+                      Required
+                    </ng-container>
+                    <ng-container *ngIf="debtor.get('accountNumber').hasError('invalidAccount')" translate>
+                      Invalid account
+                    </ng-container>
+                  </fims-account-select>
+                  <fims-text-input type="number" [form]="debtor" controlName="amount"
+                                   placeholder="{{'Amount' | translate}}"></fims-text-input>
+                  <button mat-raised-button color="warn" title="{{'Remove' | translate}}" (click)="removeDebtor(i)">{{'Remove' |
+                    translate}}
+                  </button>
+                </div>
+                <p *ngIf="form.get('debtors').hasError('minItemsInvalid')" class="tc-red-600" translate>
+                  Please add at least one debit.
+                </p>
+              </mat-card-content>
+              <mat-card-actions>
+                <button mat-button mat-raised-button (click)="addDebtor()">{{'ADD DEBIT' | translate}}</button>
+              </mat-card-actions>
+            </mat-card>
+            <mat-card flex formArrayName="creditors">
+              <mat-card-title>Credit</mat-card-title>
+              <mat-card-content>
+                <div layout="row" layout-align="start center" [formGroupName]="i" *ngFor="let creditor of creditors; let i = index;">
+                  <fims-account-select formControlName="accountNumber" [title]="'Account' | translate" flex="50">
+                    <ng-container *ngIf="!creditor.get('accountNumber').pristine && creditor.get('accountNumber').hasError('required')"
+                                  translate>
+                      Required
+                    </ng-container>
+                    <ng-container *ngIf="creditor.get('accountNumber').hasError('invalidAccount')" translate>
+                      Invalid account
+                    </ng-container>
+                  </fims-account-select>
+                  <fims-text-input type="number" [form]="creditor" controlName="amount"
+                                   placeholder="{{'Amount' | translate}}"></fims-text-input>
+                  <button mat-raised-button color="warn" title="{{'Remove' | translate}}" (click)="removeCreditor(i)">{{'Remove' |
+                    translate}}
+                  </button>
+                </div>
+                <p *ngIf="form.get('creditors').hasError('minItemsInvalid')" class="tc-red-600" translate>
+                  Please add at least one credit.
+                </p>
+              </mat-card-content>
+              <mat-card-actions>
+                <button mat-button mat-raised-button (click)="addCreditor()">{{'ADD CREDIT' | translate}}</button>
+              </mat-card-actions>
+            </mat-card>
+
+          </div>
+          <p *ngIf="!form.pristine && form.hasError('sumInvalid')" class="tc-red-600" translate>
+            Sum of debit and sum of credit must match.
+          </p>
+        </div>
+      </td-step>
+      <td-step label="{{'Final step' | translate}}" [state]="'complete'">
+        <ng-template td-step-summary>
+          <fims-form-final-action
+            [resourceName]="'JOURNAL ENTRY'"
+            [editMode]="false"
+            [disabled]="!form.valid"
+            (onCancel)="cancel()"
+            (onSave)="save()">
+          </fims-form-final-action>
+        </ng-template>
+      </td-step>
+    </td-steps>
+  </form>
diff --git a/src/app/accounting/journalEntries/form/form.component.spec.ts b/src/app/accounting/journalEntries/form/form.component.spec.ts
new file mode 100644
index 0000000..f90d337
--- /dev/null
+++ b/src/app/accounting/journalEntries/form/form.component.spec.ts
@@ -0,0 +1,179 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, ViewChild} from '@angular/core';
+import {JournalEntryFormComponent} from './form.component';
+import {JournalEntry} from '../../../services/accounting/domain/journal-entry.model';
+import {ComponentFixture, TestBed} from '@angular/core/testing';
+import {TranslateModule} from '@ngx-translate/core';
+import {FimsSharedModule} from '../../../common/common.module';
+import {ReactiveFormsModule} from '@angular/forms';
+import {CovalentStepsModule} from '@covalent/core';
+import {NoopAnimationsModule} from '@angular/platform-browser/animations';
+import {AccountingService} from '../../../services/accounting/accounting.service';
+import {Observable} from 'rxjs/Observable';
+import {By} from '@angular/platform-browser';
+import {MatAutocompleteModule, MatCardModule, MatInputModule, MatOptionModule} from '@angular/material';
+import {TransactionTypeSelectComponent} from './transaction-type-select/transaction-type-select.component';
+
+describe('Test JournalEntryFormComponent', () => {
+
+  let fixture: ComponentFixture<TestComponent>;
+
+  let testComponent: TestComponent;
+
+  let baseDate: Date;
+
+  function mockValidJournalEntry(date: Date): JournalEntry {
+    const journalEntry: JournalEntry = {
+      transactionIdentifier: 'testId',
+      transactionDate: date.toISOString(),
+      transactionType: 'transactionType',
+      note: 'testNote',
+      message: 'testMessage',
+      debtors: [
+        { accountNumber: '1234', amount: '11' }
+      ],
+      creditors: [
+        { accountNumber: '5678', amount: '11' }
+      ],
+      clerk: 'testClerk'
+    };
+
+    return journalEntry;
+  }
+
+  beforeEach(() => {
+    TestBed.configureTestingModule({
+      declarations: [
+        TestComponent,
+        TransactionTypeSelectComponent,
+        JournalEntryFormComponent
+      ],
+      imports: [
+        TranslateModule.forRoot(),
+        FimsSharedModule,
+        ReactiveFormsModule,
+        MatAutocompleteModule,
+        MatOptionModule,
+        MatInputModule,
+        MatCardModule,
+        ReactiveFormsModule,
+        CovalentStepsModule,
+        NoopAnimationsModule
+      ],
+      providers: [
+        {
+          provide: AccountingService, useClass: class {
+          findTransactionType = jasmine.createSpy('findTransactionType').and.returnValue(Observable.of(null));
+          fetchTransactionTypes = jasmine.createSpy('fetchTransactionTypes').and.returnValue(Observable.of([
+            { code: 'transactionType', name: 'transactionType' }
+          ]));
+          findAccount = jasmine.createSpy('findAccount').and.returnValue(Observable.of(null));
+          fetchAccounts = jasmine.createSpy('fetchAccounts').and.returnValue(Observable.of([
+            { identifier: '1234', name: '1234' },
+            { identifier: '5678', name: '1234' }
+          ]));
+        }}
+      ]
+    });
+
+    fixture = TestBed.createComponent(TestComponent);
+    testComponent = fixture.componentInstance;
+  });
+
+  beforeEach(() => {
+    jasmine.clock().install();
+    baseDate = new Date(2017, 1, 1);
+    baseDate.setUTCHours(0, 0, 0, 1);
+    jasmine.clock().mockDate(baseDate);
+  });
+
+  afterEach(() => {
+    jasmine.clock().uninstall();
+  });
+
+  function clickSaveButton(): void {
+    const button: DebugElement = fixture.debugElement.query(By.css('.mat-raised-button.mat-primary'));
+
+    expect(button.properties['disabled']).toBeFalsy('Button should be enabled');
+
+    button.nativeElement.click();
+  }
+
+  it('should save correct values', () => {
+    const journalEntry: JournalEntry = mockValidJournalEntry(baseDate);
+
+    testComponent.journalEntry = journalEntry;
+
+    fixture.detectChanges();
+
+    clickSaveButton();
+
+    expect(testComponent.savedJournalEntry).toEqual(journalEntry);
+  });
+
+  it('should disable button when form is invalid', () => {
+    const journalEntry: JournalEntry = mockValidJournalEntry(baseDate);
+
+    journalEntry.transactionType = '';
+
+    testComponent.journalEntry = journalEntry;
+
+    fixture.detectChanges();
+
+    const button: DebugElement = fixture.debugElement.query(By.css('.mat-raised-button.mat-primary'));
+
+    expect(button.properties['disabled']).toBeTruthy('Button should be disabled');
+  });
+
+  it('should render accounts', () => {
+    const journalEntry: JournalEntry = mockValidJournalEntry(baseDate);
+
+    testComponent.journalEntry = journalEntry;
+
+    fixture.detectChanges();
+
+    // Choose placeholder as selector as I could not find any other attribute to select on
+    const debugElement: DebugElement[] = fixture.debugElement.queryAll(By.css('input[placeholder="Account"]'));
+
+    // 1 debtor, 1 creditor
+    expect(debugElement.length).toEqual(2);
+  });
+});
+
+@Component({
+  template: `
+    <fims-journal-entry-form #form (onSave)="onSave($event)" (onCancel)="onCancel()" [journalEntry]="journalEntry">
+    </fims-journal-entry-form>`
+})
+class TestComponent {
+
+  @ViewChild('form') formComponent: JournalEntryFormComponent;
+
+  journalEntry: JournalEntry;
+
+  savedJournalEntry: JournalEntry;
+
+  user: 'test';
+
+  onSave(journalEntry: JournalEntry): void {
+    this.savedJournalEntry = journalEntry;
+  }
+
+}
diff --git a/src/app/accounting/journalEntries/form/form.component.ts b/src/app/accounting/journalEntries/form/form.component.ts
new file mode 100644
index 0000000..78e927e
--- /dev/null
+++ b/src/app/accounting/journalEntries/form/form.component.ts
@@ -0,0 +1,157 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 {JournalEntry} from '../../../services/accounting/domain/journal-entry.model';
+import {Component, EventEmitter, Input, OnChanges, OnInit, Output, SimpleChanges, ViewChild} from '@angular/core';
+import {AbstractControl, FormArray, FormBuilder, FormGroup, Validators} from '@angular/forms';
+import {TdStepComponent} from '@covalent/core';
+import {addCurrentTime, parseDate} from '../../../services/domain/date.converter';
+import {FimsValidators} from '../../../common/validator/validators';
+import {Error} from '../../../services/domain/error.model';
+import {JournalEntryValidators} from './journal-entry.validator';
+import {AccountingService} from '../../../services/accounting/accounting.service';
+import {transactionTypeExists} from './transaction-type-select/validator/transaction-type-exists.validator';
+import {accountExists} from '../../../common/validator/account-exists.validator';
+import {Creditor} from '../../../services/accounting/domain/creditor.model';
+import {Debtor} from '../../../services/accounting/domain/debtor.model';
+
+@Component({
+  selector: 'fims-journal-entry-form',
+  templateUrl: './form.component.html'
+})
+export class JournalEntryFormComponent implements OnInit, OnChanges {
+
+  @ViewChild('detailsStep') detailsStep: TdStepComponent;
+
+  @Input() journalEntry: JournalEntry;
+
+  @Input() error: Error;
+
+  @Output('onSave') onSave = new EventEmitter<JournalEntry>();
+
+  @Output('onCancel') onCancel = new EventEmitter<void>();
+
+  form: FormGroup;
+
+  constructor(private formBuilder: FormBuilder, private accountingService: AccountingService) {
+
+    this.form = this.formBuilder.group({
+      transactionIdentifier: ['', [Validators.required, Validators.minLength(3), Validators.maxLength(32), FimsValidators.urlSafe]],
+      transactionType: ['', [Validators.required], transactionTypeExists(this.accountingService)],
+      transactionDate: ['', Validators.required],
+      note: [''],
+      message: [''],
+      creditors: this.formBuilder.array([], JournalEntryValidators.minItems(1)),
+      debtors: this.formBuilder.array([], JournalEntryValidators.minItems(1))
+    }, { validator: JournalEntryValidators.equalSum('creditors', 'debtors') });
+
+  }
+
+  ngOnInit(): void {
+    this.detailsStep.open();
+  }
+
+  ngOnChanges(changes: SimpleChanges): void {
+    if (changes.journalEntry) {
+      this.form.reset({
+        transactionIdentifier: this.journalEntry.transactionIdentifier,
+        transactionType: this.journalEntry.transactionType,
+        transactionDate: this.journalEntry.transactionDate,
+        note: this.journalEntry.note,
+        message: this.journalEntry.message
+      });
+
+      this.journalEntry.debtors.forEach(debtor => this.addDebtor(debtor));
+      this.journalEntry.creditors.forEach(creditor => this.addCreditor(creditor));
+    }
+
+    if (changes.error) {
+      this.form.get('transactionIdentifier').setErrors({
+        unique: true
+      });
+      this.detailsStep.open();
+    }
+  }
+
+  save(): void {
+    const date: Date = parseDate(this.form.get('transactionDate').value);
+    const dateWithTime = addCurrentTime(date);
+
+    const journalEntry: JournalEntry = {
+      transactionIdentifier: this.form.get('transactionIdentifier').value,
+      transactionType: this.form.get('transactionType').value,
+      transactionDate: dateWithTime.toISOString(),
+      clerk: this.journalEntry.clerk,
+      note: this.form.get('note').value,
+      message: this.form.get('message').value,
+      creditors: this.form.get('creditors').value,
+      debtors: this.form.get('debtors').value,
+    };
+
+    this.onSave.emit(journalEntry);
+  }
+
+  addCreditor(creditor?: Creditor): void {
+    const control: FormArray = this.form.get('creditors') as FormArray;
+    control.push(this.initCreditor(creditor));
+  }
+
+  removeCreditor(index: number): void {
+    const control: FormArray = this.form.get('creditors') as FormArray;
+    control.removeAt(index);
+  }
+
+  addDebtor(debtor?: Debtor): void {
+    const control: FormArray = this.form.get('debtors') as FormArray;
+    control.push(this.initDebtor(debtor));
+  }
+
+  removeDebtor(index: number): void {
+    const control: FormArray = this.form.get('debtors') as FormArray;
+    control.removeAt(index);
+  }
+
+  cancel() {
+    this.onCancel.emit();
+  }
+
+  get debtors(): AbstractControl[] {
+    const debtors: FormArray = this.form.get('debtors') as FormArray;
+    return debtors.controls;
+  }
+
+  get creditors(): AbstractControl[] {
+    const creditors: FormArray = this.form.get('creditors') as FormArray;
+    return creditors.controls;
+  }
+
+  private initCreditor(creditor: Creditor = { accountNumber: '', amount: '0' }): FormGroup {
+    return this.formBuilder.group({
+      accountNumber: [creditor.accountNumber, [Validators.required], accountExists(this.accountingService)],
+      amount: [creditor.amount, [Validators.required, FimsValidators.greaterThanValue(0)]]
+    });
+  }
+
+  private initDebtor(debtor: Debtor = { accountNumber: '', amount: '0' }): FormGroup {
+    return this.formBuilder.group({
+      accountNumber: [debtor.accountNumber, [Validators.required], accountExists(this.accountingService)],
+      amount: [debtor.amount, [Validators.required, FimsValidators.greaterThanValue(0)]]
+    });
+  }
+
+}
diff --git a/src/app/accounting/journalEntries/form/journal-entry.validator.spec.ts b/src/app/accounting/journalEntries/form/journal-entry.validator.spec.ts
new file mode 100644
index 0000000..ed3156f
--- /dev/null
+++ b/src/app/accounting/journalEntries/form/journal-entry.validator.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 {FormArray, FormBuilder} from '@angular/forms';
+import {JournalEntryValidators} from './journal-entry.validator';
+
+describe('JournalEntryValidators', () => {
+
+  const formBuilder: FormBuilder = new FormBuilder();
+
+  describe('minItems', () => {
+
+    function setup(): FormArray {
+      return formBuilder.array([
+        formBuilder.group({ amount: [10] }),
+      ]);
+    }
+
+    it('should not return error when array contains items', () => {
+      const validator = JournalEntryValidators.minItems(1);
+
+      const formArray = setup();
+
+      expect(validator(formArray)).toBeNull();
+    });
+
+    it('should return error when array contains less items as specified', () => {
+      const validator = JournalEntryValidators.minItems(2);
+
+      const formArray = setup();
+
+      expect(validator(formArray)).toEqual({minItemsInvalid: true});
+    });
+
+  });
+
+  describe('equalSum', () => {
+
+    it('should not return error when sum equal', () => {
+      const validator = JournalEntryValidators.equalSum('valOne', 'valTwo');
+
+      const formGroup = formBuilder.group({
+        valOne: formBuilder.array([
+          formBuilder.group({ amount: [10] }),
+          formBuilder.group({ amount: [10] }),
+        ]),
+        valTwo: formBuilder.array([
+          formBuilder.group({ amount: [10] }),
+          formBuilder.group({ amount: [10] }),
+        ]),
+      });
+
+      expect(validator(formGroup)).toBeNull();
+    });
+
+    it('should return error when sum not equal', () => {
+      const validator = JournalEntryValidators.equalSum('valOne', 'valTwo');
+
+      const formGroup = formBuilder.group({
+        valOne: formBuilder.array([
+          formBuilder.group({ amount: [10] }),
+          formBuilder.group({ amount: [10] }),
+        ]),
+        valTwo: formBuilder.array([
+          formBuilder.group({ amount: [10] }),
+        ]),
+      });
+
+      expect(validator(formGroup)).toEqual({sumInvalid: true});
+    });
+
+  });
+
+});
diff --git a/src/app/accounting/journalEntries/form/journal-entry.validator.ts b/src/app/accounting/journalEntries/form/journal-entry.validator.ts
new file mode 100644
index 0000000..6e3a28b
--- /dev/null
+++ b/src/app/accounting/journalEntries/form/journal-entry.validator.ts
@@ -0,0 +1,63 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 {FormArray, FormGroup} from '@angular/forms';
+
+export class JournalEntryValidators {
+
+  static minItems(min: number = 1) {
+    return (formArray: FormArray): { [key: string]: any } => {
+      const minLength = min - 1;
+      if (formArray.length <= minLength) {
+        return {
+          minItemsInvalid: true
+        };
+      }
+
+      return null;
+    };
+  }
+
+  static equalSum(firstValue: string, secondValue: string) {
+    return (group: FormGroup): { [key: string]: any } => {
+      const firstSum: number = this.sum(group.get(firstValue).value);
+
+      const secondSum: number = this.sum(group.get(secondValue).value);
+
+      if (firstSum !== secondSum) {
+        return {
+          sumInvalid: true
+        };
+      }
+
+      return null;
+    };
+  }
+
+  private static sum(accounts: any[]): number {
+    let sum = 0;
+
+    for (const account of accounts) {
+      sum += parseInt(account.amount, 10);
+    }
+
+    return sum;
+  }
+
+}
diff --git a/src/app/accounting/journalEntries/form/transaction-type-select/transaction-type-select.component.html b/src/app/accounting/journalEntries/form/transaction-type-select/transaction-type-select.component.html
new file mode 100644
index 0000000..95e7aff
--- /dev/null
+++ b/src/app/accounting/journalEntries/form/transaction-type-select/transaction-type-select.component.html
@@ -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.
+-->
+
+<div layout="row">
+  <mat-form-field layout-margin flex>
+    <input matInput [placeholder]="title" [matAutocomplete]="auto" [formControl]="formControl">
+    <mat-hint class="tc-red-600">
+      <ng-content></ng-content>
+    </mat-hint>
+  </mat-form-field>
+</div>
+
+<mat-autocomplete #auto="matAutocomplete">
+  <mat-option *ngFor="let transactionType of transactionTypes | async" [value]="transactionType.code">
+    {{ transactionType.code }}({{ transactionType.name }})
+  </mat-option>
+</mat-autocomplete>
diff --git a/src/app/accounting/journalEntries/form/transaction-type-select/transaction-type-select.component.ts b/src/app/accounting/journalEntries/form/transaction-type-select/transaction-type-select.component.ts
new file mode 100644
index 0000000..bc0b81f
--- /dev/null
+++ b/src/app/accounting/journalEntries/form/transaction-type-select/transaction-type-select.component.ts
@@ -0,0 +1,96 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, forwardRef, Input, OnInit} from '@angular/core';
+import {Observable} from 'rxjs/Observable';
+import {ControlValueAccessor, FormControl, NG_VALUE_ACCESSOR} from '@angular/forms';
+import {TransactionType} from '../../../../services/accounting/domain/transaction-type.model';
+import {AccountingService} from '../../../../services/accounting/accounting.service';
+import {FetchRequest} from '../../../../services/domain/paging/fetch-request.model';
+import {TransactionTypePage} from '../../../../services/accounting/domain/transaction-type-page.model';
+
+const noop: () => void = () => {
+  // empty method
+};
+
+@Component({
+  providers: [
+    {provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => TransactionTypeSelectComponent), multi: true}
+  ],
+  selector: 'fims-transaction-type-select',
+  templateUrl: './transaction-type-select.component.html'
+})
+export class TransactionTypeSelectComponent implements ControlValueAccessor, OnInit {
+
+  private _onTouchedCallback: () => void = noop;
+
+  private _onChangeCallback: (_: any) => void = noop;
+
+  formControl: FormControl;
+
+  @Input() title: string;
+
+  @Input() required: boolean;
+
+  transactionTypes: Observable<TransactionType[]>;
+
+  constructor(private accountingService: AccountingService) {
+  }
+
+  ngOnInit(): void {
+    this.formControl = new FormControl('');
+
+    this.transactionTypes = this.formControl.valueChanges
+      .distinctUntilChanged()
+      .debounceTime(500)
+      .do(name => this.changeValue(name))
+      .filter(name => name)
+      .switchMap(name => this.onSearch(name));
+  }
+
+  changeValue(value: string): void {
+    this._onChangeCallback(value);
+  }
+
+  writeValue(value: any): void {
+    this.formControl.setValue(value);
+  }
+
+  registerOnChange(fn: any): void {
+    this._onChangeCallback = fn;
+  }
+
+  registerOnTouched(fn: any): void {
+    this._onTouchedCallback = fn;
+  }
+
+  onSearch(searchTerm?: string): Observable<TransactionType[]> {
+    const fetchRequest: FetchRequest = {
+      page: {
+        pageIndex: 0,
+        size: 5
+      },
+      searchTerm: searchTerm
+    };
+
+    return this.accountingService.fetchTransactionTypes(fetchRequest)
+      .map((transactionTypePage: TransactionTypePage) => transactionTypePage.transactionTypes);
+  }
+
+}
diff --git a/src/app/accounting/journalEntries/form/transaction-type-select/validator/transaction-type-exists.validator.ts b/src/app/accounting/journalEntries/form/transaction-type-select/validator/transaction-type-exists.validator.ts
new file mode 100644
index 0000000..62f8399
--- /dev/null
+++ b/src/app/accounting/journalEntries/form/transaction-type-select/validator/transaction-type-exists.validator.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 {AbstractControl, AsyncValidatorFn} from '@angular/forms';
+import {Observable} from 'rxjs/Observable';
+import {AccountingService} from '../../../../../services/accounting/accounting.service';
+import {isEmptyInputValue, isString} from '../../../../../common/validator/validators';
+
+const invalid = Observable.of({
+  invalidTransactionType: true
+});
+
+export function transactionTypeExists(accountingService: AccountingService): AsyncValidatorFn {
+  return (control: AbstractControl): Observable<any> => {
+    if (!control.dirty || isEmptyInputValue(control.value)) {
+      return Observable.of(null);
+    }
+
+    if (isString(control.value) && control.value.trim().length === 0) {
+      return invalid;
+    }
+
+    return accountingService.findTransactionType(control.value, true)
+      .map(account => null)
+      .catch(() => invalid);
+  };
+}
diff --git a/src/app/accounting/journalEntries/journal-entry.list.component.html b/src/app/accounting/journalEntries/journal-entry.list.component.html
new file mode 100644
index 0000000..91bf533
--- /dev/null
+++ b/src/app/accounting/journalEntries/journal-entry.list.component.html
@@ -0,0 +1,96 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<fims-layout-card-over title="{{'Journal entries' | translate}}" [navigateBackTo]="['../']">
+  <form [formGroup]="form" layout="row">
+    <mat-form-field layout-margin>
+      <input matInput type="date" placeholder="{{'Start date' | translate}}" formControlName="startDate">
+    </mat-form-field>
+    <mat-form-field layout-margin>
+      <input matInput type="date" placeholder="{{'End date' | translate}}" formControlName="endDate">
+      <mat-error *ngIf="form.hasError('rangeInvalid')" translate>Invalid date range</mat-error>
+    </mat-form-field>
+    <fims-text-input [form]="form" controlName="account" placeholder="{{'Account' | translate}}"></fims-text-input>
+    <fims-text-input type="number" [form]="form" controlName="amount" placeholder="{{'Amount' | translate}}"></fims-text-input>
+    <span>
+      <button layout-margin mat-button mat-icon-button (click)="fetchJournalEntries()" [disabled]="!form.valid"><mat-icon>search</mat-icon></button>
+    </span>
+  </form>
+  <fims-two-column-layout>
+    <mat-nav-list left>
+      <a mat-list-item *ngFor="let entry of (journalEntries$ | async)" (click)="select(entry)" [ngClass]="journalEntry == entry ? 'active' : false">
+        <mat-icon matListAvatar>assignment</mat-icon>
+        <h4 matLine translate>{{entry.transactionDate | date:'short'}}</h4>
+        <p matLine translate>{{entry.transactionType}}</p>
+        <p matLine translate>Amount: {{sumDebtors(entry.debtors) | number:numberFormat}}</p>
+        <ng-container [ngSwitch]="entry.state">
+          <mat-icon class="tc-amber-800" *ngSwitchCase="'PENDING'">more_horiz</mat-icon>
+          <mat-icon class="tc-green-800" *ngSwitchCase="'PROCESSED'">done</mat-icon>
+        </ng-container>
+      </a>
+    </mat-nav-list>
+    <mat-card right *ngIf="journalEntry">
+      <mat-list>
+        <mat-list-item>
+          <h3 matLine translate>Clerk</h3>
+          <p matLine>{{journalEntry?.clerk}}</p>
+        </mat-list-item>
+        <mat-list-item>
+          <h3 matLine translate>Message</h3>
+          <p matLine>{{journalEntry?.message}}</p>
+        </mat-list-item>
+        <mat-list-item>
+          <h3 matLine translate>Note</h3>
+          <p matLine>{{journalEntry?.note}}</p>
+        </mat-list-item>
+      </mat-list>
+      <div class="mat-content">
+        <table td-data-table>
+          <thead>
+            <tr td-data-table-column-row>
+              <th td-data-table-column translate>
+                Debit
+              </th>
+              <th td-data-table-column translate>
+                Credit
+              </th>
+              <th td-data-table-column translate>
+                Amount
+              </th>
+            </tr>
+          </thead>
+          <tbody>
+            <tr td-data-table-row *ngFor="let debit of journalEntry?.debtors">
+              <td td-data-table-cell>
+                {{debit.accountNumber}}
+              </td>
+              <td td-data-table-cell></td>
+              <td td-data-table-cell>{{debit.amount | number:numberFormat}}</td>
+            </tr>
+            <tr td-data-table-row *ngFor="let credit of journalEntry?.creditors">
+              <td td-data-table-cell></td>
+              <td td-data-table-cell>{{credit.accountNumber}}</td>
+              <td td-data-table-cell>{{credit.amount | number:numberFormat}}</td>
+            </tr>
+          </tbody>
+        </table>
+      </div>
+    </mat-card>
+  </fims-two-column-layout>
+
+</fims-layout-card-over>
+<fims-fab-button title="{{'Add Journal Entry' | translate}}" icon="add" [link]="['create']" [permission]="{ id: 'accounting_journals', accessLevel: 'CHANGE'}"></fims-fab-button>
diff --git a/src/app/accounting/journalEntries/journal-entry.list.component.ts b/src/app/accounting/journalEntries/journal-entry.list.component.ts
new file mode 100644
index 0000000..6fc918d
--- /dev/null
+++ b/src/app/accounting/journalEntries/journal-entry.list.component.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 {Component, OnInit} from '@angular/core';
+import {FormBuilder, FormGroup, Validators} from '@angular/forms';
+import {todayAsISOString, toShortISOString} from '../../services/domain/date.converter';
+import {FimsValidators} from '../../common/validator/validators';
+import * as fromAccounting from '../store';
+import {SEARCH} from '../store/ledger/journal-entry/journal-entry.actions';
+import {Observable} from 'rxjs/Observable';
+import {AccountingStore} from '../store/index';
+import {DatePipe} from '@angular/common';
+import {JournalEntry} from '../../services/accounting/domain/journal-entry.model';
+import {Debtor} from '../../services/accounting/domain/debtor.model';
+
+@Component({
+  templateUrl: './journal-entry.list.component.html',
+  providers: [DatePipe]
+})
+export class JournalEntryListComponent implements OnInit {
+
+  numberFormat = '1.2-2';
+
+  form: FormGroup;
+
+  journalEntries$: Observable<JournalEntry[]>;
+
+  journalEntry: JournalEntry;
+
+  constructor(private formBuilder: FormBuilder, private store: AccountingStore) {
+  }
+
+  ngOnInit(): void {
+    this.journalEntries$ = this.store.select(fromAccounting.getJournalEntriesSearchResult)
+      .do(journalEntries => this.select(journalEntries.length > 0 ? journalEntries[0] : undefined));
+
+    const today = todayAsISOString();
+
+    this.form = this.formBuilder.group({
+      'startDate': [today, [Validators.required]],
+      'endDate': [today, [Validators.required]],
+      'account': [],
+      'amount': [],
+    }, {validator: FimsValidators.matchRange('startDate', 'endDate')});
+
+    this.fetchJournalEntries();
+  }
+
+  fetchJournalEntries(): void {
+    const startDate = toShortISOString(this.form.get('startDate').value);
+    const endDate = toShortISOString(this.form.get('endDate').value);
+    const account = this.form.get('account').value;
+    const amount = this.form.get('amount').value;
+
+    this.store.dispatch({
+      type: SEARCH, payload: {
+        startDate,
+        endDate,
+        account,
+        amount: amount ? amount.toFixed(2) : undefined
+      }
+    });
+  }
+
+  select(journalEntry: JournalEntry): void {
+    this.journalEntry = journalEntry;
+  }
+
+  sumDebtors(debtors: Debtor[]): number {
+    return debtors.reduce((sum, debtor) => {
+      return sum + parseFloat(debtor.amount);
+    }, 0);
+  }
+
+}
diff --git a/src/app/accounting/ledger-exists.guard.ts b/src/app/accounting/ledger-exists.guard.ts
new file mode 100644
index 0000000..ac41526
--- /dev/null
+++ b/src/app/accounting/ledger-exists.guard.ts
@@ -0,0 +1,66 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 {Store} from '@ngrx/store';
+import {ActivatedRouteSnapshot, CanActivate, RouterStateSnapshot} from '@angular/router';
+import {Injectable} from '@angular/core';
+import * as fromAccounting from './store';
+import {Observable} from 'rxjs/Observable';
+import {LoadAction} from './store/ledger/ledger.actions';
+import {of} from 'rxjs/observable/of';
+import {AccountingService} from '../services/accounting/accounting.service';
+import {ExistsGuardService} from '../common/guards/exists-guard';
+
+@Injectable()
+export class LedgerExistsGuard implements CanActivate {
+
+  constructor(private store: Store<fromAccounting.State>,
+              private accountingService: AccountingService,
+              private existsGuardService: ExistsGuardService) {}
+
+  hasLedgerInStore(id: string): Observable<boolean> {
+    const timestamp$ = this.store.select(fromAccounting.getLedgersLoadedAt)
+      .map(loadedAt => loadedAt[id]);
+
+    return this.existsGuardService.isWithinExpiry(timestamp$);
+  }
+
+  hasLedgerInApi(id: string): Observable<boolean> {
+    const findLedger$ = this.accountingService.findLedger(id)
+      .map(ledgerEntity => new LoadAction(ledgerEntity))
+      .do((action: LoadAction) => this.store.dispatch(action))
+      .map(ledger => !!ledger);
+
+    return this.existsGuardService.routeTo404OnError(findLedger$);
+  }
+
+  hasLedger(id: string): Observable<boolean> {
+    return this.hasLedgerInStore(id)
+      .switchMap(inStore => {
+        if (inStore) {
+          return of(inStore);
+        }
+
+        return this.hasLedgerInApi(id);
+      });
+  }
+
+  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
+    return this.hasLedger(route.params['id']);
+  }
+}
diff --git a/src/app/accounting/ledger.resolver.ts b/src/app/accounting/ledger.resolver.ts
new file mode 100644
index 0000000..d68b67a
--- /dev/null
+++ b/src/app/accounting/ledger.resolver.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 {Injectable} from '@angular/core';
+import {ActivatedRouteSnapshot, Resolve, RouterStateSnapshot} from '@angular/router';
+import {Ledger} from '../services/accounting/domain/ledger.model';
+import {AccountingService} from '../services/accounting/accounting.service';
+import {Observable} from 'rxjs/Observable';
+
+@Injectable()
+export class LedgerResolver implements Resolve<Ledger> {
+
+  constructor(private accountingService: AccountingService) {}
+
+  resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<Ledger> {
+    return this.accountingService.findLedger(route.params['id']);
+  }
+}
diff --git a/src/app/accounting/payroll/form/create.form.component.html b/src/app/accounting/payroll/form/create.form.component.html
new file mode 100644
index 0000000..1450c54
--- /dev/null
+++ b/src/app/accounting/payroll/form/create.form.component.html
@@ -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.
+-->
+
+<fims-layout-card-over title="{{'Create new payroll' | translate}}">
+  <fims-payroll-form #form
+                     (onSave)="onSave($event)"
+                     (onCancel)="onCancel()">
+  </fims-payroll-form>
+</fims-layout-card-over>
diff --git a/src/app/accounting/payroll/form/create.form.component.ts b/src/app/accounting/payroll/form/create.form.component.ts
new file mode 100644
index 0000000..ef52820
--- /dev/null
+++ b/src/app/accounting/payroll/form/create.form.component.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 {Component} from '@angular/core';
+import {AccountingStore} from '../../store/index';
+import {PayrollCollectionSheet} from '../../../services/payroll/domain/payroll-collection-sheet.model';
+import {CREATE} from '../../store/payroll/payroll-collection.actions';
+import {ActivatedRoute, Router} from '@angular/router';
+
+@Component({
+  templateUrl: './create.form.component.html'
+})
+export class CreatePayrollFormComponent {
+
+  constructor(private store: AccountingStore, private router: Router, private route: ActivatedRoute) {}
+
+  onSave(sheet: PayrollCollectionSheet): void {
+    this.store.dispatch({ type: CREATE, payload: {
+      sheet,
+      activatedRoute: this.route
+    }});
+  }
+
+  onCancel(): void {
+    this.navigateAway();
+  }
+
+  navigateAway(): void {
+    this.router.navigate(['../'], { relativeTo: this.route });
+  }
+}
diff --git a/src/app/accounting/payroll/form/form.component.html b/src/app/accounting/payroll/form/form.component.html
new file mode 100644
index 0000000..f90bac2
--- /dev/null
+++ b/src/app/accounting/payroll/form/form.component.html
@@ -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.
+-->
+
+<td-steps mode="'vertical'">
+  <td-step #detailsStep label="{{'Payments' | translate}}" [state]="form.valid ? 'complete' : form.pristine ? 'none' : 'required'">
+    <form [formGroup]="form">
+      <fims-account-select title="{{'From account' | translate}}" formControlName="sourceAccountNumber">
+        <ng-container *ngIf="!form.get('sourceAccountNumber').pristine && form.get('sourceAccountNumber').hasError('required')" translate>
+          Required
+        </ng-container>
+        <ng-container *ngIf="form.get('sourceAccountNumber').hasError('invalidAccount')" translate>
+          Invalid account
+        </ng-container>
+      </fims-account-select>
+      <div layout-gt-xs="column" layout-margin formArrayName="payments">
+        <h4 translate>Payments</h4>
+        <div *ngFor="let payment of payments; let i=index" layout="row" [formGroupName]="i">
+          <fims-customer-select title="Member" formControlName="customerIdentifier">
+            <ng-container *ngIf="!payment.get('customerIdentifier').pristine && payment.get('customerIdentifier').hasError('required')" translate>
+              Required
+            </ng-container>
+            <ng-container *ngIf="payment.get('customerIdentifier').hasError('invalidCustomer')" translate>
+              Invalid member or has no payroll created
+            </ng-container>
+          </fims-customer-select>
+          <fims-text-input [form]="payment" controlName="employer" placeholder="{{'Employer' | translate}}"></fims-text-input>
+          <fims-number-input placeholder="Salary" [form]="payment" controlName="salary"></fims-number-input>
+          <button mat-button (click)="removePayment(i)" [disabled]="i === 0">{{'Remove' | translate}}</button>
+        </div>
+        <button mat-button (click)="addPayment()">{{'Add payment' | translate}}</button>
+      </div>
+    </form>
+    <ng-template td-step-actions>
+      <fims-form-final-action
+        [resourceName]="'PAYMENTS'"
+        [editMode]="editMode"
+        [disabled]="!form.valid"
+        (onCancel)="cancel()"
+        (onSave)="save()">
+      </fims-form-final-action>
+    </ng-template>
+  </td-step>
+</td-steps>
diff --git a/src/app/accounting/payroll/form/form.component.ts b/src/app/accounting/payroll/form/form.component.ts
new file mode 100644
index 0000000..726cc7f
--- /dev/null
+++ b/src/app/accounting/payroll/form/form.component.ts
@@ -0,0 +1,99 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, OnInit, Output, ViewChild} from '@angular/core';
+import {PayrollCollectionSheet} from '../../../services/payroll/domain/payroll-collection-sheet.model';
+import {AbstractControl, FormArray, FormBuilder, FormGroup, Validators} from '@angular/forms';
+import {accountExists} from '../../../common/validator/account-exists.validator';
+import {AccountingService} from '../../../services/accounting/accounting.service';
+import {CustomerService} from '../../../services/customer/customer.service';
+import {FimsValidators} from '../../../common/validator/validators';
+import {TdStepComponent} from '@covalent/core';
+import {customerWithConfigExists} from './validator/customer-payroll-exists.validator';
+import {PayrollService} from '../../../services/payroll/payroll.service';
+
+@Component({
+  selector: 'fims-payroll-form',
+  templateUrl: './form.component.html'
+})
+export class PayrollFormComponent implements OnInit {
+
+  form: FormGroup;
+
+  @ViewChild('detailsStep') detailsStep: TdStepComponent;
+
+  @Output() onSave = new EventEmitter<PayrollCollectionSheet>();
+
+  @Output() onCancel = new EventEmitter<void>();
+
+  constructor(private formBuilder: FormBuilder, private accountingService: AccountingService, private customerService: CustomerService,
+              private payrollService: PayrollService) {
+    this.form = this.formBuilder.group({
+      sourceAccountNumber: ['', [Validators.required], accountExists(accountingService)],
+      payments: this.initPayments()
+    });
+  }
+
+  ngOnInit(): void {
+    this.detailsStep.open();
+
+    this.addPayment();
+  }
+
+  save(): void {
+    const sheet: PayrollCollectionSheet = {
+      sourceAccountNumber: this.form.get('sourceAccountNumber').value,
+      payrollPayments: this.form.get('payments').value
+    };
+
+    this.onSave.emit(sheet);
+  }
+
+  cancel(): void {
+    this.onCancel.emit();
+  }
+
+  private initPayments(): FormArray {
+    const formControls: FormGroup[] = [];
+    return this.formBuilder.array(formControls);
+  }
+
+  private initPayment(): FormGroup {
+    return this.formBuilder.group({
+      customerIdentifier: ['', [Validators.required], customerWithConfigExists(this.customerService, this.payrollService)],
+      employer: ['', [Validators.required]],
+      salary: ['', [Validators.required, FimsValidators.minValue(0.001), FimsValidators.maxValue(9999999999.99999)]]
+    });
+  }
+
+  addPayment(): void {
+    const commands: FormArray = this.form.get('payments') as FormArray;
+    commands.push(this.initPayment());
+  }
+
+  removePayment(index: number): void {
+    const commands: FormArray = this.form.get('payments') as FormArray;
+    commands.removeAt(index);
+  }
+
+  get payments(): AbstractControl[] {
+    const commands: FormArray = this.form.get('payments') as FormArray;
+    return commands.controls;
+  }
+}
diff --git a/src/app/accounting/payroll/form/validator/customer-payroll-exists.validator.ts b/src/app/accounting/payroll/form/validator/customer-payroll-exists.validator.ts
new file mode 100644
index 0000000..ccd4937
--- /dev/null
+++ b/src/app/accounting/payroll/form/validator/customer-payroll-exists.validator.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 {AbstractControl, AsyncValidatorFn} from '@angular/forms';
+import {Observable} from 'rxjs/Observable';
+import {CustomerService} from '../../../../services/customer/customer.service';
+import {isString} from '../../../../common/validator/validators';
+import {PayrollService} from '../../../../services/payroll/payroll.service';
+
+const invalid = Observable.of({
+  invalidCustomer: true
+});
+
+export function customerWithConfigExists(customerService: CustomerService, payrollService: PayrollService): AsyncValidatorFn {
+  return (control: AbstractControl): Observable<any> => {
+    if (!control.dirty || !control.value || control.value.length === 0) {
+      return Observable.of(null);
+    }
+
+    if (isString(control.value) && control.value.trim().length === 0) {
+      return invalid;
+    }
+
+    return customerService.getCustomer(control.value, true)
+      .switchMap(customer => payrollService.findPayrollConfiguration(customer.identifier, true))
+      .map(config => null)
+      .catch(() => invalid);
+  };
+}
diff --git a/src/app/accounting/payroll/payments.list.component.html b/src/app/accounting/payroll/payments.list.component.html
new file mode 100644
index 0000000..82ff419
--- /dev/null
+++ b/src/app/accounting/payroll/payments.list.component.html
@@ -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.
+-->
+
+<fims-layout-card-over title="Payments" [navigateBackTo]="['../../']" *ngIf="selectedPayrollCollection$ | async as collection">
+  <fims-data-table flex
+                   (onActionCellClick)="rowSelect($event)"
+                   (onFetch)="fetchPayments(collection.identifier, $event)"
+                   [columns]="columns"
+                   [data]="paymentData$ | async"
+                   [pageable]="true"
+                   [actionColumn]="false">
+  </fims-data-table>
+</fims-layout-card-over>
diff --git a/src/app/accounting/payroll/payments.list.component.ts b/src/app/accounting/payroll/payments.list.component.ts
new file mode 100644
index 0000000..45a6b5b
--- /dev/null
+++ b/src/app/accounting/payroll/payments.list.component.ts
@@ -0,0 +1,75 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, OnDestroy} from '@angular/core';
+import {Observable} from 'rxjs/Observable';
+import {TableData} from '../../common/data-table/data-table.component';
+import * as fromAccounting from '../store/index';
+import {AccountingStore} from '../store/index';
+import {PaymentSearchPayload, SEARCH} from '../store/payroll/payment.actions';
+import {FetchRequest} from '../../services/domain/paging/fetch-request.model';
+import {PayrollCollectionHistory} from '../../services/payroll/domain/payroll-collection-history.model';
+import {SelectAction} from '../store/payroll/payroll-collection.actions';
+import {ActivatedRoute} from '@angular/router';
+import {Subscription} from 'rxjs/Subscription';
+
+@Component({
+  templateUrl: './payments.list.component.html'
+})
+export class PaymentsListComponent implements OnDestroy {
+
+  private actionsSubscription: Subscription;
+
+  selectedPayrollCollection$: Observable<PayrollCollectionHistory>;
+
+  paymentData$: Observable<TableData>;
+
+  columns: any[] = [
+    { name: 'customerIdentifier', label: 'Member ID' },
+    { name: 'employer', label: 'Employer' },
+    { name: 'salary', label: 'Salary' }
+  ];
+
+  constructor(private route: ActivatedRoute, private store: AccountingStore) {
+    this.actionsSubscription = this.route.params
+      .map(params => new SelectAction(params['id']))
+      .subscribe(this.store);
+
+    this.paymentData$ = this.store.select(fromAccounting.getPayrollPaymentSearchResults);
+
+    this.selectedPayrollCollection$ = this.store.select(fromAccounting.getSelectedPayrollCollection)
+      .do((payrollCollection: PayrollCollectionHistory) => this.fetchPayments(payrollCollection.identifier));
+  }
+
+  ngOnDestroy(): void {
+    this.actionsSubscription.unsubscribe();
+  }
+
+  fetchPayments(payrollIdentifier: string, fetchRequest?: FetchRequest): void {
+    const payload: PaymentSearchPayload = {
+      payrollIdentifier,
+      fetchRequest
+    };
+
+    this.store.dispatch({
+      type: SEARCH,
+      payload
+    });
+  }
+
+}
diff --git a/src/app/accounting/payroll/payroll.list.component.html b/src/app/accounting/payroll/payroll.list.component.html
new file mode 100644
index 0000000..9b952a5
--- /dev/null
+++ b/src/app/accounting/payroll/payroll.list.component.html
@@ -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.
+-->
+
+<fims-layout-card-over title="Manage payrolls" [navigateBackTo]="['../']">
+  <fims-data-table flex
+                   (onActionCellClick)="rowSelect($event)"
+                   [columns]="columns"
+                   [data]="payrollData$ | async">
+  </fims-data-table>
+</fims-layout-card-over>
+<fims-fab-button title="{{'Create payroll' | translate}}" icon="add" [link]="['create']" [permission]="{ id: 'payroll_distribution', accessLevel: 'CHANGE'}"></fims-fab-button>
diff --git a/src/app/accounting/payroll/payroll.list.component.ts b/src/app/accounting/payroll/payroll.list.component.ts
new file mode 100644
index 0000000..cd39a27
--- /dev/null
+++ b/src/app/accounting/payroll/payroll.list.component.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 {Component, OnInit} from '@angular/core';
+import * as fromAccouting from '../store/index';
+import {AccountingStore} from '../store/index';
+import {Observable} from 'rxjs/Observable';
+import {TableData} from '../../common/data-table/data-table.component';
+import {PayrollCollectionHistory} from '../../services/payroll/domain/payroll-collection-history.model';
+import {ActivatedRoute, Router} from '@angular/router';
+import {DatePipe} from '@angular/common';
+import {LOAD_ALL_COLLECTIONS} from '../store/payroll/payroll-collection.actions';
+
+@Component({
+  providers: [DatePipe],
+  templateUrl: './payroll.list.component.html'
+})
+export class PayrollListComponent implements OnInit {
+
+  payrollData$: Observable<TableData>;
+
+  columns: any[] = [
+    { name: 'createdBy', label: 'Created by' },
+    { name: 'createdOn', label: 'Created on', format: value => this.datePipe.transform(value, 'short') },
+    { name: 'sourceAccountNumber', label: 'Account number' }
+  ];
+
+  constructor(private router: Router, private route: ActivatedRoute, private datePipe: DatePipe,
+              private store: AccountingStore) {
+    this.payrollData$ = this.store.select(fromAccouting.getAllPayrollCollectionEntities)
+      .map((collections: PayrollCollectionHistory[]) => ({
+        data: collections,
+        totalElements: collections.length,
+        totalPages: 1
+      }));
+  }
+
+  ngOnInit(): void {
+    this.store.dispatch({
+      type: LOAD_ALL_COLLECTIONS
+    });
+  }
+
+  rowSelect(collection: PayrollCollectionHistory): void {
+    this.router.navigate(['payments', collection.identifier], { relativeTo: this.route });
+  }
+}
diff --git a/src/app/accounting/payrolls/payrolls.component.html b/src/app/accounting/payrolls/payrolls.component.html
deleted file mode 100644
index 9f8be54..0000000
--- a/src/app/accounting/payrolls/payrolls.component.html
+++ /dev/null
@@ -1,42 +0,0 @@
-<div class="main-div mat-elevation-z2">
-  <h3 class="heading">Payrolls</h3>
-  <mat-divider></mat-divider>
-  <div class="fineract-button">
-  <a mat-raised-button  color="primary" [routerLink]="['/navbar/add_payroll']">
-    <mat-icon>add</mat-icon>Add Payroll</a>
-    </div>
-  <mat-form-field>
-    <input matInput (keyup)="applyFilter($event.target.value)" placeholder="Filter by name/short name">
-  </mat-form-field>
-  
-<br>
-<mat-divider></mat-divider>
-
-<mat-table #table [dataSource]="dataSource">
-
-  <!-- Position Column -->
-  <ng-container matColumnDef="createdby">
-    <mat-header-cell *matHeaderCellDef>Created by</mat-header-cell>
-    <mat-cell *matCellDef="let element">
-      <a routerLink="{{element.id}}">{{element.createdby}}</a>
-    </mat-cell>
-  </ng-container>
-
-  <!-- Name Column -->
-  <ng-container matColumnDef="createdon">
-    <mat-header-cell *matHeaderCellDef>Created on</mat-header-cell>
-    <mat-cell *matCellDef="let element"> {{element.createdon}} </mat-cell>
-  </ng-container>
-
-  <ng-container matColumnDef="accountno">
-      <mat-header-cell *matHeaderCellDef>Account number</mat-header-cell>
-      <mat-cell *matCellDef="let element"> {{element.accountno}} </mat-cell>
-    </ng-container>
-
-  <mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
-  <mat-row *matRowDef="let row; columns: displayedColumns;"></mat-row>
-</mat-table>
-  
-  
-</div>
-  
\ No newline at end of file
diff --git a/src/app/accounting/payrolls/payrolls.component.scss b/src/app/accounting/payrolls/payrolls.component.scss
deleted file mode 100644
index 0961a8e..0000000
--- a/src/app/accounting/payrolls/payrolls.component.scss
+++ /dev/null
@@ -1,18 +0,0 @@
-.my-div{
-    margin-left: 2%;
-    margin-right:2%;
-    border-radius: 5px 5px 5px 5px;
-    background-color: #e6e6ff;
-    min-height: 100%;
-   
-  }
-  table {
-      width: 100%;
-    }
-    
-    .mat-form-field {
-      font-size: 14px;
-      width: 100%;
-    }
-    
-    
\ No newline at end of file
diff --git a/src/app/accounting/payrolls/payrolls.component.spec.ts b/src/app/accounting/payrolls/payrolls.component.spec.ts
deleted file mode 100644
index c5a27ea..0000000
--- a/src/app/accounting/payrolls/payrolls.component.spec.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-import { async, ComponentFixture, TestBed } from '@angular/core/testing';
-
-import { PayrollsComponent } from './payrolls.component';
-
-describe('PayrollsComponent', () => {
-  let component: PayrollsComponent;
-  let fixture: ComponentFixture<PayrollsComponent>;
-
-  beforeEach(async(() => {
-    TestBed.configureTestingModule({
-      declarations: [ PayrollsComponent ]
-    })
-    .compileComponents();
-  }));
-
-  beforeEach(() => {
-    fixture = TestBed.createComponent(PayrollsComponent);
-    component = fixture.componentInstance;
-    fixture.detectChanges();
-  });
-
-  it('should create', () => {
-    expect(component).toBeTruthy();
-  });
-});
diff --git a/src/app/accounting/payrolls/payrolls.component.ts b/src/app/accounting/payrolls/payrolls.component.ts
deleted file mode 100644
index 0049e46..0000000
--- a/src/app/accounting/payrolls/payrolls.component.ts
+++ /dev/null
@@ -1,34 +0,0 @@
-import { Component, OnInit } from '@angular/core';
-import {MatTableDataSource} from '@angular/material';
-
-@Component({
-  selector: 'app-payrolls',
-  templateUrl: './payrolls.component.html',
-  styleUrls: ['./payrolls.component.scss']
-})
-export class PayrollsComponent implements OnInit {
-  displayedColumns = ['createdby','createdon','accountno'];
-  dataSource = new MatTableDataSource(ELEMENT_DATA);
-
-  applyFilter(filterValue: string) {
-    filterValue = filterValue.trim(); // Remove whitespace
-    filterValue = filterValue.toLowerCase(); // MatTableDataSource defaults to lowercase matches
-    this.dataSource.filter = filterValue;
-  }
-
-  constructor() { }
-
-  ngOnInit() {
-  }
-
-}
-export interface Element {
-  createdby: string;
-  createdon: Date;
-  accountno: number;
-  
-}
-
-const ELEMENT_DATA: Element[] = [
-  
-];
diff --git a/src/app/accounting/status/status.component.html b/src/app/accounting/status/status.component.html
new file mode 100644
index 0000000..2fe2260
--- /dev/null
+++ b/src/app/accounting/status/status.component.html
@@ -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.
+-->
+
+<fims-layout-card-over title="{{'Change state' | translate}}" [navigateBackTo]="['../']">
+  <div layout="row" layout-align="start start" layout-margin>
+    <div layout="column" flex="100">
+      <div layout="row" layout-align="start start" *ngFor="let command of statusCommands">
+          <div layout="column" flex="100">
+            <div layout="row" layout-align="start center">
+              <mat-form-field flex>
+                <textarea matInput placeholder="{{'Enter your comment here...' | translate}}" [(ngModel)]="command.comment"></textarea>
+              </mat-form-field>
+              <button mat-raised-button style="margin-left: 10px;" color="accent" (click)="executeCommand(command)">{{command.action}}</button>
+            </div>
+          </div>
+      </div>
+    </div>
+  </div>
+</fims-layout-card-over>
+
diff --git a/src/app/accounting/status/status.component.ts b/src/app/accounting/status/status.component.ts
new file mode 100644
index 0000000..edec50c
--- /dev/null
+++ b/src/app/accounting/status/status.component.ts
@@ -0,0 +1,63 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, OnInit} from '@angular/core';
+import {ActivatedRoute, Params} from '@angular/router';
+import {AccountCommandAction} from '../../services/accounting/domain/account-command-action.model';
+import {AccountCommand} from '../../services/accounting/domain/account-command.model';
+import {AccountingStore} from '../store/index';
+import {EXECUTE_COMMAND} from '../store/account/task/task.actions';
+
+interface StatusCommand {
+  action: AccountCommandAction;
+  comment?: string;
+}
+
+@Component({
+  templateUrl: './status.component.html'
+})
+export class AccountStatusComponent implements OnInit {
+
+  private accountIdentifier: string;
+
+  statusCommands: StatusCommand[] = [
+    { action: 'LOCK' },
+    { action: 'UNLOCK' },
+    { action: 'CLOSE' },
+    { action: 'REOPEN' }
+  ];
+
+  constructor(private route: ActivatedRoute, private store: AccountingStore) {}
+
+  ngOnInit(): void {
+    this.route.params.subscribe((params: Params) => this.accountIdentifier = params['id']);
+  }
+
+  executeCommand(statusCommand: StatusCommand): void {
+    const command: AccountCommand = {
+      comment: statusCommand.comment,
+      action: statusCommand.action
+    };
+    this.store.dispatch({ type: EXECUTE_COMMAND, payload: {
+      accountId: this.accountIdentifier,
+      command: command,
+      activatedRoute: this.route
+    } });
+  }
+
+}
diff --git a/src/app/accounting/store/account/account.actions.ts b/src/app/accounting/store/account/account.actions.ts
new file mode 100644
index 0000000..ac10027
--- /dev/null
+++ b/src/app/accounting/store/account/account.actions.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 {Action} from '@ngrx/store';
+import {type} from '../../../store/util';
+import {Account} from '../../../services/accounting/domain/account.model';
+import {Error} from '../../../services/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('[Account] Load');
+export const SELECT = type('[Account] Select');
+
+export const CREATE = type('[Account] Create');
+export const CREATE_SUCCESS = type('[Account] Create Success');
+export const CREATE_FAIL = type('[Account] Create Fail');
+
+export const UPDATE = type('[Account] Update');
+export const UPDATE_SUCCESS = type('[Account] Update Success');
+export const UPDATE_FAIL = type('[Account] Update Fail');
+
+export const DELETE = type('[Account] Delete');
+export const DELETE_SUCCESS = type('[Account] Delete Success');
+export const DELETE_FAIL = type('[Account] Delete Fail');
+
+export const RESET_FORM = type('[Account] Reset Form');
+
+export interface AccountRoutePayload extends RoutePayload {
+  account: Account;
+}
+
+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 CreateAccountAction implements Action {
+  readonly type = CREATE;
+
+  constructor(public payload: AccountRoutePayload) { }
+}
+
+export class CreateAccountSuccessAction implements Action {
+  readonly type = CREATE_SUCCESS;
+
+  constructor(public payload: CreateResourceSuccessPayload) { }
+}
+
+export class CreateAccountFailAction implements Action {
+  readonly type = CREATE_FAIL;
+
+  constructor(public payload: Error) { }
+}
+
+export class UpdateAccountAction implements Action {
+  readonly type = UPDATE;
+
+  constructor(public payload: AccountRoutePayload) { }
+}
+
+export class UpdateAccountSuccessAction implements Action {
+  readonly type = UPDATE_SUCCESS;
+
+  constructor(public payload: UpdateResourceSuccessPayload) { }
+}
+
+export class UpdateAccountFailAction implements Action {
+  readonly type = UPDATE_FAIL;
+
+  constructor(public payload: Error) { }
+}
+
+export class DeleteAccountAction implements Action {
+  readonly type = DELETE;
+
+  constructor(public payload: AccountRoutePayload) { }
+}
+
+export class DeleteAccountSuccessAction implements Action {
+  readonly type = DELETE_SUCCESS;
+
+  constructor(public payload: DeleteResourceSuccessPayload) { }
+}
+
+export class DeleteAccountFailAction implements Action {
+  readonly type = DELETE_FAIL;
+
+  constructor(public payload: Error) { }
+}
+
+export class ResetAccountFormAction implements Action {
+  readonly type = RESET_FORM;
+
+  constructor() {}
+}
+
+export type Actions
+  = LoadAction
+  | SelectAction
+  | CreateAccountAction
+  | CreateAccountSuccessAction
+  | CreateAccountFailAction
+  | UpdateAccountAction
+  | UpdateAccountSuccessAction
+  | UpdateAccountFailAction
+  | DeleteAccountAction
+  | DeleteAccountSuccessAction
+  | DeleteAccountFailAction
+  | ResetAccountFormAction;
diff --git a/src/app/accounting/store/account/accounts.reducer.spec.ts b/src/app/accounting/store/account/accounts.reducer.spec.ts
new file mode 100644
index 0000000..6260e85
--- /dev/null
+++ b/src/app/accounting/store/account/accounts.reducer.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 {reducer} from './accounts.reducer';
+import {ResourceState} from '../../../common/store/resource.reducer';
+import {ExecuteCommandPayload, ExecuteCommandSuccessAction} from './task/task.actions';
+import {AccountCommandAction} from '../../../services/accounting/domain/account-command-action.model';
+import {AccountState} from '../../../services/accounting/domain/account-state.model';
+
+describe('Accounts Reducer', () => {
+
+  function mockSuccessAction(action: AccountCommandAction): ExecuteCommandPayload {
+    return {
+      accountId: 'test',
+      command: {
+        action: action,
+        comment: 'test'
+      },
+      activatedRoute: null
+    };
+  }
+
+  function mockState(state: AccountState): ResourceState {
+    return {
+      ids: ['test'],
+      entities: {
+        'test': {
+          identifier: 'test',
+          name: 'test',
+          state: state
+        }
+      },
+      selectedId: null,
+      loadedAt: null
+    };
+  }
+
+  it('should open the account when reopened', () => {
+    const result = reducer(mockState('CLOSED'), new ExecuteCommandSuccessAction(mockSuccessAction('REOPEN')));
+
+    expect(result).toEqual(mockState('OPEN'));
+  });
+
+  it('should open the account when unlocked', () => {
+    const result = reducer(mockState('LOCKED'), new ExecuteCommandSuccessAction(mockSuccessAction('UNLOCK')));
+
+    expect(result).toEqual(mockState('OPEN'));
+  });
+
+  it('should lock the account when locked', () => {
+    const result = reducer(mockState('OPEN'), new ExecuteCommandSuccessAction(mockSuccessAction('LOCK')));
+
+    expect(result).toEqual(mockState('LOCKED'));
+  });
+
+  it('should close the account when closed', () => {
+    const result = reducer(mockState('OPEN'), new ExecuteCommandSuccessAction(mockSuccessAction('CLOSE')));
+
+    expect(result).toEqual(mockState('CLOSED'));
+  });
+
+});
diff --git a/src/app/accounting/store/account/accounts.reducer.ts b/src/app/accounting/store/account/accounts.reducer.ts
new file mode 100644
index 0000000..d25e220
--- /dev/null
+++ b/src/app/accounting/store/account/accounts.reducer.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 {ResourceState} from '../../../common/store/resource.reducer';
+import * as accounts from './account.actions';
+import * as accountTasks from './task/task.actions';
+import {AccountCommand} from '../../../services/accounting/domain/account-command.model';
+import {AccountState} from '../../../services/accounting/domain/account-state.model';
+import {Account} from '../../../services/accounting/domain/account.model';
+
+export const initialState: ResourceState = {
+  ids: [],
+  entities: {},
+  loadedAt: {},
+  selectedId: null,
+};
+
+export function reducer(state = initialState, action: accounts.Actions | accountTasks.Actions): ResourceState {
+
+  switch (action.type) {
+
+    case accountTasks.EXECUTE_COMMAND_SUCCESS: {
+      const payload = action.payload;
+
+      const accountId = payload.accountId;
+      const command: AccountCommand = payload.command;
+
+      const account: Account = state.entities[accountId];
+
+      let accountState: AccountState = null;
+
+      if (command.action === 'LOCK') {
+        accountState = 'LOCKED';
+      } else if (command.action === 'UNLOCK' || command.action === 'REOPEN') {
+        accountState = 'OPEN';
+      } else if (command.action === 'CLOSE') {
+        accountState = 'CLOSED';
+      }
+
+      account.state = accountState;
+
+      return {
+        ids: [...state.ids],
+        entities: Object.assign({}, state.entities, {
+          [account.identifier]: account
+        }),
+        loadedAt: state.loadedAt,
+        selectedId: state.selectedId
+      };
+    }
+
+    default: {
+      return state;
+    }
+  }
+}
diff --git a/src/app/accounting/store/account/effects/notification.effects.ts b/src/app/accounting/store/account/effects/notification.effects.ts
new file mode 100644
index 0000000..8d575b0
--- /dev/null
+++ b/src/app/accounting/store/account/effects/notification.effects.ts
@@ -0,0 +1,56 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 accountActions from '../account.actions';
+import {NotificationService, NotificationType} from '../../../../services/notification/notification.service';
+
+@Injectable()
+export class AccountNotificationEffects {
+
+  @Effect({ dispatch: false })
+  createAccountSuccess$: Observable<Action> = this.actions$
+    .ofType(accountActions.CREATE_SUCCESS, accountActions.UPDATE_SUCCESS)
+    .do(() => this.notificationService.send({
+      type: NotificationType.MESSAGE,
+      message: 'Account is going to be saved'
+    }));
+
+  @Effect({ dispatch: false })
+  deleteAccountSuccess$: Observable<Action> = this.actions$
+    .ofType(accountActions.DELETE_SUCCESS)
+    .do(() => this.notificationService.send({
+      type: NotificationType.MESSAGE,
+      message: 'Account is going to be deleted'
+    }));
+
+  @Effect({ dispatch: false })
+  deleteAccountFail$: Observable<Action> = this.actions$
+    .ofType(accountActions.DELETE_FAIL)
+    .do(() => this.notificationService.send({
+      type: NotificationType.ALERT,
+      title: 'Account can\'t be deleted',
+      message: 'Account has account entries'
+    }));
+
+  constructor(private actions$: Actions, private notificationService: NotificationService) {}
+}
+
diff --git a/src/app/accounting/store/account/effects/route.effects.ts b/src/app/accounting/store/account/effects/route.effects.ts
new file mode 100644
index 0000000..2bb544e
--- /dev/null
+++ b/src/app/accounting/store/account/effects/route.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 accountActions from '../account.actions';
+import {Router} from '@angular/router';
+
+@Injectable()
+export class AccountRouteEffects {
+
+  @Effect({ dispatch: false })
+  createAccountSuccess$: Observable<Action> = this.actions$
+    .ofType(accountActions.CREATE_SUCCESS)
+    .map(action => action.payload)
+    .do(payload => this.router.navigate(['../../'], { relativeTo: payload.activatedRoute }));
+
+  @Effect({ dispatch: false })
+  updateAccountSuccess$: Observable<Action> = this.actions$
+    .ofType(accountActions.UPDATE_SUCCESS)
+    .map(action => action.payload)
+    .do(payload => this.router.navigate(['../'], { relativeTo: payload.activatedRoute }));
+
+  @Effect({ dispatch: false })
+  deleteAccountSuccess$: Observable<Action> = this.actions$
+    .ofType(accountActions.DELETE_SUCCESS)
+    .map(action => action.payload)
+    .do(payload => this.router.navigate(['../../../ledgers/detail', payload.resource.ledger], { relativeTo: payload.activatedRoute }));
+
+  constructor(private actions$: Actions, private router: Router) { }
+}
diff --git a/src/app/accounting/store/account/effects/service.effects.ts b/src/app/accounting/store/account/effects/service.effects.ts
new file mode 100644
index 0000000..a7ea458
--- /dev/null
+++ b/src/app/accounting/store/account/effects/service.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 {Action} from '@ngrx/store';
+import {of} from 'rxjs/observable/of';
+import * as accountActions from '../account.actions';
+import {AccountingService} from '../../../../services/accounting/accounting.service';
+import {Observable} from 'rxjs/Observable';
+
+@Injectable()
+export class AccountApiEffects {
+
+  @Effect()
+  createAccount$: Observable<Action> = this.actions$
+    .ofType(accountActions.CREATE)
+    .map((action: accountActions.CreateAccountAction) => action.payload)
+    .mergeMap(payload =>
+      this.accountingService.createAccount(payload.account)
+        .map(() => new accountActions.CreateAccountSuccessAction({
+          resource: payload.account,
+          activatedRoute: payload.activatedRoute
+        }))
+        .catch(error => of(new accountActions.CreateAccountFailAction(error)))
+    );
+
+  @Effect()
+  updateAccount$: Observable<Action> = this.actions$
+    .ofType(accountActions.UPDATE)
+    .map((action: accountActions.UpdateAccountAction) => action.payload)
+    .mergeMap(payload =>
+        this.accountingService.modifyAccount(payload.account)
+          .map(() => new accountActions.UpdateAccountSuccessAction({
+            resource: payload.account,
+            activatedRoute: payload.activatedRoute
+          }))
+          .catch(error => of(new accountActions.UpdateAccountFailAction(error)))
+    );
+
+  @Effect()
+  deleteAccount$: Observable<Action> = this.actions$
+    .ofType(accountActions.DELETE)
+    .map((action: accountActions.DeleteAccountAction) => action.payload)
+    .mergeMap(payload =>
+      this.accountingService.deleteAccount(payload.account)
+        .map(() => new accountActions.DeleteAccountSuccessAction({
+          resource: payload.account,
+          activatedRoute: payload.activatedRoute
+        }))
+        .catch(error => of(new accountActions.DeleteAccountFailAction(error)))
+    );
+
+  constructor(private actions$: Actions, private accountingService: AccountingService) { }
+
+}
diff --git a/src/app/accounting/store/account/entries/effects/service.effect.ts b/src/app/accounting/store/account/entries/effects/service.effect.ts
new file mode 100644
index 0000000..15dad3f
--- /dev/null
+++ b/src/app/accounting/store/account/entries/effects/service.effect.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 {Action} from '@ngrx/store';
+import {Observable} from 'rxjs/Observable';
+import {Actions, Effect} from '@ngrx/effects';
+import * as accountEntryActions from '../entries.actions';
+import {Injectable} from '@angular/core';
+import {of} from 'rxjs/observable/of';
+import {AccountingService} from '../../../../../services/accounting/accounting.service';
+
+@Injectable()
+export class AccountEntryApiEffects {
+
+  @Effect()
+  loadAccountEntries$: Observable<Action> = this.actions$
+    .ofType(accountEntryActions.SEARCH)
+    .map((action: accountEntryActions.SearchAction) => action.payload)
+    .mergeMap(payload =>
+      this.accountingService.fetchAccountEntries(payload.accountId, payload.startDate, payload.endDate, payload.fetchRequest)
+        .map(accountEntryPage => new accountEntryActions.SearchCompleteAction(accountEntryPage))
+        .catch(() => of(new accountEntryActions.SearchCompleteAction({
+          accountEntries: [],
+          totalPages: 0,
+          totalElements: 0
+        })))
+    );
+
+  constructor(private actions$: Actions, private accountingService: AccountingService) { }
+}
diff --git a/src/app/accounting/store/account/entries/entries.actions.ts b/src/app/accounting/store/account/entries/entries.actions.ts
new file mode 100644
index 0000000..dd35376
--- /dev/null
+++ b/src/app/accounting/store/account/entries/entries.actions.ts
@@ -0,0 +1,47 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 {FetchRequest} from '../../../../services/domain/paging/fetch-request.model';
+import {AccountEntryPage} from '../../../../services/accounting/domain/account-entry-page.model';
+
+export const SEARCH = type('[Account Entry] Search');
+export const SEARCH_COMPLETE = type('[Account Entry] Search Complete');
+
+export interface SearchActionPayload {
+  accountId: string;
+  startDate: string;
+  endDate: string;
+  fetchRequest: FetchRequest;
+}
+
+export class SearchAction implements Action {
+  readonly type = SEARCH;
+
+  constructor(public payload: SearchActionPayload) { }
+}
+
+export class SearchCompleteAction implements Action {
+  readonly type = SEARCH_COMPLETE;
+
+  constructor(public payload: AccountEntryPage) { }
+}
+
+export type Actions = SearchAction
+  | SearchCompleteAction;
diff --git a/src/app/accounting/store/account/entries/search.reducer.ts b/src/app/accounting/store/account/entries/search.reducer.ts
new file mode 100644
index 0000000..9032bb6
--- /dev/null
+++ b/src/app/accounting/store/account/entries/search.reducer.ts
@@ -0,0 +1,87 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 entries from './entries.actions';
+import {AccountEntry} from '../../../../services/accounting/domain/account-entry.model';
+import {FetchRequest} from '../../../../services/domain/paging/fetch-request.model';
+
+export interface State {
+  entries: AccountEntry[];
+  startDate: string;
+  endDate: string;
+  totalPages: number;
+  totalElements: number;
+  loading: boolean;
+  fetchRequest: FetchRequest;
+}
+
+const initialState: State = {
+  entries: [],
+  startDate: null,
+  endDate: null,
+  totalPages: 0,
+  totalElements: 0,
+  loading: false,
+  fetchRequest: null
+};
+
+export function reducer(state = initialState, action: entries.Actions): State {
+
+  switch (action.type) {
+
+    case entries.SEARCH: {
+      const payload = action.payload;
+
+      return Object.assign({}, state, {
+        startDate: payload.startDate,
+        endDate: payload.endDate,
+        fetchRequest: payload.fetchRequest,
+        loading: true
+      });
+    }
+
+    case entries.SEARCH_COMPLETE: {
+      const entryPage = action.payload;
+
+      return {
+        entries: entryPage.accountEntries,
+        loading: false,
+        fetchRequest: state.fetchRequest,
+        totalElements: entryPage.totalElements,
+        totalPages: entryPage.totalPages,
+        startDate: state.startDate,
+        endDate: state.endDate
+      };
+    }
+
+    default: {
+      return state;
+    }
+  }
+}
+
+
+export const getEntries = (state: State) => state.entries;
+
+export const getFetchRequest = (state: State) => state.fetchRequest;
+
+export const getLoading = (state: State) => state.loading;
+
+export const getTotalPages = (state: State) => state.totalPages;
+
+export const getTotalElements = (state: State) => state.totalElements;
diff --git a/src/app/accounting/store/account/task/effects/notification.effects.ts b/src/app/accounting/store/account/task/effects/notification.effects.ts
new file mode 100644
index 0000000..0946ba1
--- /dev/null
+++ b/src/app/accounting/store/account/task/effects/notification.effects.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 {Injectable} from '@angular/core';
+import {Actions, Effect} from '@ngrx/effects';
+import {Observable} from 'rxjs/Observable';
+import {Action} from '@ngrx/store';
+import * as taskActions from '../task.actions';
+import {NotificationService, NotificationType} from '../../../../../services/notification/notification.service';
+
+@Injectable()
+export class AccountCommandNotificationEffects {
+
+  @Effect({ dispatch: false })
+  executeAccountCommandSuccess$: Observable<Action> = this.actions$
+    .ofType(taskActions.EXECUTE_COMMAND_SUCCESS)
+    .do(() => this.notificationService.send({
+      type: NotificationType.MESSAGE,
+      message: 'Command is going to be executed'
+    }));
+
+  constructor(private actions$: Actions, private notificationService: NotificationService) {}
+
+}
+
diff --git a/src/app/accounting/store/account/task/effects/route.effects.ts b/src/app/accounting/store/account/task/effects/route.effects.ts
new file mode 100644
index 0000000..9078955
--- /dev/null
+++ b/src/app/accounting/store/account/task/effects/route.effects.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 {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 taskActions from '../task.actions';
+
+@Injectable()
+export class AccountCommandRouteEffects {
+
+  @Effect({ dispatch: false })
+  executeCommandSuccess$: Observable<Action> = this.actions$
+    .ofType(taskActions.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/accounting/store/account/task/effects/service.effects.ts b/src/app/accounting/store/account/task/effects/service.effects.ts
new file mode 100644
index 0000000..8acba09
--- /dev/null
+++ b/src/app/accounting/store/account/task/effects/service.effects.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 {Actions, Effect} from '@ngrx/effects';
+import {Observable} from 'rxjs/Observable';
+import {Action} from '@ngrx/store';
+import {of} from 'rxjs/observable/of';
+import * as taskActions from '../task.actions';
+import {AccountingService} from '../../../../../services/accounting/accounting.service';
+
+@Injectable()
+export class AccountCommandApiEffects {
+
+  @Effect()
+  executeCommand: Observable<Action> = this.actions$
+    .ofType(taskActions.EXECUTE_COMMAND)
+    .map((action: taskActions.ExecuteCommandAction) => action.payload)
+    .mergeMap(payload =>
+      this.accountingService.accountCommand(payload.accountId, payload.command)
+        .map(() => new taskActions.ExecuteCommandSuccessAction(payload))
+        .catch((error) => of(new taskActions.ExecuteCommandFailAction(error)))
+    );
+
+  constructor(private actions$: Actions, private accountingService: AccountingService) { }
+
+}
diff --git a/src/app/accounting/store/account/task/task.actions.ts b/src/app/accounting/store/account/task/task.actions.ts
new file mode 100644
index 0000000..df4ccbd
--- /dev/null
+++ b/src/app/accounting/store/account/task/task.actions.ts
@@ -0,0 +1,53 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 {AccountCommand} from '../../../../services/accounting/domain/account-command.model';
+import {RoutePayload} from '../../../../common/store/route-payload';
+
+export const EXECUTE_COMMAND = type('[Account Command] Execute');
+export const EXECUTE_COMMAND_SUCCESS = type('[Account Command] Success');
+export const EXECUTE_COMMAND_FAIL = type('[Account Command] Fail');
+
+export interface ExecuteCommandPayload extends RoutePayload {
+  accountId: string;
+  command: AccountCommand;
+}
+
+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: Error) { }
+}
+
+export type Actions = ExecuteCommandAction
+  | ExecuteCommandSuccessAction
+  | ExecuteCommandFailAction;
diff --git a/src/app/accounting/store/cheques/cheque.actions.ts b/src/app/accounting/store/cheques/cheque.actions.ts
new file mode 100644
index 0000000..961525f
--- /dev/null
+++ b/src/app/accounting/store/cheques/cheque.actions.ts
@@ -0,0 +1,59 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 {createResourceActions} from '../../../common/store/action-creator/action-creator';
+import {type} from '../../../store/util';
+import {Action} from '@ngrx/store';
+import {ChequeProcessingCommand} from '../../../services/cheque/domain/cheque-processing-command';
+import {Error} from '../../../services/domain/error.model';
+import {FimsCheque} from '../../../services/cheque/domain/fims-cheque.model';
+
+export const ChequeCRUDActions = createResourceActions<FimsCheque>('Cheque');
+
+export const PROCESS = type('[Cheque] Process');
+export const PROCESS_SUCCESS = type('[Cheque] Process Success');
+export const PROCESS_FAIL = type('[Cheque] Process Fail');
+
+export interface ProcessPayload {
+  chequeIdentifier: string;
+  command: ChequeProcessingCommand;
+}
+
+export class ProcessAction implements Action {
+  readonly type = PROCESS;
+
+  constructor(public payload: ProcessPayload) { }
+}
+
+export class ProcessSuccessAction implements Action {
+  readonly type = PROCESS_SUCCESS;
+
+  constructor(public payload: ProcessPayload) { }
+}
+
+export class ProcessFailAction implements Action {
+  readonly type = PROCESS_FAIL;
+
+  constructor(public payload: Error) { }
+}
+
+export type Actions
+  = ProcessAction
+  | ProcessSuccessAction
+  | ProcessFailAction;
+
diff --git a/src/app/accounting/store/cheques/cheques.reducer.ts b/src/app/accounting/store/cheques/cheques.reducer.ts
new file mode 100644
index 0000000..472c29b
--- /dev/null
+++ b/src/app/accounting/store/cheques/cheques.reducer.ts
@@ -0,0 +1,87 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 '../../../services/cheque/domain/fims-cheque.model';
+import {ResourceState} from '../../../common/store/resource.reducer';
+import {Actions} from '../../../common/store/action-creator/action-creator';
+import * as cheque from './cheque.actions';
+import {idsToHashWithCurrentTimestamp, resourcesToHash} from '../../../common/store/reducer.helper';
+import {ChequeProcessingCommand} from '../../../services/cheque/domain/cheque-processing-command';
+
+export interface State extends ResourceState {
+  ids: string[];
+  entities: { [id: string]: FimsCheque };
+  selectedId: string | null;
+}
+
+export const initialState: State = {
+  ids: [],
+  entities: {},
+  loadedAt: {},
+  selectedId: null,
+};
+
+export function reducer(state = initialState, action: Actions<FimsCheque> | cheque.Actions): ResourceState {
+
+  switch (action.type) {
+
+    case cheque.ChequeCRUDActions.LOAD_ALL: {
+      return initialState;
+    }
+
+    case cheque.ChequeCRUDActions.LOAD_ALL_COMPLETE: {
+      const ranges: FimsCheque[] = action.payload.resources;
+
+      const ids = ranges.map(chargeDefinition => chargeDefinition.identifier);
+
+      const entities = resourcesToHash(ranges);
+
+      const loadedAt = idsToHashWithCurrentTimestamp(ids);
+
+      return {
+        ids: [ ...ids ],
+        entities: entities,
+        loadedAt: loadedAt,
+        selectedId: state.selectedId
+      };
+    }
+
+    case cheque.PROCESS_SUCCESS: {
+      const chequeIdentifier = action.payload.chequeIdentifier;
+      const command: ChequeProcessingCommand = action.payload.command;
+
+      const cheque = state.entities[chequeIdentifier];
+
+      if (command.action === 'APPROVE') {
+        cheque.state = 'PROCESSED';
+      } else {
+        cheque.state = 'CANCELED';
+      }
+
+      return Object.assign({}, state, {
+        entities: Object.assign({}, state.entities, {
+          [cheque.identifier]: cheque
+        }),
+      });
+    }
+
+    default: {
+      return state;
+    }
+  }
+}
diff --git a/src/app/accounting/store/cheques/effects/service.effects.ts b/src/app/accounting/store/cheques/effects/service.effects.ts
new file mode 100644
index 0000000..a38c9f7
--- /dev/null
+++ b/src/app/accounting/store/cheques/effects/service.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 {of} from 'rxjs/observable/of';
+import {Action} from '@ngrx/store';
+import {Observable} from 'rxjs/Observable';
+import {Actions, Effect} from '@ngrx/effects';
+import {ChequeService} from '../../../../services/cheque/cheque.service';
+import {Injectable} from '@angular/core';
+import * as chequeActions from '../cheque.actions';
+import {ChequeCRUDActions} from '../cheque.actions';
+import {LoadAllAction} from '../../../../common/store/action-creator/actions';
+
+@Injectable()
+export class ChequeApiEffects {
+
+  @Effect()
+  loadAllChequesByState$: Observable<Action> = this.actions$
+    .ofType(ChequeCRUDActions.LOAD_ALL)
+    .map((action: LoadAllAction) => action.payload)
+    .mergeMap(payload =>
+      this.chequeService.fetch(payload.state)
+        .map(cheques => ChequeCRUDActions.loadAllCompleteAction({
+          resources: cheques,
+          data: payload.data
+        }))
+        .catch(() => of(ChequeCRUDActions.loadAllCompleteAction({
+          resources: [],
+          data: payload.data
+        })))
+    );
+
+  @Effect()
+  processCheque$: Observable<Action> = this.actions$
+    .ofType(chequeActions.PROCESS)
+    .map((action: chequeActions.ProcessAction) => action.payload)
+    .mergeMap(payload =>
+      this.chequeService.process(payload.chequeIdentifier, payload.command)
+        .map(() => new chequeActions.ProcessSuccessAction(payload))
+        .catch(error => of(new chequeActions.ProcessFailAction(error)))
+    );
+
+  constructor(private actions$: Actions, private chequeService: ChequeService) {}
+
+}
diff --git a/src/app/accounting/store/index.ts b/src/app/accounting/store/index.ts
new file mode 100644
index 0000000..e198807
--- /dev/null
+++ b/src/app/accounting/store/index.ts
@@ -0,0 +1,235 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 fromLedgers from './ledger/ledgers.reducer';
+import * as fromLedgerForm from './ledger/form.reducer';
+import * as fromTrialBalance from './ledger/trial-balance.reducer';
+import * as fromChartOfAccounts from './ledger/chart-of-account.reducer';
+import * as fromJournalEntrySearch from './ledger/journal-entry/search.reducer';
+import * as fromAccounts from './account/accounts.reducer';
+import * as fromAccountEntrySearch from './account/entries/search.reducer';
+import * as fromCheques from './cheques/cheques.reducer';
+import * as fromPayrolls from './payroll/payrolls.reducer';
+import {ActionReducer, Store} from '@ngrx/store';
+import {createReducer} from '../../store/index';
+import {createSelector} from 'reselect';
+import {
+  createResourceReducer,
+  getResourceAll,
+  getResourceLoadedAt,
+  getResourceSelected,
+  ResourceState
+} from '../../common/store/resource.reducer';
+import {createFormReducer, FormState, getFormError} from '../../common/store/form.reducer';
+import {
+  createSearchReducer,
+  getSearchEntities,
+  getSearchLoading,
+  getSearchTotalElements,
+  getSearchTotalPages,
+  SearchState
+} from '../../common/store/search.reducer';
+
+export interface State extends fromRoot.State {
+  accounts: ResourceState;
+  accountForm: FormState;
+  accountEntrySearch: fromAccountEntrySearch.State;
+
+  ledgers: fromLedgers.State;
+  ledgerForm: FormState;
+  trialBalance: fromTrialBalance.State;
+  chartOfAccounts: fromChartOfAccounts.State;
+  journalEntrySearch: fromJournalEntrySearch.State;
+  journalEntryForm: FormState;
+
+  transactionTypes: ResourceState;
+  transactionTypeSearch: SearchState;
+  transactionForm: FormState;
+
+  cheques: ResourceState;
+
+  payrollCollections: ResourceState;
+  payrollPayments: SearchState;
+}
+
+const reducers = {
+  ledgers: fromLedgers.reducer,
+  ledgerForm: createFormReducer('Ledger', fromLedgerForm.reducer),
+  trialBalance: fromTrialBalance.reducer,
+  chartOfAccounts: fromChartOfAccounts.reducer,
+
+  journalEntrySearch: fromJournalEntrySearch.reducer,
+  journalEntryForm: createFormReducer('Journal Entry'),
+
+  transactionTypes: createResourceReducer('Transaction Type', undefined, 'code'),
+  transactionTypeSearch: createSearchReducer('Transaction Type'),
+  transactionForm: createFormReducer('Transaction Type'),
+
+  accounts: createResourceReducer('Account', fromAccounts.reducer),
+  accountForm: createFormReducer('Account'),
+  accountEntrySearch: fromAccountEntrySearch.reducer,
+
+  cheques: createResourceReducer('Cheque', fromCheques.reducer),
+
+  payrollCollections: createResourceReducer('Payroll Collection', fromPayrolls.reducer),
+  payrollPayments: createSearchReducer('Payroll Payment')
+};
+
+export const accountingModuleReducer: ActionReducer<State> = createReducer(reducers);
+
+export class AccountingStore extends Store<State> {}
+
+export function accountingStoreFactory(appStore: Store<fromRoot.State>) {
+  appStore.replaceReducer(accountingModuleReducer);
+  return appStore;
+}
+
+/**
+ * Ledger Selectors
+ */
+export const getLedgerState = (state: State) => state.ledgers;
+
+export const getLedgerFormState = (state: State) => state.ledgerForm;
+export const getLedgerFormError = createSelector(getLedgerFormState, getFormError);
+
+export const getTrialBalanceState = (state: State) => state.trialBalance;
+
+export const getChartOfAccountsState = (state: State) => state.chartOfAccounts;
+
+export const getLedgerEntities = createSelector(getLedgerState, fromLedgers.getEntities);
+export const getLedgersLoadedAt = createSelector(getLedgerState, fromLedgers.getLoadedAt);
+export const getLedgerTopLevelIds = createSelector(getLedgerState, fromLedgers.getTopLevelIds);
+export const getSelectedLedger = createSelector(getLedgerState, fromLedgers.getSelected);
+
+export const getAllTopLevelLedgerEntities = createSelector(getLedgerTopLevelIds, getLedgerEntities, (topLevelIds, entities) => {
+  return topLevelIds.map(id => entities[id]);
+});
+
+export const getTrialBalance = createSelector(getTrialBalanceState, fromTrialBalance.getTrialBalance);
+
+export const getChartOfAccountEntries = createSelector(getChartOfAccountsState, fromChartOfAccounts.getChartOfAccountEntries);
+
+export const getChartOfAccountLoading = createSelector(getChartOfAccountsState, fromChartOfAccounts.getLoading);
+
+/**
+ * Journal Entries Selectors
+ */
+export const getJournalEntrySearchState = (state: State) => state.journalEntrySearch;
+
+export const getJournalEntryFormState = (state: State) => state.journalEntryForm;
+export const getJournalEntryFormError = createSelector(getJournalEntryFormState, getFormError);
+
+export const getJournalEntryEntities = createSelector(getJournalEntrySearchState, fromJournalEntrySearch.getEntities);
+
+export const getSearchJournalEntryIds = createSelector(getJournalEntrySearchState, fromJournalEntrySearch.getIds);
+
+export const getJournalEntriesSearchResult = createSelector(getJournalEntryEntities, getSearchJournalEntryIds, (entities, ids) => {
+  return ids.map(id => entities[id]);
+});
+
+/**
+ * Accounts
+ */
+export const getAccountsState = (state: State) => state.accounts;
+
+export const getAccountFormState = (state: State) => state.accountForm;
+export const getAccountFormError = createSelector(getAccountFormState, getFormError);
+
+export const getAccountsLoadedAt = createSelector(getAccountsState, getResourceLoadedAt);
+export const getSelectedAccount = createSelector(getAccountsState, getResourceSelected);
+
+export const getAccountEntrySearchState = (state: State) => state.accountEntrySearch;
+export const getAccountEntrySearchEntities = createSelector(getAccountEntrySearchState, fromAccountEntrySearch.getEntries);
+export const getAccountEntrySearchTotalElements = createSelector(getAccountEntrySearchState, fromAccountEntrySearch.getTotalElements);
+export const getAccountEntrySearchTotalPages = createSelector(getAccountEntrySearchState, fromAccountEntrySearch.getTotalPages);
+
+export const getAccountEntrySearchResults = createSelector(
+  getAccountEntrySearchEntities, getAccountEntrySearchTotalElements, getAccountEntrySearchTotalPages,
+  (entities, totalElements, totalPages) => {
+    return {
+      entries: entities,
+      totalPages: totalPages,
+      totalElements: totalElements
+    };
+  });
+
+/**
+ * Transaction Types
+ */
+export const getTransactionTypesState = (state: State) => state.transactionTypes;
+
+export const getTransactionTypeLoadedAt = createSelector(getTransactionTypesState, getResourceLoadedAt);
+export const getSelectedTransactionType = createSelector(getTransactionTypesState, getResourceSelected);
+
+export const getTransactionTypeSearchState = (state: State) => state.transactionTypeSearch;
+
+export const getTransactionTypeFormState = (state: State) => state.transactionForm;
+export const getTransactionTypeFormError = createSelector(getTransactionTypeFormState, getFormError);
+
+
+export const getSearchTransactionTypes = createSelector(getTransactionTypeSearchState, getSearchEntities);
+export const getTransactionTypeSearchTotalElements = createSelector(getTransactionTypeSearchState, getSearchTotalElements);
+export const getTransactionTypeSearchTotalPages = createSelector(getTransactionTypeSearchState, getSearchTotalPages);
+export const getTransactionTypeSearchLoading = createSelector(getTransactionTypeSearchState, getSearchLoading);
+
+export const getTransactionTypeSearchResults = createSelector(
+  getSearchTransactionTypes, getTransactionTypeSearchTotalPages, getTransactionTypeSearchTotalElements,
+  (transactionTypes, totalPages, totalElements) => {
+    return {
+      transactionTypes,
+      totalPages,
+      totalElements
+    };
+  });
+
+/**
+ * Cheques
+ */
+export const getChequesState = (state: State) => state.cheques;
+
+export const getChequeLoadedAt = createSelector(getChequesState, getResourceLoadedAt);
+export const getSelectedCheque = createSelector(getChequesState, getResourceSelected);
+
+export const getAllChequeEntities = createSelector(getChequesState, getResourceAll);
+
+/**
+ * Payroll collections
+ */
+export const getPayrollCollectionsState = (state: State) => state.payrollCollections;
+
+export const getPayrollCollectionLoadedAt = createSelector(getPayrollCollectionsState, getResourceLoadedAt);
+export const getSelectedPayrollCollection = createSelector(getPayrollCollectionsState, getResourceSelected);
+
+export const getAllPayrollCollectionEntities = createSelector(getPayrollCollectionsState, getResourceAll);
+
+export const getPayrollPaymentSearchState = (state: State) => state.payrollPayments;
+
+export const getSearchPayrollPayments = createSelector(getPayrollPaymentSearchState, getSearchEntities);
+export const getPayrollPaymentsSearchTotalElements = createSelector(getPayrollPaymentSearchState, getSearchTotalElements);
+export const getPayrollPaymentsSearchTotalPages = createSelector(getPayrollPaymentSearchState, getSearchTotalPages);
+
+export const getPayrollPaymentSearchResults = createSelector(
+  getSearchPayrollPayments, getPayrollPaymentsSearchTotalElements, getPayrollPaymentsSearchTotalPages,
+  (data, totalPages, totalElements) => {
+    return {
+      data,
+      totalPages,
+      totalElements
+    };
+  });
diff --git a/src/app/accounting/store/ledger/chart-of-account.reducer.ts b/src/app/accounting/store/ledger/chart-of-account.reducer.ts
new file mode 100644
index 0000000..e72afb7
--- /dev/null
+++ b/src/app/accounting/store/ledger/chart-of-account.reducer.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 {ChartOfAccountEntry} from '../../../services/accounting/domain/chart-of-account-entry.model';
+import * as ledger from './ledger.actions';
+
+export interface State {
+  chartOfAccountEntries: ChartOfAccountEntry[];
+  loading: boolean;
+}
+
+const initialState: State = {
+  chartOfAccountEntries: [],
+  loading: false
+};
+
+export function reducer(state = initialState, action: ledger.Actions): State {
+
+  switch (action.type) {
+
+    case ledger.LOAD_CHART_OF_ACCOUNTS: {
+
+      return Object.assign({}, state, {
+        chartOfAccountEntries: [],
+        loading: true
+      });
+    }
+
+    case ledger.LOAD_CHART_OF_ACCOUNTS_COMPLETE: {
+      const chartOfAccountEntries: ChartOfAccountEntry[] = action.payload;
+
+      return {
+        chartOfAccountEntries: chartOfAccountEntries,
+        loading: false
+      };
+    }
+
+    default: {
+      return state;
+    }
+  }
+}
+
+
+export const getChartOfAccountEntries = (state: State) => state.chartOfAccountEntries;
+
+export const getLoading = (state: State) => state.loading;
diff --git a/src/app/accounting/store/ledger/effects/notification.effects.ts b/src/app/accounting/store/ledger/effects/notification.effects.ts
new file mode 100644
index 0000000..35bc719
--- /dev/null
+++ b/src/app/accounting/store/ledger/effects/notification.effects.ts
@@ -0,0 +1,56 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 ledgerActions from '../ledger.actions';
+import {NotificationService, NotificationType} from '../../../../services/notification/notification.service';
+
+@Injectable()
+export class LedgerNotificationEffects {
+
+  @Effect({ dispatch: false })
+  createLedgerSuccess$: Observable<Action> = this.actions$
+    .ofType(ledgerActions.CREATE_SUCCESS, ledgerActions.CREATE_SUB_LEDGER_SUCCESS, ledgerActions.UPDATE_SUCCESS)
+    .do(() => this.notificationService.send({
+      type: NotificationType.MESSAGE,
+      message: 'Ledger is going to be saved'
+    }));
+
+  @Effect({ dispatch: false })
+  deleteLedgerSuccess$: Observable<Action> = this.actions$
+    .ofType(ledgerActions.DELETE_SUCCESS)
+    .do(() => this.notificationService.send({
+      type: NotificationType.MESSAGE,
+      message: 'Ledger is going to be deleted'
+    }));
+
+  @Effect({ dispatch: false })
+  deleteLedgerFail$: Observable<Action> = this.actions$
+    .ofType(ledgerActions.DELETE_FAIL)
+    .do(() => this.notificationService.send({
+      type: NotificationType.ALERT,
+      title: 'Ledger can\'t be deleted',
+      message: 'Ledger has accounts or sub ledgers'
+    }));
+
+  constructor(private actions$: Actions, private notificationService: NotificationService) {}
+}
+
diff --git a/src/app/accounting/store/ledger/effects/route.effects.ts b/src/app/accounting/store/ledger/effects/route.effects.ts
new file mode 100644
index 0000000..3e04e0f
--- /dev/null
+++ b/src/app/accounting/store/ledger/effects/route.effects.ts
@@ -0,0 +1,47 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 ledgerActions from '../ledger.actions';
+import {Router} from '@angular/router';
+
+@Injectable()
+export class LedgerRouteEffects {
+  @Effect({ dispatch: false })
+  createLedgerSuccess$: Observable<Action> = this.actions$
+    .ofType(ledgerActions.CREATE_SUCCESS, ledgerActions.CREATE_SUB_LEDGER_SUCCESS, ledgerActions.UPDATE_SUCCESS)
+    .map(action => action.payload)
+    .do(payload => this.router.navigate(['../'], { relativeTo: payload.activatedRoute }));
+
+  @Effect({ dispatch: false })
+  deleteLedgerSuccess$: Observable<Action> = this.actions$
+    .ofType(ledgerActions.DELETE_SUCCESS)
+    .map(action => action.payload)
+    .do(payload => {
+      if (payload.ledger.parentLedgerIdentifier) {
+        this.router.navigate(['../../', payload.ledger.parentLedgerIdentifier, 'ledgers'], { relativeTo: payload.activatedRoute });
+      } else {
+        this.router.navigate(['../../../../'], { relativeTo: payload.activatedRoute });
+      }
+    });
+
+  constructor(private actions$: Actions, private router: Router) { }
+}
diff --git a/src/app/accounting/store/ledger/effects/service.effects.ts b/src/app/accounting/store/ledger/effects/service.effects.ts
new file mode 100644
index 0000000..e107277
--- /dev/null
+++ b/src/app/accounting/store/ledger/effects/service.effects.ts
@@ -0,0 +1,105 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 '../../../../services/accounting/accounting.service';
+
+@Injectable()
+export class LedgerApiEffects {
+
+  @Effect()
+  search$: Observable<Action> = this.actions$
+    .ofType(ledgerActions.LOAD_ALL_TOP_LEVEL)
+    .debounceTime(300)
+    .switchMap(() => {
+      const nextSearch$ = this.actions$.ofType(ledgerActions.LOAD_ALL_TOP_LEVEL).skip(1);
+
+      return this.accountingService.fetchLedgers()
+        .takeUntil(nextSearch$)
+        .map(ledgerPage => ledgerPage.ledgers)
+        .map(ledgers => new ledgerActions.LoadAllTopLevelComplete(ledgers))
+        .catch(() => of(new ledgerActions.LoadAllTopLevelComplete([])));
+    });
+
+  @Effect()
+  createLedger$: Observable<Action> = this.actions$
+    .ofType(ledgerActions.CREATE)
+    .map((action: ledgerActions.CreateLedgerAction) => action.payload)
+    .mergeMap(payload =>
+      this.accountingService.createLedger(payload.ledger)
+        .map(() => new ledgerActions.CreateLedgerSuccessAction(payload))
+        .catch((error) => of(new ledgerActions.CreateLedgerFailAction(error)))
+    );
+
+  @Effect()
+  createSubLedger$: Observable<Action> = this.actions$
+    .ofType(ledgerActions.CREATE_SUB_LEDGER)
+    .map((action: ledgerActions.CreateSubLedgerAction) => action.payload)
+    .mergeMap(payload =>
+      this.accountingService.addSubLedger(payload.parentLedgerId, payload.ledger)
+        .map(() => new ledgerActions.CreateSubLedgerSuccessAction(payload))
+        .catch((error) => of(new ledgerActions.CreateSubLedgerFailAction(error)))
+    );
+
+  @Effect()
+  updateLedger$: Observable<Action> = this.actions$
+    .ofType(ledgerActions.UPDATE)
+    .map((action: ledgerActions.UpdateLedgerAction) => action.payload)
+    .mergeMap(payload =>
+      this.accountingService.modifyLedger(payload.ledger)
+          .map(() => new ledgerActions.UpdateLedgerSuccessAction(payload))
+          .catch((error) => of(new ledgerActions.UpdateLedgerFailAction(error)))
+    );
+
+  @Effect()
+  deleteLedger$: Observable<Action> = this.actions$
+    .ofType(ledgerActions.DELETE)
+    .map((action: ledgerActions.DeleteLedgerAction) => action.payload)
+    .mergeMap(payload =>
+      this.accountingService.deleteLedger(payload.ledger.identifier)
+        .map(() => new ledgerActions.DeleteLedgerSuccessAction(payload))
+        .catch((error) => of(new ledgerActions.DeleteLedgerFailAction(error)))
+    );
+
+  @Effect()
+  loadTrialBalance$: Observable<Action> = this.actions$
+    .ofType(ledgerActions.LOAD_TRIAL_BALANCE)
+    .map((action: ledgerActions.LoadTrialBalanceAction) => action.payload)
+    .mergeMap(includeEmpty =>
+      this.accountingService.getTrialBalance(includeEmpty)
+        .map(trialBalance => new ledgerActions.LoadTrialBalanceActionComplete(trialBalance))
+        .catch(() => of(new ledgerActions.LoadTrialBalanceActionComplete(null)))
+    );
+
+  @Effect()
+  loadChartOfAccounts$: Observable<Action> = this.actions$
+    .ofType(ledgerActions.LOAD_CHART_OF_ACCOUNTS)
+    .mergeMap(() =>
+      this.accountingService.getChartOfAccounts()
+        .map(chartOfAccountEntries => new ledgerActions.LoadChartOfAccountsActionComplete(chartOfAccountEntries))
+        .catch(() => of(new ledgerActions.LoadChartOfAccountsActionComplete([])))
+    );
+
+  constructor(private actions$: Actions, private accountingService: AccountingService) { }
+
+}
diff --git a/src/app/accounting/store/ledger/form.reducer.ts b/src/app/accounting/store/ledger/form.reducer.ts
new file mode 100644
index 0000000..a11ef00
--- /dev/null
+++ b/src/app/accounting/store/ledger/form.reducer.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 * as ledger from './ledger.actions';
+import {FormState} from '../../../common/store/form.reducer';
+
+export const initialState: FormState = {};
+
+export function reducer(state = initialState, action: ledger.Actions): FormState {
+  switch (action.type) {
+
+    case ledger.CREATE_SUB_LEDGER_FAIL:
+      return {
+        error: action.payload
+      };
+
+    case ledger.CREATE_SUB_LEDGER_SUCCESS:
+      return initialState;
+
+    default:
+      return state;
+
+  }
+}
diff --git a/src/app/accounting/store/ledger/journal-entry/effects/notification.effects.ts b/src/app/accounting/store/ledger/journal-entry/effects/notification.effects.ts
new file mode 100644
index 0000000..bf70591
--- /dev/null
+++ b/src/app/accounting/store/ledger/journal-entry/effects/notification.effects.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 {Injectable} from '@angular/core';
+import {Actions, Effect} from '@ngrx/effects';
+import {Observable} from 'rxjs/Observable';
+import {Action} from '@ngrx/store';
+import * as journalEntryActions from '../journal-entry.actions';
+import {NotificationService, NotificationType} from '../../../../../services/notification/notification.service';
+
+@Injectable()
+export class JournalEntryNotificationEffects {
+
+  @Effect({ dispatch: false })
+  createJournalEntrySuccess$: Observable<Action> = this.actions$
+    .ofType(journalEntryActions.CREATE_SUCCESS)
+    .do(() => this.notificationService.send({
+      type: NotificationType.MESSAGE,
+      message: 'Journal entry is going to be processed'
+    }));
+
+  constructor(private actions$: Actions, private notificationService: NotificationService) {}
+
+}
+
diff --git a/src/app/accounting/store/ledger/journal-entry/effects/route.effects.ts b/src/app/accounting/store/ledger/journal-entry/effects/route.effects.ts
new file mode 100644
index 0000000..fdcf486
--- /dev/null
+++ b/src/app/accounting/store/ledger/journal-entry/effects/route.effects.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 {Injectable} from '@angular/core';
+import {Actions, Effect} from '@ngrx/effects';
+import {Observable} from 'rxjs/Observable';
+import {Action} from '@ngrx/store';
+import * as journalEntryActions from '../journal-entry.actions';
+import {Router} from '@angular/router';
+
+@Injectable()
+export class JournalEntryRouteEffects {
+
+  @Effect({ dispatch: false })
+  createJournalEntrySuccess$: Observable<Action> = this.actions$
+    .ofType(journalEntryActions.CREATE_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/accounting/store/ledger/journal-entry/effects/service.effects.ts b/src/app/accounting/store/ledger/journal-entry/effects/service.effects.ts
new file mode 100644
index 0000000..995ccaa
--- /dev/null
+++ b/src/app/accounting/store/ledger/journal-entry/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 {of} from 'rxjs/observable/of';
+import * as journalEntryActions from '../journal-entry.actions';
+import {AccountingService} from '../../../../../services/accounting/accounting.service';
+
+@Injectable()
+export class JournalEntryApiEffects {
+
+  @Effect()
+  loadJournalEntries$: Observable<Action> = this.actions$
+    .ofType(journalEntryActions.SEARCH)
+    .map((action: journalEntryActions.SearchAction) => action.payload)
+    .mergeMap(payload =>
+      this.accountingService.fetchJournalEntries(payload.startDate, payload.endDate, payload.account, payload.amount)
+        .map(journalEntries => new journalEntryActions.SearchCompleteAction(journalEntries))
+        .catch(() => of(new journalEntryActions.SearchCompleteAction([])))
+    );
+
+  @Effect()
+  createJournalEntry$: Observable<Action> = this.actions$
+    .ofType(journalEntryActions.CREATE)
+    .map((action: journalEntryActions.CreateJournalEntryAction) => action.payload)
+    .mergeMap(payload =>
+      this.accountingService.createJournalEntry(payload.journalEntry)
+        .map(() => new journalEntryActions.CreateJournalEntrySuccessAction(payload))
+        .catch((error) => of(new journalEntryActions.CreateJournalEntryFailAction(error)))
+    );
+
+  constructor(private actions$: Actions, private accountingService: AccountingService) { }
+
+}
diff --git a/src/app/accounting/store/ledger/journal-entry/journal-entry.actions.ts b/src/app/accounting/store/ledger/journal-entry/journal-entry.actions.ts
new file mode 100644
index 0000000..0e3b92c
--- /dev/null
+++ b/src/app/accounting/store/ledger/journal-entry/journal-entry.actions.ts
@@ -0,0 +1,86 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 {Error} from '../../../../services/domain/error.model';
+import {type} from '../../../../store/util';
+import {JournalEntry} from '../../../../services/accounting/domain/journal-entry.model';
+import {RoutePayload} from '../../../../common/store/route-payload';
+
+export const SEARCH = type('[Journal Entry] Search');
+export const SEARCH_COMPLETE = type('[Journal Entry] Search Complete');
+
+export const CREATE = type('[Journal Entry] Create');
+export const CREATE_SUCCESS = type('[Journal Entry] Create Success');
+export const CREATE_FAIL = type('[Journal Entry] Create Fail');
+
+export const RESET_FORM = type('[Journal Entry] Reset Form');
+
+export interface SearchPayload {
+  startDate: string;
+  endDate: string;
+  account?: string;
+  amount?: string;
+}
+
+export interface JournalEntryRoutePayload extends RoutePayload {
+  journalEntry: JournalEntry;
+}
+
+export class SearchAction implements Action {
+  readonly type = SEARCH;
+
+  constructor(public payload: SearchPayload) { }
+}
+
+export class SearchCompleteAction implements Action {
+  readonly type = SEARCH_COMPLETE;
+
+  constructor(public payload: JournalEntry[]) { }
+}
+
+export class CreateJournalEntryAction implements Action {
+  readonly type = CREATE;
+
+  constructor(public payload: JournalEntryRoutePayload) { }
+}
+
+export class CreateJournalEntrySuccessAction implements Action {
+  readonly type = CREATE_SUCCESS;
+
+  constructor(public payload: JournalEntryRoutePayload) { }
+}
+
+export class CreateJournalEntryFailAction implements Action {
+  readonly type = CREATE_FAIL;
+
+  constructor(public payload: Error) { }
+}
+
+export class ResetJournalEntryFormAction implements Action {
+  readonly type = RESET_FORM;
+
+  constructor() {}
+}
+
+export type Actions = SearchAction
+  | SearchCompleteAction
+  | CreateJournalEntryAction
+  | CreateJournalEntrySuccessAction
+  | CreateJournalEntryFailAction
+  | ResetJournalEntryFormAction;
diff --git a/src/app/accounting/store/ledger/journal-entry/search.reducer.ts b/src/app/accounting/store/ledger/journal-entry/search.reducer.ts
new file mode 100644
index 0000000..83e8b85
--- /dev/null
+++ b/src/app/accounting/store/ledger/journal-entry/search.reducer.ts
@@ -0,0 +1,88 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 journalEntry from './journal-entry.actions';
+import {JournalEntry} from '../../../../services/accounting/domain/journal-entry.model';
+
+
+export interface State {
+  ids: string[];
+  entities: { [id: string]: JournalEntry };
+  loading: boolean;
+  startDate: string;
+  endDate: string;
+}
+
+const initialState: State = {
+  ids: [],
+  entities: {},
+  loading: false,
+  startDate: null,
+  endDate: null
+};
+
+export function reducer(state = initialState, action: journalEntry.Actions): State {
+
+  switch (action.type) {
+
+    case journalEntry.SEARCH: {
+      const payload = action.payload;
+
+      return Object.assign({}, state, {
+        startDate: payload.startDate,
+        endDate: payload.endDate,
+        loading: true
+      });
+    }
+
+    case journalEntry.SEARCH_COMPLETE: {
+      const journalEntries = action.payload;
+
+      const journalEntryIds = journalEntries.map(journalEntry => journalEntry.transactionIdentifier);
+
+      const newJournalEntryEntities = journalEntries.reduce((entities: { [id: string]: JournalEntry }, journalEntry: JournalEntry) => {
+        return Object.assign(entities, {
+          [journalEntry.transactionIdentifier]: journalEntry
+        });
+      }, {});
+
+      return {
+        ids: [ ...journalEntryIds ],
+        entities: newJournalEntryEntities,
+        startDate: state.startDate,
+        endDate: state.endDate,
+        loading: false
+      };
+    }
+
+    default: {
+      return state;
+    }
+  }
+}
+
+
+export const getIds = (state: State) => state.ids;
+
+export const getEntities = (state: State) => state.entities;
+
+export const getStartDate = (state: State) => state.startDate;
+
+export const getEndDate = (state: State) => state.endDate;
+
+export const getLoading = (state: State) => state.loading;
diff --git a/src/app/accounting/store/ledger/ledger.actions.ts b/src/app/accounting/store/ledger/ledger.actions.ts
new file mode 100644
index 0000000..6af9cc0
--- /dev/null
+++ b/src/app/accounting/store/ledger/ledger.actions.ts
@@ -0,0 +1,214 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 {Ledger} from '../../../services/accounting/domain/ledger.model';
+import {Error} from '../../../services/domain/error.model';
+import {TrialBalance} from '../../../services/accounting/domain/trial-balance.model';
+import {RoutePayload} from '../../../common/store/route-payload';
+import {ChartOfAccountEntry} from '../../../services/accounting/domain/chart-of-account-entry.model';
+
+export const LOAD_ALL_TOP_LEVEL = type('[Ledger] Load All Top Level');
+export const LOAD_ALL_TOP_LEVEL_COMPLETE = type('[Ledger] Load All Top Level Complete');
+
+export const LOAD = type('[Ledger] Load');
+export const SELECT = type('[Ledger] Select');
+
+export const CREATE = type('[Ledger] Create');
+export const CREATE_SUCCESS = type('[Ledger] Create Success');
+export const CREATE_FAIL = type('[Ledger] Create Fail');
+
+export const CREATE_SUB_LEDGER = type('[Ledger] Create Subledger');
+export const CREATE_SUB_LEDGER_SUCCESS = type('[Ledger] Create Subledger Success');
+export const CREATE_SUB_LEDGER_FAIL = type('[Ledger] Create Subledger Fail');
+
+
+export const UPDATE = type('[Ledger] Update');
+export const UPDATE_SUCCESS = type('[Ledger] Update Success');
+export const UPDATE_FAIL = type('[Ledger] Update Fail');
+
+export const DELETE = type('[Ledger] Delete');
+export const DELETE_SUCCESS = type('[Ledger] Delete Success');
+export const DELETE_FAIL = type('[Ledger] Delete Fail');
+
+export const LOAD_TRIAL_BALANCE = type('[Ledger] Load Trial Balance');
+export const LOAD_TRIAL_BALANCE_COMPLETE = type('[Ledger] Load Trial Balance Complete');
+
+export const LOAD_CHART_OF_ACCOUNTS = type('[Ledger] Load Chart Of Accounts');
+export const LOAD_CHART_OF_ACCOUNTS_COMPLETE = type('[Ledger] Load Chart Of Accounts Complete');
+
+export const RESET_FORM = type('[Ledger] Reset Form');
+
+export interface CreateSubLedgerPayload extends RoutePayload {
+  parentLedgerId: string;
+  ledger: Ledger;
+}
+
+export interface LedgerRoutePayload extends RoutePayload {
+  ledger: Ledger;
+}
+
+export class LoadAllTopLevel implements Action {
+  readonly type = LOAD_ALL_TOP_LEVEL;
+
+  constructor() { }
+}
+
+export class LoadAllTopLevelComplete implements Action {
+  readonly type = LOAD_ALL_TOP_LEVEL_COMPLETE;
+
+  constructor(public payload: Ledger[]) { }
+}
+
+export class LoadAction implements Action {
+  readonly type = LOAD;
+
+  constructor(public payload: Ledger) { }
+}
+
+export class SelectAction implements Action {
+  readonly type = SELECT;
+
+  constructor(public payload: string) { }
+}
+
+export class CreateLedgerAction implements Action {
+  readonly type = CREATE;
+
+  constructor(public payload: LedgerRoutePayload) { }
+}
+
+export class CreateLedgerSuccessAction implements Action {
+  readonly type = CREATE_SUCCESS;
+
+  constructor(public payload: LedgerRoutePayload) { }
+}
+
+export class CreateLedgerFailAction implements Action {
+  readonly type = CREATE_FAIL;
+
+  constructor(public payload: Error) { }
+}
+
+export class CreateSubLedgerAction implements Action {
+  readonly type = CREATE_SUB_LEDGER;
+
+  constructor(public payload: CreateSubLedgerPayload) { }
+}
+
+export class CreateSubLedgerSuccessAction implements Action {
+  readonly type = CREATE_SUB_LEDGER_SUCCESS;
+
+  constructor(public payload: CreateSubLedgerPayload) { }
+}
+
+export class CreateSubLedgerFailAction implements Action {
+  readonly type = CREATE_SUB_LEDGER_FAIL;
+
+  constructor(public payload: Error) { }
+}
+
+export class UpdateLedgerAction implements Action {
+  readonly type = UPDATE;
+
+  constructor(public payload: LedgerRoutePayload) { }
+}
+
+export class UpdateLedgerSuccessAction implements Action {
+  readonly type = UPDATE_SUCCESS;
+
+  constructor(public payload: LedgerRoutePayload) { }
+}
+
+export class UpdateLedgerFailAction implements Action {
+  readonly type = UPDATE_FAIL;
+
+  constructor(public payload: Error) { }
+}
+
+export class DeleteLedgerAction implements Action {
+  readonly type = DELETE;
+
+  constructor(public payload: LedgerRoutePayload) { }
+}
+
+export class DeleteLedgerSuccessAction implements Action {
+  readonly type = DELETE_SUCCESS;
+
+  constructor(public payload: LedgerRoutePayload) { }
+}
+
+export class DeleteLedgerFailAction implements Action {
+  readonly type = DELETE_FAIL;
+
+  constructor(public payload: Error) { }
+}
+
+export class LoadTrialBalanceAction implements Action {
+  readonly type = LOAD_TRIAL_BALANCE;
+
+  constructor(public payload: boolean) { }
+}
+
+export class LoadTrialBalanceActionComplete implements Action {
+  readonly type = LOAD_TRIAL_BALANCE_COMPLETE;
+
+  constructor(public payload: TrialBalance) { }
+}
+
+export class LoadChartOfAccountsAction implements Action {
+  readonly type = LOAD_CHART_OF_ACCOUNTS;
+
+  constructor() { }
+}
+
+export class LoadChartOfAccountsActionComplete implements Action {
+  readonly type = LOAD_CHART_OF_ACCOUNTS_COMPLETE;
+
+  constructor(public payload: ChartOfAccountEntry[]) { }
+}
+
+export class ResetLedgerFormAction implements Action {
+  readonly type = RESET_FORM;
+
+  constructor() {}
+}
+
+export type Actions
+  = LoadAllTopLevel
+  | LoadAllTopLevelComplete
+  | LoadAction
+  | SelectAction
+  | CreateLedgerAction
+  | CreateLedgerSuccessAction
+  | CreateLedgerFailAction
+  | CreateSubLedgerAction
+  | CreateSubLedgerSuccessAction
+  | CreateSubLedgerFailAction
+  | UpdateLedgerAction
+  | UpdateLedgerSuccessAction
+  | UpdateLedgerFailAction
+  | DeleteLedgerAction
+  | DeleteLedgerSuccessAction
+  | DeleteLedgerFailAction
+  | LoadTrialBalanceAction
+  | LoadTrialBalanceActionComplete
+  | LoadChartOfAccountsAction
+  | LoadChartOfAccountsActionComplete
+  | ResetLedgerFormAction;
diff --git a/src/app/accounting/store/ledger/ledgers.reducer.spec.ts b/src/app/accounting/store/ledger/ledgers.reducer.spec.ts
new file mode 100644
index 0000000..03860b9
--- /dev/null
+++ b/src/app/accounting/store/ledger/ledgers.reducer.spec.ts
@@ -0,0 +1,226 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, State} from './ledgers.reducer';
+import {
+  CreateLedgerSuccessAction,
+  CreateSubLedgerPayload,
+  CreateSubLedgerSuccessAction,
+  DeleteLedgerSuccessAction,
+  LedgerRoutePayload,
+  LoadAllTopLevelComplete,
+  UpdateLedgerSuccessAction
+} from './ledger.actions';
+import {Ledger} from '../../../services/accounting/domain/ledger.model';
+
+describe('Ledgers Reducer', () => {
+
+  function createLedger(value: string): Ledger {
+    return { identifier: value, name: value, type: 'ASSET', showAccountsInChart: true, subLedgers: []};
+  }
+
+  describe('LOAD_ALL_TOP_LEVEL_COMPLETE', () => {
+
+    it('should add all ledgers if not in store', () => {
+      spyOn(Date, 'now').and.returnValue(1000);
+
+      const ledgerOne = createLedger('test1');
+      const ledgerTwo = createLedger('test2');
+
+      const payload: Ledger[] = [
+        ledgerOne,
+        ledgerTwo
+      ];
+
+      const expectedResult: State = {
+        ids: [ledgerOne.identifier, ledgerTwo.identifier],
+        topLevelIds: [ledgerOne.identifier, ledgerTwo.identifier],
+        entities: {
+          'test1': ledgerOne,
+          'test2': ledgerTwo
+        },
+        loadedAt: {
+          'test1': 1000,
+          'test2': 1000
+        },
+        selectedLedgerId: null,
+      };
+
+      const result = reducer(undefined, new LoadAllTopLevelComplete(payload));
+
+      expect(result).toEqual(expectedResult);
+    });
+  });
+
+  describe('CREATE_SUCCESS', () => {
+    it('should add ledger to top level ids if not in store', () => {
+      const ledgerOne = createLedger('test1');
+
+      const payload: LedgerRoutePayload = {
+        ledger: ledgerOne,
+        activatedRoute: null
+      };
+
+      const expectedResult: State = {
+        ids: [ledgerOne.identifier],
+        topLevelIds: [ledgerOne.identifier],
+        entities: {
+          'test1': ledgerOne
+        },
+        loadedAt: {},
+        selectedLedgerId: null,
+      };
+
+      const result = reducer(undefined, new CreateLedgerSuccessAction(payload));
+
+      expect(result).toEqual(expectedResult);
+    });
+  });
+
+  describe('CREATE_SUB_LEDGER_SUCCESS', () => {
+    it('should add ledger not to top level ids', () => {
+      const parentLedger = createLedger('parent');
+      const ledgerOne = createLedger('test1');
+
+      const initialState: State = {
+        ids: [parentLedger.identifier],
+        topLevelIds: [parentLedger.identifier],
+        entities: {
+          'parent': parentLedger
+        },
+        loadedAt: {},
+        selectedLedgerId: null,
+      };
+
+      const payload: CreateSubLedgerPayload = {
+        parentLedgerId: parentLedger.identifier,
+        ledger: ledgerOne,
+        activatedRoute: null
+      };
+
+      const expectedResult: State = {
+        ids: [parentLedger.identifier, ledgerOne.identifier],
+        topLevelIds: [parentLedger.identifier],
+        entities: {
+          'test1': ledgerOne,
+          'parent': Object.assign({}, parentLedger, {
+            subLedgers: [ledgerOne]
+          })
+        },
+        loadedAt: {},
+        selectedLedgerId: null,
+      };
+
+      const result = reducer(initialState, new CreateSubLedgerSuccessAction(payload));
+
+      expect(result).toEqual(expectedResult);
+      expect(result.entities['test1'].parentLedgerIdentifier).toEqual(parentLedger.identifier);
+    });
+  });
+
+  describe('UPDATE_SUCCESS', () => {
+    it('should update the new ledger in entities', () => {
+      const ledgerOne = createLedger('test1');
+
+      const updatedLedger = Object.assign({}, ledgerOne, {
+        name: 'newName'
+      });
+
+      const payload: LedgerRoutePayload = {
+        ledger: updatedLedger,
+        activatedRoute: null
+      };
+
+      const initialState: State = {
+        ids: [ledgerOne.identifier],
+        topLevelIds: [],
+        entities: {
+          'test1': ledgerOne
+        },
+        loadedAt: {
+          'test1': 1000
+        },
+        selectedLedgerId: null,
+      };
+
+      const expectedResult: State = {
+        ids: [ledgerOne.identifier],
+        topLevelIds: [],
+        entities: {
+          'test1': updatedLedger
+        },
+        loadedAt: {
+          'test1': 1000
+        },
+        selectedLedgerId: null,
+      };
+
+      const result = reducer(initialState, new UpdateLedgerSuccessAction(payload));
+
+      expect(result).toEqual(expectedResult);
+    });
+  });
+
+  describe('DELETE_SUCCESS', () => {
+
+    it('should delete sub ledger from parent ledger', () => {
+      const parentLedgerWithoutSub = createLedger('test1');
+      const subLedger = createLedger('test2');
+      subLedger.parentLedgerIdentifier = parentLedgerWithoutSub.identifier;
+
+      const parentLedgerWithSub = Object.assign({}, parentLedgerWithoutSub, {
+        subLedgers: [subLedger]
+      });
+
+      const initialState: State = {
+        ids: [parentLedgerWithSub.identifier, subLedger.identifier],
+        topLevelIds: ['test1'],
+        entities: {
+          'test1': parentLedgerWithSub,
+          'test2': subLedger
+        },
+        loadedAt: {
+          'test1': 1000,
+          'test2': 2000
+        },
+        selectedLedgerId: null,
+      };
+
+      const result: State = reducer(initialState, new DeleteLedgerSuccessAction({
+        ledger: subLedger,
+        activatedRoute: null
+      }));
+
+      const expectedResult: State = {
+        ids: [parentLedgerWithoutSub.identifier],
+        topLevelIds: ['test1'],
+        entities: {
+          'test1': parentLedgerWithoutSub
+        },
+        loadedAt: {
+          'test1': 1000
+        },
+        selectedLedgerId: null
+      };
+
+      expect(result).toEqual(expectedResult);
+      expect(result.entities['test1'].subLedgers.length === 0).toBeTruthy();
+    });
+  });
+
+});
diff --git a/src/app/accounting/store/ledger/ledgers.reducer.ts b/src/app/accounting/store/ledger/ledgers.reducer.ts
new file mode 100644
index 0000000..e4a5795
--- /dev/null
+++ b/src/app/accounting/store/ledger/ledgers.reducer.ts
@@ -0,0 +1,197 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 ledger from './ledger.actions';
+import {createSelector} from 'reselect';
+import {Ledger} from '../../../services/accounting/domain/ledger.model';
+import {resourcesToHash} from '../../../common/store/reducer.helper';
+
+export interface State {
+  ids: string[];
+  topLevelIds: string[];
+  entities: { [id: string]: Ledger };
+  loadedAt: { [id: string]: number };
+  selectedLedgerId: string | null;
+}
+
+export const initialState: State = {
+  ids: [],
+  topLevelIds: [],
+  entities: {},
+  loadedAt: {},
+  selectedLedgerId: null,
+};
+
+export function reducer(state = initialState, action: ledger.Actions): State {
+  switch (action.type) {
+
+    case ledger.LOAD_ALL_TOP_LEVEL_COMPLETE: {
+      const ledgers: Ledger[] = action.payload;
+
+      const newLedgers: Ledger[] = ledgers.filter(ledger => !state.entities[ledger.identifier]);
+
+      const newLedgerIds: string[] = newLedgers.map(ledger => ledger.identifier);
+
+      const newLedgerEntities = resourcesToHash(newLedgers);
+
+      const newLoadedAt = newLedgers.reduce((entities: { [id: string]: any }, ledger: Ledger) => {
+        return Object.assign(entities, {
+          [ledger.identifier]: Date.now()
+        });
+      }, {});
+
+      return {
+        ids: [ ...state.ids, ...newLedgerIds ],
+        topLevelIds: [ ...state.topLevelIds, ...newLedgerIds],
+        entities: Object.assign({}, state.entities, newLedgerEntities),
+        loadedAt: Object.assign({}, state.loadedAt, newLoadedAt),
+        selectedLedgerId: state.selectedLedgerId
+      };
+    }
+
+    case ledger.LOAD: {
+      const ledger: Ledger = action.payload;
+
+      const newIds = state.ids.filter(id => id !== ledger.identifier);
+
+      return {
+        ids: [ ...newIds, ledger.identifier ],
+        topLevelIds: state.topLevelIds,
+        entities: Object.assign({}, state.entities, {
+          [ledger.identifier]: ledger
+        }),
+        loadedAt: Object.assign({}, state.entities, {
+          [ledger.identifier]: Date.now()
+        }),
+        selectedLedgerId: state.selectedLedgerId
+      };
+    }
+
+    case ledger.SELECT: {
+      return Object.assign({}, state, {
+        selectedLedgerId: action.payload
+      });
+    }
+
+    case ledger.CREATE_SUCCESS: {
+      const ledger: Ledger = action.payload.ledger;
+
+      return {
+        ids: [ ...state.ids, ledger.identifier ],
+        topLevelIds: [ ...state.topLevelIds, ledger.identifier ],
+        entities: Object.assign({}, state.entities, {
+          [ledger.identifier]: ledger
+        }),
+        selectedLedgerId: state.selectedLedgerId,
+        loadedAt: state.loadedAt
+      };
+    }
+
+    case ledger.UPDATE_SUCCESS: {
+      const ledger: Ledger = action.payload.ledger;
+
+      return {
+        ids: [ ...state.ids ],
+        topLevelIds: [ ...state.topLevelIds ],
+        entities: Object.assign({}, state.entities, {
+          [ledger.identifier]: ledger
+        }),
+        selectedLedgerId: state.selectedLedgerId,
+        loadedAt: state.loadedAt
+      };
+    }
+
+    case ledger.CREATE_SUB_LEDGER_SUCCESS: {
+      const subLedger: Ledger = action.payload.ledger;
+      const parentLedgerId = action.payload.parentLedgerId;
+      const parentLedger: Ledger = Object.assign({}, state.entities[parentLedgerId]);
+      subLedger.parentLedgerIdentifier = parentLedgerId;
+      parentLedger.subLedgers.push(subLedger);
+
+      return {
+        ids: [ ...state.ids, subLedger.identifier ],
+        topLevelIds: [ ...state.topLevelIds ],
+        entities: Object.assign({}, state.entities, {
+          [subLedger.identifier]: subLedger,
+          [parentLedger.identifier]: parentLedger
+        }),
+        selectedLedgerId: state.selectedLedgerId,
+        loadedAt: state.loadedAt
+      };
+    }
+
+    case ledger.DELETE_SUCCESS: {
+      const deletedLedger: Ledger = action.payload.ledger;
+
+      const newIds: string[] = state.ids.filter(id => id !== deletedLedger.identifier);
+      const newTopLevelIds: string[] = state.topLevelIds.filter(id => id !== deletedLedger.identifier);
+
+      const newEntities = newIds.reduce((entities: { [id: string]: Ledger }, id: string) => {
+        let ledger = state.entities[id];
+
+        // Remove sub ledger from parent ledger
+        if (ledger.identifier === deletedLedger.parentLedgerIdentifier) {
+          ledger = Object.assign({}, ledger, {
+            subLedgers: ledger.subLedgers.filter(subLedger => subLedger.identifier !== deletedLedger.identifier)
+          });
+        }
+
+        return Object.assign(entities, {
+          [ledger.identifier]: ledger
+        });
+      }, {});
+
+      const newLoadedAt = newIds.reduce((entities: { [id: string]: any }, id: string) => {
+        const loadedAt = state.loadedAt[id];
+        return Object.assign(entities, {
+          [id]: loadedAt
+        });
+      }, {});
+
+      return {
+        ids: [...newIds],
+        topLevelIds: [...newTopLevelIds],
+        entities: newEntities,
+        loadedAt: newLoadedAt,
+        selectedLedgerId: state.selectedLedgerId
+      };
+    }
+
+    default: {
+      return state;
+    }
+  }
+}
+
+export const getEntities = (state: State) => state.entities;
+
+export const getLoadedAt = (state: State) => state.loadedAt;
+
+export const getIds = (state: State) => state.ids;
+
+export const getTopLevelIds = (state: State) => state.topLevelIds;
+
+export const getSelectedId = (state: State) => state.selectedLedgerId;
+
+export const getSelected = createSelector(getEntities, getSelectedId, (entities, selectedId) => {
+  return entities[selectedId];
+});
+
+export const getAll = createSelector(getEntities, getIds, (entities, ids) => {
+  return ids.map(id => entities[id]);
+});
diff --git a/src/app/accounting/store/ledger/transaction-type/effects/notification.effects.ts b/src/app/accounting/store/ledger/transaction-type/effects/notification.effects.ts
new file mode 100644
index 0000000..2f915c5
--- /dev/null
+++ b/src/app/accounting/store/ledger/transaction-type/effects/notification.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 {Observable} from 'rxjs/Observable';
+import {Action} from '@ngrx/store';
+import * as transactionTypeActions from '../transaction-type.actions';
+import {NotificationService, NotificationType} from '../../../../../services/notification/notification.service';
+
+@Injectable()
+export class TransactionTypeNotificationEffects {
+
+  @Effect({ dispatch: false })
+  createTransactionTypeSuccess$: Observable<Action> = this.actions$
+    .ofType(transactionTypeActions.CREATE_SUCCESS)
+    .do(() => this.notificationService.send({
+      type: NotificationType.MESSAGE,
+      message: 'Transaction type is going to be created'
+    }));
+
+  @Effect({ dispatch: false })
+  updateTransactionTypeSuccess$: Observable<Action> = this.actions$
+    .ofType(transactionTypeActions.UPDATE_SUCCESS)
+    .do(() => this.notificationService.send({
+      type: NotificationType.MESSAGE,
+      message: 'Transaction type is going to be updated'
+    }));
+
+  constructor(private actions$: Actions, private notificationService: NotificationService) {}
+}
diff --git a/src/app/accounting/store/ledger/transaction-type/effects/route.effects.ts b/src/app/accounting/store/ledger/transaction-type/effects/route.effects.ts
new file mode 100644
index 0000000..08c7dcf
--- /dev/null
+++ b/src/app/accounting/store/ledger/transaction-type/effects/route.effects.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 {Injectable} from '@angular/core';
+import {Actions, Effect} from '@ngrx/effects';
+import {Observable} from 'rxjs/Observable';
+import {Action} from '@ngrx/store';
+import * as transactionTypeActions from '../transaction-type.actions';
+import {Router} from '@angular/router';
+
+@Injectable()
+export class TransactionTypeRouteEffects {
+
+  @Effect({ dispatch: false })
+  createTransactionTypeSuccess$: Observable<Action> = this.actions$
+    .ofType(transactionTypeActions.CREATE_SUCCESS)
+    .map(action => action.payload)
+    .do(payload => this.router.navigate(['../'], { relativeTo: payload.activatedRoute }));
+
+  @Effect({ dispatch: false })
+  updateTransactionTypeSuccess$: Observable<Action> = this.actions$
+    .ofType(transactionTypeActions.UPDATE_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/accounting/store/ledger/transaction-type/effects/service.effects.ts b/src/app/accounting/store/ledger/transaction-type/effects/service.effects.ts
new file mode 100644
index 0000000..96ccb67
--- /dev/null
+++ b/src/app/accounting/store/ledger/transaction-type/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 transactionTypeActions from '../transaction-type.actions';
+import {AccountingService} from '../../../../../services/accounting/accounting.service';
+import {emptySearchResult} from '../../../../../common/store/search.reducer';
+
+@Injectable()
+export class TransactionTypeApiEffects {
+
+  @Effect()
+  searchTransactionTypes$: Observable<Action> = this.actions$
+    .ofType(transactionTypeActions.SEARCH)
+    .map((action: transactionTypeActions.SearchAction) => action.payload)
+    .mergeMap(fetchRequest =>
+      this.accountingService.fetchTransactionTypes(fetchRequest)
+        .map(transactionTypePage => new transactionTypeActions.SearchCompleteAction({
+          elements: transactionTypePage.transactionTypes,
+          totalElements: transactionTypePage.totalElements,
+          totalPages: transactionTypePage.totalPages
+        }))
+        .catch(() => of(new transactionTypeActions.SearchCompleteAction(emptySearchResult())))
+    );
+
+  @Effect()
+  createTransactionType$: Observable<Action> = this.actions$
+    .ofType(transactionTypeActions.CREATE)
+    .map((action: transactionTypeActions.CreateTransactionTypeAction) => action.payload)
+    .mergeMap(payload =>
+      this.accountingService.createTransactionType(payload.transactionType)
+        .map(() => new transactionTypeActions.CreateTransactionTypeSuccessAction({
+          resource: payload.transactionType,
+          activatedRoute: payload.activatedRoute
+        }))
+        .catch((error) => of(new transactionTypeActions.CreateTransactionTypeFailAction(error)))
+    );
+
+  @Effect()
+  updateTransactionType$: Observable<Action> = this.actions$
+    .ofType(transactionTypeActions.UPDATE)
+    .map((action: transactionTypeActions.UpdateTransactionTypeAction) => action.payload)
+    .mergeMap(payload =>
+      this.accountingService.changeTransactionType(payload.transactionType)
+        .map(() => new transactionTypeActions.UpdateTransactionTypeSuccessAction({
+          resource: payload.transactionType,
+          activatedRoute: payload.activatedRoute
+        }))
+        .catch((error) => of(new transactionTypeActions.UpdateTransactionTypeFailAction(error)))
+    );
+
+  constructor(private actions$: Actions, private accountingService: AccountingService) { }
+}
diff --git a/src/app/accounting/store/ledger/transaction-type/transaction-type.actions.ts b/src/app/accounting/store/ledger/transaction-type/transaction-type.actions.ts
new file mode 100644
index 0000000..080a1fe
--- /dev/null
+++ b/src/app/accounting/store/ledger/transaction-type/transaction-type.actions.ts
@@ -0,0 +1,128 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 {Action} from '@ngrx/store';
+import {TransactionType} from '../../../../services/accounting/domain/transaction-type.model';
+import {RoutePayload} from '../../../../common/store/route-payload';
+import {SearchPayload, SearchResult} from '../../../../common/store/search.reducer';
+import {
+  CreateResourceSuccessPayload,
+  LoadResourcePayload,
+  SelectResourcePayload,
+  UpdateResourceSuccessPayload
+} from '../../../../common/store/resource.reducer';
+
+export const SEARCH = type('[Transaction Type] Search');
+export const SEARCH_COMPLETE = type('[Transaction Type] Search Complete');
+
+export const LOAD = type('[Transaction Type] Load');
+export const SELECT = type('[Transaction Type] Select');
+
+export const CREATE = type('[Transaction Type] Create');
+export const CREATE_SUCCESS = type('[Transaction Type] Create Success');
+export const CREATE_FAIL = type('[Transaction Type] Create Fail');
+
+export const UPDATE = type('[Transaction Type] Update');
+export const UPDATE_SUCCESS = type('[Transaction Type] Update Success');
+export const UPDATE_FAIL = type('[Transaction Type] Update Fail');
+
+export const RESET_FORM = type('[Transaction Type] Reset Form');
+
+export interface TransactionTypePayload extends RoutePayload {
+  transactionType: TransactionType;
+}
+
+export class SearchAction implements Action {
+  readonly type = SEARCH;
+
+  constructor(public payload: SearchPayload) { }
+}
+
+export class SearchCompleteAction implements Action {
+  readonly type = SEARCH_COMPLETE;
+
+  constructor(public payload: SearchResult) { }
+}
+
+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 CreateTransactionTypeAction implements Action {
+  readonly type = CREATE;
+
+  constructor(public payload: TransactionTypePayload) { }
+}
+
+export class CreateTransactionTypeSuccessAction implements Action {
+  readonly type = CREATE_SUCCESS;
+
+  constructor(public payload: CreateResourceSuccessPayload) { }
+}
+
+export class CreateTransactionTypeFailAction implements Action {
+  readonly type = CREATE_FAIL;
+
+  constructor(public payload: Error) { }
+}
+
+export class UpdateTransactionTypeAction implements Action {
+  readonly type = UPDATE;
+
+  constructor(public payload: TransactionTypePayload) { }
+}
+
+export class UpdateTransactionTypeSuccessAction implements Action {
+  readonly type = UPDATE_SUCCESS;
+
+  constructor(public payload: UpdateResourceSuccessPayload) { }
+}
+
+export class UpdateTransactionTypeFailAction implements Action {
+  readonly type = UPDATE_FAIL;
+
+  constructor(public payload: Error) { }
+}
+
+export class ResetTransactionTypeFormAction implements Action {
+  readonly type = RESET_FORM;
+
+  constructor() {}
+}
+
+export type Actions
+  = SearchAction
+  | SearchCompleteAction
+  | LoadAction
+  | SelectAction
+  | CreateTransactionTypeAction
+  | CreateTransactionTypeSuccessAction
+  | CreateTransactionTypeFailAction
+  | UpdateTransactionTypeAction
+  | UpdateTransactionTypeSuccessAction
+  | UpdateTransactionTypeFailAction
+  | ResetTransactionTypeFormAction;
diff --git a/src/app/accounting/store/ledger/trial-balance.reducer.ts b/src/app/accounting/store/ledger/trial-balance.reducer.ts
new file mode 100644
index 0000000..69f642a
--- /dev/null
+++ b/src/app/accounting/store/ledger/trial-balance.reducer.ts
@@ -0,0 +1,69 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 ledger from './ledger.actions';
+import {TrialBalance} from '../../../services/accounting/domain/trial-balance.model';
+
+
+export interface State {
+  trialBalance: TrialBalance;
+  includeEmpty: boolean;
+  loading: boolean;
+}
+
+const initialState: State = {
+  trialBalance: null,
+  includeEmpty: false,
+  loading: false
+};
+
+export function reducer(state = initialState, action: ledger.Actions): State {
+
+  switch (action.type) {
+
+    case ledger.LOAD_TRIAL_BALANCE: {
+      const includeEmpty = action.payload;
+
+      return Object.assign({}, state, {
+        includeEmpty,
+        loading: true
+      });
+    }
+
+    case ledger.LOAD_TRIAL_BALANCE_COMPLETE: {
+      const trialBalance = action.payload;
+
+      return {
+        trialBalance: trialBalance,
+        loading: false,
+        includeEmpty: state.includeEmpty
+      };
+    }
+
+    default: {
+      return state;
+    }
+  }
+}
+
+
+export const getTrialBalance = (state: State) => state.trialBalance;
+
+export const getLoading = (state: State) => state.loading;
+
+export const getIncludeEmpty = (state: State) => state.includeEmpty;
diff --git a/src/app/accounting/store/payroll/effects/notification.effects.ts b/src/app/accounting/store/payroll/effects/notification.effects.ts
new file mode 100644
index 0000000..a0a274b
--- /dev/null
+++ b/src/app/accounting/store/payroll/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 {NotificationService, NotificationType} from '../../../../services/notification/notification.service';
+import {Observable} from 'rxjs/Observable';
+import {Action} from '@ngrx/store';
+import * as payrollActions from '../../payroll/payroll-collection.actions';
+
+@Injectable()
+export class PayrollCollectionNotificationEffects {
+
+  @Effect({ dispatch: false })
+  createPayrollSuccess$: Observable<Action> = this.actions$
+    .ofType(payrollActions.CREATE_SUCCESS)
+    .do(() => this.notificationService.send({
+      type: NotificationType.MESSAGE,
+      message: 'Payroll is going to be created'
+    }));
+
+  constructor(private actions$: Actions, private notificationService: NotificationService) {}
+
+}
diff --git a/src/app/accounting/store/payroll/effects/route.effects.ts b/src/app/accounting/store/payroll/effects/route.effects.ts
new file mode 100644
index 0000000..254f62a
--- /dev/null
+++ b/src/app/accounting/store/payroll/effects/route.effects.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 {Injectable} from '@angular/core';
+import {Actions, Effect} from '@ngrx/effects';
+import {Router} from '@angular/router';
+import {Observable} from 'rxjs/Observable';
+import * as payrollActions from '../../payroll/payroll-collection.actions';
+import {Action} from '@ngrx/store';
+
+@Injectable()
+export class PayrollCollectionRouteEffects {
+
+  @Effect({ dispatch: false })
+  createPayrollSuccess$: Observable<Action> = this.actions$
+    .ofType(payrollActions.CREATE_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/accounting/store/payroll/effects/service.effects.ts b/src/app/accounting/store/payroll/effects/service.effects.ts
new file mode 100644
index 0000000..71330cb
--- /dev/null
+++ b/src/app/accounting/store/payroll/effects/service.effects.ts
@@ -0,0 +1,78 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 * as payrollActions from '../payroll-collection.actions';
+import {CreateSheetPayload} from '../payroll-collection.actions';
+import * as paymentActions from '../payment.actions';
+import {Action} from '@ngrx/store';
+import {of} from 'rxjs/observable/of';
+import {CreateResourceSuccessPayload} from '../../../../common/store/resource.reducer';
+import {emptySearchResult} from '../../../../common/store/search.reducer';
+import {PayrollService} from '../../../../services/payroll/payroll.service';
+
+@Injectable()
+export class PayrollCollectionApiEffects {
+
+  @Effect()
+  loadAllCollections$: Observable<Action> = this.actions$
+    .ofType(payrollActions.LOAD_ALL_COLLECTIONS)
+    .switchMap(() => this.payrollService.fetchDistributionHistory()
+      .map(payrolls => new payrollActions.LoadAllCompleteAction(payrolls))
+      .catch(() => of(new payrollActions.LoadAllCompleteAction([])))
+    );
+
+  @Effect()
+  createSheet$: Observable<Action> = this.actions$
+    .ofType(payrollActions.CREATE)
+    .map((action: payrollActions.CreateAction) => action.payload)
+    .switchMap(payload => this.payrollService.distribute(payload.sheet)
+      .map(() => new payrollActions.CreateSuccessAction(this.map(payload)))
+      .catch(error => of(new payrollActions.CreateFailAction(error)))
+    );
+
+  @Effect()
+  searchPayments$: Observable<Action> = this.actions$
+    .ofType(paymentActions.SEARCH)
+    .map((action: paymentActions.SearchAction) => action.payload)
+    .mergeMap(payload =>
+      this.payrollService.fetchPayments(payload.payrollIdentifier, payload.fetchRequest)
+        .map(payrollPaymentPage => new paymentActions.SearchCompleteAction({
+          elements: payrollPaymentPage.payrollPayments,
+          totalElements: payrollPaymentPage.totalElements,
+          totalPages: payrollPaymentPage.totalPages
+        }))
+        .catch(() => of(new paymentActions.SearchCompleteAction(emptySearchResult())))
+    );
+
+  private map(payload: CreateSheetPayload): CreateResourceSuccessPayload {
+    return {
+      resource: {
+        identifier: Date.now().toString(),
+        sourceAccountNumber: payload.sheet.sourceAccountNumber,
+        createdBy: '',
+        createdOn: new Date().toISOString()
+      },
+      activatedRoute: payload.activatedRoute
+    };
+  }
+
+  constructor(private actions$: Actions, private payrollService: PayrollService) {}
+}
diff --git a/src/app/accounting/store/payroll/payment.actions.ts b/src/app/accounting/store/payroll/payment.actions.ts
new file mode 100644
index 0000000..be459ce
--- /dev/null
+++ b/src/app/accounting/store/payroll/payment.actions.ts
@@ -0,0 +1,44 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 {SearchPayload, SearchResult} from '../../../common/store/search.reducer';
+import {Action} from '@ngrx/store';
+
+export const SEARCH = type('[Payroll Payment] Search');
+export const SEARCH_COMPLETE = type('[Payroll Payment] Search Complete');
+
+export interface PaymentSearchPayload extends SearchPayload {
+  payrollIdentifier: string;
+}
+
+export class SearchAction implements Action {
+  readonly type = SEARCH;
+
+  constructor(public payload: PaymentSearchPayload) { }
+}
+
+export class SearchCompleteAction implements Action {
+  readonly type = SEARCH_COMPLETE;
+
+  constructor(public payload: SearchResult) { }
+}
+
+export type Actions
+  = SearchAction
+  | SearchCompleteAction;
diff --git a/src/app/accounting/store/payroll/payroll-collection.actions.ts b/src/app/accounting/store/payroll/payroll-collection.actions.ts
new file mode 100644
index 0000000..346f5fc
--- /dev/null
+++ b/src/app/accounting/store/payroll/payroll-collection.actions.ts
@@ -0,0 +1,98 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 {Action} from '@ngrx/store';
+import {RoutePayload} from '../../../common/store/route-payload';
+import {PayrollCollectionSheet} from '../../../services/payroll/domain/payroll-collection-sheet.model';
+import {PayrollCollectionHistory} from '../../../services/payroll/domain/payroll-collection-history.model';
+import {CreateResourceSuccessPayload} from '../../../common/store/resource.reducer';
+
+export const LOAD_ALL_COLLECTIONS = type('[Payroll Collection] Load All');
+export const LOAD_ALL_COLLECTIONS_COMPLETE = type('[Payroll Collection] Load All Complete');
+
+export const LOAD = type('[Payroll Collection] Load');
+export const SELECT = type('[Payroll Collection] Select');
+
+export const CREATE = type('[Payroll Collection] Create');
+export const CREATE_SUCCESS = type('[Payroll Collection] Create Success');
+export const CREATE_FAIL = type('[Payroll Collection] Create Fail');
+
+export const RESET_FORM = type('[Payroll Collection] Reset Form');
+
+export interface CreateSheetPayload extends RoutePayload {
+  sheet: PayrollCollectionSheet;
+}
+
+export class LoadAllAction implements Action {
+  readonly type = LOAD_ALL_COLLECTIONS;
+
+  constructor() { }
+}
+
+export class LoadAllCompleteAction implements Action {
+  readonly type = LOAD_ALL_COLLECTIONS_COMPLETE;
+
+  constructor(public payload: PayrollCollectionHistory[]) { }
+}
+
+export class LoadAction implements Action {
+  readonly type = LOAD;
+
+  constructor(public payload: PayrollCollectionHistory) { }
+}
+
+export class SelectAction implements Action {
+  readonly type = SELECT;
+
+  constructor(public payload: string) { }
+}
+
+export class CreateAction implements Action {
+  readonly type = CREATE;
+
+  constructor(public payload: CreateSheetPayload) { }
+}
+
+export class CreateSuccessAction implements Action {
+  readonly type = CREATE_SUCCESS;
+
+  constructor(public payload: CreateResourceSuccessPayload) { }
+}
+
+export class CreateFailAction implements Action {
+  readonly type = CREATE_FAIL;
+
+  constructor(public payload: Error) { }
+}
+
+export class ResetFormAction implements Action {
+  readonly type = RESET_FORM;
+
+  constructor() { }
+}
+
+export type Actions
+  = LoadAllAction
+  | LoadAllCompleteAction
+  | LoadAction
+  | SelectAction
+  | CreateAction
+  | CreateSuccessAction
+  | CreateFailAction
+  | ResetFormAction;
diff --git a/src/app/accounting/store/payroll/payrolls.reducer.ts b/src/app/accounting/store/payroll/payrolls.reducer.ts
new file mode 100644
index 0000000..37b96cc
--- /dev/null
+++ b/src/app/accounting/store/payroll/payrolls.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 {ResourceState} from '../../../common/store/resource.reducer';
+import {idsToHashWithCurrentTimestamp, resourcesToHash} from '../../../common/store/reducer.helper';
+import * as payroll from './payroll-collection.actions';
+import {PayrollCollectionHistory} from '../../../services/payroll/domain/payroll-collection-history.model';
+
+export const initialState: ResourceState = {
+  ids: [],
+  entities: {},
+  loadedAt: {},
+  selectedId: null,
+};
+
+export function reducer(state = initialState, action: payroll.Actions): ResourceState {
+
+  switch (action.type) {
+
+    case payroll.LOAD_ALL_COLLECTIONS: {
+      return initialState;
+    }
+
+    case payroll.LOAD_ALL_COLLECTIONS_COMPLETE: {
+      const payrolls: PayrollCollectionHistory[] = action.payload;
+
+      const ids = payrolls.map(payroll => payroll.identifier);
+
+      const entities = resourcesToHash(payrolls);
+
+      const loadedAt = idsToHashWithCurrentTimestamp(ids);
+
+      return {
+        ids: [ ...ids ],
+        entities: entities,
+        loadedAt: loadedAt,
+        selectedId: state.selectedId
+      };
+    }
+
+    default: {
+      return state;
+    }
+  }
+}
diff --git a/src/app/accounting/subLedger/sub-ledger.component.html b/src/app/accounting/subLedger/sub-ledger.component.html
new file mode 100644
index 0000000..ca721b3
--- /dev/null
+++ b/src/app/accounting/subLedger/sub-ledger.component.html
@@ -0,0 +1,18 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<router-outlet></router-outlet>
diff --git a/src/app/accounting/subLedger/sub-ledger.component.ts b/src/app/accounting/subLedger/sub-ledger.component.ts
new file mode 100644
index 0000000..299f988
--- /dev/null
+++ b/src/app/accounting/subLedger/sub-ledger.component.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 {Component, OnDestroy, OnInit} from '@angular/core';
+import {SelectAction} from '../store/ledger/ledger.actions';
+import {AccountingStore} from '../store/index';
+import {Subscription} from 'rxjs/Subscription';
+import {ActivatedRoute} from '@angular/router';
+
+@Component({
+  templateUrl: './sub-ledger.component.html'
+})
+export class SubLedgerComponent implements OnInit, OnDestroy {
+
+  private actionsSubscription: Subscription;
+
+  constructor(private route: ActivatedRoute, private store: AccountingStore) {}
+
+  ngOnInit(): void {
+    this.actionsSubscription = this.route.params
+      .map(params => new SelectAction(params['id']))
+      .subscribe(this.store);
+  }
+
+  ngOnDestroy(): void {
+    this.actionsSubscription.unsubscribe();
+  }
+}
diff --git a/src/app/accounting/subLedger/sub-ledger.detail.component.html b/src/app/accounting/subLedger/sub-ledger.detail.component.html
new file mode 100644
index 0000000..ec0d1bf
--- /dev/null
+++ b/src/app/accounting/subLedger/sub-ledger.detail.component.html
@@ -0,0 +1,63 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<fims-layout-card-over title="{{ ledger.identifier + ' - ' + ledger.name }}" [subTitle]="ledger.description" [navigateBackTo]="ledger.parentLedgerIdentifier ? ['../../', ledger.parentLedgerIdentifier, 'ledgers'] : ['../../../../']">
+  <fims-layout-card-over-header-menu layout="row" layout-align="end center">
+    <a mat-icon-button [routerLink]="['edit']" *hasPermission="{ id: 'accounting_ledgers', accessLevel: 'CHANGE' }"><mat-icon>mode_edit</mat-icon></a>
+    <button mat-icon-button (click)="deleteLedger()" title="{{'Delete this ledger' | translate}}" *hasPermission="{ id: 'accounting_ledgers', accessLevel: 'DELETE' }"><mat-icon>delete</mat-icon></button>
+  </fims-layout-card-over-header-menu>
+  <fims-two-column-layout>
+    <mat-nav-list left>
+      <h3 mat-subheader translate>Details</h3>
+      <a mat-list-item [routerLink]="['./ledgers']">
+        <mat-icon matListAvatar>library_books</mat-icon>
+        <h3 matLine translate>Subledgers</h3>
+        <p matLine translate>View subledgers</p>
+      </a>
+    </mat-nav-list>
+    <ng-container right>
+      <div layout="row">
+        <mat-list>
+          <mat-list-item>
+            <mat-icon matListAvatar>account_balance</mat-icon>
+            <h3 matLine translate>Type</h3>
+            <p matLine>{{ledger.type}}</p>
+          </mat-list-item>
+          <mat-list-item>
+            <h3 matLine translate>Balance</h3>
+            <p matLine>{{ledger.totalValue | displayFimsNumber}}</p>
+          </mat-list-item>
+        </mat-list>
+      </div>
+      <div layout="row">
+        <div layout="column" flex-gt-md="100">
+          <h3 translate>Accounts</h3>
+          <fims-data-table flex
+                           (onFetch)="fetchAccounts($event)"
+                           (onActionCellClick)="rowSelect($event)"
+                           [columns]="columns"
+                           [data]="accountData$ | async"
+                           [loading]="loading$ | async"
+                           [sortable]="true"
+                           [pageable]="true">
+          </fims-data-table>
+        </div>
+      </div>
+    </ng-container>
+  </fims-two-column-layout>
+</fims-layout-card-over>
+<fims-fab-button title="{{'Create account' | translate}}" icon="add" [link]="['accounts/create']" [permission]="{ id: 'accounting_accounts', accessLevel: 'CHANGE'}"></fims-fab-button>
diff --git a/src/app/accounting/subLedger/sub-ledger.detail.component.ts b/src/app/accounting/subLedger/sub-ledger.detail.component.ts
new file mode 100644
index 0000000..dc9db69
--- /dev/null
+++ b/src/app/accounting/subLedger/sub-ledger.detail.component.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 {Component, OnDestroy, OnInit} from '@angular/core';
+import {ActivatedRoute, Router} from '@angular/router';
+import {Ledger} from '../../services/accounting/domain/ledger.model';
+import {TableData, TableFetchRequest} from '../../common/data-table/data-table.component';
+import {Observable} from 'rxjs/Observable';
+import {Subscription} from 'rxjs/Subscription';
+import {TdDialogService} from '@covalent/core';
+import {TranslateService} from '@ngx-translate/core';
+import * as fromAccounting from '../store';
+import * as fromRoot from '../../store';
+import {DELETE} from '../store/ledger/ledger.actions';
+import {AccountingStore} from '../store/index';
+import {SEARCH_BY_LEDGER} from '../../store/account/account.actions';
+import {FetchRequest} from '../../services/domain/paging/fetch-request.model';
+import {Account} from '../../services/accounting/domain/account.model';
+
+@Component({
+  templateUrl: './sub-ledger.detail.component.html'
+})
+export class SubLedgerDetailComponent implements OnInit, OnDestroy {
+
+  private ledgerSubscription: Subscription;
+
+  private lastFetchRequest: FetchRequest = {};
+
+  ledger: Ledger;
+
+  accountData$: Observable<TableData>;
+
+  loading$: Observable<boolean>;
+
+  columns: any[] = [
+    { name: 'identifier', label: 'Id' },
+    { name: 'name', label: 'Name' },
+    { name: 'state', label: 'State' },
+    { name: 'balance', label: 'Balance' }
+  ];
+
+  constructor(private router: Router, private route: ActivatedRoute, private dialogService: TdDialogService,
+              private translate: TranslateService, private store: AccountingStore) {}
+
+  ngOnInit(): void {
+    this.ledgerSubscription = this.store.select(fromAccounting.getSelectedLedger)
+      .filter(ledger => !!ledger)
+      .subscribe(ledger => {
+        this.ledger = ledger;
+        this.fetchAccounts();
+      });
+
+    this.accountData$ = this.store.select(fromRoot.getAccountSearchResults)
+      .map(accountPage => ({
+        data: accountPage.accounts,
+        totalElements: accountPage.totalElements,
+        totalPages: accountPage.totalPages
+      }));
+
+    this.loading$ = this.store.select(fromRoot.getAccountSearchLoading);
+  }
+
+  ngOnDestroy(): void {
+    this.ledgerSubscription.unsubscribe();
+  }
+
+  rowSelect(account: Account): void {
+    this.router.navigate(['../../../../accounts/detail', account.identifier], { relativeTo: this.route });
+  }
+
+  confirmDeletion(): Observable<boolean> {
+    const message = 'Do you want to delete this ledger?';
+    const title = 'Confirm deletion';
+    const button = 'DELETE LEDGER';
+
+    return this.translate.get([title, message, button])
+      .flatMap(result =>
+        this.dialogService.openConfirm({
+          message: result[message],
+          title: result[title],
+          acceptButton: result[button]
+        }).afterClosed()
+    );
+  }
+
+  deleteLedger(): void {
+    this.confirmDeletion()
+      .filter(accept => accept)
+      .subscribe(() => {
+        this.store.dispatch({ type: DELETE, payload: {
+          ledger: this.ledger,
+          activatedRoute: this.route
+        }});
+      });
+  }
+
+  fetchAccounts(fetchRequest?: TableFetchRequest): void {
+    if (fetchRequest) {
+      this.lastFetchRequest = fetchRequest;
+    }
+
+    this.store.dispatch({ type: SEARCH_BY_LEDGER, payload: {
+      ledgerId: this.ledger.identifier,
+      fetchRequest: this.lastFetchRequest
+    } });
+
+  }
+
+}
diff --git a/src/app/accounting/subLedger/sub-ledger.list.component.html b/src/app/accounting/subLedger/sub-ledger.list.component.html
new file mode 100644
index 0000000..768a0ef
--- /dev/null
+++ b/src/app/accounting/subLedger/sub-ledger.list.component.html
@@ -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.
+-->
+
+<fims-layout-card-over title="{{ ledger.identifier + ' - ' + ledger.name }}" [subTitle]="ledger.description" [navigateBackTo]="ledger.parentLedgerIdentifier ? ['../'] : ['../../../../']">
+  <fims-layout-card-over-header-menu layout="row" layout-align="end center">
+    <a mat-icon-button [routerLink]="['edit']" *hasPermission="{ id: 'accounting_ledgers', accessLevel: 'CHANGE' }"><mat-icon>mode_edit</mat-icon></a>
+    <button mat-icon-button (click)="deleteLedger()" title="{{'Delete this ledger' | translate}}" *hasPermission="{ id: 'accounting_ledgers', accessLevel: 'DELETE' }"><mat-icon>delete</mat-icon></button>
+  </fims-layout-card-over-header-menu>
+  <div class="mat-content inset" flex>
+    <div layout="row">
+      <mat-list>
+        <mat-list-item>
+          <mat-icon matListAvatar>account_balance</mat-icon>
+          <h3 matLine translate>Type</h3>
+          <p matLine>{{ledger.type}}</p>
+        </mat-list-item>
+        <mat-list-item>
+          <h3 matLine translate>Balance</h3>
+          <p matLine>{{ledger.totalValue | displayFimsNumber}}</p>
+        </mat-list-item>
+      </mat-list>
+    </div>
+    <div layout="row">
+      <div layout="column" flex-gt-md="100">
+        <h3 translate>Subledgers</h3>
+        <fims-data-table flex
+                         (onActionCellClick)="rowSelect($event)"
+                         [columns]="columns"
+                         [data]="ledgerData">
+        </fims-data-table>
+      </div>
+    </div>
+  </div>
+</fims-layout-card-over>
+<fims-fab-button title="{{'Add subledger' | translate}}" icon="add" [link]="['create']" [permission]="{ id: 'accounting_ledgers', accessLevel: 'CHANGE'}"></fims-fab-button>
diff --git a/src/app/accounting/subLedger/sub-ledger.list.component.ts b/src/app/accounting/subLedger/sub-ledger.list.component.ts
new file mode 100644
index 0000000..4540d2e
--- /dev/null
+++ b/src/app/accounting/subLedger/sub-ledger.list.component.ts
@@ -0,0 +1,109 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, OnDestroy, OnInit} from '@angular/core';
+import {Subscription} from 'rxjs/Subscription';
+import {Ledger} from '../../services/accounting/domain/ledger.model';
+import {TableData} from '../../common/data-table/data-table.component';
+import {AccountingStore} from '../store/index';
+import {ActivatedRoute, Router} from '@angular/router';
+import * as fromAccounting from '../store';
+import {Observable} from 'rxjs/Observable';
+import {DELETE} from '../store/ledger/ledger.actions';
+import {TranslateService} from '@ngx-translate/core';
+import {TdDialogService} from '@covalent/core';
+
+@Component({
+  templateUrl: './sub-ledger.list.component.html'
+})
+export class SubLedgerListComponent implements OnInit, OnDestroy {
+
+  private selectionSubscription: Subscription;
+
+  private _ledger: Ledger;
+
+  ledgerData: TableData = {
+    totalElements: 0,
+    totalPages: 1,
+    data: []
+  };
+
+  columns: any[] = [
+    { name: 'identifier', label: 'Id' },
+    { name: 'name', label: 'Name' },
+    { name: 'description', label: 'Description' },
+    { name: 'totalValue', label: 'Balance', format: value => value ? value.toFixed(2) : '-' }
+  ];
+
+  constructor(private route: ActivatedRoute, private router: Router, private store: AccountingStore, private translate: TranslateService,
+              private dialogService: TdDialogService) {}
+
+  ngOnInit(): void {
+    this.selectionSubscription = this.store.select(fromAccounting.getSelectedLedger)
+      .filter(ledger => !!ledger)
+      .subscribe(ledger => this.ledger = ledger);
+  }
+
+  ngOnDestroy(): void {
+    this.selectionSubscription.unsubscribe();
+  }
+
+  rowSelect(ledger: Ledger): void {
+    this.router.navigate(['/accounting/ledgers/detail', ledger.identifier]);
+  }
+
+  set ledger(ledger: Ledger) {
+    this._ledger = ledger;
+
+    if (ledger.subLedgers) {
+      this.ledgerData.data = ledger.subLedgers;
+      this.ledgerData.totalElements = ledger.subLedgers.length;
+    }
+  }
+
+  get ledger(): Ledger {
+    return this._ledger;
+  }
+
+  confirmDeletion(): Observable<boolean> {
+    const message = 'Do you want to delete this ledger?';
+    const title = 'Confirm deletion';
+    const button = 'DELETE LEDGER';
+
+    return this.translate.get([title, message, button])
+      .flatMap(result =>
+        this.dialogService.openConfirm({
+          message: result[message],
+          title: result[title],
+          acceptButton: result[button]
+        }).afterClosed()
+      );
+  }
+
+  deleteLedger(): void {
+    this.confirmDeletion()
+      .filter(accept => accept)
+      .subscribe(() => {
+        this.store.dispatch({ type: DELETE, payload: {
+          ledger: this.ledger,
+          activatedRoute: this.route
+        }});
+      });
+  }
+
+}
diff --git a/src/app/accounting/trailBalance/trail-balance.component.html b/src/app/accounting/trailBalance/trail-balance.component.html
new file mode 100644
index 0000000..9d7120b
--- /dev/null
+++ b/src/app/accounting/trailBalance/trail-balance.component.html
@@ -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.
+-->
+
+<fims-layout-card-over title="{{'Trial balance' | translate}}" [navigateBackTo]="['../']">
+  <fims-layout-card-over-header-menu>
+    <mat-checkbox [(ngModel)]="includeEmptyEntries" (change)="fetchTrialBalance($event)" translate>Include empty entries?</mat-checkbox>
+  </fims-layout-card-over-header-menu>
+  <div layout="row">
+    <table td-data-table>
+      <thead>
+        <tr td-data-table-column-row>
+          <th td-data-table-column>
+            <span translate>Ledger</span>
+          </th>
+          <th td-data-table-column>
+            <span translate>Debit</span>
+          </th>
+          <th td-data-table-column>
+            <span translate>Credit</span>
+          </th>
+        </tr>
+      </thead>
+      <tbody>
+        <tr td-data-table-row *ngFor="let row of (trialBalance$ | async)?.trialBalanceEntries">
+          <td td-data-table-cell>
+            <a [routerLink]="['../ledgers/detail', row['ledger'].identifier]" *hasPermission="{ id: 'accounting_ledgers', accessLevel: 'READ' }">{{row['ledger'].identifier }} - {{row['ledger'].name }}</a>
+          </td>
+          <td td-data-table-cell>
+            {{row['type'] == 'DEBIT' ? (row['amount'] | number): '-'}}
+          </td>
+          <td td-data-table-cell>
+            {{row['type'] == 'CREDIT' ? (row['amount'] | number) : '-'}}
+          </td>
+        </tr>
+        <tr td-data-table-row>
+          <td td-data-table-cell translate>Total</td>
+          <td td-data-table-cell>
+            {{(trialBalance$ | async)?.debitTotal | number}}
+          </td>
+          <td td-data-table-cell>
+            {{(trialBalance$ | async)?.creditTotal | number}}
+          </td>
+        </tr>
+      </tbody>
+    </table>
+  </div>
+</fims-layout-card-over>
diff --git a/src/app/accounting/trailBalance/trial-balance.component.ts b/src/app/accounting/trailBalance/trial-balance.component.ts
new file mode 100644
index 0000000..63e1b9d
--- /dev/null
+++ b/src/app/accounting/trailBalance/trial-balance.component.ts
@@ -0,0 +1,47 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, OnInit} from '@angular/core';
+import {TrialBalance} from '../../services/accounting/domain/trial-balance.model';
+import * as fromAccounting from '../store';
+import {LOAD_TRIAL_BALANCE} from '../store/ledger/ledger.actions';
+import {Observable} from 'rxjs/Observable';
+import {AccountingStore} from '../store/index';
+import {MatCheckboxChange} from '@angular/material';
+
+@Component({
+  templateUrl: './trail-balance.component.html'
+})
+export class TrailBalanceComponent implements OnInit {
+
+  includeEmptyEntries = false;
+
+  trialBalance$: Observable<TrialBalance>;
+
+  constructor(private store: AccountingStore) {}
+
+  ngOnInit(): void {
+    this.trialBalance$ = this.store.select(fromAccounting.getTrialBalance);
+    this.fetchTrialBalance();
+  }
+
+  fetchTrialBalance(event?: MatCheckboxChange): void {
+    this.store.dispatch({ type: LOAD_TRIAL_BALANCE, payload: this.includeEmptyEntries });
+  }
+
+}
diff --git a/src/app/accounting/transaction-type/transaction-type.component.html b/src/app/accounting/transaction-type/transaction-type.component.html
deleted file mode 100644
index 6090aaa..0000000
--- a/src/app/accounting/transaction-type/transaction-type.component.html
+++ /dev/null
@@ -1,42 +0,0 @@
-<div class="main-div mat-elevation-z2">
-  <h3 class="heading">Transaction Types</h3>
-  <mat-divider></mat-divider>
-  <div class="fineract-button">
-  <a mat-raised-button  color="primary" [routerLink]="['/navbar/add_transaction_type']">
-    <mat-icon>add</mat-icon>Add Transaction Type</a>
-    </div>
-  <mat-form-field>
-    <input matInput (keyup)="applyFilter($event.target.value)" placeholder="Filter by name/code">
-  </mat-form-field>
-  
-<br>
-<mat-divider></mat-divider>
-
-<mat-table #table [dataSource]="dataSource">
-
-  <!-- Position Column -->
-  <ng-container matColumnDef="code">
-    <mat-header-cell *matHeaderCellDef>Code</mat-header-cell>
-    <mat-cell *matCellDef="let element">
-      <a routerLink="{{element.code}}">{{element.code}}</a>
-    </mat-cell>
-  </ng-container>
-
-  <!-- Name Column -->
-  <ng-container matColumnDef="name">
-    <mat-header-cell *matHeaderCellDef>Name</mat-header-cell>
-    <mat-cell *matCellDef="let element"> {{element.name}} </mat-cell>
-  </ng-container>
-
-  <ng-container matColumnDef="description">
-      <mat-header-cell *matHeaderCellDef>Description</mat-header-cell>
-      <mat-cell *matCellDef="let element"> {{element.description}} </mat-cell>
-    </ng-container>
-
-  <mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
-  <mat-row *matRowDef="let row; columns: displayedColumns;"></mat-row>
-</mat-table>
-  
-  
-</div>
-  
\ No newline at end of file
diff --git a/src/app/accounting/transaction-type/transaction-type.component.scss b/src/app/accounting/transaction-type/transaction-type.component.scss
deleted file mode 100644
index 87b71f6..0000000
--- a/src/app/accounting/transaction-type/transaction-type.component.scss
+++ /dev/null
@@ -1,16 +0,0 @@
-.my-div{
-    margin-left: 2%;
-    margin-right:2%;
-    border-radius: 5px 5px 5px 5px;
-    background-color: #e6e6ff;
-    min-height: 100%;
-   
-  }
-  table {
-      width: 100%;
-    }
-    
-    .mat-form-field {
-      font-size: 14px;
-      width: 100%;
-    }
\ No newline at end of file
diff --git a/src/app/accounting/transaction-type/transaction-type.component.spec.ts b/src/app/accounting/transaction-type/transaction-type.component.spec.ts
deleted file mode 100644
index 8c544db..0000000
--- a/src/app/accounting/transaction-type/transaction-type.component.spec.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-import { async, ComponentFixture, TestBed } from '@angular/core/testing';
-
-import { TransactionTypeComponent } from './transaction-type.component';
-
-describe('TransactionTypeComponent', () => {
-  let component: TransactionTypeComponent;
-  let fixture: ComponentFixture<TransactionTypeComponent>;
-
-  beforeEach(async(() => {
-    TestBed.configureTestingModule({
-      declarations: [ TransactionTypeComponent ]
-    })
-    .compileComponents();
-  }));
-
-  beforeEach(() => {
-    fixture = TestBed.createComponent(TransactionTypeComponent);
-    component = fixture.componentInstance;
-    fixture.detectChanges();
-  });
-
-  it('should create', () => {
-    expect(component).toBeTruthy();
-  });
-});
diff --git a/src/app/accounting/transaction-type/transaction-type.component.ts b/src/app/accounting/transaction-type/transaction-type.component.ts
deleted file mode 100644
index 847bb6c..0000000
--- a/src/app/accounting/transaction-type/transaction-type.component.ts
+++ /dev/null
@@ -1,34 +0,0 @@
-import { Component, OnInit } from '@angular/core';
-import {MatTableDataSource} from '@angular/material';
-
-@Component({
-  selector: 'app-transaction-type',
-  templateUrl: './transaction-type.component.html',
-  styleUrls: ['./transaction-type.component.scss']
-})
-export class TransactionTypeComponent implements OnInit {
-  displayedColumns = ['code','name','description'];
-  dataSource = new MatTableDataSource(ELEMENT_DATA);
-
-  applyFilter(filterValue: string) {
-    filterValue = filterValue.trim(); // Remove whitespace
-    filterValue = filterValue.toLowerCase(); // MatTableDataSource defaults to lowercase matches
-    this.dataSource.filter = filterValue;
-  }
-
-  constructor() { }
-
-  ngOnInit() {
-  }
-
-}
-export interface Element {
-  code: string;
-  name: string;
-  description: string;
-  
-}
-
-const ELEMENT_DATA: Element[] = [
-  
-];
diff --git a/src/app/accounting/transactionTypes/form/create/create.form.component.html b/src/app/accounting/transactionTypes/form/create/create.form.component.html
new file mode 100644
index 0000000..9e0c416
--- /dev/null
+++ b/src/app/accounting/transactionTypes/form/create/create.form.component.html
@@ -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.
+-->
+
+<fims-layout-card-over title="{{'Create new transaction type' | translate}}">
+  <fims-transaction-type-form #form
+                           (onSave)="onSave($event)"
+                           (onCancel)="onCancel()"
+                           [transactionType]="transactionType">
+  </fims-transaction-type-form>
+</fims-layout-card-over>
diff --git a/src/app/accounting/transactionTypes/form/create/create.form.component.ts b/src/app/accounting/transactionTypes/form/create/create.form.component.ts
new file mode 100644
index 0000000..3f5bb10
--- /dev/null
+++ b/src/app/accounting/transactionTypes/form/create/create.form.component.ts
@@ -0,0 +1,73 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, OnDestroy, OnInit, ViewChild} from '@angular/core';
+import {Subscription} from 'rxjs/Subscription';
+import {ActivatedRoute, Router} from '@angular/router';
+import * as fromAccounting from '../../../store/index';
+import {AccountingStore} from '../../../store/index';
+import {Error} from '../../../../services/domain/error.model';
+import {TransactionType} from '../../../../services/accounting/domain/transaction-type.model';
+import {CREATE, RESET_FORM} from '../../../store/ledger/transaction-type/transaction-type.actions';
+import {TransactionTypeFormComponent} from '../transaction-type-form.component';
+
+@Component({
+  templateUrl: './create.form.component.html'
+})
+export class CreateTransactionTypeFormComponent implements OnInit, OnDestroy {
+
+  private formStateSubscription: Subscription;
+
+  @ViewChild('form') formComponent: TransactionTypeFormComponent;
+
+  transactionType: TransactionType = {
+    code: '',
+    name: ''
+  };
+
+  constructor(private router: Router, private route: ActivatedRoute, private store: AccountingStore) {}
+
+  ngOnInit() {
+    this.formStateSubscription = this.store.select(fromAccounting.getTransactionTypeFormError)
+      .filter((error: Error) => !!error)
+      .subscribe((error: Error) => {
+        this.formComponent.showNumberValidationError();
+      });
+  }
+
+  ngOnDestroy(): void {
+    this.formStateSubscription.unsubscribe();
+
+    this.store.dispatch({ type: RESET_FORM });
+  }
+
+  onSave(transactionType: TransactionType) {
+    this.store.dispatch({ type: CREATE, payload: {
+      transactionType,
+      activatedRoute: this.route
+    } });
+  }
+
+  onCancel() {
+    this.navigateAway();
+  }
+
+  navigateAway(): void {
+    this.router.navigate(['../'], { relativeTo: this.route });
+  }
+}
diff --git a/src/app/accounting/transactionTypes/form/edit/edit.form.component.html b/src/app/accounting/transactionTypes/form/edit/edit.form.component.html
new file mode 100644
index 0000000..c295aca
--- /dev/null
+++ b/src/app/accounting/transactionTypes/form/edit/edit.form.component.html
@@ -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.
+-->
+
+<fims-layout-card-over title="{{'Edit transaction type' | translate}}">
+  <fims-transaction-type-form #form
+                              (onSave)="onSave($event)"
+                              (onCancel)="onCancel()"
+                              [editMode]="true"
+                              [transactionType]="transactionType$ | async">
+  </fims-transaction-type-form>
+</fims-layout-card-over>
diff --git a/src/app/accounting/transactionTypes/form/edit/edit.form.component.ts b/src/app/accounting/transactionTypes/form/edit/edit.form.component.ts
new file mode 100644
index 0000000..7fb5b3c
--- /dev/null
+++ b/src/app/accounting/transactionTypes/form/edit/edit.form.component.ts
@@ -0,0 +1,65 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, OnDestroy, OnInit} from '@angular/core';
+import {ActivatedRoute, Router} from '@angular/router';
+import * as fromAccounting from '../../../store/index';
+import {AccountingStore} from '../../../store/index';
+import {TransactionType} from '../../../../services/accounting/domain/transaction-type.model';
+import {SelectAction, UPDATE} from '../../../store/ledger/transaction-type/transaction-type.actions';
+import {Observable} from 'rxjs/Observable';
+import {Subscription} from 'rxjs/Subscription';
+
+@Component({
+  templateUrl: './edit.form.component.html'
+})
+export class EditTransactionTypeFormComponent implements OnInit, OnDestroy {
+
+  private actionsSubscription: Subscription;
+
+  transactionType$: Observable<TransactionType>;
+
+  constructor(private router: Router, private route: ActivatedRoute, private store: AccountingStore) {}
+
+  ngOnInit() {
+    this.actionsSubscription = this.route.params
+      .map(params => new SelectAction(params['code']))
+      .subscribe(this.store);
+
+    this.transactionType$ = this.store.select(fromAccounting.getSelectedTransactionType);
+  }
+
+  ngOnDestroy(): void {
+    this.actionsSubscription.unsubscribe();
+  }
+
+  onSave(transactionType: TransactionType) {
+    this.store.dispatch({ type: UPDATE, payload: {
+      transactionType,
+      activatedRoute: this.route
+    } });
+  }
+
+  onCancel() {
+    this.navigateAway();
+  }
+
+  navigateAway(): void {
+    this.router.navigate(['../../'], { relativeTo: this.route });
+  }
+}
diff --git a/src/app/accounting/transactionTypes/form/transaction-type-form.component.html b/src/app/accounting/transactionTypes/form/transaction-type-form.component.html
new file mode 100644
index 0000000..5faa964
--- /dev/null
+++ b/src/app/accounting/transactionTypes/form/transaction-type-form.component.html
@@ -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.
+-->
+
+<td-steps mode="'vertical'">
+  <td-step #detailsStep label="{{'Transaction type' | translate}}" [state]="form.valid ? 'complete' : form.pristine ? 'none' : 'required'">
+    <form [formGroup]="form" layout="column">
+      <fims-id-input [form]="form" controlName="code" [placeholder]="'Code'" [readonly]="editMode"></fims-id-input>
+      <fims-text-input [form]="form" controlName="name" placeholder="{{'Name' | translate}}"></fims-text-input>
+      <fims-text-input [form]="form" controlName="description" placeholder="{{'Description(optional)' | translate}}"></fims-text-input>
+    </form>
+    <ng-template td-step-actions>
+      <fims-form-final-action
+        [resourceName]="'TRANSACTION TYPE'"
+        [editMode]="editMode"
+        [disabled]="!form.valid"
+        (onCancel)="cancel()"
+        (onSave)="save()">
+      </fims-form-final-action>
+    </ng-template>
+  </td-step>
+</td-steps>
diff --git a/src/app/accounting/transactionTypes/form/transaction-type-form.component.spec.ts b/src/app/accounting/transactionTypes/form/transaction-type-form.component.spec.ts
new file mode 100644
index 0000000..ca63cef
--- /dev/null
+++ b/src/app/accounting/transactionTypes/form/transaction-type-form.component.spec.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 {Component, DebugElement} from '@angular/core';
+import {async, ComponentFixture, TestBed} from '@angular/core/testing';
+import {By} from '@angular/platform-browser';
+import {TranslateModule} from '@ngx-translate/core';
+import {ReactiveFormsModule} from '@angular/forms';
+import {FimsSharedModule} from '../../../common/common.module';
+import {CovalentStepsModule} from '@covalent/core';
+import {NoopAnimationsModule} from '@angular/platform-browser/animations';
+import {TransactionTypeFormComponent} from './transaction-type-form.component';
+import {TransactionType} from '../../../services/accounting/domain/transaction-type.model';
+import {MatInputModule} from '@angular/material';
+
+describe('Test transaction type form', () => {
+
+  let fixture: ComponentFixture<TestComponent>;
+
+  let testComponent: TestComponent;
+
+  beforeEach( async(() => {
+    TestBed.configureTestingModule({
+      imports: [
+        TranslateModule.forRoot(),
+        MatInputModule,
+        CovalentStepsModule,
+        FimsSharedModule,
+        ReactiveFormsModule,
+        NoopAnimationsModule
+      ],
+      declarations: [
+        TransactionTypeFormComponent,
+        TestComponent
+      ]
+    }).compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture  = TestBed.createComponent(TestComponent);
+    testComponent = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should trigger save event', () => {
+    const button: DebugElement = fixture.debugElement.query(By.css('button[mat-raised-button]'));
+
+    button.nativeElement.click();
+
+    expect(testComponent.savedType).toEqual(testComponent.type);
+  });
+
+  it('should trigger cancel event', () => {
+    const button: DebugElement = fixture.debugElement.query(By.css('button[mat-button]'));
+
+    button.nativeElement.click();
+
+    expect(testComponent.canceled).toBeTruthy();
+  });
+
+});
+
+@Component({
+  template: `
+    <fims-transaction-type-form (onSave)="onSave($event)" (onCancel)="onCancel($event)" [transactionType]="type" [editMode]="true">
+    </fims-transaction-type-form>`
+})
+class TestComponent {
+
+  type: TransactionType = {
+    code: 'test',
+    name: 'test',
+    description: 'test'
+  };
+
+  savedType: TransactionType;
+
+  canceled: boolean;
+
+  onSave(type: TransactionType): void {
+    this.savedType = type;
+  }
+
+  onCancel(): void {
+    this.canceled = true;
+  }
+
+}
diff --git a/src/app/accounting/transactionTypes/form/transaction-type-form.component.ts b/src/app/accounting/transactionTypes/form/transaction-type-form.component.ts
new file mode 100644
index 0000000..b6cc982
--- /dev/null
+++ b/src/app/accounting/transactionTypes/form/transaction-type-form.component.ts
@@ -0,0 +1,79 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, OnInit, Output, ViewChild} from '@angular/core';
+import {FormComponent} from '../../../common/forms/form.component';
+import {TransactionType} from '../../../services/accounting/domain/transaction-type.model';
+import {TdStepComponent} from '@covalent/core';
+import {FormBuilder, Validators} from '@angular/forms';
+import {FimsValidators} from '../../../common/validator/validators';
+
+@Component({
+  selector: 'fims-transaction-type-form',
+  templateUrl: './transaction-type-form.component.html'
+})
+export class TransactionTypeFormComponent extends FormComponent<TransactionType> implements OnInit {
+
+  @ViewChild('detailsStep') step: TdStepComponent;
+
+  @Input() transactionType: TransactionType;
+
+  @Input() editMode = false;
+
+  @Output('onSave') onSave = new EventEmitter<TransactionType>();
+
+  @Output('onCancel') onCancel = new EventEmitter<void>();
+
+  constructor(private formBuilder: FormBuilder) {
+    super();
+  }
+
+  ngOnInit(): void {
+    this.form = this.formBuilder.group({
+      code: [this.transactionType.code, [Validators.required, Validators.minLength(3), Validators.maxLength(32), FimsValidators.urlSafe]],
+      name: [this.transactionType.name, [Validators.required, Validators.maxLength(256)]],
+      description: [this.transactionType.description, [Validators.maxLength(2048)]]
+    });
+
+    this.step.open();
+  }
+
+  showNumberValidationError(): void {
+    this.setError('number', 'unique', true);
+  }
+
+  get formData(): TransactionType {
+    // Not needed
+    return;
+  }
+
+  cancel(): void {
+    this.onCancel.emit();
+  }
+
+  save(): void {
+    const transactionType: TransactionType = {
+      code: this.form.get('code').value,
+      name: this.form.get('name').value,
+      description: this.form.get('description').value
+    };
+
+    this.onSave.emit(transactionType);
+  }
+
+}
diff --git a/src/app/accounting/transactionTypes/transaction-type-exists.guard.ts b/src/app/accounting/transactionTypes/transaction-type-exists.guard.ts
new file mode 100644
index 0000000..af36452
--- /dev/null
+++ b/src/app/accounting/transactionTypes/transaction-type-exists.guard.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 {Injectable} from '@angular/core';
+import {ActivatedRouteSnapshot, CanActivate, RouterStateSnapshot} from '@angular/router';
+import * as fromAccounting from '../store/index';
+import {AccountingStore} from '../store/index';
+import {ExistsGuardService} from '../../common/guards/exists-guard';
+import {AccountingService} from '../../services/accounting/accounting.service';
+import {Observable} from 'rxjs/Observable';
+import {LoadAction} from '../store/ledger/transaction-type/transaction-type.actions';
+import {of} from 'rxjs/observable/of';
+
+@Injectable()
+export class TransactionTypeExistsGuard implements CanActivate {
+
+  constructor(private store: AccountingStore,
+              private accountingService: AccountingService,
+              private existsGuardService: ExistsGuardService) {
+  }
+
+  hasTransactionTypeInStore(code: string): Observable<boolean> {
+    const timestamp$: Observable<number> = this.store.select(fromAccounting.getTransactionTypeLoadedAt)
+      .map(loadedAt => loadedAt[code]);
+
+    return this.existsGuardService.isWithinExpiry(timestamp$);
+  }
+
+  hasTransactionTypeInApi(code: string): Observable<boolean> {
+    const getTransactionType: Observable<any> = this.accountingService.findTransactionType(code)
+      .map(transactionTypeEntity => new LoadAction({
+        resource: transactionTypeEntity
+      }))
+      .do((action: LoadAction) => this.store.dispatch(action))
+      .map(transactionType => !!transactionType);
+
+    return this.existsGuardService.routeTo404OnError(getTransactionType);
+  }
+
+  hasTransactionType(code: string): Observable<boolean> {
+    return this.hasTransactionTypeInStore(code)
+      .switchMap(inStore => {
+        if (inStore) {
+          return of(inStore);
+        }
+        return this.hasTransactionTypeInApi(code);
+      });
+  }
+
+  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
+    return this.hasTransactionType(route.params['code']);
+  }
+}
diff --git a/src/app/accounting/transactionTypes/transaction-types.list.component.html b/src/app/accounting/transactionTypes/transaction-types.list.component.html
new file mode 100644
index 0000000..bcd2565
--- /dev/null
+++ b/src/app/accounting/transactionTypes/transaction-types.list.component.html
@@ -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.
+-->
+
+<fims-layout-card-over title="{{'Manage transaction types' | translate}}" [navigateBackTo]="['../']">
+  <fims-data-table
+    (onFetch)="fetchTransactionTypes($event)"
+    (onActionCellClick)="rowSelect($event)"
+    [columns]="columns"
+    [data]="transactionTypesData$ | async"
+    [loading]="loading$ | async"
+    [sortable]="true"
+    [pageable]="true"
+    [actionColumnLabel]="'EDIT'">
+  </fims-data-table>
+</fims-layout-card-over>
+<fims-fab-button title="{{'Create new transaction type' | translate}}" icon="add" [link]="['create']" [permission]="{ id: 'accounting_tx_types', accessLevel: 'CHANGE'}"></fims-fab-button>
diff --git a/src/app/accounting/transactionTypes/transaction-types.list.component.ts b/src/app/accounting/transactionTypes/transaction-types.list.component.ts
new file mode 100644
index 0000000..6b7f04c
--- /dev/null
+++ b/src/app/accounting/transactionTypes/transaction-types.list.component.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 {Component, OnInit} from '@angular/core';
+import {Observable} from 'rxjs/Observable';
+import * as fromAccounting from '../store/index';
+import {AccountingStore} from '../store/index';
+import {TableData, TableFetchRequest} from '../../common/data-table/data-table.component';
+import {FetchRequest} from '../../services/domain/paging/fetch-request.model';
+import {SEARCH} from '../store/ledger/transaction-type/transaction-type.actions';
+import {TransactionType} from '../../services/accounting/domain/transaction-type.model';
+import {ActivatedRoute, Router} from '@angular/router';
+
+@Component({
+  templateUrl: './transaction-types.list.component.html'
+})
+export class TransactionTypeListComponent implements OnInit {
+
+  transactionTypesData$: Observable<TableData>;
+
+  loading$: Observable<boolean>;
+
+  columns: any[] = [
+    { name: 'code', label: 'Code' },
+    { name: 'name', label: 'Name' },
+    { name: 'description', label: 'Description' }
+  ];
+
+  private searchTerm: string;
+
+  private lastFetchRequest: FetchRequest = {};
+
+  constructor(private router: Router, private route: ActivatedRoute, private store: AccountingStore) {}
+
+  ngOnInit(): void {
+    this.transactionTypesData$ = this.store.select(fromAccounting.getTransactionTypeSearchResults)
+      .map(transactionTypePage => ({
+        data: transactionTypePage.transactionTypes,
+        totalElements: transactionTypePage.totalElements,
+        totalPages: transactionTypePage.totalPages
+      }));
+
+    this.loading$ = this.store.select(fromAccounting.getTransactionTypeSearchLoading);
+
+    this.fetchTransactionTypes();
+  }
+
+  rowSelect(transactionType: TransactionType): void {
+    this.router.navigate(['edit', transactionType.code], { relativeTo: this.route });
+  }
+
+  fetchTransactionTypes(fetchRequest?: TableFetchRequest): void {
+    if (fetchRequest) {
+      this.lastFetchRequest = fetchRequest;
+    }
+
+    this.lastFetchRequest.searchTerm = this.searchTerm;
+
+    this.store.dispatch({ type: SEARCH, payload: this.lastFetchRequest });
+  }
+}
diff --git a/src/app/accounting/trial-balance/trial-balance.component.html b/src/app/accounting/trial-balance/trial-balance.component.html
deleted file mode 100644
index 7672f4a..0000000
--- a/src/app/accounting/trial-balance/trial-balance.component.html
+++ /dev/null
@@ -1,39 +0,0 @@
-<div class="my-div mat-elevation-z2">
-  <h3 class="heading">Trial Balance</h3>
-  <mat-divider></mat-divider>
- 
-  <mat-form-field>
-    <input matInput (keyup)="applyFilter($event.target.value)" placeholder="Filter by name/short name">
-  </mat-form-field>
-  
-<br>
-<mat-divider></mat-divider>
-
-<mat-table #table [dataSource]="dataSource">
-
-  <!-- Position Column -->
-  <ng-container matColumnDef="ledger">
-    <mat-header-cell *matHeaderCellDef>Ledger </mat-header-cell>
-    <mat-cell *matCellDef="let element">
-      <a routerLink="{{element.ledger}}">{{element.ledger}}</a>
-    </mat-cell>
-  </ng-container>
-
-  <!-- Name Column -->
-  <ng-container matColumnDef="debit">
-    <mat-header-cell *matHeaderCellDef>Dedit</mat-header-cell>
-    <mat-cell *matCellDef="let element"> {{element.debit}} </mat-cell>
-  </ng-container>
-
-  <ng-container matColumnDef="credit">
-      <mat-header-cell *matHeaderCellDef>Credit</mat-header-cell>
-      <mat-cell *matCellDef="let element"> {{element.credit}} </mat-cell>
-    </ng-container>
-
-  <mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
-  <mat-row *matRowDef="let row; columns: displayedColumns;"></mat-row>
-</mat-table>
-  
-  
-</div>
-  
diff --git a/src/app/accounting/trial-balance/trial-balance.component.scss b/src/app/accounting/trial-balance/trial-balance.component.scss
deleted file mode 100644
index 0961a8e..0000000
--- a/src/app/accounting/trial-balance/trial-balance.component.scss
+++ /dev/null
@@ -1,18 +0,0 @@
-.my-div{
-    margin-left: 2%;
-    margin-right:2%;
-    border-radius: 5px 5px 5px 5px;
-    background-color: #e6e6ff;
-    min-height: 100%;
-   
-  }
-  table {
-      width: 100%;
-    }
-    
-    .mat-form-field {
-      font-size: 14px;
-      width: 100%;
-    }
-    
-    
\ No newline at end of file
diff --git a/src/app/accounting/trial-balance/trial-balance.component.spec.ts b/src/app/accounting/trial-balance/trial-balance.component.spec.ts
deleted file mode 100644
index 23f92c1..0000000
--- a/src/app/accounting/trial-balance/trial-balance.component.spec.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-import { async, ComponentFixture, TestBed } from '@angular/core/testing';
-
-import { TrialBalanceComponent } from './trial-balance.component';
-
-describe('TrialBalanceComponent', () => {
-  let component: TrialBalanceComponent;
-  let fixture: ComponentFixture<TrialBalanceComponent>;
-
-  beforeEach(async(() => {
-    TestBed.configureTestingModule({
-      declarations: [ TrialBalanceComponent ]
-    })
-    .compileComponents();
-  }));
-
-  beforeEach(() => {
-    fixture = TestBed.createComponent(TrialBalanceComponent);
-    component = fixture.componentInstance;
-    fixture.detectChanges();
-  });
-
-  it('should create', () => {
-    expect(component).toBeTruthy();
-  });
-});
diff --git a/src/app/accounting/trial-balance/trial-balance.component.ts b/src/app/accounting/trial-balance/trial-balance.component.ts
deleted file mode 100644
index fb15451..0000000
--- a/src/app/accounting/trial-balance/trial-balance.component.ts
+++ /dev/null
@@ -1,34 +0,0 @@
-import { Component, OnInit } from '@angular/core';
-import {MatTableDataSource} from '@angular/material';
-
-@Component({
-  selector: 'app-trial-balance',
-  templateUrl: './trial-balance.component.html',
-  styleUrls: ['./trial-balance.component.scss']
-})
-export class TrialBalanceComponent implements OnInit {
-  displayedColumns = ['ledger','debit','credit'];
-  dataSource = new MatTableDataSource(ELEMENT_DATA);
-
-  applyFilter(filterValue: string) {
-    filterValue = filterValue.trim(); // Remove whitespace
-    filterValue = filterValue.toLowerCase(); // MatTableDataSource defaults to lowercase matches
-    this.dataSource.filter = filterValue;
-  }
-
-  constructor() { }
-
-  ngOnInit() {
-  }
-
-}
-export interface Element {
-  ledger: string;
-  debit: number;
-  credit: number;
-  
-}
-
-const ELEMENT_DATA: Element[] = [
-  
-];
diff --git a/src/app/app.component.html b/src/app/app.component.html
index 90c6b64..ca721b3 100644
--- a/src/app/app.component.html
+++ b/src/app/app.component.html
@@ -1 +1,18 @@
-<router-outlet></router-outlet>
\ No newline at end of file
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<router-outlet></router-outlet>
diff --git a/src/app/app.component.scss b/src/app/app.component.scss
index e69de29..fcd652e 100644
--- a/src/app/app.component.scss
+++ b/src/app/app.component.scss
@@ -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.
+ */
+
+/* :host /deep/ lets shadowdom style child elements */
+:host /deep/ {
+  /**
+  * CSS Overrides for bug fixes
+  */
+
+  /**
+  * END CSS Overrides for bug fixes
+  */
+
+  /* Manage list custom styles */
+  .md-sort-item {
+    /deep/ {
+      .md-list-item {
+        padding: 0;
+      }
+    }
+  }
+  .md-sort-icon {
+    font-size: 15px;
+    margin-right: 10px;
+  }
+  .md-sort-header {
+    padding: 10px;
+    &:hover {
+      background-color: #EEEEEE;
+      cursor: pointer;
+    }
+  }
+
+}
diff --git a/src/app/app.component.spec.ts b/src/app/app.component.spec.ts
deleted file mode 100644
index bcbdf36..0000000
--- a/src/app/app.component.spec.ts
+++ /dev/null
@@ -1,27 +0,0 @@
-import { TestBed, async } from '@angular/core/testing';
-import { AppComponent } from './app.component';
-describe('AppComponent', () => {
-  beforeEach(async(() => {
-    TestBed.configureTestingModule({
-      declarations: [
-        AppComponent
-      ],
-    }).compileComponents();
-  }));
-  it('should create the app', async(() => {
-    const fixture = TestBed.createComponent(AppComponent);
-    const app = fixture.debugElement.componentInstance;
-    expect(app).toBeTruthy();
-  }));
-  it(`should have as title 'app'`, async(() => {
-    const fixture = TestBed.createComponent(AppComponent);
-    const app = fixture.debugElement.componentInstance;
-    expect(app.title).toEqual('app');
-  }));
-  it('should render title in a h1 tag', async(() => {
-    const fixture = TestBed.createComponent(AppComponent);
-    fixture.detectChanges();
-    const compiled = fixture.debugElement.nativeElement;
-    expect(compiled.querySelector('h1').textContent).toContain('Welcome to app!');
-  }));
-});
diff --git a/src/app/app.component.ts b/src/app/app.component.ts
index 57b1bce..8f9f642 100644
--- a/src/app/app.component.ts
+++ b/src/app/app.component.ts
@@ -1,10 +1,62 @@
-import { Component } from '@angular/core';
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, OnDestroy, OnInit} from '@angular/core';
+import {TranslateService} from '@ngx-translate/core';
+import * as fromRoot from './store';
+import {Store} from '@ngrx/store';
+import {LoginSuccessAction} from './store/security/security.actions';
+import {Subscription} from 'rxjs/Subscription';
+import {getSelectedLanguage} from './common/i18n/translate';
 
 @Component({
-  selector: 'app-root',
+  selector: 'fims-app',
   templateUrl: './app.component.html',
-  styleUrls: ['./app.component.scss']
+  styleUrls: ['./app.component.scss'],
 })
-export class AppComponent {
-  
+export class AppComponent implements OnInit, OnDestroy {
+
+  private authSubscription: Subscription;
+
+  constructor(private translate: TranslateService, private store: Store<fromRoot.State>) {}
+
+  ngOnInit(): void {
+    this.translate.addLangs(['en', 'es']);
+    this.translate.setDefaultLang('en');
+    this.translate.use(getSelectedLanguage(this.translate));
+    // this.relogin();
+  }
+
+  relogin(): void {
+    this.store.select(fromRoot.getAuthenticationState)
+      .filter(state => !!state.authentication)
+      .take(1)
+      .map(state => ({
+        username: state.username,
+        tenant: state.tenant,
+        authentication: state.authentication
+      }))
+      .map(payload => new LoginSuccessAction(payload))
+      .subscribe((action: LoginSuccessAction) => this.store.dispatch(action));
+  }
+
+  ngOnDestroy(): void {
+    this.authSubscription.unsubscribe();
+  }
 }
diff --git a/src/app/app.module.ts b/src/app/app.module.ts
index 70b772a..2607262 100644
--- a/src/app/app.module.ts
+++ b/src/app/app.module.ts
@@ -1,221 +1,100 @@
-import { BrowserModule } from '@angular/platform-browser';
-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,
-  MatButtonToggleModule,
-  MatCheckboxModule,
-  MatToolbarModule,
-  MatTooltipModule,
-  MatCardModule,
-  MatChipsModule,
-  MatDatepickerModule,
-  MatDialogModule,
-  MatExpansionModule,
-  MatFormFieldModule,
-  MatGridListModule,
-  MatIconModule,
-  MatInputModule,
-  MatListModule,
-  MatMenuModule,
-  MatNativeDateModule,
-  MatPaginatorModule,
-  MatProgressBarModule,
-  MatProgressSpinnerModule,
-  MatRadioModule,
-  MatRippleModule,
-  MatSelectModule,
-  MatSidenavModule,
-  MatSliderModule,
-  MatSlideToggleModule,
-  MatSnackBarModule,
-  MatSortModule,
-  MatTableModule,
-  MatTabsModule,
-  MatStepperModule
-
-} from '@angular/material';
-
-
-import { LOCALE_ID, NgModule } from '@angular/core';
-
-
-import { AppComponent } from './app.component';
-import { LoginComponent } from './login/login.component';
-import { NavbarComponent } from './navbar/navbar.component';
-import { DashboardComponent } from './dashboard/dashboard.component';
-import { AccountingComponent } from './accounting/accounting.component';
-import { GeneralLedgerComponent } from './accounting/general-ledger/general-ledger.component';
-import { AddJournalEntryComponent } from './accounting/add-journal-entry/add-journal-entry.component';
-
-import { PayrollsComponent } from './accounting/payrolls/payrolls.component';
-import { ChartOfAccountsComponent } from './accounting/chart-of-accounts/chart-of-accounts.component';
-import { AddTransactionTypeComponent } from './accounting/add-transaction-type/add-transaction-type.component';
-import { TrialBalanceComponent } from './accounting/trial-balance/trial-balance.component';
-import { ChequeClearingComponent } from './accounting/cheque-clearing/cheque-clearing.component';
-import { TransactionTypeComponent } from './accounting/transaction-type/transaction-type.component';
-import { AddMemberComponent } from './customer/add-member/add-member.component';
-import { ManageMembersComponent } from './customer/manage-members/manage-members.component';
-import { AddEmployeeComponent } from './employee/add-employee/add-employee.component';
-import { ManageEmployeeComponent } from './employee/manage-employee/manage-employee.component';
-import { AddOfficeComponent } from './office/add-office/add-office.component';
-import { ViewOfficesComponent } from './office/view-offices/view-offices.component';
-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 { HttpClients } from './sevices/http/http.service';
-import { IdentityService } from './sevices/identity/identity.service';
-import { OfficeService } from './sevices/office/office.service';
-import { CustomerService } from './sevices/customer/customer.service';
-import { AuthenticationService } from './sevices/security/authn/authentication.service';
-import { CatalogService } from './sevices/catalog/catalog.service';
-import { AccountingService } from './sevices/accounting/accounting.service';
-import { PortfolioService } from './sevices/portfolio/portfolio.service';
-import { TranslateLoader, TranslateService } from '@ngx-translate/core';
-import { TranslateHttpLoader } from '@ngx-translate/http-loader';
-import { PermittableGroupIdMapper } from './sevices/security/authz/permittable-group-id-mapper';
-import { reducer } from './store';
-import { StoreModule } from '@ngrx/store';
-import { EffectsModule } from '@ngrx/effects';
-import { NotificationService } from './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 { ImageService } from './sevices/image/image.service';
-import { DepositAccountService } from './sevices/depositAccount/deposit-account.service';
-import { CurrencyService } from './sevices/currency/currency.service';
-import { TellerService } from './sevices/teller/teller-service';
-import { ReportingService } from './sevices/reporting/reporting.service';
-import { getSelectedLanguage } from './common/i18n/translate';
-import { ChequeService } from './sevices/cheque/cheque.service';
-import { PayrollService } from './sevices/payroll/payroll.service';
-import { CountryService } from './sevices/country/country.service';
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 {LOCALE_ID, NgModule} from '@angular/core';
+import {BrowserModule} from '@angular/platform-browser';
+import {Http, HttpModule} from '@angular/http';
+import {AppComponent} from './app.component';
+import {appRoutes, appRoutingProviders} from './app.routes';
+import {HttpClient} from './services/http/http.service';
+import {IdentityService} from './services/identity/identity.service';
+import {OfficeService} from './services/office/office.service';
+import {CustomerService} from './services/customer/customer.service';
+import {AuthenticationService} from './services/security/authn/authentication.service';
+import {CatalogService} from './services/catalog/catalog.service';
+import {AccountingService} from './services/accounting/accounting.service';
+import {PortfolioService} from './services/portfolio/portfolio.service';
+import {TranslateLoader, TranslateModule, TranslateService} from '@ngx-translate/core';
+import {TranslateHttpLoader} from '@ngx-translate/http-loader';
+import {PermittableGroupIdMapper} from './services/security/authz/permittable-group-id-mapper';
+import {reducer} from './store';
+import {StoreModule} from '@ngrx/store';
+import {EffectsModule} from '@ngrx/effects';
+import {NotificationService} from './services/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 {BrowserAnimationsModule} from '@angular/platform-browser/animations';
+import {ExistsGuardService} from './common/guards/exists-guard';
+import {CountryService} from './services/country/country.service';
+import {ImageService} from './services/image/image.service';
+import {DepositAccountService} from './services/depositAccount/deposit-account.service';
+import {CurrencyService} from './services/currency/currency.service';
+import {TellerService} from './services/teller/teller-service';
+import {ReportingService} from './services/reporting/reporting.service';
+import {getSelectedLanguage} from './common/i18n/translate';
+import {ChequeService} from './services/cheque/cheque.service';
+import {PayrollService} from './services/payroll/payroll.service';
 
 export function HttpLoaderFactory(http: Http) {
   return new TranslateHttpLoader(http, './assets/i18n/', '.json');
 }
 
-
-const appRoutes: Routes = [
-  { path: 'login', component: LoginComponent },
-  { path: '', redirectTo: 'login', pathMatch: 'full' },
-
-  {
-    path: 'navbar', component: NavbarComponent, children: [
-      { path: '', redirectTo: 'dashboard', pathMatch: 'full' },
-      { path: 'dashboard', component: DashboardComponent },
-      { path: 'accounting', component: AccountingComponent },
-      { path: 'GL', component: GeneralLedgerComponent },
-      { path: 'add_journal_entry', component: AddJournalEntryComponent },
-      { path: 'payroll', component: PayrollsComponent },
-      { path: 'chart_of_accounts', component: ChartOfAccountsComponent },
-      { path: 'add_transaction_type', component: AddTransactionTypeComponent },
-      { path: 'trial_balance', component: TrialBalanceComponent },
-      { path: 'cheque_clearing', component: ChequeClearingComponent },
-      { path: 'transaction_type', component: TransactionTypeComponent },
-      { path: 'add_member', component: AddMemberComponent },
-      { path: 'manage_members', component: ManageMembersComponent },
-      { path: 'add_employee', component: AddEmployeeComponent },
-      { path: 'manage_employees', component: ManageEmployeeComponent },
-      { path: 'view_offices', component: ViewOfficesComponent },
-      { path: 'add_office', component: AddOfficeComponent },
-      { path: 'add_ledger', component: AddLedgerComponent },
-      { path: 'account_payable', component: AccountPayableComponent },
-      { path: 'add_cheque', component: AddChequeComponent },
-      { path: 'add_payroll', component: AddPayrollComponent },
-    ]
-  }
-];
-
-
 @NgModule({
   declarations: [
-    AppComponent, LoginComponent, NavbarComponent, DashboardComponent,
-    AccountingComponent, GeneralLedgerComponent, AddJournalEntryComponent,
-    PayrollsComponent, ChartOfAccountsComponent, AddTransactionTypeComponent,
-    TrialBalanceComponent, ChequeClearingComponent, TransactionTypeComponent,
-    AddMemberComponent, ManageMembersComponent, AddEmployeeComponent, ManageEmployeeComponent, AddOfficeComponent,
-    ViewOfficesComponent, AddLedgerComponent, AccountPayableComponent, AddChequeComponent, AddPayrollComponent,
-
+    AppComponent
   ],
-  imports: [RouterModule.forRoot(appRoutes),
-    BrowserModule, BrowserAnimationsModule,
-    CommonModule, TranslateModule, CovalentLoadingModule,
-    FormsModule, ReactiveFormsModule, HttpModule,
-  TranslateModule.forRoot({
-    loader: {
-      provide: TranslateLoader,
-      useFactory: HttpLoaderFactory,
-      deps: [Http]
-    }
-  }),
-  StoreModule.forRoot(reducer),
+  imports: [
+    BrowserModule,
+    BrowserAnimationsModule,
+    HttpModule,
+    TranslateModule.forRoot({
+      loader: {
+        provide: TranslateLoader,
+        useFactory: HttpLoaderFactory,
+        deps: [Http]
+      }
+    }),
+    appRoutes,
+    StoreModule.provideStore(reducer),
 
-  /**
-   * Root effects
-   */
-  EffectsModule.forRoot([SecurityApiEffects,
-  SecurityRouteEffects,
-  SecurityNotificationEffects,
+    /**
+     * Root effects
+     */
+    EffectsModule.run(SecurityApiEffects),
+    EffectsModule.run(SecurityRouteEffects),
+    EffectsModule.run(SecurityNotificationEffects),
 
-  OfficeSearchApiEffects,
-  EmployeeSearchApiEffects,
-  CustomerSearchApiEffects,
-  AccountSearchApiEffects,
-  RoleSearchApiEffects,
-  LedgerSearchApiEffects
-  ]),
-
-    MatAutocompleteModule,
-    MatButtonModule,
-    MatButtonToggleModule,
-    MatCheckboxModule,
-    MatToolbarModule,
-    MatTooltipModule,
-    MatCardModule,
-    MatChipsModule,
-    MatDatepickerModule,
-    MatDialogModule,
-    MatExpansionModule,
-    MatFormFieldModule,
-    MatGridListModule,
-    MatIconModule,
-    MatInputModule,
-    MatListModule,
-    MatMenuModule,
-    MatNativeDateModule,
-    MatPaginatorModule,
-    MatProgressBarModule,
-    MatProgressSpinnerModule,
-    MatRadioModule,
-    MatRippleModule,
-    MatSelectModule,
-    MatSidenavModule,
-    MatSliderModule,
-    MatSlideToggleModule,
-    MatSnackBarModule,
-    MatSortModule,
-    MatTableModule,
-    MatTabsModule,
-    MatStepperModule
+    EffectsModule.run(OfficeSearchApiEffects),
+    EffectsModule.run(EmployeeSearchApiEffects),
+    EffectsModule.run(CustomerSearchApiEffects),
+    EffectsModule.run(AccountSearchApiEffects),
+    EffectsModule.run(RoleSearchApiEffects),
+    EffectsModule.run(LedgerSearchApiEffects)
   ],
-  providers: [HttpClients,
+  providers: [
+    HttpClient,
     AuthenticationService,
     PermittableGroupIdMapper,
     IdentityService,
@@ -232,14 +111,13 @@
     CountryService,
     CurrencyService,
     NotificationService,
-    TranslateService,
-
-    // ExistsGuardService,
-    // ...appRoutingProviders,
+    ExistsGuardService,
+    ...appRoutingProviders,
     ImageService,
     {
       provide: LOCALE_ID, useFactory: getSelectedLanguage, deps: [TranslateService],
-    }],
-  bootstrap: [AppComponent]
+    }
+  ],
+  bootstrap: [ AppComponent ]
 })
-export class AppModule { }
+export class AppModule {}
diff --git a/src/app/app.routes.ts b/src/app/app.routes.ts
new file mode 100644
index 0000000..8803f78
--- /dev/null
+++ b/src/app/app.routes.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 {NoPreloading, RouterModule, Routes} from '@angular/router';
+import {AuthGuard} from './services/security/authn/auth-guard.service';
+
+const routes: Routes = [
+  {path: 'login', loadChildren: './login/login.module#LoginModule'},
+  {path: '', loadChildren: './main/main.module#MainModule', canActivate: [AuthGuard] }
+];
+
+export const appRoutingProviders: any[] = [
+  AuthGuard
+];
+
+export const appRoutes: any = RouterModule.forRoot(routes, { preloadingStrategy: NoPreloading });
diff --git a/src/app/centers/center-exists.guard.ts b/src/app/centers/center-exists.guard.ts
new file mode 100644
index 0000000..6936b08
--- /dev/null
+++ b/src/app/centers/center-exists.guard.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 {ActivatedRouteSnapshot, CanActivate, RouterStateSnapshot} from '@angular/router';
+import {Injectable} from '@angular/core';
+import {OfficeService} from '../services/office/office.service';
+import * as fromEmployees from './store';
+import {Observable} from 'rxjs/Observable';
+import {LoadAction} from './store/employee.actions';
+import {of} from 'rxjs/observable/of';
+import {EmployeesStore} from './store/index';
+import {ExistsGuardService} from '../common/guards/exists-guard';
+
+@Injectable()
+export class CenterExistsGuard implements CanActivate {
+
+  constructor(private store: EmployeesStore,
+              private officeService: OfficeService,
+              private existsGuardService: ExistsGuardService) {}
+
+  hasEmployeeInStore(id: string): Observable<boolean> {
+    const timestamp$ = this.store.select(fromEmployees.getEmployeesLoadedAt)
+      .map(loadedAt => loadedAt[id]);
+
+    return this.existsGuardService.isWithinExpiry(timestamp$);
+  }
+
+  hasEmployeeInApi(id: string): Observable<boolean> {
+    const getEmployee$ = this.officeService.getEmployee(id)
+      .map(employeeEntity => new LoadAction({
+        resource: employeeEntity
+      }))
+      .do((action: LoadAction) => this.store.dispatch(action))
+      .map(employee => !!employee);
+
+    return this.existsGuardService.routeTo404OnError(getEmployee$);
+  }
+
+  hasEmployee(id: string): Observable<boolean> {
+    return this.hasEmployeeInStore(id)
+      .switchMap(inStore => {
+        if (inStore) {
+          return of(inStore);
+        }
+
+        return this.hasEmployeeInApi(id);
+      });
+  }
+
+  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
+    return this.hasEmployee(route.params['id']);
+  }
+}
diff --git a/src/app/centers/center.component.html b/src/app/centers/center.component.html
new file mode 100644
index 0000000..bd1065a
--- /dev/null
+++ b/src/app/centers/center.component.html
@@ -0,0 +1,15 @@
+<fims-layout-card-over title="{{'Manage centers' | translate}}">
+    <fims-layout-card-over-header-menu>
+      <td-search-box #searchBox placeholder="{{'Search' | translate}}" (search)="search($event)" [alwaysVisible]="false"></td-search-box>
+    </fims-layout-card-over-header-menu>
+    <fims-data-table flex
+                     (onFetch)="fetchEmployees($event)"
+                     (onActionCellClick)="rowSelect($event)"
+                     [columns]="columns"
+                     [data]="employeeData$ | async"
+                     [loading]="loading$ | async"
+                     [sortable]="true"
+                     [pageable]="true">
+    </fims-data-table>
+  </fims-layout-card-over>
+  <fims-fab-button title="{{'Create new center' | translate}}" icon="add" [link]="['create']" [permission]="{ id: 'office_employees', accessLevel: 'CHANGE'}"></fims-fab-button>
\ No newline at end of file
diff --git a/src/app/centers/center.component.scss b/src/app/centers/center.component.scss
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/app/centers/center.component.scss
diff --git a/src/app/centers/center.component.spec.ts b/src/app/centers/center.component.spec.ts
new file mode 100644
index 0000000..da7698d
--- /dev/null
+++ b/src/app/centers/center.component.spec.ts
@@ -0,0 +1,25 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { CenterComponent } from './center.component';
+
+describe('CenterComponent', () => {
+  let component: CenterComponent;
+  let fixture: ComponentFixture<CenterComponent>;
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      declarations: [ CenterComponent ]
+    })
+    .compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(CenterComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});
diff --git a/src/app/centers/center.component.ts b/src/app/centers/center.component.ts
new file mode 100644
index 0000000..ab58913
--- /dev/null
+++ b/src/app/centers/center.component.ts
@@ -0,0 +1,72 @@
+import {Component, OnInit} from '@angular/core';
+import {ActivatedRoute, Params, Router} from '@angular/router';
+import {Employee} from '../services/office/domain/employee.model';
+import {FetchRequest} from '../services/domain/paging/fetch-request.model';
+import {TableData} from '../common/data-table/data-table.component';
+import {Store} from '@ngrx/store';
+import * as fromRoot from '../store';
+import {Observable} from 'rxjs/Observable';
+import {SEARCH} from '../store/employee/employee.actions';
+
+
+
+@Component({
+  selector: 'fims-center',
+  templateUrl: './center.component.html',
+  styleUrls: ['./center.component.scss']
+})
+export class CenterComponent implements OnInit {
+  
+  employeeData$: Observable<TableData>;
+
+  loading$: Observable<boolean>;
+
+  columns: any[] = [
+    { name: 'name', label: 'Name' },
+    { name: 'accountNumber', label: 'Account #' },
+    { name: 'id', label: 'External Id' },
+    { name: 'status', label: 'Status' },
+    { name: 'office', label: 'Office' }
+  ];
+
+  searchTerm: string;
+
+  private lastFetchRequest: FetchRequest = {};
+
+  constructor(private router: Router, private route: ActivatedRoute, private store: Store<fromRoot.State>) {}
+
+  ngOnInit(): void {
+
+    this.employeeData$ = this.store.select(fromRoot.getEmployeeSearchResults)
+      .map(employeePage => ({
+        data: employeePage.employees,
+        totalElements: employeePage.totalElements,
+        totalPages: employeePage.totalPages
+      }));
+
+    this.loading$ = this.store.select(fromRoot.getEmployeeSearchLoading);
+
+    this.route.queryParams.subscribe((params: Params) => {
+      this.search(params['term']);
+    });
+  }
+
+  search(searchTerm: string): void {
+    this.searchTerm = searchTerm;
+    this.fetchEmployees();
+  }
+
+  rowSelect(row: Employee): void {
+    this.router.navigate(['detail', row.identifier], { relativeTo: this.route });
+  }
+
+  fetchEmployees(fetchRequest?: FetchRequest) {
+    if (fetchRequest) {
+      this.lastFetchRequest = fetchRequest;
+    }
+
+    this.lastFetchRequest.searchTerm = this.searchTerm;
+
+    this.store.dispatch({ type: SEARCH, payload: this.lastFetchRequest });
+  }
+}
diff --git a/src/app/centers/center.module.ts b/src/app/centers/center.module.ts
new file mode 100644
index 0000000..10ded43
--- /dev/null
+++ b/src/app/centers/center.module.ts
@@ -0,0 +1,66 @@
+import {NgModule} from '@angular/core';
+import {RouterModule} from '@angular/router';
+import {CenterComponent} from './center.component';
+import {CenterRoutes} from './center.routing';
+import {CenterFormComponent} from './form/form.component';
+import {CreateCenterFormComponent} from './form/create/create.form.component';
+import {EmployeeDetailComponent} from './detail/employee.detail.component';
+import {EditEmployeeFormComponent} from './form/edit/edit.form.component';
+import {UserResolver} from './user.resolver';
+import {FimsSharedModule} from '../common/common.module';
+import {CenterExistsGuard} from './center-exists.guard';
+import {Store} from '@ngrx/store';
+import {EmployeesStore, employeeStoreFactory} from './store/index';
+import {EmployeeNotificationEffects} from './store/effects/notification.effects';
+import {EffectsModule} from '@ngrx/effects';
+import {EmployeeApiEffects} from './store/effects/service.effects';
+import {EmployeeRouteEffects} from './store/effects/route.effects';
+import {
+  MatButtonModule,
+  MatIconModule,
+  MatInputModule,
+  MatListModule,
+  MatOptionModule,
+  MatSelectModule,
+  MatToolbarModule
+} from '@angular/material';
+import {CovalentSearchModule, CovalentStepsModule} from '@covalent/core';
+import {TranslateModule} from '@ngx-translate/core';
+import {CommonModule} from '@angular/common';
+import {ReactiveFormsModule} from '@angular/forms';
+
+@NgModule({
+  imports: [
+    RouterModule.forChild(CenterRoutes),
+    FimsSharedModule,
+    ReactiveFormsModule,
+    CommonModule,
+    TranslateModule,
+    MatIconModule,
+    MatListModule,
+    MatToolbarModule,
+    MatOptionModule,
+    MatInputModule,
+    MatButtonModule,
+    MatSelectModule,
+    CovalentSearchModule,
+    CovalentStepsModule,
+
+    EffectsModule.run(EmployeeApiEffects),
+    EffectsModule.run(EmployeeRouteEffects),
+    EffectsModule.run(EmployeeNotificationEffects)
+  ],
+  declarations: [
+    CenterComponent,
+    CenterFormComponent,
+    CreateCenterFormComponent,
+    EditEmployeeFormComponent,
+    EmployeeDetailComponent
+  ],
+  providers: [
+    UserResolver,
+    CenterExistsGuard,
+    { provide: EmployeesStore, useFactory: employeeStoreFactory, deps: [Store]}
+  ]
+})
+    export class CenterModule {}
\ No newline at end of file
diff --git a/src/app/centers/center.routing.ts b/src/app/centers/center.routing.ts
new file mode 100644
index 0000000..3b7718a
--- /dev/null
+++ b/src/app/centers/center.routing.ts
@@ -0,0 +1,34 @@
+import {Routes} from '@angular/router';
+import {CenterComponent} from './center.component';
+import {CreateCenterFormComponent} from './form/create/create.form.component';
+import {EmployeeDetailComponent} from './detail/employee.detail.component';
+import {EditEmployeeFormComponent} from './form/edit/edit.form.component';
+import {UserResolver} from './user.resolver';
+import {CenterExistsGuard} from './center-exists.guard';
+
+export const CenterRoutes: Routes = [
+  {
+    path: '',
+    component: CenterComponent,
+    data: {title: 'Manage Employees', hasPermission: {id: 'office_employees', accessLevel: 'READ'}}
+  },
+  {
+    path: 'create',
+    component: CreateCenterFormComponent,
+    data: {title: 'Create Employee', hasPermission: {id: 'office_employees', accessLevel: 'CHANGE'}}
+  },
+  {
+    path: 'detail/:id/edit',
+    component: EditEmployeeFormComponent,
+    canActivate: [CenterExistsGuard],
+    resolve: {user: UserResolver},
+    data: {title: 'Edit Employee', hasPermission: {id: 'office_employees', accessLevel: 'CHANGE'}}
+  },
+  {
+    path: 'detail/:id',
+    component: EmployeeDetailComponent,
+    canActivate: [CenterExistsGuard],
+    resolve: {user: UserResolver},
+    data: {title: 'View Employee', hasPermission: {id: 'office_employees', accessLevel: 'READ'}}
+  }
+];
diff --git a/src/app/centers/detail/employee.detail.component.html b/src/app/centers/detail/employee.detail.component.html
new file mode 100644
index 0000000..f4d6824
--- /dev/null
+++ b/src/app/centers/detail/employee.detail.component.html
@@ -0,0 +1,47 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<fims-layout-card-over title="{{employee.givenName}} {{employee.surname}}" [navigateBackTo]="'/employees'">
+  <fims-layout-card-over-header-menu layout="row" layout-align="end center">
+    <td-search-box placeholder="{{'Search' | translate}}" (search)="searchEmployee($event)" [alwaysVisible]="false"></td-search-box>
+    <button mat-icon-button (click)="deleteEmployee()" title="{{'Delete this employee' | translate}}" *hasPermission="{ id: 'office_employees', accessLevel: 'DELETE' }"><mat-icon>delete</mat-icon></button>
+  </fims-layout-card-over-header-menu>
+  <mat-list>
+    <h3 mat-subheader translate>Assigned office</h3>
+    <mat-list-item>
+      <mat-icon matListAvatar>store</mat-icon>
+      <h3 matLine *ngIf="employee.assignedOffice">{{employee.assignedOffice}}</h3>
+      <h3 matLine *ngIf="!employee.assignedOffice" translate>No office assigned</h3>
+    </mat-list-item>
+    <h3 mat-subheader translate>Assigned role</h3>
+    <mat-list-item>
+      <mat-icon matListAvatar>lock</mat-icon>
+      <h3 matLine>{{user.role}}</h3>
+    </mat-list-item>
+    <h3 mat-subheader translate>Contact Information</h3>
+    <mat-list-item [ngSwitch]="detail.type" *ngFor="let detail of employee.contactDetails">
+      <mat-icon *ngSwitchCase="'EMAIL'" matListAvatar>email</mat-icon>
+      <mat-icon *ngSwitchCase="'PHONE'" matListAvatar>phone</mat-icon>
+      <mat-icon *ngSwitchCase="'MOBILE'" matListAvatar>smartphone</mat-icon>
+      <h3 matLine>{{detail.value}}</h3>
+    </mat-list-item>
+    <mat-list-item *ngIf="!employee.contactDetails.length">
+      <h3 matLine translate>No contact details available</h3>
+    </mat-list-item>
+  </mat-list>
+</fims-layout-card-over>
+<fims-fab-button title="{{'Edit member ' | translate}}" icon="mode_edit" [link]="['edit']" [permission]="{ id: 'office_employees', accessLevel: 'CHANGE'}"></fims-fab-button>
diff --git a/src/app/centers/detail/employee.detail.component.ts b/src/app/centers/detail/employee.detail.component.ts
new file mode 100644
index 0000000..c019833
--- /dev/null
+++ b/src/app/centers/detail/employee.detail.component.ts
@@ -0,0 +1,96 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, Router} from '@angular/router';
+import {Component, OnDestroy, OnInit} from '@angular/core';
+import {Center} from '../../services/office/domain/employee.model';
+import {TdDialogService} from '@covalent/core';
+import {Observable} from 'rxjs/Observable';
+import {Subscription} from 'rxjs/Subscription';
+import {User} from '../../services/identity/domain/user.model';
+import * as fromEmployee from '../store';
+import {DELETE, SelectAction} from '../store/employee.actions';
+import {EmployeesStore} from '../store/index';
+
+@Component({
+  selector: 'fims-employee-detail',
+  templateUrl: './employee.detail.component.html'
+})
+export class EmployeeDetailComponent implements OnInit, OnDestroy {
+
+  private actionsSubscription: Subscription;
+
+  private employeeSubscription: Subscription;
+
+  employee: Center;
+
+  user: User;
+
+  constructor(private route: ActivatedRoute, private router: Router, private dialogService: TdDialogService,
+              private store: EmployeesStore) {}
+
+  ngOnInit(): void {
+    this.actionsSubscription = this.route.params
+      .map(params => new SelectAction(params['id']))
+      .subscribe(this.store);
+
+    this.employeeSubscription = this.store.select(fromEmployee.getSelectedEmployee)
+      .filter(employee => !!employee)
+      .subscribe(employee => this.employee = employee);
+
+    // TODO load user via store
+    this.route.data.subscribe(( data: { user: User }) => {
+      this.user = data.user;
+    });
+  }
+
+  ngOnDestroy(): void {
+    this.actionsSubscription.unsubscribe();
+    this.employeeSubscription.unsubscribe();
+  }
+
+  searchEmployee(term): void {
+    if (!term) {
+      return;
+    }
+    this.goToOverviewPage(term);
+  }
+
+  confirmDeletion(): Observable<boolean> {
+    return this.dialogService.openConfirm({
+      message: 'Do you want to delete employee "' + this.employee.givenName + ' ' + this.employee.surname + '"?',
+      title: 'Confirm deletion',
+      acceptButton: 'DELETE EMPLOYEE',
+    }).afterClosed();
+  }
+
+  deleteEmployee(): void {
+    this.confirmDeletion()
+      .filter(accept => accept)
+      .subscribe(() => {
+        this.store.dispatch({ type: DELETE, payload: {
+          employee: this.employee,
+          activatedRoute: this.route
+        } });
+      });
+  }
+
+  goToOverviewPage(term?: string): void {
+    this.router.navigate(['../../'], { queryParams: { term: term }, relativeTo: this.route });
+  }
+}
diff --git a/src/app/centers/form/create/create.form.component.html b/src/app/centers/form/create/create.form.component.html
new file mode 100644
index 0000000..16a62e0
--- /dev/null
+++ b/src/app/centers/form/create/create.form.component.html
@@ -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.
+-->
+
+<fims-layout-card-over title="{{'Create new center' | translate}}">
+  <fims-center-form-component #form
+                                  (onSave)="onSave($event)"
+                                  (onCancel)="onCancel()"
+                                  [formData]="centerFormData">
+  </fims-center-form-component>
+</fims-layout-card-over>
diff --git a/src/app/centers/form/create/create.form.component.spec.ts b/src/app/centers/form/create/create.form.component.spec.ts
new file mode 100644
index 0000000..0475674
--- /dev/null
+++ b/src/app/centers/form/create/create.form.component.spec.ts
@@ -0,0 +1,130 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 {async, ComponentFixture, inject, TestBed} from '@angular/core/testing';
+import {TranslateModule} from '@ngx-translate/core';
+import {ReactiveFormsModule} from '@angular/forms';
+import {CovalentStepsModule} from '@covalent/core';
+import {CenterFormComponent, CenterSaveEvent} from '../form.component';
+import {ActivatedRoute, Router} from '@angular/router';
+import {Observable} from 'rxjs/Observable';
+import {CreateCenterFormComponent} from './create.form.component';
+import {mapEmployee, mapUser} from '../form.mapper';
+import {EmployeesStore} from '../../store/index';
+import {CREATE} from '../../store/employee.actions';
+import {Store} from '@ngrx/store';
+import {NoopAnimationsModule} from '@angular/platform-browser/animations';
+import {MatCardModule, MatInputModule, MatOptionModule, MatSelectModule} from '@angular/material';
+import {CUSTOM_ELEMENTS_SCHEMA} from '@angular/core';
+import {FimsSharedModule} from '../../../common/common.module';
+
+const eventMock: CenterSaveEvent = {
+  detailForm: {
+    identifier: 'test',
+    firstName: 'test',
+    middleName: 'test',
+    lastName: 'test',
+    password: 'test',
+    role: 'test'
+  },
+  contactForm: {
+    email: 'test',
+    mobile: 'test',
+    phone: 'test'
+  },
+  
+  officeForm: {
+    assignedOffice: 'test'
+  }
+};
+
+let router: Router;
+
+describe('Test employee form component', () => {
+
+  let fixture: ComponentFixture<CreateCenterFormComponent>;
+
+  let testComponent: CreateCenterFormComponent;
+
+  beforeEach(() => {
+    router = jasmine.createSpyObj('Router', ['navigate']);
+
+    TestBed.configureTestingModule({
+      declarations: [
+        CenterFormComponent,
+        CreateCenterFormComponent,
+      ],
+      imports: [
+        TranslateModule.forRoot(),
+        FimsSharedModule,
+        ReactiveFormsModule,
+        MatInputModule,
+        MatCardModule,
+        MatSelectModule,
+        MatOptionModule,
+        CovalentStepsModule,
+        NoopAnimationsModule
+      ],
+      providers: [
+        { provide: Router, useValue: router},
+        { provide: ActivatedRoute, useValue: {} },
+        {
+          provide: Store, useClass: class {
+          dispatch = jasmine.createSpy('dispatch');
+          select = jasmine.createSpy('select').and.returnValue(Observable.empty());
+        }},
+        {
+          provide: EmployeesStore, useClass: class {
+            dispatch = jasmine.createSpy('dispatch');
+            select = jasmine.createSpy('select').and.returnValue(Observable.empty());
+          }
+        }
+      ],
+      schemas: [CUSTOM_ELEMENTS_SCHEMA]
+    });
+
+    fixture = TestBed.createComponent(CreateCenterFormComponent);
+    testComponent = fixture.componentInstance;
+  });
+
+  it('should test if employee is created', async(inject([EmployeesStore], (store: EmployeesStore) => {
+    fixture.detectChanges();
+
+    testComponent.onSave(eventMock);
+
+    fixture.whenStable().then(() => {
+      const employee = mapEmployee(eventMock);
+      const user = mapUser(eventMock);
+
+      expect(store.dispatch).toHaveBeenCalledWith({ type: CREATE, payload: {
+        employee: employee,
+        user: user,
+        activatedRoute: {}
+      }});
+    });
+  })));
+
+  xit('should test if error is set on 409', async(() => {
+    fixture.detectChanges();
+
+    fixture.whenStable().then(() => {
+      expect(testComponent.formComponent.detailForm.get('identifier').errors).toBeDefined();
+      expect(testComponent.formComponent.detailForm.get('identifier').errors['unique']).toBeTruthy();
+    });
+  }));
+});
diff --git a/src/app/centers/form/create/create.form.component.ts b/src/app/centers/form/create/create.form.component.ts
new file mode 100644
index 0000000..f5c1d0a
--- /dev/null
+++ b/src/app/centers/form/create/create.form.component.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 {Component, OnDestroy, OnInit, ViewChild} from '@angular/core';
+import {ActivatedRoute, Router} from '@angular/router';
+import {CenterFormComponent, CenterFormData, CenterSaveEvent} from '../form.component';
+import {mapEmployee, mapUser} from '../form.mapper';
+import {Center} from '../../../services/office/domain/employee.model';
+import {UserWithPassword} from '../../../services/identity/domain/user-with-password.model';
+import * as fromEmployees from '../../store';
+import {Subscription} from 'rxjs/Subscription';
+import {CREATE, RESET_FORM} from '../../store/employee.actions';
+import {Error} from '../../../services/domain/error.model';
+import {EmployeesStore} from '../../store/index';
+
+@Component({
+  templateUrl: './create.form.component.html'
+})
+export class CreateCenterFormComponent implements OnInit, OnDestroy {
+
+  private formStateSubscription: Subscription;
+
+  @ViewChild('form') formComponent: CenterFormComponent;
+
+  centerFormData: CenterFormData = {
+    user: { identifier: '', role: ''},
+    employee: { identifier: '', givenName: '', surname: '', contactDetails: [] }
+  };
+
+  constructor(private router: Router, private route: ActivatedRoute, private store: EmployeesStore) {}
+
+  ngOnInit(): void {
+    this.formStateSubscription = this.store.select(fromEmployees.getEmployeeFormError)
+      .filter((error: Error) => !!error)
+      .subscribe((error: Error) => {
+        const detailForm = this.formComponent.detailForm;
+        const errors = detailForm.get('identifier').errors || {};
+        errors['unique'] = true;
+        detailForm.get('identifier').setErrors(errors);
+        this.formComponent.step.open();
+      });
+  }
+
+  ngOnDestroy(): void {
+    this.formStateSubscription.unsubscribe();
+
+    this.store.dispatch({ type: RESET_FORM });
+  }
+
+  onSave(event: CenterSaveEvent): void {
+    const employee: Center = mapEmployee(event);
+    const user: UserWithPassword = mapUser(event);
+
+    this.store.dispatch({ type: CREATE, payload: {
+      employee: employee,
+      user: user,
+      activatedRoute: this.route
+    }});
+
+  }
+
+  onCancel(): void {
+    this.navigateAway();
+  }
+
+  navigateAway(): void {
+    this.router.navigate(['../'], { relativeTo: this.route });
+  }
+}
diff --git a/src/app/centers/form/edit/edit.form.component.html b/src/app/centers/form/edit/edit.form.component.html
new file mode 100644
index 0000000..5750425
--- /dev/null
+++ b/src/app/centers/form/edit/edit.form.component.html
@@ -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.
+-->
+
+<fims-layout-card-over title="{{'Edit employee' | translate}}">
+  <fims-center-form-component #form
+                                  [editMode]="true"
+                                  (onSave)="onSave($event)"
+                                  (onCancel)="onCancel()"
+                                  [formData]="formData | async">
+  </fims-center-form-component>
+</fims-layout-card-over>
diff --git a/src/app/centers/form/edit/edit.form.component.spec.ts b/src/app/centers/form/edit/edit.form.component.spec.ts
new file mode 100644
index 0000000..c3dd78b
--- /dev/null
+++ b/src/app/centers/form/edit/edit.form.component.spec.ts
@@ -0,0 +1,141 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 {async, ComponentFixture, inject, TestBed} from '@angular/core/testing';
+import {TranslateModule} from '@ngx-translate/core';
+import {ReactiveFormsModule} from '@angular/forms';
+import {CovalentStepsModule} from '@covalent/core';
+import {EditEmployeeFormComponent} from './edit.form.component';
+import {CenterFormComponent} from '../form.component';
+import {ActivatedRoute, Router} from '@angular/router';
+import {User} from '../../../services/identity/domain/user.model';
+import {Center} from '../../../services/office/domain/employee.model';
+import {Observable} from 'rxjs/Observable';
+import {EmployeesStore} from '../../store/index';
+import {Store} from '@ngrx/store';
+import {UPDATE} from '../../store/employee.actions';
+import * as fromEmployees from '../../store';
+import {NoopAnimationsModule} from '@angular/platform-browser/animations';
+import {MatCardModule, MatInputModule, MatOptionModule, MatSelectModule} from '@angular/material';
+import {CUSTOM_ELEMENTS_SCHEMA} from '@angular/core';
+import {FimsSharedModule} from '../../../common/common.module';
+
+const userMock: User = {
+  identifier: 'test',
+  role: 'test'
+};
+
+const employeeMock: Center = {
+  identifier: 'test',
+  assignedOffice: 'test',
+  givenName: 'test',
+  middleName: 'test',
+  surname: 'test',
+  contactDetails: [
+    {
+      group: 'BUSINESS',
+      type: 'EMAIL',
+      value: 'test',
+      preferenceLevel: 1
+    }
+  ]
+  
+};
+
+const activatedRoute = {
+  data: Observable.of({
+    user: userMock
+  })
+};
+let router: Router;
+
+describe('Test employee form component', () => {
+
+  let fixture: ComponentFixture<EditEmployeeFormComponent>;
+
+  let testComponent: EditEmployeeFormComponent;
+
+  beforeEach(() => {
+    router = jasmine.createSpyObj('Router', ['navigate']);
+
+    TestBed.configureTestingModule({
+      declarations: [
+        CenterFormComponent,
+        EditEmployeeFormComponent,
+      ],
+      imports: [
+        TranslateModule.forRoot(),
+        FimsSharedModule,
+        ReactiveFormsModule,
+        MatInputModule,
+        MatCardModule,
+        MatSelectModule,
+        MatOptionModule,
+        CovalentStepsModule,
+        NoopAnimationsModule
+      ],
+      providers: [
+        { provide: Router, useValue: router},
+        { provide: ActivatedRoute, useValue: activatedRoute },
+        {
+          provide: Store, useClass: class {
+            dispatch = jasmine.createSpy('dispatch');
+            select = jasmine.createSpy('select').and.returnValue(Observable.empty());
+          }
+        },
+        {
+          provide: EmployeesStore, useClass: class {
+            dispatch = jasmine.createSpy('dispatch');
+            select = jasmine.createSpy('select').and.callFake(selector => {
+              if (selector === fromEmployees.getSelectedEmployee) {
+                return Observable.of(employeeMock);
+              }
+
+              return Observable.empty();
+            });
+          }
+        }
+      ],
+      schemas: [CUSTOM_ELEMENTS_SCHEMA]
+    });
+
+    fixture = TestBed.createComponent(EditEmployeeFormComponent);
+    testComponent = fixture.componentInstance;
+  });
+
+  it('should test if employee is updated', async(inject([EmployeesStore], (store: EmployeesStore) => {
+    fixture.detectChanges();
+
+    testComponent.formComponent.detailForm.get('password').setValue('newPassword');
+
+    fixture.detectChanges();
+
+    testComponent.formComponent.save();
+
+    fixture.whenStable().then(() => {
+      expect(store.dispatch).toHaveBeenCalledWith({ type: UPDATE, payload: {
+        employee: employeeMock,
+        contactDetails: employeeMock.contactDetails,
+        role: userMock.role,
+        password: 'newPassword',
+        activatedRoute: activatedRoute
+      }});
+    });
+
+  })));
+});
diff --git a/src/app/centers/form/edit/edit.form.component.ts b/src/app/centers/form/edit/edit.form.component.ts
new file mode 100644
index 0000000..f0ab243
--- /dev/null
+++ b/src/app/centers/form/edit/edit.form.component.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 {Component, OnInit, ViewChild} from '@angular/core';
+import {ActivatedRoute, Router} from '@angular/router';
+import {CenterFormComponent, CenterFormData, CenterSaveEvent} from '../form.component';
+import {mapContactDetails, mapEmployee} from '../form.mapper';
+import {Center} from '../../../services/office/domain/employee.model';
+import {User} from '../../../services/identity/domain/user.model';
+import {UPDATE} from '../../store/employee.actions';
+import {Observable} from 'rxjs/Observable';
+import {EmployeesStore, getSelectedEmployee} from '../../store/index';
+
+@Component({
+  templateUrl: './edit.form.component.html'
+})
+export class EditEmployeeFormComponent implements OnInit {
+
+  @ViewChild('form') formComponent: CenterFormComponent;
+
+  formData: Observable<CenterFormData>;
+
+  employee: Center;
+
+  user: User;
+
+  constructor(private router: Router, private route: ActivatedRoute, private store: EmployeesStore) {}
+
+  ngOnInit() {
+    this.formData = Observable.combineLatest(
+      this.store.select(getSelectedEmployee),
+      this.route.data,
+      (employee: Center, data: { user: User }) => {
+        return {
+          user: data.user,
+          employee: employee
+        };
+      }
+    );
+  }
+
+  onSave(event: CenterSaveEvent) {
+    const employee: Center = mapEmployee(event);
+
+    this.store.dispatch({ type: UPDATE, payload: {
+      employee: employee,
+      contactDetails: mapContactDetails(event.contactForm),
+      role: event.detailForm.role,
+      password: event.detailForm.password,
+      activatedRoute: this.route
+    }});
+  }
+
+  onCancel() {
+    this.navigateToOffice();
+  }
+
+  private navigateToOffice() {
+    this.router.navigate(['../'], { relativeTo: this.route });
+  }
+}
diff --git a/src/app/centers/form/form.component.html b/src/app/centers/form/form.component.html
new file mode 100644
index 0000000..43ff2d4
--- /dev/null
+++ b/src/app/centers/form/form.component.html
@@ -0,0 +1,88 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<td-steps mode="'vertical'">
+  <td-step #detailsStep label="{{'Center details' | translate}}" [state]="detailForm.valid ? 'complete' : detailForm.pristine ? 'none' : 'required'">
+    <form [formGroup]="detailForm" layout="column">
+      <fims-id-input [form]="detailForm" placeholder="External id" controlName="identifier" [readonly]="editMode"></fims-id-input>
+      <fims-text-input [form]="detailForm" controlName="firstName" placeholder="{{'Name' | translate}}"></fims-text-input>
+     
+    
+    </form>
+    <ng-template td-step-actions>
+      <fims-form-continue-action (onContinue)="officeStep.open()"></fims-form-continue-action>
+    </ng-template>
+  </td-step>
+
+  <td-step #officeStep label="{{'Assign office to center' | translate}}"
+           [state]="officeForm.get('assignedOffice').value ? 'complete' : 'none'">
+
+    <fims-select-list #officeList flex
+                        [data]="offices"
+                        id="identifier"
+                        listIcon="store"
+                        [preSelection]="officeForm.get('assignedOffice').value ? [officeForm.get('assignedOffice').value] : []"
+                        (onSearch)="searchOffice($event)"
+                        (onSelectionChange)="assignOffice($event)"
+                        title="{{'Assigned Office' | translate}}"
+                        noResultsMessage="{{'No office was found.' | translate}}"
+                        noSelectionMessage="{{'No office assigned to employee, yet.' | translate}}">
+    </fims-select-list>
+<br/>
+    <mat-form-field>
+      <mat-select placeholder="Select Staff">
+        <mat-option *ngFor="let staff of staffs" [value]="staff.value">
+          {{staff.viewValue}}
+        </mat-option>
+      </mat-select>
+    </mat-form-field>
+    
+    
+    <ng-template td-step-actions>
+      <fims-form-continue-action (onContinue)="contactStep.open()"></fims-form-continue-action>
+    </ng-template>
+  </td-step>
+
+  <td-step #officeStep label="{{'Assign group to center' | translate}}"
+           [state]="officeForm.get('assignedOffice').value ? 'complete' : 'none'">
+
+    <fims-select-list #officeList flex
+                        
+                        title="{{'Assigned Group' | translate}}"
+                        noResultsMessage="{{'No group was found.' | translate}}"
+                        noSelectionMessage="{{'No group assigned to center, yet.' | translate}}">
+    </fims-select-list>
+
+    
+    <ng-template td-step-actions>
+      <fims-form-continue-action (onContinue)="contactStep.open()"></fims-form-continue-action>
+    </ng-template>
+  </td-step>
+
+  <td-step label="{{'Final step' | translate}}" [state]="'complete'">
+    <ng-template td-step-summary>
+      <fims-form-final-action
+        [resourceName]="'CENTER'"
+        [editMode]="editMode"
+        [disabled]="formsInvalid()"
+        (onCancel)="cancel()"
+        (onSave)="save()">
+      </fims-form-final-action>
+    </ng-template>
+  </td-step>
+
+</td-steps>
diff --git a/src/app/centers/form/form.component.spec.ts b/src/app/centers/form/form.component.spec.ts
new file mode 100644
index 0000000..621eefa
--- /dev/null
+++ b/src/app/centers/form/form.component.spec.ts
@@ -0,0 +1,135 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, TestBed} from '@angular/core/testing';
+import {Component, EventEmitter, ViewChild} from '@angular/core';
+import {Center} from '../../services/office/domain/employee.model';
+import {CenterFormComponent, CenterFormData, CenterSaveEvent} from './form.component';
+import {User} from '../../services/identity/domain/user.model';
+import {TranslateModule} from '@ngx-translate/core';
+import {ReactiveFormsModule} from '@angular/forms';
+import {CovalentStepsModule} from '@covalent/core';
+import {Observable} from 'rxjs/Observable';
+import {Store} from '@ngrx/store';
+import {NoopAnimationsModule} from '@angular/platform-browser/animations';
+import {FimsSharedModule} from '../../common/common.module';
+import {MatIconModule, MatInputModule, MatOptionModule, MatSelectModule} from '@angular/material';
+
+const employeeTemplate: Center = {
+  identifier: 'test',
+  givenName: 'test',
+  middleName: 'test',
+  surname: 'test',
+  contactDetails: [{
+    type: 'EMAIL',
+    group: 'BUSINESS',
+    value: 'test@test.de',
+    preferenceLevel: 0
+  }], 
+  assignedOffice: 'test'
+};
+
+const userTemplate: User = {
+  identifier: 'test',
+  role: 'test'
+};
+
+describe('Test employee form component', () => {
+
+  let fixture: ComponentFixture<TestComponent>;
+
+  let testComponent: TestComponent;
+
+  beforeEach(() => {
+    TestBed.configureTestingModule({
+      declarations: [
+        TestComponent,
+        CenterFormComponent
+      ],
+      imports: [
+        TranslateModule.forRoot(),
+        FimsSharedModule,
+        ReactiveFormsModule,
+        MatSelectModule,
+        MatOptionModule,
+        MatIconModule,
+        MatInputModule,
+        CovalentStepsModule,
+        NoopAnimationsModule
+      ],
+      providers: [
+        {
+          provide: Store, useClass: class {
+          dispatch = jasmine.createSpy('dispatch');
+          select = jasmine.createSpy('select').and.returnValue(Observable.empty());
+        }}
+      ]
+    });
+
+    fixture = TestBed.createComponent(TestComponent);
+    testComponent = fixture.componentInstance;
+  });
+
+  it('should test if the form save the original values', () => {
+    fixture.detectChanges();
+
+    testComponent.saveEmitter.subscribe((saveEvent: CenterSaveEvent) => {
+      expect(employeeTemplate.identifier).toEqual(saveEvent.detailForm.identifier);
+      expect(employeeTemplate.givenName).toEqual(saveEvent.detailForm.firstName);
+      expect(employeeTemplate.middleName).toEqual(saveEvent.detailForm.middleName);
+      expect(employeeTemplate.surname).toEqual(saveEvent.detailForm.lastName);
+      expect(saveEvent.detailForm.password).toEqual('');
+
+      expect(employeeTemplate.assignedOffice).toEqual(saveEvent.officeForm.assignedOffice);
+
+      expect(employeeTemplate.contactDetails.length).toEqual(1);
+      expect(employeeTemplate.contactDetails[0].value).toEqual(saveEvent.contactForm.email);
+
+      expect(userTemplate.role).toEqual(saveEvent.detailForm.role);
+    });
+
+    testComponent.triggerSave();
+
+  });
+});
+
+@Component({
+  template: `
+    <fims-employee-form-component #form (onSave)="onSave($event)" (onCancel)="onCancel($event)" [formData]="employeeFormData">
+    </fims-employee-form-component>`
+})
+class TestComponent {
+
+  saveEmitter = new EventEmitter<CenterSaveEvent>();
+
+  @ViewChild('form') formComponent: CenterFormComponent;
+
+  employeeFormData: CenterFormData = {
+    employee: employeeTemplate,
+    user: userTemplate
+  };
+
+  triggerSave(): void {
+    this.formComponent.save();
+  }
+
+  onSave(event: CenterSaveEvent): void {
+    this.saveEmitter.emit(event);
+  }
+
+}
diff --git a/src/app/centers/form/form.component.ts b/src/app/centers/form/form.component.ts
new file mode 100644
index 0000000..6f1e6cc
--- /dev/null
+++ b/src/app/centers/form/form.component.ts
@@ -0,0 +1,189 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, OnInit, Output, ViewChild} from '@angular/core';
+import {FormBuilder, FormGroup, ValidatorFn, Validators} from '@angular/forms';
+import {Observable} from 'rxjs/Observable';
+import {TdStepComponent} from '@covalent/core';
+import {Office} from '../../services/office/domain/office.model';
+import {Center} from '../../services/office/domain/employee.model';
+import {BUSINESS, ContactDetail, EMAIL, MOBILE, PHONE} from '../../services/domain/contact/contact-detail.model';
+import {FetchRequest} from '../../services/domain/paging/fetch-request.model';
+import {Role} from '../../services/identity/domain/role.model';
+import {User} from '../../services/identity/domain/user.model';
+import {FimsValidators} from '../../common/validator/validators';
+import {Store} from '@ngrx/store';
+import * as fromRoot from '../../store';
+import {SEARCH as SEARCH_OFFICE} from '../../store/office/office.actions';
+import {SEARCH as SEARCH_ROLE} from '../../store/role/role.actions';
+
+export interface CenterFormData {
+  user: User;
+  employee: Center;
+}
+
+export interface CenterSaveEvent {
+  detailForm: {
+    identifier: string;
+    firstName: string;
+    middleName: string;
+    lastName: string;
+    password: string;
+    role: string;
+  };
+  contactForm: {
+    email: string;
+    phone: string;
+    mobile: string;
+  };
+  
+  officeForm: {
+    assignedOffice: string;
+  };
+}
+
+@Component({
+  selector: 'fims-center-form-component',
+  templateUrl: './form.component.html'
+})
+export class CenterFormComponent implements OnInit {
+
+  offices: Observable<Office[]>;
+
+  roles: Observable<Role[]>;
+
+  detailForm: FormGroup;
+  contactForm: FormGroup;
+  officeForm: FormGroup;
+
+  @ViewChild('detailsStep') step: TdStepComponent;
+
+  @Input('editMode') editMode: boolean;
+
+  @Input('formData') set formData(formData: CenterFormData) {
+    this.prepareDetailForm(formData.employee, formData.user);
+    this.prepareOfficeForm(formData.employee);
+    this.prepareContactForm(formData.employee.contactDetails);
+  }
+
+  @Output('onSave') onSave = new EventEmitter<CenterSaveEvent>();
+  @Output('onCancel') onCancel = new EventEmitter<void>();
+
+  constructor(private formBuilder: FormBuilder, private store: Store<fromRoot.State>) {}
+
+  ngOnInit(): void {
+    this.offices = this.store.select(fromRoot.getOfficeSearchResults)
+      .map(officePage => officePage.offices);
+
+    this.roles = this.store.select(fromRoot.getRoleSearchResults)
+      .map(rolesPage => rolesPage.roles);
+
+    this.fetchRoles();
+
+    this.step.open();
+  }
+
+  prepareDetailForm(employee: Center, user: User): void {
+    const passwordValidators: ValidatorFn[] = [Validators.minLength(8)];
+
+    if (!this.editMode) {
+      passwordValidators.push(Validators.required);
+    }
+
+    this.detailForm = this.formBuilder.group({
+      identifier: [employee.identifier, [Validators.required, Validators.minLength(3), Validators.maxLength(32), FimsValidators.urlSafe]],
+      firstName: [employee.givenName, [Validators.required, Validators.maxLength(256)]],
+      middleName: [employee.middleName, Validators.maxLength(256)],
+      lastName: [employee.surname, [Validators.required, Validators.maxLength(256)]],
+      password: ['', passwordValidators],
+      role: [user ? user.role : '', Validators.required]
+    });
+  }
+
+  private prepareOfficeForm(employee: Center) {
+    this.officeForm = this.formBuilder.group({
+      assignedOffice: [employee.assignedOffice]
+    });
+  }
+
+ private prepareContactForm(contactDetails: ContactDetail[]): void {
+    let phone = '';
+    let mobile = '';
+    let email = '';
+
+    const businessContacts: ContactDetail[] = contactDetails.filter(contactDetail => contactDetail.group === BUSINESS);
+
+    if (businessContacts.length) {
+      phone = this.getFirstItemByType(businessContacts, PHONE);
+      mobile = this.getFirstItemByType(businessContacts, MOBILE);
+      email = this.getFirstItemByType(businessContacts, EMAIL);
+    }
+
+    this.contactForm = this.formBuilder.group({
+      email: [email, [Validators.maxLength(256), FimsValidators.email]],
+      phone: [phone, Validators.maxLength(256)],
+      mobile: [mobile, Validators.maxLength(256)]
+    });
+  }
+  
+
+  getFirstItemByType(contactDetails: ContactDetail[], type: string): string {
+    const items = contactDetails.filter(contact => contact.type === type);
+    return items.length ? items[0].value : '';
+  }
+
+  formsInvalid(): boolean {
+    return (!this.officeForm.pristine && this.officeForm.invalid) ||
+    (!this.contactForm.pristine && this.contactForm.invalid)
+      || this.detailForm.invalid;
+  }
+
+  
+
+  save(): void {
+    this.onSave.emit({
+      detailForm: this.detailForm.value,
+      contactForm: this.contactForm.value,
+      officeForm: this.officeForm.value
+    });
+  }
+
+  cancel(): void {
+    this.onCancel.emit();
+  }
+
+  searchOffice(searchTerm: string): void {
+    const fetchRequest: FetchRequest = {
+      searchTerm
+    };
+    this.store.dispatch({ type: SEARCH_OFFICE, payload: fetchRequest });
+  }
+
+  assignOffice(selections: string[]): void {
+    this.setFormValue(this.officeForm, {'assignedOffice': selections && selections.length > 0 ? selections[0] : undefined});
+  }
+
+  fetchRoles(): void {
+    this.store.dispatch({ type: SEARCH_ROLE });
+  }
+
+  private setFormValue(form: FormGroup, value: any) {
+    form.setValue(value);
+    form.markAsDirty();
+  }
+}
diff --git a/src/app/centers/form/form.mapper.ts b/src/app/centers/form/form.mapper.ts
new file mode 100644
index 0000000..c74aab3
--- /dev/null
+++ b/src/app/centers/form/form.mapper.ts
@@ -0,0 +1,78 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 {CenterSaveEvent} from './form.component';
+import {ContactDetail, ContactDetailType} from '../../services/domain/contact/contact-detail.model';
+import {Center} from '../../services/office/domain/employee.model';
+import {UserWithPassword} from '../../services/identity/domain/user-with-password.model';
+
+function buildContactDetail(type: ContactDetailType, value: string): ContactDetail {
+  return {
+    group: 'BUSINESS',
+    type: type,
+    value: value,
+    preferenceLevel: 1
+  };
+} 
+
+export function mapContactDetails(contactForm: any): ContactDetail[] {
+  const contactDetails: ContactDetail[] = [];
+
+  if (contactForm.phone) {
+    contactDetails.push(buildContactDetail('PHONE', contactForm.phone));
+  }
+
+  if (contactForm.mobile) {
+    contactDetails.push(buildContactDetail('MOBILE', contactForm.mobile));
+  }
+
+  if (contactForm.email) {
+    contactDetails.push(buildContactDetail('EMAIL', contactForm.email));
+  }
+
+  return contactDetails;
+}
+
+
+export function mapEmployee(event: CenterSaveEvent): Center {
+  const assignedOffice = event.officeForm.assignedOffice;
+
+  const contactDetails: ContactDetail[] = mapContactDetails(event.contactForm);
+
+  const employee: Center = {
+    identifier: event.detailForm.identifier,
+    givenName: event.detailForm.firstName,
+    middleName: event.detailForm.middleName,
+    surname: event.detailForm.lastName,
+    contactDetails: contactDetails,
+    assignedOffice: assignedOffice ? assignedOffice : undefined
+  };
+
+  return employee;
+}
+
+export function mapUser(event: CenterSaveEvent): UserWithPassword {
+  const userWithPassword: UserWithPassword = {
+    identifier: event.detailForm.identifier,
+    password: event.detailForm.password,
+    role: event.detailForm.role
+  };
+
+  return userWithPassword;
+}
diff --git a/src/app/centers/store/effects/notification.effects.ts b/src/app/centers/store/effects/notification.effects.ts
new file mode 100644
index 0000000..3de745e
--- /dev/null
+++ b/src/app/centers/store/effects/notification.effects.ts
@@ -0,0 +1,47 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 employeeActions from '../employee.actions';
+import {NotificationService, NotificationType} from '../../../services/notification/notification.service';
+
+@Injectable()
+export class EmployeeNotificationEffects {
+
+  @Effect({ dispatch: false })
+  createEmployeeSuccess$: Observable<Action> = this.actions$
+    .ofType(employeeActions.CREATE_SUCCESS, employeeActions.UPDATE_SUCCESS)
+    .do(() => this.notificationService.send({
+      type: NotificationType.MESSAGE,
+      message: 'Employee is going to be saved'
+    }));
+
+  @Effect({ dispatch: false })
+  deleteEmployeeSuccess$: Observable<Action> = this.actions$
+    .ofType(employeeActions.DELETE_SUCCESS)
+    .do(() => this.notificationService.send({
+      type: NotificationType.MESSAGE,
+      message: 'Employee is going to be deleted'
+    }));
+
+  constructor(private actions$: Actions, private notificationService: NotificationService) {}
+}
+
diff --git a/src/app/centers/store/effects/route.effects.ts b/src/app/centers/store/effects/route.effects.ts
new file mode 100644
index 0000000..3e35dd1
--- /dev/null
+++ b/src/app/centers/store/effects/route.effects.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 {Actions, Effect} from '@ngrx/effects';
+import {Observable} from 'rxjs/Observable';
+import {Action} from '@ngrx/store';
+import * as employeeActions from '../employee.actions';
+import {Router} from '@angular/router';
+
+@Injectable()
+export class EmployeeRouteEffects {
+
+  @Effect({ dispatch: false })
+  createEmployeeSuccess$: Observable<Action> = this.actions$
+    .ofType(employeeActions.CREATE_SUCCESS, employeeActions.UPDATE_SUCCESS)
+    .map(action => action.payload)
+    .do(payload => this.router.navigate(['../'], { relativeTo: payload.activatedRoute} ));
+
+  @Effect({ dispatch: false })
+  deleteEmployeeSuccess$: Observable<Action> = this.actions$
+    .ofType(employeeActions.DELETE_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/centers/store/effects/service.effects.spec.ts b/src/app/centers/store/effects/service.effects.spec.ts
new file mode 100644
index 0000000..5afcd87
--- /dev/null
+++ b/src/app/centers/store/effects/service.effects.spec.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 {fakeAsync, TestBed, tick} from '@angular/core/testing';
+import {EffectsRunner, EffectsTestingModule} from '@ngrx/effects/testing';
+import {EmployeeApiEffects} from './service.effects';
+import {OfficeService} from '../../../services/office/office.service';
+import {IdentityService} from '../../../services/identity/identity.service';
+import {Observable} from 'rxjs/Observable';
+import {UpdateEmployeeAction, UpdateEmployeeSuccessAction} from '../employee.actions';
+import {Employee} from '../../../services/office/domain/employee.model';
+
+describe('Account Search Api Effects', () => {
+  beforeEach(() => {
+
+    TestBed.configureTestingModule({
+      imports: [
+        EffectsTestingModule
+      ],
+      providers: [
+        EmployeeApiEffects,
+        {
+          provide: OfficeService,
+          useValue: jasmine.createSpyObj('officeService', ['createEmployee', 'updateEmployee', 'setContactDetails'])
+        },
+        {
+          provide: IdentityService,
+          useValue: jasmine.createSpyObj('identityService', ['createUser', 'changeUserRole', 'changePassword'])
+        }
+      ]
+    });
+
+  });
+
+  describe('updateEmployee$', () => {
+
+    function setup() {
+      const officeService = TestBed.get(OfficeService);
+
+      officeService.updateEmployee.and.returnValue(Observable.of({}));
+
+      return {
+        runner: TestBed.get(EffectsRunner),
+        officeService: officeService,
+        identityService: TestBed.get(IdentityService),
+        employeeEffects: TestBed.get(EmployeeApiEffects)
+      };
+    }
+
+    it('should update employee', fakeAsync(() => {
+      const { runner, officeService, employeeEffects } = setup();
+
+      const employee: Employee = {
+        identifier: '',
+        givenName: '',
+        surname: '',
+        contactDetails: []
+      };
+
+      const expectedResult = new UpdateEmployeeSuccessAction({
+        resource: employee,
+        activatedRoute: null
+      });
+
+      runner.queue(new UpdateEmployeeAction({
+        employee: employee,
+        activatedRoute: null
+      }));
+
+      let result = null;
+      employeeEffects.updateEmployee$.subscribe(_result => result = _result);
+
+      tick();
+
+      expect(result).toEqual(expectedResult);
+
+      expect(officeService.updateEmployee).toHaveBeenCalled();
+    }));
+
+    it('should update contact details', fakeAsync(() => {
+      const { runner, officeService, employeeEffects } = setup();
+
+      officeService.setContactDetails.and.returnValue(Observable.of({}));
+
+      const employee: Employee = {
+        identifier: '',
+        givenName: '',
+        surname: '',
+        contactDetails: []
+      };
+
+      const expectedResult = new UpdateEmployeeSuccessAction({
+        resource: employee,
+        activatedRoute: null
+      });
+
+      runner.queue(new UpdateEmployeeAction({
+        employee: employee,
+        contactDetails: [
+          {
+            type: 'EMAIL',
+            group: 'BUSINESS',
+            value: 'dont@call.me',
+            preferenceLevel: 0
+          }
+        ],
+        activatedRoute: null
+      }));
+
+      let result = null;
+      employeeEffects.updateEmployee$.subscribe(_result => result = _result);
+
+      tick();
+
+      expect(result).toEqual(expectedResult);
+
+      expect(officeService.setContactDetails).toHaveBeenCalled();
+    }));
+
+    it('should update password', fakeAsync(() => {
+      const { runner, identityService, employeeEffects } = setup();
+
+      identityService.changePassword.and.returnValue(Observable.of({}));
+
+      const employee: Employee = {
+        identifier: '',
+        givenName: '',
+        surname: '',
+        contactDetails: []
+      };
+
+      const expectedResult = new UpdateEmployeeSuccessAction({
+        resource: employee,
+        activatedRoute: null
+      });
+
+      runner.queue(new UpdateEmployeeAction({
+        employee: employee,
+        password: 'test',
+        activatedRoute: null
+      }));
+
+      let result = null;
+      employeeEffects.updateEmployee$.subscribe(_result => result = _result);
+
+      tick();
+
+      expect(result).toEqual(expectedResult);
+
+      expect(identityService.changePassword).toHaveBeenCalled();
+    }));
+
+    it('should update role', fakeAsync(() => {
+      const { runner, identityService, employeeEffects } = setup();
+
+      identityService.changeUserRole.and.returnValue(Observable.of({}));
+
+      const employee: Employee = {
+        identifier: '',
+        givenName: '',
+        surname: '',
+        contactDetails: []
+      };
+
+      const expectedResult = new UpdateEmployeeSuccessAction({
+        resource: employee,
+        activatedRoute: null
+      });
+
+      runner.queue(new UpdateEmployeeAction({
+        employee: employee,
+        role: 'test',
+        activatedRoute: null
+      }));
+
+      let result = null;
+      employeeEffects.updateEmployee$.subscribe(_result => result = _result);
+
+      tick();
+
+      expect(result).toEqual(expectedResult);
+
+      expect(identityService.changeUserRole).toHaveBeenCalled();
+    }));
+  });
+});
diff --git a/src/app/centers/store/effects/service.effects.ts b/src/app/centers/store/effects/service.effects.ts
new file mode 100644
index 0000000..0a17834
--- /dev/null
+++ b/src/app/centers/store/effects/service.effects.ts
@@ -0,0 +1,95 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 '../../../services/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 {IdentityService} from '../../../services/identity/identity.service';
+import {RoleIdentifier} from '../../../services/identity/domain/role-identifier.model';
+import {Password} from '../../../services/identity/domain/password.model';
+
+@Injectable()
+export class EmployeeApiEffects {
+
+  @Effect()
+  createEmployee$: Observable<Action> = this.actions$
+    .ofType(employeeActions.CREATE)
+    .map((action: employeeActions.CreateEmployeeAction) => action.payload)
+    .mergeMap(payload =>
+      this.identityService.createUser(payload.user)
+        .mergeMap(() => this.officeService.createEmployee(payload.employee) )
+        .map(() => new employeeActions.CreateEmployeeSuccessAction({
+          resource: payload.employee,
+          activatedRoute: payload.activatedRoute
+        }))
+        .catch((error) => of(new employeeActions.CreateEmployeeFailAction(error)))
+    );
+
+  @Effect()
+  updateEmployee$: Observable<Action> = this.actions$
+    .ofType(employeeActions.UPDATE)
+    .map((action: employeeActions.UpdateEmployeeAction) => action.payload)
+    .mergeMap(payload => {
+        const employee = payload.employee;
+        const httpCalls: Observable<any>[] = [];
+
+        httpCalls.push(this.officeService.updateEmployee(employee));
+
+        if (payload.contactDetails) {
+          httpCalls.push(this.officeService.setContactDetails(employee.identifier, payload.contactDetails));
+        }
+
+        if (payload.role) {
+          httpCalls.push(this.identityService.changeUserRole(employee.identifier, new RoleIdentifier(payload.role)));
+        }
+
+        if (payload.password) {
+          httpCalls.push(this.identityService.changePassword(employee.identifier, new Password(payload.password)));
+        }
+
+        return Observable.forkJoin(httpCalls)
+          .map(() => new employeeActions.UpdateEmployeeSuccessAction({
+            resource: payload.employee,
+            activatedRoute: payload.activatedRoute
+          }))
+          .catch((error) => of(new employeeActions.UpdateEmployeeFailAction(error)));
+    });
+
+  @Effect()
+  deleteEmployee$: Observable<Action> = this.actions$
+    .ofType(employeeActions.DELETE)
+    .map((action: employeeActions.DeleteEmployeeAction) => action.payload)
+    .mergeMap(payload => {
+      return Observable.forkJoin(
+        this.officeService.deleteEmployee(payload.employee.identifier),
+        this.identityService.changeUserRole(payload.employee.identifier, new RoleIdentifier('deactivated'))
+      )
+        .map(() => new employeeActions.DeleteEmployeeSuccessAction({
+          resource: payload.employee,
+          activatedRoute: payload.activatedRoute
+        }))
+        .catch((error) => of(new employeeActions.DeleteEmployeeFailAction(error)));
+      }
+    );
+
+  constructor(private actions$: Actions, private officeService: OfficeService, private identityService: IdentityService) { }
+}
diff --git a/src/app/centers/store/employee.actions.ts b/src/app/centers/store/employee.actions.ts
new file mode 100644
index 0000000..75c3a99
--- /dev/null
+++ b/src/app/centers/store/employee.actions.ts
@@ -0,0 +1,149 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 {Employee} from '../../services/office/domain/employee.model';
+import {Error} from '../../services/domain/error.model';
+import {ContactDetail} from '../../services/domain/contact/contact-detail.model';
+import {UserWithPassword} from '../../services/identity/domain/user-with-password.model';
+import {RoutePayload} from '../../common/store/route-payload';
+import {
+  CreateResourceSuccessPayload,
+  DeleteResourceSuccessPayload,
+  LoadResourcePayload,
+  SelectResourcePayload,
+  UpdateResourceSuccessPayload
+} from '../../common/store/resource.reducer';
+
+export const LOAD = type('[Center] Load');
+export const SELECT = type('[Center] Select');
+
+export const CREATE = type('[Center] Create');
+export const CREATE_SUCCESS = type('[Center] Create Success');
+export const CREATE_FAIL = type('[Center] Create Fail');
+
+export const UPDATE = type('[Center] Update');
+export const UPDATE_SUCCESS = type('[Center] Update Success');
+export const UPDATE_FAIL = type('[Center] Update Fail');
+
+export const DELETE = type('[Center] Delete');
+export const DELETE_SUCCESS = type('[Center] Delete Success');
+export const DELETE_FAIL = type('[Center] Delete Fail');
+
+export const RESET_FORM = type('[Center] Reset Form');
+
+export interface EmployeeRoutePayload extends RoutePayload {
+  employee: Employee;
+}
+
+export interface CreateEmployeePayload extends EmployeeRoutePayload {
+  user: UserWithPassword;
+}
+
+export interface UpdateEmployeePayload extends EmployeeRoutePayload {
+  contactDetails?: ContactDetail[];
+  password?: string;
+  role?: string;
+}
+
+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 CreateEmployeeAction implements Action {
+  readonly type = CREATE;
+
+  constructor(public payload: CreateEmployeePayload) { }
+}
+
+export class CreateEmployeeSuccessAction implements Action {
+  readonly type = CREATE_SUCCESS;
+
+  constructor(public payload: CreateResourceSuccessPayload) { }
+}
+
+export class CreateEmployeeFailAction implements Action {
+  readonly type = CREATE_FAIL;
+
+  constructor(public payload: Error) { }
+}
+
+export class UpdateEmployeeAction implements Action {
+  readonly type = UPDATE;
+
+  constructor(public payload: UpdateEmployeePayload) { }
+}
+
+export class UpdateEmployeeSuccessAction implements Action {
+  readonly type = UPDATE_SUCCESS;
+
+  constructor(public payload: UpdateResourceSuccessPayload) { }
+}
+
+export class UpdateEmployeeFailAction implements Action {
+  readonly type = UPDATE_FAIL;
+
+  constructor(public payload: Error) { }
+}
+
+export class DeleteEmployeeAction implements Action {
+  readonly type = DELETE;
+
+  constructor(public payload: EmployeeRoutePayload) { }
+}
+
+export class DeleteEmployeeSuccessAction implements Action {
+  readonly type = DELETE_SUCCESS;
+
+  constructor(public payload: DeleteResourceSuccessPayload) { }
+}
+
+export class DeleteEmployeeFailAction implements Action {
+  readonly type = DELETE_FAIL;
+
+  constructor(public payload: Error) { }
+}
+
+export class ResetEmployeeFormAction implements Action {
+  readonly type = RESET_FORM;
+
+  constructor() { }
+}
+
+export type Actions
+  = LoadAction
+  | SelectAction
+  | CreateEmployeeAction
+  | CreateEmployeeSuccessAction
+  | CreateEmployeeFailAction
+  | UpdateEmployeeAction
+  | UpdateEmployeeSuccessAction
+  | UpdateEmployeeFailAction
+  | DeleteEmployeeAction
+  | DeleteEmployeeSuccessAction
+  | DeleteEmployeeFailAction
+  | ResetEmployeeFormAction;
diff --git a/src/app/centers/store/index.ts b/src/app/centers/store/index.ts
new file mode 100644
index 0000000..6b4ea4e
--- /dev/null
+++ b/src/app/centers/store/index.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 * as fromRoot from '../../store';
+import {ActionReducer, Store} from '@ngrx/store';
+import {createReducer} from '../../store/index';
+import {createSelector} from 'reselect';
+import {createResourceReducer, getResourceLoadedAt, getResourceSelected, ResourceState} from '../../common/store/resource.reducer';
+import {createFormReducer, FormState, getFormError} from '../../common/store/form.reducer';
+
+export interface State extends fromRoot.State {
+  employees: ResourceState;
+  employeeForm: FormState;
+}
+
+const reducers = {
+  employees: createResourceReducer('Employee'),
+  employeeForm: createFormReducer('Employee')
+};
+
+export const employeeModuleReducer: ActionReducer<State> = createReducer(reducers);
+
+export const getEmployeesState = (state: State) => state.employees;
+
+export const getEmployeeFormState = (state: State) => state.employeeForm;
+export const getEmployeeFormError = createSelector(getEmployeeFormState, getFormError);
+
+export const getEmployeesLoadedAt = createSelector(getEmployeesState, getResourceLoadedAt);
+export const getSelectedEmployee = createSelector(getEmployeesState, getResourceSelected);
+
+export class EmployeesStore extends Store<State> {}
+
+export function employeeStoreFactory(appStore: Store<fromRoot.State>) {
+  appStore.replaceReducer(employeeModuleReducer);
+  return appStore;
+}
diff --git a/src/app/centers/user.resolver.ts b/src/app/centers/user.resolver.ts
new file mode 100644
index 0000000..34f29cd
--- /dev/null
+++ b/src/app/centers/user.resolver.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 {Injectable} from '@angular/core';
+import {ActivatedRouteSnapshot, Resolve} from '@angular/router';
+import {Observable} from 'rxjs/Observable';
+import {IdentityService} from '../services/identity/identity.service';
+import {User} from '../services/identity/domain/user.model';
+
+@Injectable()
+export class UserResolver implements Resolve<User> {
+
+  constructor(private identityService: IdentityService) {}
+
+  resolve(route: ActivatedRouteSnapshot): Observable<User> {
+    return this.identityService.getUser(route.params['id']);
+  }
+}
diff --git a/src/app/common/account-select/account-select.component.html b/src/app/common/account-select/account-select.component.html
new file mode 100644
index 0000000..8368bd2
--- /dev/null
+++ b/src/app/common/account-select/account-select.component.html
@@ -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.
+-->
+
+<div layout="row">
+  <mat-form-field layout-margin flex>
+    <input matInput [placeholder]="title" [matAutocomplete]="auto" [formControl]="formControl">
+    <mat-hint class="tc-red-600">
+      <ng-content></ng-content>
+    </mat-hint>
+  </mat-form-field>
+</div>
+
+<mat-autocomplete #auto="matAutocomplete">
+  <mat-option *ngFor="let account of accounts | async" [value]="account.identifier">
+    {{ account.identifier }}({{account.name}})
+  </mat-option>
+</mat-autocomplete>
diff --git a/src/app/common/account-select/account-select.component.ts b/src/app/common/account-select/account-select.component.ts
new file mode 100644
index 0000000..40fb645
--- /dev/null
+++ b/src/app/common/account-select/account-select.component.ts
@@ -0,0 +1,105 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, forwardRef, Input, OnInit} from '@angular/core';
+import {FetchRequest} from '../../services/domain/paging/fetch-request.model';
+import {Observable} from 'rxjs/Observable';
+import {AccountingService} from '../../services/accounting/accounting.service';
+import {Account} from '../../services/accounting/domain/account.model';
+import {AccountPage} from '../../services/accounting/domain/account-page.model';
+import {ControlValueAccessor, FormControl, NG_VALUE_ACCESSOR} from '@angular/forms';
+import {AccountType} from '../../services/accounting/domain/account-type.model';
+
+const noop: () => void = () => {
+  // empty method
+};
+
+@Component({
+  providers: [
+    { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => AccountSelectComponent), multi: true }
+  ],
+  selector: 'fims-account-select',
+  templateUrl: './account-select.component.html'
+})
+export class AccountSelectComponent implements ControlValueAccessor, OnInit {
+
+  formControl: FormControl;
+
+  @Input() title: string;
+
+  @Input() required: boolean;
+
+  @Input() type: AccountType;
+
+  accounts: Observable<Account[]>;
+
+  private _onTouchedCallback: () => void = noop;
+
+  private _onChangeCallback: (_: any) => void = noop;
+
+  constructor(private accountingService: AccountingService) {}
+
+  ngOnInit(): void {
+    this.formControl = new FormControl('');
+
+    this.accounts = this.formControl.valueChanges
+      .distinctUntilChanged()
+      .debounceTime(500)
+      .do(name => this.changeValue(name))
+      .filter(name => name)
+      .switchMap(name => this.onSearch(name));
+  }
+
+  changeValue(value: string): void {
+    this._onChangeCallback(value);
+  }
+
+  writeValue(value: any): void {
+    this.formControl.setValue(value);
+  }
+
+  registerOnChange(fn: any): void {
+    this._onChangeCallback = fn;
+  }
+
+  registerOnTouched(fn: any): void {
+    this._onTouchedCallback = fn;
+  }
+
+  setDisabledState(isDisabled: boolean): void {
+    if (isDisabled) {
+      this.formControl.disable();
+    } else {
+      this.formControl.enable();
+    }
+  }
+
+  onSearch(searchTerm?: string): Observable<Account[]> {
+    const fetchRequest: FetchRequest = {
+      page: {
+        pageIndex: 0,
+        size: 5
+      },
+      searchTerm: searchTerm
+    };
+
+    return this.accountingService.fetchAccounts(fetchRequest, this.type)
+      .map((accountPage: AccountPage) => accountPage.accounts);
+  }
+
+}
diff --git a/src/app/common/address/address.component.html b/src/app/common/address/address.component.html
new file mode 100644
index 0000000..3199b61
--- /dev/null
+++ b/src/app/common/address/address.component.html
@@ -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.
+-->
+
+<form [formGroup]="form" layout="column">
+  <fims-text-input [form]="form" controlName="street" placeholder="{{'Street' | translate}}"></fims-text-input>
+  <fims-text-input [form]="form" controlName="city" placeholder="{{'City' | translate}}"></fims-text-input>
+  <fims-text-input [form]="form" controlName="postalCode" placeholder="{{'Postal code(optional)' | translate}}"></fims-text-input>
+  <mat-form-field layout-margin flex>
+    <input matInput placeholder="{{'Country' | translate}}" formControlName="country" [matAutocomplete]="auto">
+    <mat-error *ngIf="form.get('country').hasError('required')" translate>Required</mat-error>
+    <mat-error *ngIf="form.get('country').hasError('invalidCountry')" translate>Invalid country</mat-error>
+  </mat-form-field>
+  <mat-autocomplete #auto="matAutocomplete" [displayWith]="countryDisplay">
+    <mat-option *ngFor="let country of filteredCountries | async" [value]="country">
+      {{country.displayName}}
+    </mat-option>
+  </mat-autocomplete>
+  <fims-text-input [form]="form" controlName="region" placeholder="{{'Region(optional)' | translate}}"></fims-text-input>
+</form>
diff --git a/src/app/common/address/address.component.spec.ts b/src/app/common/address/address.component.spec.ts
new file mode 100644
index 0000000..e65aeb6
--- /dev/null
+++ b/src/app/common/address/address.component.spec.ts
@@ -0,0 +1,98 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, ViewChild} from '@angular/core';
+import {async, ComponentFixture, TestBed} from '@angular/core/testing';
+import {TranslateModule} from '@ngx-translate/core';
+import {ReactiveFormsModule} from '@angular/forms';
+import {CovalentStepsModule} from '@covalent/core';
+import {NoopAnimationsModule} from '@angular/platform-browser/animations';
+import {AddressFormComponent} from './address.component';
+import {Address} from '../../services/domain/address/address.model';
+import {CountryService} from '../../services/country/country.service';
+import {Country} from '../../services/country/model/country.model';
+import {FimsSharedModule} from '../common.module';
+import {MatAutocompleteModule, MatInputModule} from '@angular/material';
+
+const country: Country = {
+  displayName: 'country',
+  name: 'country',
+  alpha2Code: 'countryCode',
+  translations: {}
+};
+
+describe('Test address form', () => {
+
+  let fixture: ComponentFixture<TestComponent>;
+
+  let testComponent: TestComponent;
+
+  beforeEach( async(() => {
+    TestBed.configureTestingModule({
+      imports: [
+        TranslateModule.forRoot(),
+        FimsSharedModule,
+        MatInputModule,
+        MatAutocompleteModule,
+        CovalentStepsModule,
+        ReactiveFormsModule,
+        NoopAnimationsModule
+      ],
+      declarations: [
+        TestComponent
+      ],
+      providers: [
+        {
+          provide: CountryService, useClass: class {
+            fetchByCountryCode = jasmine.createSpy('fetchByCountryCode').and.returnValue(country);
+            fetchCountries = jasmine.createSpy('fetchCountries').and.returnValue([country]);
+          }
+        }
+      ]
+    }).compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture  = TestBed.createComponent(TestComponent);
+    testComponent = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should return same address', () => {
+    expect(testComponent.form.formData).toEqual(testComponent.address);
+  });
+
+});
+
+@Component({
+  template: '<fims-address-form #form [formData]="address"></fims-address-form>'
+})
+class TestComponent {
+
+  @ViewChild('form') form: AddressFormComponent;
+
+  address: Address = {
+    street: 'street',
+    city: 'city',
+    region: 'region',
+    postalCode: 'postalCode',
+    countryCode: 'countryCode',
+    country: 'country'
+  };
+
+}
diff --git a/src/app/common/address/address.component.ts b/src/app/common/address/address.component.ts
new file mode 100644
index 0000000..d27dd73
--- /dev/null
+++ b/src/app/common/address/address.component.ts
@@ -0,0 +1,79 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, Input, OnInit} from '@angular/core';
+import {FormComponent} from '../forms/form.component';
+import {FormBuilder, Validators} from '@angular/forms';
+import {Address} from '../../services/domain/address/address.model';
+import {Country} from '../../services/country/model/country.model';
+import {CountryService} from '../../services/country/country.service';
+import {countryExists} from '../validator/country-exists.validator';
+import {Observable} from 'rxjs/Observable';
+
+@Component({
+  selector: 'fims-address-form',
+  templateUrl: './address.component.html'
+})
+export class AddressFormComponent extends FormComponent<Address> implements OnInit {
+
+  filteredCountries: Observable<Country[]>;
+
+  @Input() set formData(address: Address) {
+    let country: Country;
+
+    if (address) {
+      country = this.countryService.fetchByCountryCode(address.countryCode);
+    }
+
+    this.form = this.formBuilder.group({
+      street: [address ? address.street : undefined, [Validators.required, Validators.maxLength(256)]],
+      city: [address ? address.city : undefined, [Validators.required, Validators.maxLength(256)]],
+      postalCode: [address ? address.postalCode : undefined, Validators.maxLength(32)],
+      region: [address ? address.region : undefined, Validators.maxLength(256)],
+      country: [country, [Validators.required], countryExists(this.countryService)]
+    });
+  };
+
+  constructor(private formBuilder: FormBuilder, private countryService: CountryService) {
+    super();
+  }
+
+  ngOnInit(): void {
+    this.filteredCountries = this.form.get('country').valueChanges
+      .startWith(null)
+      .map(country => country && typeof country === 'object' ? country.displayName : country)
+      .map(searchTerm => this.countryService.fetchCountries(searchTerm));
+  }
+
+  get formData(): Address {
+    const country: Country = this.form.get('country').value;
+
+    return {
+      street: this.form.get('street').value,
+      city: this.form.get('city').value,
+      postalCode: this.form.get('postalCode').value,
+      region: this.form.get('region').value,
+      country: country.name,
+      countryCode: country.alpha2Code
+    };
+  }
+
+  countryDisplay(country: Country): string {
+    return country ? country.displayName : undefined;
+  }
+}
diff --git a/src/app/common/command-display/command-display.component.html b/src/app/common/command-display/command-display.component.html
new file mode 100644
index 0000000..52f2345
--- /dev/null
+++ b/src/app/common/command-display/command-display.component.html
@@ -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.
+-->
+
+<mat-list-item [ngSwitch]="command.action">
+  <mat-icon matListAvatar color="primary" *ngSwitchCase="'ACTIVATE'">check_circle</mat-icon>
+  <mat-icon matListAvatar color="primary" *ngSwitchCase="'LOCK'">lock</mat-icon>
+  <mat-icon matListAvatar color="primary" *ngSwitchCase="'CLOSE'">close</mat-icon>
+  <mat-icon matListAvatar color="primary" *ngSwitchCase="'UNLOCK'">lock_open</mat-icon>
+  <h3 matLine *ngSwitchCase="'ACTIVATE'" translate>ACTIVATE</h3>
+  <h3 matLine *ngSwitchCase="'LOCK'" translate>LOCK</h3>
+  <h3 matLine *ngSwitchCase="'CLOSE'" translate>CLOSE</h3>
+  <h3 matLine *ngSwitchCase="'UNLOCK'" translate>UNLOCK</h3>
+  <h4 matLine>{{command.createdBy}}, {{command.createdOn | date:'medium'}}</h4>
+  <p matLine>{{command.comment}}</p>
+</mat-list-item>
diff --git a/src/app/common/command-display/command-display.component.ts b/src/app/common/command-display/command-display.component.ts
new file mode 100644
index 0000000..d641dc3
--- /dev/null
+++ b/src/app/common/command-display/command-display.component.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 {Component, Input} from '@angular/core';
+
+@Component({
+  selector: 'fims-command-display',
+  templateUrl: './command-display.component.html'
+})
+export class CommandDisplayComponent {
+  @Input() command: any;
+}
diff --git a/src/app/common/common.module.ts b/src/app/common/common.module.ts
new file mode 100644
index 0000000..88a67d6
--- /dev/null
+++ b/src/app/common/common.module.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 {NgModule} from '@angular/core';
+import {FormsModule, ReactiveFormsModule} from '@angular/forms';
+import {CovalentCommonModule, CovalentDataTableModule, CovalentDialogsModule, CovalentPagingModule} from '@covalent/core';
+import {LayoutCardOverComponent, LayoutCardOverComponentTagsDirective} from './layout-card-over/layout-card-over.component';
+import {IdInputComponent} from './id-input/id-input.component';
+import {PermissionDirective} from '../services/security/authz/permission.directive';
+import {DataTableComponent} from './data-table/data-table.component';
+import {StateDisplayComponent} from './state-display/state-display.component';
+import {CommandDisplayComponent} from './command-display/command-display.component';
+import {CustomerSelectComponent} from './customer-select/customer-select.component';
+import {SelectListComponent} from './select-list/select-list.component';
+import {EmployeeSelectComponent} from './employee-select/employee-select.component';
+import {AccountSelectComponent} from './account-select/account-select.component';
+import {ProductSelectComponent} from './product-select/product-select.component';
+import {TranslateModule} from '@ngx-translate/core';
+import {MinMaxComponent} from './min-max/min-max.component';
+import {ValidateOnBlurDirective} from './validate-on-blur.directive';
+import {LedgerSelectComponent} from './ledger-select/ledger-select.component';
+import {FormContinueActionComponent} from './forms/form-continue-action.component';
+import {FormFinalActionComponent} from './forms/form-final-action.component';
+import {AddressFormComponent} from './address/address.component';
+import {PortraitComponent} from './portrait/portrait.component';
+import {CommonModule} from '@angular/common';
+import {
+  MatAutocompleteModule,
+  MatButtonModule,
+  MatCardModule,
+  MatIconModule,
+  MatInputModule,
+  MatListModule,
+  MatOptionModule,
+  MatSelectModule,
+  MatSnackBarModule,
+  MatToolbarModule,
+  MatTooltipModule
+} from '@angular/material';
+import {EmployeeAutoCompleteComponent} from './employee-autocomplete/employee-auto-complete.component';
+import {TextMaskModule} from 'angular2-text-mask';
+import {NumberInputComponent} from './number-input/number-input.component';
+import {ImageComponent} from './image/image.component';
+import {FimsTwoColumnLayoutComponent} from './layouts/two-column-layout.component';
+import {FimsFabButtonComponent} from './fab-button/fab-button.component';
+import {RouterModule} from '@angular/router';
+import {DisplayFimsDate} from './date/fims-date.pipe';
+import {DateInputComponent} from './date-input/date-input.component';
+import {TextInputComponent} from './text-input/text-input.component';
+import {DisplayFimsNumber} from './number/fims-number.pipe';
+import {DisplayFimsFinancialNumber} from './number/fims-financial-number.pipe';
+
+@NgModule({
+  imports: [
+    RouterModule,
+    CommonModule,
+    CovalentCommonModule,
+    CovalentDataTableModule,
+    CovalentDialogsModule,
+    CovalentPagingModule,
+    FormsModule,
+    MatAutocompleteModule,
+    MatButtonModule,
+    MatCardModule,
+    MatIconModule,
+    MatInputModule,
+    MatListModule,
+    MatSelectModule,
+    MatOptionModule,
+    MatSnackBarModule,
+    MatToolbarModule,
+    MatTooltipModule,
+    ReactiveFormsModule,
+    TextMaskModule,
+    TranslateModule
+  ],
+  declarations: [
+    LayoutCardOverComponent,
+    LayoutCardOverComponentTagsDirective,
+    FimsTwoColumnLayoutComponent,
+    SelectListComponent,
+    CustomerSelectComponent,
+    EmployeeSelectComponent,
+    AccountSelectComponent,
+    LedgerSelectComponent,
+    ProductSelectComponent,
+    EmployeeAutoCompleteComponent,
+    IdInputComponent,
+    PermissionDirective,
+    DataTableComponent,
+    StateDisplayComponent,
+    CommandDisplayComponent,
+    MinMaxComponent,
+    ValidateOnBlurDirective,
+    FormFinalActionComponent,
+    FormContinueActionComponent,
+    AddressFormComponent,
+    PortraitComponent,
+    NumberInputComponent,
+    DateInputComponent,
+    TextInputComponent,
+    ImageComponent,
+    FimsFabButtonComponent,
+    DisplayFimsDate,
+    DisplayFimsNumber,
+    DisplayFimsFinancialNumber
+  ],
+  exports: [
+    LayoutCardOverComponent,
+    LayoutCardOverComponentTagsDirective,
+    FimsTwoColumnLayoutComponent,
+    SelectListComponent,
+    CustomerSelectComponent,
+    EmployeeSelectComponent,
+    AccountSelectComponent,
+    LedgerSelectComponent,
+    ProductSelectComponent,
+    EmployeeAutoCompleteComponent,
+    IdInputComponent,
+    PermissionDirective,
+    DataTableComponent,
+    StateDisplayComponent,
+    CommandDisplayComponent,
+    MinMaxComponent,
+    ValidateOnBlurDirective,
+    FormFinalActionComponent,
+    FormContinueActionComponent,
+    AddressFormComponent,
+    PortraitComponent,
+    NumberInputComponent,
+    DateInputComponent,
+    TextInputComponent,
+    ImageComponent,
+    FimsFabButtonComponent,
+    DisplayFimsDate,
+    DisplayFimsNumber,
+    DisplayFimsFinancialNumber
+  ],
+  entryComponents: [
+    ImageComponent
+  ]
+})
+export class FimsSharedModule {}
diff --git a/src/app/common/customer-select/customer-select.component.html b/src/app/common/customer-select/customer-select.component.html
new file mode 100644
index 0000000..f037af8
--- /dev/null
+++ b/src/app/common/customer-select/customer-select.component.html
@@ -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.
+-->
+
+<div layout="row">
+  <mat-form-field layout-margin flex>
+    <input matInput [placeholder]="title" [matAutocomplete]="auto" [formControl]="formControl">
+    <mat-hint class="tc-red-600">
+      <ng-content></ng-content>
+    </mat-hint>
+  </mat-form-field>
+</div>
+
+<mat-autocomplete #auto="matAutocomplete">
+  <mat-option *ngFor="let customer of customers | async" [value]="customer.identifier">
+    {{ customer.identifier }}
+  </mat-option>
+</mat-autocomplete>
diff --git a/src/app/common/customer-select/customer-select.component.ts b/src/app/common/customer-select/customer-select.component.ts
new file mode 100644
index 0000000..f27fcad
--- /dev/null
+++ b/src/app/common/customer-select/customer-select.component.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 {Component, forwardRef, Input, OnInit} from '@angular/core';
+import {FetchRequest} from '../../services/domain/paging/fetch-request.model';
+import {Observable} from 'rxjs/Observable';
+import {ControlValueAccessor, FormControl, NG_VALUE_ACCESSOR} from '@angular/forms';
+import {Customer} from '../../services/customer/domain/customer.model';
+import {CustomerService} from '../../services/customer/customer.service';
+import {CustomerPage} from '../../services/customer/domain/customer-page.model';
+
+const noop: () => void = () => {
+  // empty method
+};
+
+@Component({
+  providers: [
+    { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => CustomerSelectComponent), multi: true }
+  ],
+  selector: 'fims-customer-select',
+  templateUrl: './customer-select.component.html'
+})
+export class CustomerSelectComponent implements ControlValueAccessor, OnInit {
+
+  formControl: FormControl;
+
+  @Input() title: string;
+
+  @Input() required: boolean;
+
+  customers: Observable<Customer[]>;
+
+  private _onTouchedCallback: () => void = noop;
+
+  private _onChangeCallback: (_: any) => void = noop;
+
+  constructor(private customerService: CustomerService) {}
+
+  ngOnInit(): void {
+    this.formControl = new FormControl('');
+
+    this.customers = this.formControl.valueChanges
+      .distinctUntilChanged()
+      .debounceTime(500)
+      .do(name => this.changeValue(name))
+      .filter(name => name)
+      .switchMap(name => this.onSearch(name));
+  }
+
+  changeValue(value: string): void {
+    this._onChangeCallback(value);
+  }
+
+  writeValue(value: any): void {
+    this.formControl.setValue(value);
+  }
+
+  registerOnChange(fn: any): void {
+    this._onChangeCallback = fn;
+  }
+
+  registerOnTouched(fn: any): void {
+    this._onTouchedCallback = fn;
+  }
+
+  onSearch(searchTerm?: string): Observable<Customer[]> {
+    const fetchRequest: FetchRequest = {
+      page: {
+        pageIndex: 0,
+        size: 5
+      },
+      searchTerm
+    };
+
+    return this.customerService.fetchCustomers(fetchRequest)
+      .map((customerPage: CustomerPage) => customerPage.customers);
+  }
+
+}
diff --git a/src/app/common/data-table/data-table.component.ts b/src/app/common/data-table/data-table.component.ts
index ded0277..1d3f8f9 100644
--- a/src/app/common/data-table/data-table.component.ts
+++ b/src/app/common/data-table/data-table.component.ts
@@ -17,8 +17,8 @@
  * under the License.
  */
 import {Component, EventEmitter, Input, Output} from '@angular/core';
-import {Sort} from '../../sevices/domain/paging/sort.model';
-import {Page} from '../../sevices/domain/paging/page.model';
+import {Sort} from '../../services/domain/paging/sort.model';
+import {Page} from '../../services/domain/paging/page.model';
 import {IPageChangeEvent, ITdDataTableColumn, ITdDataTableSortChangeEvent, TdDataTableSortingOrder} from '@covalent/core';
 import {TranslateService} from '@ngx-translate/core';
 
diff --git a/src/app/common/date-input/date-input.component.html b/src/app/common/date-input/date-input.component.html
new file mode 100644
index 0000000..4aad02d
--- /dev/null
+++ b/src/app/common/date-input/date-input.component.html
@@ -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.
+-->
+
+<mat-form-field layout-margin [formGroup]="form">
+  <input matInput type="date" [id]="controlName" [placeholder]="placeholder" [formControlName]="controlName" [title]="title"/>
+  <mat-error *ngIf="hasRequiredError" translate>Required</mat-error>
+  <mat-error *ngIf="hasBeforeTodayError" translate>Date must be before today</mat-error>
+  <mat-error *ngIf="hasAfterTodayError" translate>Date must be after today</mat-error>
+</mat-form-field>
diff --git a/src/app/common/date-input/date-input.component.ts b/src/app/common/date-input/date-input.component.ts
new file mode 100644
index 0000000..41e3efa
--- /dev/null
+++ b/src/app/common/date-input/date-input.component.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 {Component, Input} from '@angular/core';
+import {FormGroup} from '@angular/forms';
+
+@Component({
+  selector: 'fims-date-input',
+  templateUrl: './date-input.component.html'
+})
+export class DateInputComponent {
+
+  @Input() placeholder;
+
+  @Input() controlName: string;
+
+  @Input() form: FormGroup;
+
+  @Input() title = '';
+
+  get hasRequiredError(): boolean {
+    return this.hasError('required');
+  }
+
+  get hasBeforeTodayError(): boolean {
+    return this.hasError('beforeToday');
+  }
+
+  get hasAfterTodayError(): boolean {
+    return this.hasError('afterToday');
+  }
+
+  hasError(key: string): boolean {
+    return this.form.get(this.controlName).hasError(key);
+  }
+}
diff --git a/src/app/common/date/fims-date.pipe.spec.ts b/src/app/common/date/fims-date.pipe.spec.ts
new file mode 100644
index 0000000..9d4cd69
--- /dev/null
+++ b/src/app/common/date/fims-date.pipe.spec.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 {DisplayFimsDate} from './fims-date.pipe';
+import {FimsDate} from '../../services/domain/date.converter';
+
+describe('DisplayFimsDate', () => {
+  it('should show short date by default', () => {
+    const pipe = new DisplayFimsDate('en-US');
+
+    const fimsDate: FimsDate = {
+      day: 1,
+      month: 1,
+      year: 2017
+    };
+
+    expect(pipe.transform(fimsDate)).toBe('1/1/2017');
+  });
+
+  it('should not add tz offset', () => {
+    const pipe = new DisplayFimsDate('en-US');
+
+    const fimsDate: FimsDate = {
+      day: 1,
+      month: 1,
+      year: 2017
+    };
+
+    expect(pipe.transform(fimsDate, 'shortTime')).toBe('12:00 AM');
+  });
+});
diff --git a/src/app/common/date/fims-date.pipe.ts b/src/app/common/date/fims-date.pipe.ts
new file mode 100644
index 0000000..f688687
--- /dev/null
+++ b/src/app/common/date/fims-date.pipe.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 {Inject, LOCALE_ID, Pipe, PipeTransform} from '@angular/core';
+import {FimsDate, toISOString} from '../../services/domain/date.converter';
+import {DatePipe} from '@angular/common';
+
+@Pipe({
+  name: 'displayFimsDate',
+  pure: true
+})
+export class DisplayFimsDate extends DatePipe implements PipeTransform {
+
+  constructor(@Inject(LOCALE_ID) private locale: string) {
+    super(locale);
+  }
+
+  transform(fimsDate: FimsDate, format = 'shortDate'): string {
+    return super.transform(toISOString(fimsDate), format);
+  }
+}
diff --git a/src/app/common/domain/action-option.model.ts b/src/app/common/domain/action-option.model.ts
new file mode 100644
index 0000000..f3af6c6
--- /dev/null
+++ b/src/app/common/domain/action-option.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 {WorkflowAction} from '../../services/portfolio/domain/individuallending/workflow-action.model';
+
+export interface ActionOption {
+  type: WorkflowAction;
+  label: string;
+}
+
+export const ActionOptions: ActionOption[] = [
+  { type: 'OPEN', label: 'loan is opened' },
+  { type: 'DENY', label: 'loan is denied' },
+  { type: 'APPROVE', label: 'loan is approved' },
+  { type: 'ACCEPT_PAYMENT', label: 'payment is accepted' },
+  { type: 'DISBURSE', label: 'loan is disbursed' },
+  { type: 'MARK_LATE', label: 'payment is late' },
+  { type: 'APPLY_INTEREST', label: 'interest is applied' },
+  { type: 'WRITE_OFF', label: 'loan is written off' },
+  { type: 'CLOSE', label: 'loan is closed' },
+  { type: 'RECOVER', label: 'loan is recovered' }
+];
diff --git a/src/app/common/domain/alignment.model.ts b/src/app/common/domain/alignment.model.ts
new file mode 100644
index 0000000..15e4cb6
--- /dev/null
+++ b/src/app/common/domain/alignment.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 const alignmentOptions: any[] = [
+  { type: 0, label: 'first'},
+  { type: 1, label: 'second'},
+  { type: 2, label: 'third'},
+  { type: 3, label: 'last'}
+];
diff --git a/src/app/common/domain/months.model.ts b/src/app/common/domain/months.model.ts
new file mode 100644
index 0000000..7a2fa03
--- /dev/null
+++ b/src/app/common/domain/months.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.
+ */
+
+export const monthOptions: any[] = [
+  { type: 0, label: 'January' },
+  { type: 1, label: 'February' },
+  { type: 2, label: 'March' },
+  { type: 3, label: 'April' },
+  { type: 4, label: 'May' },
+  { type: 5, label: 'June' },
+  { type: 6, label: 'July' },
+  { type: 7, label: 'August' },
+  { type: 8, label: 'September' },
+  { type: 9, label: 'October' },
+  { type: 10, label: 'November' },
+  { type: 11, label: 'December' }
+];
diff --git a/src/app/common/domain/temporal.domain.ts b/src/app/common/domain/temporal.domain.ts
new file mode 100644
index 0000000..04f70e9
--- /dev/null
+++ b/src/app/common/domain/temporal.domain.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 {ChronoUnit} from '../../services/portfolio/domain/chrono-unit.model';
+
+export interface TemporalOption {
+  label: string;
+  type: ChronoUnit;
+}
+
+export const temporalOptionList: TemporalOption[] = [
+  { type: 'WEEKS', label: 'weeks'},
+  { type: 'MONTHS', label: 'months'},
+  { type: 'YEARS', label: 'years'}
+];
diff --git a/src/app/common/domain/week-days.model.ts b/src/app/common/domain/week-days.model.ts
new file mode 100644
index 0000000..ccf71b7
--- /dev/null
+++ b/src/app/common/domain/week-days.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 const weekDayOptions: any[] = [
+  { type: 0, label: 'Monday' },
+  { type: 1, label: 'Tuesday' },
+  { type: 2, label: 'Wednesday' },
+  { type: 3, label: 'Thursday' },
+  { type: 4, label: 'Friday' },
+  { type: 5, label: 'Saturday' },
+  { type: 6, label: 'Sunday' }
+];
diff --git a/src/app/common/employee-autocomplete/employee-auto-complete.component.html b/src/app/common/employee-autocomplete/employee-auto-complete.component.html
new file mode 100644
index 0000000..f9dd0fd
--- /dev/null
+++ b/src/app/common/employee-autocomplete/employee-auto-complete.component.html
@@ -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.
+-->
+
+<div layout="row">
+  <mat-form-field layout-margin flex>
+    <input matInput [placeholder]="title" [matAutocomplete]="auto" [formControl]="formControl">
+    <mat-hint class="tc-red-600">
+      <ng-content></ng-content>
+    </mat-hint>
+  </mat-form-field>
+</div>
+
+<mat-autocomplete #auto="matAutocomplete">
+  <mat-option *ngFor="let employee of employees | async" [value]="employee.identifier">
+    {{ employee.surname }}, {{ employee.givenName }}
+  </mat-option>
+</mat-autocomplete>
diff --git a/src/app/common/employee-autocomplete/employee-auto-complete.component.ts b/src/app/common/employee-autocomplete/employee-auto-complete.component.ts
new file mode 100644
index 0000000..866ae0a
--- /dev/null
+++ b/src/app/common/employee-autocomplete/employee-auto-complete.component.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 {Component, forwardRef, Input, OnInit} from '@angular/core';
+import {FetchRequest} from '../../services/domain/paging/fetch-request.model';
+import {Observable} from 'rxjs/Observable';
+import {ControlValueAccessor, FormControl, NG_VALUE_ACCESSOR} from '@angular/forms';
+import {Employee} from '../../services/office/domain/employee.model';
+import {OfficeService} from '../../services/office/office.service';
+import {EmployeePage} from '../../services/office/domain/employee-page.model';
+
+const noop: () => void = () => {
+  // empty method
+};
+
+@Component({
+  providers: [
+    { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => EmployeeAutoCompleteComponent), multi: true }
+  ],
+  selector: 'fims-employee-auto-complete',
+  templateUrl: './employee-auto-complete.component.html'
+})
+export class EmployeeAutoCompleteComponent implements ControlValueAccessor, OnInit {
+
+  private _onTouchedCallback: () => void = noop;
+
+  private _onChangeCallback: (_: any) => void = noop;
+
+  formControl: FormControl;
+
+  @Input() title: string;
+
+  @Input() required: boolean;
+
+  employees: Observable<Employee[]>;
+
+  constructor(private officeService: OfficeService) {}
+
+  ngOnInit(): void {
+    this.formControl = new FormControl('');
+
+    this.employees = this.formControl.valueChanges
+      .distinctUntilChanged()
+      .debounceTime(500)
+      .do(name => this.changeValue(name))
+      .filter(name => name)
+      .switchMap(name => this.onSearch(name));
+  }
+
+  changeValue(value: string): void {
+    this._onChangeCallback(value);
+  }
+
+  writeValue(value: any): void {
+    this.formControl.setValue(value);
+  }
+
+  registerOnChange(fn: any): void {
+    this._onChangeCallback = fn;
+  }
+
+  registerOnTouched(fn: any): void {
+    this._onTouchedCallback = fn;
+  }
+
+  onSearch(searchTerm?: string): Observable<Employee[]> {
+    const fetchRequest: FetchRequest = {
+      page: {
+        pageIndex: 0,
+        size: 5
+      },
+      searchTerm
+    };
+
+    return this.officeService.listEmployees(fetchRequest)
+      .map((employeePage: EmployeePage) => employeePage.employees);
+  }
+
+}
diff --git a/src/app/common/employee-select/employee-select.component.html b/src/app/common/employee-select/employee-select.component.html
new file mode 100644
index 0000000..c887626
--- /dev/null
+++ b/src/app/common/employee-select/employee-select.component.html
@@ -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.
+-->
+
+<fims-select-list flex
+                    [data]="employees"
+                    id="identifier"
+                    displayName="surname"
+                    listIcon="face"
+                    [preSelection]="preSelection"
+                    [multiple]="multiple"
+                    (onSearch)="onSearch($event)"
+                    (onSelectionChange)="selectionChange($event)"
+                    [title]="title"
+                    noResultsMessage="{{'No employee was found.' | translate}}"
+                    noSelectionMessage="{{'No selection' | translate}}"
+></fims-select-list>
diff --git a/src/app/common/employee-select/employee-select.component.ts b/src/app/common/employee-select/employee-select.component.ts
new file mode 100644
index 0000000..21637de
--- /dev/null
+++ b/src/app/common/employee-select/employee-select.component.ts
@@ -0,0 +1,63 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, OnInit, Output} from '@angular/core';
+import {FetchRequest} from '../../services/domain/paging/fetch-request.model';
+import {Observable} from 'rxjs/Observable';
+import {Employee} from '../../services/office/domain/employee.model';
+import {OfficeService} from '../../services/office/office.service';
+import {EmployeePage} from '../../services/office/domain/employee-page.model';
+
+@Component({
+  selector: 'fims-employee-select',
+  templateUrl: './employee-select.component.html'
+})
+export class EmployeeSelectComponent implements OnInit {
+
+  @Input() title: string;
+
+  @Input() preSelection: string[];
+
+  @Input() multiple = true;
+
+  @Output() onSelectionChange: EventEmitter<any> = new EventEmitter();
+
+  employees: Observable<Employee[]>;
+
+  constructor(private officeService: OfficeService) {}
+
+  ngOnInit(): void {
+    this.onSearch();
+  }
+
+  onSearch(searchTerm?: string): void {
+    const fetchRequest: FetchRequest = {
+      page: {
+        pageIndex: 0,
+        size: 5
+      },
+      searchTerm
+    };
+    this.employees = this.officeService.listEmployees(fetchRequest).map((employeePage: EmployeePage) => employeePage.employees);
+  }
+
+  selectionChange(selections: string[]): void {
+    this.onSelectionChange.emit(selections);
+  }
+
+}
diff --git a/src/app/common/fab-button/fab-button.component.html b/src/app/common/fab-button/fab-button.component.html
new file mode 100644
index 0000000..5b34b26
--- /dev/null
+++ b/src/app/common/fab-button/fab-button.component.html
@@ -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.
+-->
+
+<a mat-fab color="accent" class="fims-fab-button" title="{{title}}" [routerLink]="link" [disabled]="disabled" *hasPermission="permission">
+  <mat-icon>{{icon}}</mat-icon>
+</a>
diff --git a/src/app/common/fab-button/fab-button.component.scss b/src/app/common/fab-button/fab-button.component.scss
new file mode 100644
index 0000000..445ad52
--- /dev/null
+++ b/src/app/common/fab-button/fab-button.component.scss
@@ -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.
+ */
+
+.fims-fab-button {
+  top: auto;
+  right: 20px;
+  left: auto;
+  bottom: 20px;
+  position: fixed;
+}
diff --git a/src/app/common/fab-button/fab-button.component.ts b/src/app/common/fab-button/fab-button.component.ts
new file mode 100644
index 0000000..be1829b
--- /dev/null
+++ b/src/app/common/fab-button/fab-button.component.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 {Component, Input, OnInit} from '@angular/core';
+import {FimsPermission} from '../../services/security/authz/fims-permission.model';
+
+@Component({
+  selector: 'fims-fab-button',
+  templateUrl: './fab-button.component.html',
+  styleUrls: ['./fab-button.component.scss']
+})
+
+export class FimsFabButtonComponent implements OnInit {
+
+  @Input() title: string;
+
+  @Input() icon: string;
+
+  @Input() link: any[];
+
+  @Input() permission: FimsPermission;
+
+  @Input() disabled = false;
+
+  constructor() {
+  }
+
+  ngOnInit() {
+  }
+}
diff --git a/src/app/common/forms/form-continue-action.component.html b/src/app/common/forms/form-continue-action.component.html
new file mode 100644
index 0000000..53af30f
--- /dev/null
+++ b/src/app/common/forms/form-continue-action.component.html
@@ -0,0 +1,18 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<button mat-raised-button color="accent" (click)="continueClick()">{{'CONTINUE' | translate}}</button>
diff --git a/src/app/common/forms/form-continue-action.component.ts b/src/app/common/forms/form-continue-action.component.ts
new file mode 100644
index 0000000..8cdd0e7
--- /dev/null
+++ b/src/app/common/forms/form-continue-action.component.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 {Component, EventEmitter, Output} from '@angular/core';
+
+@Component({
+  selector: 'fims-form-continue-action',
+  templateUrl: './form-continue-action.component.html'
+})
+export class FormContinueActionComponent {
+
+  @Output() onContinue = new EventEmitter<any>();
+
+  continueClick() {
+    this.onContinue.emit();
+  }
+
+}
diff --git a/src/app/common/forms/form-final-action.component.html b/src/app/common/forms/form-final-action.component.html
new file mode 100644
index 0000000..7af6379
--- /dev/null
+++ b/src/app/common/forms/form-final-action.component.html
@@ -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.
+-->
+
+<button mat-raised-button color="primary" *ngIf="!editMode" (click)="save()" [disabled]="disabled">{{'CREATE ' + resourceName | translate}}</button>
+<button mat-raised-button color="primary" *ngIf="editMode" (click)="save()" [disabled]="disabled">{{'UPDATE ' + resourceName | translate}}</button>
+<span flex></span>
+<button mat-button (click)="cancel()">{{'CANCEL' | translate}}</button>
diff --git a/src/app/common/forms/form-final-action.component.ts b/src/app/common/forms/form-final-action.component.ts
new file mode 100644
index 0000000..db7f997
--- /dev/null
+++ b/src/app/common/forms/form-final-action.component.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 {Component, EventEmitter, Input, Output} from '@angular/core';
+
+@Component({
+  selector: 'fims-form-final-action',
+  templateUrl: './form-final-action.component.html'
+})
+export class FormFinalActionComponent {
+
+  @Input() resourceName: string;
+
+  @Input() editMode: boolean;
+
+  @Input() disabled: boolean;
+
+  @Output() onSave = new EventEmitter<any>();
+
+  @Output() onCancel = new EventEmitter<any>();
+
+  save() {
+    this.onSave.emit();
+  }
+
+  cancel() {
+    this.onCancel.emit();
+  }
+
+}
diff --git a/src/app/common/forms/form-helper.ts b/src/app/common/forms/form-helper.ts
new file mode 100644
index 0000000..05f2787
--- /dev/null
+++ b/src/app/common/forms/form-helper.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 {AbstractControl} from '@angular/forms';
+
+export function setSelections(key: string, control: AbstractControl, selections: string[]): void {
+  const keyControl: AbstractControl = control.get(key);
+  keyControl.setValue(selections && selections.length > 0 ? selections[0] : undefined);
+  keyControl.markAsDirty();
+}
+
+export function setSelection(key: string, control: AbstractControl, selection: string): void {
+  const keyControl: AbstractControl = control.get(key);
+  keyControl.setValue(selection);
+  keyControl.markAsDirty();
+}
+
diff --git a/src/app/common/forms/form.component.ts b/src/app/common/forms/form.component.ts
new file mode 100644
index 0000000..70aa97e
--- /dev/null
+++ b/src/app/common/forms/form.component.ts
@@ -0,0 +1,73 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 {AbstractControl, FormGroup} from '@angular/forms';
+
+export abstract class FormComponent<T> {
+
+  form: FormGroup;
+
+  constructor() {
+    this.form = new FormGroup({});
+  }
+
+  abstract get formData(): T
+
+  abstract set formData(data: T)
+
+  get pristine(): boolean {
+    if (!this.form) {
+      return true;
+    }
+    return this.form.pristine;
+  }
+
+  get valid(): boolean {
+    if (!this.form) {
+      return true;
+    }
+    return this.form.valid;
+  }
+
+  get dirty(): boolean {
+    if (!this.form) {
+      return true;
+    }
+    return this.form.dirty;
+  }
+
+  /**
+   * Checks if form is pristine before doing valid check
+   * @returns {boolean}
+   */
+  get validWhenOptional(): boolean {
+    if (!this.pristine && this.valid) {
+      return true;
+    }else if (!this.pristine && !this.valid) {
+      return false;
+    }
+    return true;
+  }
+
+  setError(field: string, error: string, value: any): void {
+    const control: AbstractControl = this.form.get(field);
+    const errors = control.errors || {};
+    errors[error] = value;
+    control.setErrors(errors);
+  }
+}
diff --git a/src/app/common/guards/exists-guard.spec.ts b/src/app/common/guards/exists-guard.spec.ts
new file mode 100644
index 0000000..a6a6a76
--- /dev/null
+++ b/src/app/common/guards/exists-guard.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 {Router} from '@angular/router';
+import {ExistsGuardService} from './exists-guard';
+import {fakeAsync, inject, TestBed, tick} from '@angular/core/testing';
+import {Observable} from 'rxjs/Observable';
+
+describe('Test Exists Guard Service', () => {
+
+  beforeEach(() => {
+    TestBed.configureTestingModule({
+      providers: [
+        ExistsGuardService,
+        {
+          provide: Router,
+          useValue: jasmine.createSpyObj('router', ['navigate'])
+        },
+        {
+          provide: 'cacheExpiry',
+          useValue: 5000
+        }
+      ]
+    });
+  });
+
+  it('should return false when not within expiry', fakeAsync(() => {
+    inject([ExistsGuardService], (existsGuardService: ExistsGuardService) => {
+
+      let result = true;
+
+      existsGuardService.isWithinExpiry(Observable.of(Date.now() - 6000)).subscribe(isExpired => result = isExpired);
+
+      tick();
+
+      expect(result).toBeFalsy();
+    })();
+  }));
+
+  it('should return true when within expiry', fakeAsync(() => {
+    inject([ExistsGuardService], (existsGuardService: ExistsGuardService) => {
+
+      let result = false;
+
+      existsGuardService.isWithinExpiry(Observable.of(Date.now() - 1000)).subscribe(isExpired => result = isExpired);
+
+      tick();
+
+      expect(result).toBeTruthy();
+    })();
+  }));
+
+  it('should route to /404 on exception and return false', fakeAsync(() => {
+    inject([ExistsGuardService, Router], (existsGuardService: ExistsGuardService, router: Router) => {
+
+      let result = true;
+
+      existsGuardService.routeTo404OnError(Observable.throw({})).subscribe(canActivate => result = canActivate);
+
+      tick();
+
+      expect(router.navigate).toHaveBeenCalledWith(['/404']);
+
+      expect(result).toBeFalsy();
+    })();
+  }));
+
+});
diff --git a/src/app/common/guards/exists-guard.ts b/src/app/common/guards/exists-guard.ts
new file mode 100644
index 0000000..19c29ab
--- /dev/null
+++ b/src/app/common/guards/exists-guard.ts
@@ -0,0 +1,47 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 {of} from 'rxjs/observable/of';
+import {Router} from '@angular/router';
+import {Inject, Injectable} from '@angular/core';
+
+@Injectable()
+export class ExistsGuardService {
+
+  constructor(private router: Router, @Inject('cacheExpiry') private cacheExpiry: number) {}
+
+  isWithinExpiry(observable: Observable<number>): Observable<boolean> {
+    return observable
+      .map(loadedAtTimestamp => {
+        if (!loadedAtTimestamp) {
+          return false;
+        }
+        return loadedAtTimestamp + this.cacheExpiry > Date.now();
+      })
+      .take(1);
+  }
+
+  routeTo404OnError(observable: Observable<any>): Observable<any> {
+    return observable.catch(() => {
+      this.router.navigate(['/404']);
+      return of(false);
+    });
+  }
+
+}
diff --git a/src/app/common/id-input/id-input.component.html b/src/app/common/id-input/id-input.component.html
new file mode 100644
index 0000000..45c5b9c
--- /dev/null
+++ b/src/app/common/id-input/id-input.component.html
@@ -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.
+-->
+
+<mat-form-field [formGroup]="form" flex layout-margin>
+  <input matInput [id]="controlName" placeholder="{{placeholder | translate}}" [formControlName]="controlName"
+          [readonly]="readonly"
+          tdAutoTrim/>
+  <mat-error *ngIf="form.controls[controlName].hasError('required')" translate>
+    Unique, Required.
+  </mat-error>
+  <mat-error *ngIf="form.controls[controlName].hasError('minlength')">
+    {{ 'Must have at least characters.' | translate:{ value: form.controls[controlName].getError('minlength')['requiredLength']} }}
+  </mat-error>
+  <mat-error *ngIf="form.controls[controlName].hasError('maxlength')">
+    {{ 'Only characters allowed.' | translate:{ value: form.controls[controlName].getError('maxlength')['requiredLength']} }}
+  </mat-error>
+  <mat-error *ngIf="form.controls[controlName].hasError('unique')" translate>
+    Please enter a different identifier.
+  </mat-error>
+  <mat-error *ngIf="form.controls[controlName].hasError('urlSafe')" translate>
+    Only alphabetic, decimal digits, - _ . * characters allowed
+  </mat-error>
+</mat-form-field>
diff --git a/src/app/common/id-input/id-input.component.ts b/src/app/common/id-input/id-input.component.ts
new file mode 100644
index 0000000..9896a9c
--- /dev/null
+++ b/src/app/common/id-input/id-input.component.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 {Component, HostBinding, Input, OnInit} from '@angular/core';
+import {FormGroup} from '@angular/forms';
+
+@Component({
+    selector: 'fims-id-input',
+    templateUrl: './id-input.component.html'
+})
+export class IdInputComponent implements OnInit {
+
+  @HostBinding('attr.layout')
+  @Input() layout = 'row';
+
+  @Input() controlName: string;
+
+  @Input() form: FormGroup;
+
+  @Input() readonly: boolean;
+
+  @Input() placeholder = 'Identifier';
+
+  constructor() { }
+
+  ngOnInit() { }
+
+}
diff --git a/src/app/common/image/image.component.ts b/src/app/common/image/image.component.ts
new file mode 100644
index 0000000..08f77da
--- /dev/null
+++ b/src/app/common/image/image.component.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 {Component, Inject, OnDestroy} from '@angular/core';
+import {MAT_DIALOG_DATA} from '@angular/material';
+import {DomSanitizer, SafeUrl} from '@angular/platform-browser';
+
+@Component({
+  template: '<img [src]="safeUrl" alt/>'
+})
+export class ImageComponent implements OnDestroy {
+
+  private objectUrl: string;
+
+  constructor(private domSanitizer: DomSanitizer, @Inject(MAT_DIALOG_DATA) public blob: Blob) {
+    this.objectUrl = URL.createObjectURL(blob);
+  }
+
+  ngOnDestroy(): void {
+    URL.revokeObjectURL(this.objectUrl);
+  }
+
+  get safeUrl(): SafeUrl {
+    return this.domSanitizer.bypassSecurityTrustUrl(this.objectUrl);
+  }
+}
diff --git a/src/app/common/layout-card-over/layout-card-over.component.html b/src/app/common/layout-card-over/layout-card-over.component.html
new file mode 100644
index 0000000..b141dc6
--- /dev/null
+++ b/src/app/common/layout-card-over/layout-card-over.component.html
@@ -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.
+-->
+
+<div layout="column" layout-fill>
+  <mat-toolbar>
+    <a mat-icon-button *ngIf="navigateBackTo" [routerLink]="navigateBackTo">
+      <mat-icon>keyboard_arrow_left</mat-icon>
+    </a>
+    <span class="truncate">{{fullTitle}}</span>
+    <span flex></span>
+    <ng-content select="fims-layout-card-over-header-menu"></ng-content>
+  </mat-toolbar>
+  <div class="mat-content">
+    <mat-card>
+      <ng-content></ng-content>
+    </mat-card>
+  </div>
+</div>
diff --git a/src/app/common/layout-card-over/layout-card-over.component.scss b/src/app/common/layout-card-over/layout-card-over.component.scss
new file mode 100644
index 0000000..3fdd197
--- /dev/null
+++ b/src/app/common/layout-card-over/layout-card-over.component.scss
@@ -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.
+ */
+
+:host {
+  position: relative;
+  display: block;
+  width: 100%;
+  min-height: 100%;
+  height: 100%;
+  div[after-card] {
+    display: block;
+  }
+}
+.margin {
+  margin-top: -64px;
+}
+
+.truncate {
+  white-space: nowrap;
+  overflow: hidden;
+  text-overflow: ellipsis;
+}
diff --git a/src/app/common/layout-card-over/layout-card-over.component.ts b/src/app/common/layout-card-over/layout-card-over.component.ts
new file mode 100644
index 0000000..5bf0b20
--- /dev/null
+++ b/src/app/common/layout-card-over/layout-card-over.component.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 {Component, Directive, EventEmitter, Input, OnInit, Output} from '@angular/core';
+
+// tslint:disable-next-line:directive-selector
+@Directive({selector: 'fims-layout-card-over-header-menu'})
+export class LayoutCardOverComponentTagsDirective { }
+
+@Component({
+  selector: 'fims-layout-card-over',
+  templateUrl: './layout-card-over.component.html',
+  styleUrls: ['./layout-card-over.component.scss']
+})
+export class LayoutCardOverComponent implements OnInit {
+
+  @Input() title: string;
+
+  @Input() subTitle: string;
+
+  @Input() navigateBackTo: string[];
+
+  @Input() searchTerm: string;
+
+  @Input() searchable: boolean;
+
+  @Output() search: EventEmitter<string> = new EventEmitter<string>();
+
+  ngOnInit(): void {}
+
+  onSearch(searchTerm: string): void {
+    this.search.emit(searchTerm);
+  }
+
+  get fullTitle(): string {
+    const titles = [this.title, this.subTitle];
+    return titles.filter(title => !!title).join(' - ');
+  }
+}
diff --git a/src/app/common/layouts/two-column-layout.component.html b/src/app/common/layouts/two-column-layout.component.html
new file mode 100644
index 0000000..257247b
--- /dev/null
+++ b/src/app/common/layouts/two-column-layout.component.html
@@ -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.
+-->
+
+<div class="mat-content inset" flex>
+  <div layout="row">
+    <div layout="column" flex-gt-md="100">
+      <div layout-gt-md="row">
+        <div flex-gt-md="30">
+          <ng-content select="[left]"></ng-content>
+        </div>
+        <div flex-gt-md="70">
+          <ng-content select="[right]"></ng-content>
+        </div>
+      </div>
+    </div>
+  </div>
+</div>
diff --git a/src/app/common/layouts/two-column-layout.component.ts b/src/app/common/layouts/two-column-layout.component.ts
new file mode 100644
index 0000000..d034fe8
--- /dev/null
+++ b/src/app/common/layouts/two-column-layout.component.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 {Component} from '@angular/core';
+
+@Component({
+  selector: 'fims-two-column-layout',
+  templateUrl: './two-column-layout.component.html'
+})
+
+export class FimsTwoColumnLayoutComponent {}
diff --git a/src/app/common/ledger-select/ledger-select.component.html b/src/app/common/ledger-select/ledger-select.component.html
new file mode 100644
index 0000000..4637230
--- /dev/null
+++ b/src/app/common/ledger-select/ledger-select.component.html
@@ -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.
+-->
+
+<div layout="row">
+  <mat-form-field layout-margin flex>
+    <input matInput [placeholder]="title" [matAutocomplete]="auto" [formControl]="formControl">
+    <mat-hint class="tc-red-600">
+      <ng-content></ng-content>
+    </mat-hint>
+  </mat-form-field>
+</div>
+
+<mat-autocomplete #auto="matAutocomplete">
+  <mat-option *ngFor="let ledger of ledgers | async" [value]="ledger.identifier" [matTooltip]="ledger.description">
+    {{ ledger.identifier }}({{ledger.name}})
+  </mat-option>
+</mat-autocomplete>
diff --git a/src/app/common/ledger-select/ledger-select.component.ts b/src/app/common/ledger-select/ledger-select.component.ts
new file mode 100644
index 0000000..022970a
--- /dev/null
+++ b/src/app/common/ledger-select/ledger-select.component.ts
@@ -0,0 +1,97 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, forwardRef, Input, OnInit} from '@angular/core';
+import {FetchRequest} from '../../services/domain/paging/fetch-request.model';
+import {Observable} from 'rxjs/Observable';
+import {AccountingService} from '../../services/accounting/accounting.service';
+import {ControlValueAccessor, FormControl, NG_VALUE_ACCESSOR} from '@angular/forms';
+import {Ledger} from '../../services/accounting/domain/ledger.model';
+import {LedgerPage} from '../../services/accounting/domain/ledger-page.model';
+import {AccountType} from '../../services/accounting/domain/account-type.model';
+
+const noop: () => void = () => {
+  // empty method
+};
+
+@Component({
+  providers: [
+    { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => LedgerSelectComponent), multi: true }
+  ],
+  selector: 'fims-ledger-select',
+  templateUrl: './ledger-select.component.html'
+})
+export class LedgerSelectComponent implements ControlValueAccessor, OnInit {
+
+  private _onTouchedCallback: () => void = noop;
+
+  private _onChangeCallback: (_: any) => void = noop;
+
+  formControl: FormControl;
+
+  @Input() title: string;
+
+  @Input() required: boolean;
+
+  @Input() type: AccountType;
+
+  ledgers: Observable<Ledger[]>;
+
+  constructor(private accountingService: AccountingService) {}
+
+  ngOnInit(): void {
+    this.formControl = new FormControl('');
+
+    this.ledgers = this.formControl.valueChanges
+      .distinctUntilChanged()
+      .debounceTime(500)
+      .do(name => this.changeValue(name))
+      .filter(name => name)
+      .switchMap(name => this.onSearch(name));
+  }
+
+  changeValue(value: string): void {
+    this._onChangeCallback(value);
+  }
+
+  writeValue(value: any): void {
+    this.formControl.setValue(value);
+  }
+
+  registerOnChange(fn: any): void {
+    this._onChangeCallback = fn;
+  }
+
+  registerOnTouched(fn: any): void {
+    this._onTouchedCallback = fn;
+  }
+
+  onSearch(searchTerm?: string): Observable<Ledger[]> {
+    const fetchRequest: FetchRequest = {
+      page: {
+        pageIndex: 0,
+        size: 5
+      },
+      searchTerm
+    };
+
+    return this.accountingService.fetchLedgers(true, fetchRequest, this.type)
+      .map((ledgerPage: LedgerPage) => ledgerPage.ledgers);
+  }
+
+}
diff --git a/src/app/common/min-max/min-max.component.html b/src/app/common/min-max/min-max.component.html
new file mode 100644
index 0000000..70db6cb
--- /dev/null
+++ b/src/app/common/min-max/min-max.component.html
@@ -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.
+-->
+
+<div layout="row">
+  <fims-number-input [form]="form" [controlName]="minControlName" [placeholder]="minPlaceholder" [requireDecimal]="requireDecimal" [decimalLimit]="decimalLimit"></fims-number-input>
+</div>
+<div layout="column">
+  <fims-number-input [form]="form" [controlName]="maxControlName" [placeholder]="maxPlaceholder" [requireDecimal]="requireDecimal" [decimalLimit]="decimalLimit"></fims-number-input>
+  <mat-error *ngIf="form.touched && form.hasError('greaterThan')" layout-margin translate>Must be greater than minimum</mat-error>
+  <mat-error *ngIf="form.touched && form.hasError('greaterThanEquals')" layout-margin translate>Must be greater than or equal minimum</mat-error>
+</div>
diff --git a/src/app/common/min-max/min-max.component.ts b/src/app/common/min-max/min-max.component.ts
new file mode 100644
index 0000000..0ac3786
--- /dev/null
+++ b/src/app/common/min-max/min-max.component.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 {Component, Input} from '@angular/core';
+import {FormGroup} from '@angular/forms';
+
+@Component({
+  selector: 'fims-min-max',
+  templateUrl: './min-max.component.html'
+})
+export class MinMaxComponent {
+
+  @Input() minPlaceholder;
+
+  @Input() maxPlaceholder;
+
+  @Input() minControlName: string;
+
+  @Input() maxControlName: string;
+
+  @Input() form: FormGroup;
+
+  @Input() requireDecimal;
+
+  @Input() decimalLimit;
+}
diff --git a/src/app/common/number-input/number-input.component.html b/src/app/common/number-input/number-input.component.html
new file mode 100644
index 0000000..1b09dae
--- /dev/null
+++ b/src/app/common/number-input/number-input.component.html
@@ -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.
+-->
+
+<mat-form-field layout-margin flex [formGroup]="form">
+  <input matInput type="text" [textMask]="{mask: mask}" [placeholder]="placeholder" [formControlName]="controlName"/>
+  <mat-error *ngIf="hasRequiredError" translate>
+    Required
+  </mat-error>
+  <mat-error *ngIf="hasMinValueError" translate>
+    {{ 'Value must be greater than or equal to' | translate:{value: form.get(controlName).errors.minValue.value} }}
+  </mat-error>
+  <mat-error *ngIf="hasMaxValueError" translate>
+    {{ 'Value must be smaller than or equal to' | translate:{value: form.get(controlName).errors.maxValue.value} }}
+  </mat-error>
+  <mat-error *ngIf="hasGreaterThanValueError" translate>
+    {{ 'Value must be greater than' | translate:{value: form.get(controlName).errors.greaterThanValue.value} }}
+  </mat-error>
+  <mat-error *ngIf="hasScaleError">
+    {{ 'Must have decimal places' | translate:{value: form.get(controlName).errors.scale.value} }}
+  </mat-error>
+  <mat-hint align="end" *ngIf="form.get(controlName).enabled">
+    {{hint}}
+  </mat-hint>
+</mat-form-field>
diff --git a/src/app/common/number-input/number-input.component.ts b/src/app/common/number-input/number-input.component.ts
new file mode 100644
index 0000000..2f1af4c
--- /dev/null
+++ b/src/app/common/number-input/number-input.component.ts
@@ -0,0 +1,78 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, Input} from '@angular/core';
+import {FormGroup} from '@angular/forms';
+import {createNumberMask} from 'text-mask-addons/dist/textMaskAddons';
+
+@Component({
+  selector: 'fims-number-input',
+  templateUrl: './number-input.component.html'
+})
+export class NumberInputComponent {
+
+  @Input() placeholder;
+
+  @Input() controlName: string;
+
+  @Input() form: FormGroup;
+
+  @Input() requireDecimal = true;
+
+  @Input() decimalLimit = 2;
+
+  @Input() hint: string;
+
+  mask: any;
+
+  constructor() {
+    this.mask = createNumberMask({
+      prefix: '',
+      suffix: '',
+      includeThousandsSeparator: false,
+      requireDecimal: this.requireDecimal,
+      allowNegative: false,
+      allowLeadingZeroes: true,
+      decimalLimit: this.decimalLimit
+    });
+  }
+
+  get hasRequiredError(): boolean {
+    return this.hasError('required');
+  }
+
+  get hasMinValueError(): boolean {
+    return this.hasError('minValue');
+  }
+
+  get hasGreaterThanValueError(): boolean {
+    return this.hasError('greaterThanValue');
+  }
+
+  get hasMaxValueError(): boolean {
+    return this.hasError('maxValue');
+  }
+
+  get hasScaleError(): boolean {
+    return this.hasError('maxScale');
+  }
+
+  hasError(key: string): boolean {
+    return this.form.get(this.controlName).hasError(key);
+  }
+}
diff --git a/src/app/common/number/fims-financial-number.pipe.ts b/src/app/common/number/fims-financial-number.pipe.ts
new file mode 100644
index 0000000..f303bdf
--- /dev/null
+++ b/src/app/common/number/fims-financial-number.pipe.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 {Inject, LOCALE_ID, Pipe, PipeTransform} from '@angular/core';
+import {DecimalPipe} from '@angular/common';
+
+@Pipe({
+  name: 'displayFimsFinancialNumber',
+  pure: true
+})
+export class DisplayFimsFinancialNumber extends DecimalPipe implements PipeTransform {
+
+  constructor(@Inject(LOCALE_ID) private locale: string) {
+    super(locale);
+  }
+
+  transform(value: any, digits: string = '1.2-2'): string {
+    const transformed = super.transform(value, digits);
+
+    if (transformed.indexOf('-') === 0) {
+      return `(${transformed.substr(1, transformed.length)})`;
+    }
+
+    return transformed;
+  }
+}
diff --git a/src/app/common/number/fims-number.pipe.ts b/src/app/common/number/fims-number.pipe.ts
new file mode 100644
index 0000000..b8506a6
--- /dev/null
+++ b/src/app/common/number/fims-number.pipe.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 {Inject, LOCALE_ID, Pipe, PipeTransform} from '@angular/core';
+import {DecimalPipe} from '@angular/common';
+
+@Pipe({
+  name: 'displayFimsNumber',
+  pure: true
+})
+export class DisplayFimsNumber extends DecimalPipe implements PipeTransform {
+
+  constructor(@Inject(LOCALE_ID) private locale: string) {
+    super(locale);
+  }
+
+  transform(value: any, digits: string = '1.2-2'): string {
+    return super.transform(value, digits);
+  }
+}
diff --git a/src/app/common/portrait/portrait.component.html b/src/app/common/portrait/portrait.component.html
new file mode 100644
index 0000000..2f11d2d
--- /dev/null
+++ b/src/app/common/portrait/portrait.component.html
@@ -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.
+-->
+
+<a href="#" (click)="clickPortrait($event)" [matTooltip]="tooltip">
+  <img [ngClass]="{'small': size === 'small', 'medium': size === 'medium', 'large': size === 'large'}" [src]="safeUrl" alt>
+</a>
diff --git a/src/app/common/portrait/portrait.component.scss b/src/app/common/portrait/portrait.component.scss
new file mode 100644
index 0000000..d23c80a
--- /dev/null
+++ b/src/app/common/portrait/portrait.component.scss
@@ -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.
+ */
+
+.small {
+  font-size: 40px;
+  line-height: 40px;
+  height: 90px;
+  width: 90px;
+  margin: 16px 0 0 15px;
+  border-radius: 50%;
+}
+
+.medium {
+  font-size: 40px;
+  line-height: 40px;
+  height: 190px;
+  width: 190px;
+  margin: 16px 0 0 15px;
+  border-radius: 50%;
+}
+
+.large {
+  font-size: 40px;
+  line-height: 40px;
+  height: 290px;
+  width: 290px;
+  margin: 16px 0 0 15px;
+  border-radius: 50%;
+}
diff --git a/src/app/common/portrait/portrait.component.ts b/src/app/common/portrait/portrait.component.ts
new file mode 100644
index 0000000..419134c
--- /dev/null
+++ b/src/app/common/portrait/portrait.component.ts
@@ -0,0 +1,66 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, OnDestroy, Output} from '@angular/core';
+import {DomSanitizer, SafeUrl} from '@angular/platform-browser';
+
+export type PORTRAIT_SIZE = 'small' | 'medium' | 'large';
+
+@Component({
+  selector: 'fims-portrait',
+  templateUrl: './portrait.component.html',
+  styleUrls: ['./portrait.component.scss']
+})
+export class PortraitComponent implements OnDestroy {
+
+  private defaultUrl = './assets/images/ic_account_circle_black_48dp_2x.png';
+
+  private objectUrl: string;
+
+  @Input() set blob(blob: Blob) {
+    if (blob) {
+      this.objectUrl = URL.createObjectURL(blob);
+    } else {
+      this.objectUrl = this.defaultUrl;
+    }
+  };
+
+  @Input() size: PORTRAIT_SIZE = 'medium';
+
+  @Input() tooltip: string;
+
+  @Output() onClick = new EventEmitter();
+
+  constructor(private domSanitizer: DomSanitizer) {}
+
+  ngOnDestroy(): void {
+    if (this.objectUrl) {
+      URL.revokeObjectURL(this.objectUrl);
+    }
+  }
+
+  get safeUrl(): SafeUrl {
+    return this.objectUrl ? this.domSanitizer.bypassSecurityTrustUrl(this.objectUrl) : undefined;
+  }
+
+  clickPortrait(event: MouseEvent): void {
+    event.preventDefault();
+    this.onClick.emit();
+  }
+
+}
diff --git a/src/app/common/product-select/product-select.component.html b/src/app/common/product-select/product-select.component.html
new file mode 100644
index 0000000..d0ee48c
--- /dev/null
+++ b/src/app/common/product-select/product-select.component.html
@@ -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.
+-->
+
+<div layout="row">
+  <mat-form-field layout-margin flex>
+    <input matInput [placeholder]="title" [matAutocomplete]="auto" [formControl]="formControl">
+    <mat-hint class="tc-red-600">
+      <ng-content></ng-content>
+    </mat-hint>
+  </mat-form-field>
+</div>
+
+<mat-autocomplete #auto="matAutocomplete">
+  <mat-option *ngFor="let product of products | async" [value]="product.identifier">
+    {{ product.identifier }}
+  </mat-option>
+</mat-autocomplete>
diff --git a/src/app/common/product-select/product-select.component.ts b/src/app/common/product-select/product-select.component.ts
new file mode 100644
index 0000000..7723630
--- /dev/null
+++ b/src/app/common/product-select/product-select.component.ts
@@ -0,0 +1,105 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, forwardRef, Input, OnInit, Output} from '@angular/core';
+import {FetchRequest} from '../../services/domain/paging/fetch-request.model';
+import {Observable} from 'rxjs/Observable';
+import {ControlValueAccessor, FormControl, NG_VALUE_ACCESSOR} from '@angular/forms';
+import {PortfolioService} from '../../services/portfolio/portfolio.service';
+import {ProductPage} from '../../services/portfolio/domain/product-page.model';
+import {Product} from '../../services/portfolio/domain/product.model';
+
+const noop: () => void = () => {
+  // empty method
+};
+
+@Component({
+  providers: [
+    { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => ProductSelectComponent), multi: true }
+  ],
+  selector: 'fims-product-select',
+  templateUrl: './product-select.component.html'
+})
+export class ProductSelectComponent implements ControlValueAccessor, OnInit {
+
+  private _onTouchedCallback: () => void = noop;
+
+  private _onChangeCallback: (_: any) => void = noop;
+
+  formControl: FormControl;
+
+  @Input() title: string;
+
+  @Input() required: boolean;
+
+  @Output() onProductSelected: EventEmitter<Product> = new EventEmitter<Product>();
+
+  products: Observable<Product[]>;
+
+  constructor(private portfolioService: PortfolioService) {}
+
+  ngOnInit(): void {
+    this.formControl = new FormControl('');
+
+    this.products = this.formControl.valueChanges
+      .distinctUntilChanged()
+      .debounceTime(500)
+      .do(product => {
+        if (this.isObject(product)) {
+          this.onProductSelected.emit(product);
+        }
+      })
+      .map(product => this.isObject(product) ? product.name : product)
+      .do(product => this.changeValue(product))
+      .switchMap(name => this.onSearch(name));
+  }
+
+  private isObject(product: Product): boolean {
+    return product && typeof product === 'object';
+  }
+
+  changeValue(value: string): void {
+    this._onChangeCallback(value);
+  }
+
+  writeValue(value: any): void {
+    this.formControl.setValue(value);
+  }
+
+  registerOnChange(fn: any): void {
+    this._onChangeCallback = fn;
+  }
+
+  registerOnTouched(fn: any): void {
+    this._onTouchedCallback = fn;
+  }
+
+  onSearch(searchTerm?: string): Observable<Product[]> {
+    const fetchRequest: FetchRequest = {
+      page: {
+        pageIndex: 0,
+        size: 5
+      },
+      searchTerm
+    };
+
+    return this.portfolioService.findAllProducts(false, fetchRequest)
+      .map((productPage: ProductPage) => productPage.elements);
+  }
+
+}
diff --git a/src/app/common/select-list/select-list.component.html b/src/app/common/select-list/select-list.component.html
new file mode 100644
index 0000000..49a6672
--- /dev/null
+++ b/src/app/common/select-list/select-list.component.html
@@ -0,0 +1,59 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<h4>{{title}}</h4>
+<mat-form-field layout-gt-xs="row" layout-margin>
+  <input matInput flex placeholder="{{'Search...' | translate}}" tdAutoTrim [formControl]="term"/>
+</mat-form-field>
+<mat-card flex>
+  <mat-list flex>
+    <h3 mat-subheader translate>Results</h3>
+    <mat-list-item *ngFor="let row of data | async">
+      <div layout="row" layout-align="start center" style="width: 100%;">
+        <mat-icon matListAvatar>{{listIcon}}</mat-icon>
+        <div flex>
+          {{ row[id] }}
+        </div>
+        <div>
+          <button mat-raised-button color="accent" title="{{'Assign' | translate}}" (click)="doSelect(row[id])">{{'Select' | translate}}</button>
+        </div>
+      </div>
+    </mat-list-item>
+    <mat-list-item *ngIf="(data | async)?.length == 0">
+      {{noResultsMessage}}
+    </mat-list-item>
+    <h3 mat-subheader translate>Current selection</h3>
+    <mat-list-item *ngFor="let selection of selections" class="active">
+      <div layout="row" layout-align="start center" style="width: 100%;">
+        <mat-icon matListAvatar>{{listIcon}}</mat-icon>
+        <div flex>
+          {{selection}}
+        </div>
+        <div>
+          <button mat-raised-button color="warn" title="{{'Unassign' | translate}}" (click)="doDeselect(selection)">{{'Deselect' | translate}}</button>
+        </div>
+      </div>
+    </mat-list-item>
+    <mat-list-item *ngIf="!selections || selections.length === 0">
+      <div layout="row" layout-align="start center" style="width: 100%;">
+        <div flex>
+          {{noSelectionMessage}}
+        </div>
+      </div>
+    </mat-list-item>
+  </mat-list>
+</mat-card>
diff --git a/src/app/common/select-list/select-list.component.ts b/src/app/common/select-list/select-list.component.ts
new file mode 100644
index 0000000..40e294a
--- /dev/null
+++ b/src/app/common/select-list/select-list.component.ts
@@ -0,0 +1,88 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, OnInit, Output} from '@angular/core';
+import {FormControl} from '@angular/forms';
+import {Observable} from 'rxjs/Observable';
+
+@Component({
+  selector: 'fims-select-list',
+  templateUrl: './select-list.component.html'
+})
+export class SelectListComponent implements OnInit {
+
+  selections: string[];
+
+  term = new FormControl();
+
+  @Input('preSelection') set preSelection(preSelection: string | string[]) {
+    preSelection = preSelection || [];
+    const selections: string[] = Array.isArray(preSelection) ? preSelection : [preSelection];
+    this.selections = selections;
+  };
+
+  @Input('listIcon') listIcon: string;
+
+  @Input('noResultsMessage') noResultsMessage: string;
+
+  @Input('noSelectionMessage') noSelectionMessage: string;
+
+  @Input('data') data: Observable<any[]>;
+
+  @Input('id') id: string;
+
+  @Input('title') title: string;
+
+  @Input('multiple') multiple = false;
+
+  @Output() onSearch = new EventEmitter<string>();
+
+  @Output() onSelectionChange = new EventEmitter<any>();
+
+  constructor() {}
+
+  ngOnInit(): void {
+    this.term.valueChanges
+      .debounceTime(500)
+      .subscribe((event) => this.onSearch.emit(event));
+  }
+
+  doSelect(id: any): void {
+    if (this.selections.indexOf(id) > -1) {
+      return;
+    }
+
+    if (this.multiple) {
+      this.selections.push(id);
+    }else {
+      this.selections = [id];
+    }
+
+    this.onSelectionChange.emit(this.selections);
+  }
+
+  doDeselect(id: any): void {
+    const index = this.selections.indexOf(id);
+
+    if (index > -1) {
+      this.selections.splice(index, 1);
+    }
+
+    this.onSelectionChange.emit(this.selections);
+  }
+}
diff --git a/src/app/common/state-display/state-display.component.html b/src/app/common/state-display/state-display.component.html
new file mode 100644
index 0000000..32f5f13
--- /dev/null
+++ b/src/app/common/state-display/state-display.component.html
@@ -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.
+-->
+
+<ng-container [ngSwitch]="state">
+  <mat-list-item *ngSwitchCase="'PENDING'">
+    <mat-icon matListAvatar color="accent">hourglass_empty</mat-icon>
+    <h3 matLine translate>PENDING</h3>
+  </mat-list-item>
+  <mat-list-item *ngSwitchCase="'OPEN'">
+    <mat-icon matListAvatar color="accent">lock_open</mat-icon>
+    <h3 matLine translate>OPEN</h3>
+  </mat-list-item>
+  <mat-list-item *ngSwitchCase="'ACTIVE'">
+    <mat-icon matListAvatar color="primary">check_circle</mat-icon>
+    <h3 matLine translate>ACTIVE</h3>
+  </mat-list-item>
+  <mat-list-item *ngSwitchCase="'APPROVED'">
+    <mat-icon matListAvatar color="primary">done_all</mat-icon>
+    <h3 matLine translate>APPROVED</h3>
+  </mat-list-item>
+  <mat-list-item *ngSwitchCase="'LOCKED'">
+    <mat-icon matListAvatar color="warn">lock</mat-icon>
+    <h3 matLine translate>LOCKED</h3>
+  </mat-list-item>
+  <mat-list-item *ngSwitchCase="'CLOSED'">
+    <mat-icon matListAvatar color="warn">close</mat-icon>
+    <h3 matLine translate>CLOSED</h3>
+  </mat-list-item>
+  <mat-list-item *ngSwitchCase="'CREATED'">
+    <mat-icon matListAvatar color="accent">hourglass_empty</mat-icon>
+    <h3 matLine translate>CREATED</h3>
+  </mat-list-item>
+  <mat-list-item *ngSwitchCase="'PAUSED'">
+    <mat-icon matListAvatar color="accent">hourglass_empty</mat-icon>
+    <h3 matLine translate>PAUSED</h3>
+  </mat-list-item>
+</ng-container>
diff --git a/src/app/common/state-display/state-display.component.ts b/src/app/common/state-display/state-display.component.ts
new file mode 100644
index 0000000..3de751e
--- /dev/null
+++ b/src/app/common/state-display/state-display.component.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 {Component, Input} from '@angular/core';
+
+@Component({
+  selector: 'fims-state-display',
+  templateUrl: 'state-display.component.html'
+})
+export class StateDisplayComponent {
+
+  @Input() state: string;
+}
diff --git a/src/app/common/store/form.reducer.spec.ts b/src/app/common/store/form.reducer.spec.ts
index b7fe33b..37dae56 100644
--- a/src/app/common/store/form.reducer.spec.ts
+++ b/src/app/common/store/form.reducer.spec.ts
@@ -17,7 +17,7 @@
  * under the License.
  */
 import {createFormReducer, FormState} from './form.reducer';
-import {Error} from '../../sevices/domain/error.model';
+import {Error} from '../../services/domain/error.model';
 import {Action} from '@ngrx/store';
 
 class CreateSuccessAction implements Action {
diff --git a/src/app/common/store/form.reducer.ts b/src/app/common/store/form.reducer.ts
index ae01813..750241f 100644
--- a/src/app/common/store/form.reducer.ts
+++ b/src/app/common/store/form.reducer.ts
@@ -17,7 +17,7 @@
  * under the License.
  */
 import {Action, ActionReducer} from '@ngrx/store';
-import {Error} from '../../sevices/domain/error.model';
+import {Error} from '../../services/domain/error.model';
 
 export interface FormState {
   error?: Error;
@@ -34,7 +34,7 @@
       case `[${resource}] Create Fail`:
       case `[${resource}] Update Fail`: {
         return Object.assign({}, state, {
-         // error: action.payload
+          error: action.payload
         });
       }
 
diff --git a/src/app/common/store/resource.reducer.ts b/src/app/common/store/resource.reducer.ts
index 54c0e84..0e360fa 100644
--- a/src/app/common/store/resource.reducer.ts
+++ b/src/app/common/store/resource.reducer.ts
@@ -63,7 +63,7 @@
 
     const identifier = (resource: any) => resource[identifierName];
 
-/*    return function (state: ResourceState = initialState, action: Action): ResourceState {
+    return function (state: ResourceState = initialState, action: Action): ResourceState {
 
       switch (action.type) {
 
@@ -152,7 +152,6 @@
         }
       }
     };
-    */
   };
 
 export const getResourceEntities = (cacheState: ResourceState) => cacheState.entities;
diff --git a/src/app/common/store/search.reducer.ts b/src/app/common/store/search.reducer.ts
index c3439fd..4c397a5 100644
--- a/src/app/common/store/search.reducer.ts
+++ b/src/app/common/store/search.reducer.ts
@@ -17,7 +17,7 @@
  * under the License.
  */
 import {Action, ActionReducer} from '@ngrx/store';
-import {FetchRequest} from '../../sevices/domain/paging/fetch-request.model';
+import {FetchRequest} from '../../services/domain/paging/fetch-request.model';
 import {createSelector} from 'reselect';
 
 export function emptySearchResult(): SearchResult {
@@ -55,7 +55,7 @@
 };
 
 export const createSearchReducer = (entityName: string, reducer?: ActionReducer<SearchState>) => {
- /* return function(state: SearchState = initialState, action: Action): SearchState {
+  return function(state: SearchState = initialState, action: Action): SearchState {
 
     switch (action.type) {
 
@@ -89,7 +89,6 @@
       }
     }
   };
-  */
 };
 
 export const getSearchEntities = (state: SearchState) => state.entities;
diff --git a/src/app/common/testing/permission-stubs.ts b/src/app/common/testing/permission-stubs.ts
index 970581f..dc76fec 100644
--- a/src/app/common/testing/permission-stubs.ts
+++ b/src/app/common/testing/permission-stubs.ts
@@ -17,7 +17,7 @@
  * under the License.
  */
 import {Directive, Input} from '@angular/core';
-import {FimsPermission} from '../../sevices/security/authz/fims-permission.model';
+import {FimsPermission} from '../../services/security/authz/fims-permission.model';
 
 @Directive({
   // tslint:disable-next-line:directive-selector
diff --git a/src/app/common/text-input/text-input.component.html b/src/app/common/text-input/text-input.component.html
new file mode 100644
index 0000000..613674c
--- /dev/null
+++ b/src/app/common/text-input/text-input.component.html
@@ -0,0 +1,55 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<mat-form-field layout-margin flex [formGroup]="form" *ngIf="show">
+  <input matInput
+         [id]="controlName"
+         [placeholder]="placeholder"
+         [formControlName]="controlName"
+         [attr.type]="type"
+         [attr.title]="title"
+         step="any"
+         tdAutoTrim
+         flex/>
+  <mat-error *ngIf="hasRequiredError" translate>
+    Required
+  </mat-error>
+  <mat-error *ngIf="hasMinLengthError">
+    {{ 'Must have at least characters.' | translate:{ value: form.get(controlName).getError('minlength')['requiredLength']} }}
+  </mat-error>
+  <mat-error *ngIf="hasMaxLengthError">
+    {{ 'Only characters allowed.' | translate:{ value: form.get(controlName).getError('maxlength')['requiredLength']} }}
+  </mat-error>
+  <mat-error *ngIf="hasEmailError" translate>
+    Invalid email
+  </mat-error>
+  <mat-error *ngIf="hasIsNumberError" translate>
+    Must be a number
+  </mat-error>
+  <mat-error *ngIf="hasMinValueError">
+    {{ 'Value must be greater than or equal to' | translate:{ value: form.get(controlName).getError('minValue').value} }}
+  </mat-error>
+  <mat-error *ngIf="hasMaxValueError">
+    {{ 'Value must be smaller than or equal to' | translate:{value: form.get(controlName).getError('maxValue').value} }}
+  </mat-error>
+  <mat-error *ngIf="hasGreaterThanValueError">
+    {{ 'Value must be greater than' | translate:{ value: form.get(controlName).getError('greaterThanValue').value} }}
+  </mat-error>
+  <mat-error *ngIf="hasMaxScaleError">
+    {{ 'Value scale must be smaller than or equal to' | translate:{ value: form.get(controlName).getError('maxScale').value} }}
+  </mat-error>
+</mat-form-field>
diff --git a/src/app/common/text-input/text-input.component.ts b/src/app/common/text-input/text-input.component.ts
new file mode 100644
index 0000000..2b8df6c
--- /dev/null
+++ b/src/app/common/text-input/text-input.component.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 {Component, HostBinding, Input} from '@angular/core';
+import {FormGroup} from '@angular/forms';
+
+@Component({
+  selector: 'fims-text-input',
+  templateUrl: './text-input.component.html'
+})
+export class TextInputComponent {
+
+  @HostBinding('attr.layout')
+  @Input() layout = 'row';
+
+  @Input() placeholder: string;
+
+  @Input() controlName: string;
+
+  @Input() form: FormGroup;
+
+  @Input() type: string;
+
+  @Input() hideIfDisabled = false;
+
+  @Input() title = '';
+
+  get hasRequiredError(): boolean {
+    return this.hasError('required');
+  }
+
+  get hasMinLengthError(): boolean {
+    return this.hasError('minlength');
+  }
+
+  get hasMaxLengthError(): boolean {
+    return this.hasError('maxlength');
+  }
+
+  get hasEmailError(): boolean {
+    return this.hasError('email');
+  }
+
+  get hasIsNumberError(): boolean {
+    return this.hasError('isNumber');
+  }
+
+  get hasMinValueError(): boolean {
+    return this.hasError('minValue');
+  }
+
+  get hasMaxValueError(): boolean {
+    return this.hasError('maxValue');
+  }
+
+  get hasGreaterThanValueError(): boolean {
+    return this.hasError('greaterThanValue');
+  }
+
+  get hasMaxScaleError(): boolean {
+    return this.hasError('maxScale');
+  }
+
+  hasError(key: string): boolean {
+    return this.form.get(this.controlName).hasError(key);
+  }
+
+  get show(): boolean {
+    if (this.hideIfDisabled) {
+      return this.form.get(this.controlName).status !== 'DISABLED';
+    }
+
+    return true;
+  }
+}
diff --git a/src/app/common/util/account-assignments.ts b/src/app/common/util/account-assignments.ts
index 1805ab7..9c2e738 100644
--- a/src/app/common/util/account-assignments.ts
+++ b/src/app/common/util/account-assignments.ts
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-import {AccountAssignment} from '../../sevices/portfolio/domain/account-assignment.model';
+import {AccountAssignment} from '../../services/portfolio/domain/account-assignment.model';
 
 export function findAccountDesignator(accountAssignments: AccountAssignment[], designator: string): AccountAssignment {
   return accountAssignments.find(assignment => assignment.designator === designator);
diff --git a/src/app/common/validate-on-blur.directive.ts b/src/app/common/validate-on-blur.directive.ts
new file mode 100644
index 0000000..56e4dd9
--- /dev/null
+++ b/src/app/common/validate-on-blur.directive.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 {NgControl} from '@angular/forms';
+import {Directive} from '@angular/core';
+
+@Directive({
+  // tslint:disable-next-line:directive-selector
+  selector: '[validate-onblur]',
+  // tslint:disable-next-line:use-host-property-decorator
+  host: {
+    '(focus)': 'onFocus($event)',
+    '(blur)': 'onBlur($event)',
+    '(keyup)': 'onKeyup($event)',
+    '(change)': 'onChange($event)',
+    '(ngModelChange)': 'onNgModelChange($event)'
+  }
+})
+export class ValidateOnBlurDirective {
+
+  private validators: any;
+
+  private asyncValidators: any;
+
+  private wasChanged: any;
+
+  constructor(public formControl: NgControl) {}
+
+  onFocus($event) {
+    this.wasChanged = false;
+    this.validators = this.formControl.control.validator;
+    this.asyncValidators = this.formControl.control.asyncValidator;
+    this.formControl.control.clearAsyncValidators();
+    this.formControl.control.clearValidators();
+  }
+
+  onKeyup($event) {
+    this.wasChanged = true;
+  }
+
+  onChange($event) {
+    this.wasChanged = true;
+  }
+
+  onNgModelChange($event) {
+    this.wasChanged = true;
+  }
+
+  onBlur($event) {
+    this.formControl.control.setAsyncValidators(this.asyncValidators);
+    this.formControl.control.setValidators(this.validators);
+    if (this.wasChanged) {
+      this.formControl.control.updateValueAndValidity();
+    }
+  }
+}
diff --git a/src/app/common/validator/account-exists.validator.ts b/src/app/common/validator/account-exists.validator.ts
new file mode 100644
index 0000000..43e8a60
--- /dev/null
+++ b/src/app/common/validator/account-exists.validator.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 {AbstractControl, AsyncValidatorFn} from '@angular/forms';
+import {Observable} from 'rxjs/Observable';
+import {AccountingService} from '../../services/accounting/accounting.service';
+import {isEmptyInputValue, isString} from './validators';
+
+const invalid = Observable.of({
+  invalidAccount: true
+});
+
+export function accountExists(accountingService: AccountingService): AsyncValidatorFn {
+  return (control: AbstractControl): Observable<any> => {
+    if (!control.dirty || isEmptyInputValue(control.value)) {
+      return Observable.of(null);
+    }
+
+    if (isString(control.value) && control.value.trim().length === 0) {
+      return invalid;
+    }
+
+    return accountingService.findAccount(control.value, true)
+      .map(account => null)
+      .catch(() => invalid);
+  };
+
+}
diff --git a/src/app/common/validator/country-exists.validator.ts b/src/app/common/validator/country-exists.validator.ts
new file mode 100644
index 0000000..43ff459
--- /dev/null
+++ b/src/app/common/validator/country-exists.validator.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 {CountryService} from '../../services/country/country.service';
+import {AbstractControl, AsyncValidatorFn} from '@angular/forms';
+import {Observable} from 'rxjs/Observable';
+
+export function countryExists(countryService: CountryService): AsyncValidatorFn {
+  return (control: AbstractControl): Observable<any> => {
+    if (!control.dirty || !control.value || control.value.length === 0) {
+      return Observable.of(null);
+    }
+
+    const country = control.value;
+    const displayName: string = country && typeof country === 'object' ? country.displayName : country;
+
+    return Observable.of(displayName)
+      .map(searchTerm => countryService.fetchCountries(displayName))
+      .map(countries => {
+        if (countries.length === 1 && countries[0].displayName === displayName) {
+          return null;
+        }
+        return {
+          invalidCountry: true
+        };
+      });
+  };
+}
diff --git a/src/app/common/validator/customer-exists.validator.ts b/src/app/common/validator/customer-exists.validator.ts
new file mode 100644
index 0000000..5fed4e0
--- /dev/null
+++ b/src/app/common/validator/customer-exists.validator.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 {AbstractControl, AsyncValidatorFn} from '@angular/forms';
+import {Observable} from 'rxjs/Observable';
+import {CustomerService} from '../../services/customer/customer.service';
+import {isString} from './validators';
+
+const invalid = Observable.of({
+  invalidCustomer: true
+});
+
+export function customerExists(customerService: CustomerService): AsyncValidatorFn {
+  return (control: AbstractControl): Observable<any> => {
+    if (!control.dirty || !control.value || control.value.length === 0) {
+      return Observable.of(null);
+    }
+
+    if (isString(control.value) && control.value.trim().length === 0) {
+      return invalid;
+    }
+
+    return customerService.getCustomer(control.value, true)
+      .map(customer => null)
+      .catch(() => invalid);
+  };
+}
diff --git a/src/app/common/validator/employee-exists.validator.ts b/src/app/common/validator/employee-exists.validator.ts
new file mode 100644
index 0000000..8faa7e2
--- /dev/null
+++ b/src/app/common/validator/employee-exists.validator.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 {OfficeService} from '../../services/office/office.service';
+import {AbstractControl, AsyncValidatorFn} from '@angular/forms';
+import {Observable} from 'rxjs/Observable';
+import {isString} from './validators';
+
+const invalid = Observable.of({
+  invalidEmployee: true
+});
+
+export function employeeExists(officeService: OfficeService): AsyncValidatorFn {
+  return (control: AbstractControl): Observable<any> => {
+    if (!control.dirty || !control.value || control.value.length === 0) {
+      return Observable.of(null);
+    }
+
+    if (isString(control.value) && control.value.trim().length === 0) {
+      return invalid;
+    }
+
+    return officeService.getEmployee(control.value, true)
+      .map(employee => null)
+      .catch(() => invalid);
+  };
+}
diff --git a/src/app/common/validator/exists.validator.spec.ts b/src/app/common/validator/exists.validator.spec.ts
new file mode 100644
index 0000000..078af8d
--- /dev/null
+++ b/src/app/common/validator/exists.validator.spec.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 {Observable} from 'rxjs/Observable';
+import {FormControl, ValidationErrors} from '@angular/forms';
+import {fakeAsync, tick} from '@angular/core/testing';
+import {accountExists} from './account-exists.validator';
+import {ledgerExists} from './ledger-exists.validator';
+import {employeeExists} from './employee-exists.validator';
+import {customerExists} from './customer-exists.validator';
+
+describe('exists validator', () => {
+
+  function createValidator(validator: any, methodName: string): any {
+    const service = jasmine.createSpyObj('service', [methodName]);
+
+    return (value: string, returnValue?: any): Observable<ValidationErrors> => {
+      service[methodName].and.returnValue(returnValue);
+
+      const control = new FormControl(value);
+      control.markAsDirty();
+
+      return validator(service)(control) as Observable<ValidationErrors>;
+    };
+  }
+
+  interface TestCase {
+    validator: any;
+    expectedResult: any;
+  }
+
+  const accountValidator = createValidator(accountExists, 'findAccount');
+  const ledgerValidator = createValidator(ledgerExists, 'findLedger');
+  const employeeValidator = createValidator(employeeExists, 'getEmployee');
+  const customerValidator = createValidator(customerExists, 'getCustomer');
+
+  const testWithValidResults: TestCase[] = [
+    { validator: accountValidator, expectedResult: null },
+    { validator: ledgerValidator, expectedResult: null },
+    { validator: employeeValidator, expectedResult: null },
+    { validator: customerValidator, expectedResult: null }
+  ];
+
+  const testWithInvalidResults: TestCase[] = [
+    { validator: accountValidator, expectedResult: { invalidAccount: true } },
+    { validator: ledgerValidator, expectedResult: { invalidLedger: true } },
+    { validator: employeeValidator, expectedResult: { invalidEmployee: true } },
+    { validator: customerValidator, expectedResult: { invalidCustomer: true } }
+  ];
+
+  testWithValidResults.forEach(test => {
+    it('should not return error when no value', fakeAsync(() => {
+      const validator = test.validator('');
+
+      let result = null;
+
+      validator.subscribe(validatorResult => result = validatorResult);
+
+      tick();
+
+      expect(result).toEqual(test.expectedResult);
+    }));
+  });
+
+  testWithInvalidResults.forEach(test => {
+    it('should not return error when no value', fakeAsync(() => {
+      const validator = test.validator(' ');
+
+      let result = null;
+
+      validator.subscribe(validatorResult => result = validatorResult);
+
+      tick();
+
+      expect(result).toEqual(test.expectedResult);
+    }));
+  });
+
+  testWithValidResults.forEach(test => {
+    it('should not return error when found', fakeAsync(() => {
+
+      const validator = test.validator('test', Observable.of({
+        identifier: 'test'
+      }));
+
+      let expectedResult = null;
+
+      validator.subscribe(result => expectedResult = result);
+
+      tick();
+
+      expect(expectedResult).toEqual(test.expectedResult);
+    }));
+  });
+
+
+  testWithInvalidResults.forEach(test => {
+    it('should return error when not found', fakeAsync(() => {
+      const validator = test.validator('test', Observable.throw({}));
+
+      let expectedResult = null;
+
+      validator.subscribe(result => expectedResult = result);
+
+      tick();
+
+      expect(expectedResult).toEqual(test.expectedResult);
+    }));
+  });
+
+
+
+
+});
diff --git a/src/app/common/validator/ledger-exists.validator.ts b/src/app/common/validator/ledger-exists.validator.ts
new file mode 100644
index 0000000..d10c637
--- /dev/null
+++ b/src/app/common/validator/ledger-exists.validator.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 {AbstractControl, AsyncValidatorFn, ValidationErrors} from '@angular/forms';
+import {Observable} from 'rxjs/Observable';
+import {AccountingService} from '../../services/accounting/accounting.service';
+import {isString} from './validators';
+
+const invalid = Observable.of({
+  invalidLedger: true
+});
+
+export function ledgerExists(accountingService: AccountingService): AsyncValidatorFn {
+  return (control: AbstractControl): Observable<ValidationErrors | null> => {
+    if (!control.dirty || !control.value || control.value.length === 0) {
+      return Observable.of(null);
+    }
+
+    if (isString(control.value) && control.value.trim().length === 0) {
+      return invalid;
+    }
+
+    return accountingService.findLedger(control.value, true)
+      .map(ledger => null)
+      .catch(() => invalid);
+  };
+}
diff --git a/src/app/common/validator/validators.spec.ts b/src/app/common/validator/validators.spec.ts
new file mode 100644
index 0000000..373b17b
--- /dev/null
+++ b/src/app/common/validator/validators.spec.ts
@@ -0,0 +1,314 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 {FimsValidators} from './validators';
+import {FormControl, FormGroup} from '@angular/forms';
+import {dateAsISOString} from '../../services/domain/date.converter';
+
+describe('Validators', () => {
+  describe('urlSafe', () => {
+
+    it('should not return error when url safe', () => {
+      const result = FimsValidators.urlSafe(new FormControl('test-test'));
+      expect(result).toBeNull();
+    });
+
+    it('should return error when not url safe', () => {
+      const result = FimsValidators.urlSafe(new FormControl(' '));
+      expect(result).toEqual({ urlSafe: true });
+    });
+
+    const notAllowed: string[] = [
+      '!',
+      '\'',
+      '(',
+      ')',
+      '~',
+    ];
+
+    notAllowed.forEach(char => {
+      it(`should return error when when it contains ${char}`, () => {
+        const result = FimsValidators.urlSafe(new FormControl(`${char}`));
+        expect(result).toEqual({ urlSafe: true });
+      });
+    });
+
+  });
+
+  describe('scale', () => {
+
+    it('should not return error when scale matches', () => {
+      const validator = FimsValidators.scale(1);
+      expect(validator(new FormControl(1.1))).toBeNull();
+    });
+
+    it('should return error when scale 1', () => {
+      const validator = FimsValidators.scale(1);
+      expect(validator(new FormControl(1))).toEqual({
+        scale: {
+          valid: false,
+          value: 1
+        },
+      });
+    });
+
+    it('should return error when scale 0', () => {
+      const validator = FimsValidators.scale(0);
+      expect(validator(new FormControl(1.2))).toEqual({
+        scale: {
+          valid: false,
+          value: 0
+        }
+      });
+    });
+  });
+
+  describe('minValue with 0', () => {
+
+    it('should not return error when value 0', () => {
+      const validator = FimsValidators.minValue(0);
+      expect(validator(new FormControl(0))).toBeNull();
+    });
+
+    it('should return error when value -1', () => {
+      const validator = FimsValidators.minValue(0);
+      expect(validator(new FormControl(-1))).toEqual({
+        minValue: {
+          valid: false,
+          value: 0
+        }
+      });
+    });
+
+  });
+
+  describe('maxValue with 10', () => {
+    it('should not return error when value 10', () => {
+      const validator = FimsValidators.maxValue(10);
+      expect(validator(new FormControl(10))).toBeNull();
+    });
+
+    it('should return error when value 11', () => {
+      const validator = FimsValidators.maxValue(10);
+      expect(validator(new FormControl(11))).toEqual({
+        maxValue: {
+          valid: false,
+          value: 10
+        }
+      });
+    });
+
+  });
+
+  describe('greaterThanValue with 0', () => {
+
+    it('should not return error when value 1', () => {
+      const validator = FimsValidators.greaterThanValue(0);
+      expect(validator(new FormControl(1))).toBeNull();
+    });
+
+    it('should return error when value 0', () => {
+      const validator = FimsValidators.greaterThanValue(0);
+      expect(validator(new FormControl(0))).toEqual({
+        greaterThanValue: {
+          valid: false,
+          value: 0
+        }
+      });
+    });
+
+    it('should return error when value -1', () => {
+      const validator = FimsValidators.greaterThanValue(0);
+      expect(validator(new FormControl(-1))).toEqual({
+        greaterThanValue: {
+          valid: false,
+          value: 0
+        }
+      });
+    });
+
+  });
+
+  describe('matchRange', () => {
+    const dateOne = new Date();
+    const dateTwo = new Date(dateOne.getTime() + 1000);
+
+    it('should not return error when range is correct', () => {
+      const group = new FormGroup({f1: new FormControl(dateOne.toISOString()), f2: new FormControl(dateTwo.toISOString())});
+      const validator = FimsValidators.matchRange('f1', 'f2');
+      expect(validator(group)).toBeNull();
+    });
+
+    it('should return error when range not correct', () => {
+      const group = new FormGroup({f1: new FormControl(dateOne.toISOString()), f2: new FormControl(dateTwo.toISOString())});
+      const validator = FimsValidators.matchRange('f2', 'f1');
+      expect(validator(group)).toEqual({
+        rangeInvalid: true
+      });
+    });
+  });
+
+  describe('email', () => {
+    it('should return null when email is correct', () => {
+      const result = FimsValidators.email(new FormControl('test@test.de'));
+      expect(result).toBeNull();
+    });
+
+    it('should return error when email is "testtest.de"', () => {
+      const result = FimsValidators.email(new FormControl('testtest.de'));
+      expect(result).toEqual({
+        email: true
+      });
+    });
+  });
+
+  describe('requiredNotEmpty', () => {
+    it('should return null when value exists', () => {
+      const result = FimsValidators.requiredNotEmpty(new FormControl('value'));
+      expect(result).toBeNull();
+    });
+
+    it('should return error when no value exists', () => {
+      const result = FimsValidators.requiredNotEmpty(new FormControl(''));
+      expect(result).toEqual({
+        required: true
+      });
+    });
+
+    it('should return error when value with only whitespaces exists', () => {
+      const result = FimsValidators.requiredNotEmpty(new FormControl(' '));
+      expect(result).toEqual({
+        required: true
+      });
+    });
+  });
+
+  describe('greaterThan', () => {
+    it('should return null when min < max', () => {
+      const validator = FimsValidators.greaterThan('min', 'max');
+      const group = new FormGroup({
+        min: new FormControl(1),
+        max: new FormControl(2)
+      });
+
+      const result = validator(group);
+
+      expect(result).toBeNull();
+    });
+
+    it('should return error when min = max', () => {
+      const validator = FimsValidators.greaterThan('min', 'max');
+      const group = new FormGroup({
+        min: new FormControl(2),
+        max: new FormControl(2)
+      });
+
+      const result = validator(group);
+
+      expect(result).toEqual({
+        greaterThan: true
+      });
+    });
+
+    it('should return error when min > max', () => {
+      const validator = FimsValidators.greaterThan('min', 'max');
+      const group = new FormGroup({
+        min: new FormControl(2),
+        max: new FormControl(1)
+      });
+
+      const result = validator(group);
+
+      expect(result).toEqual({
+        greaterThan: true
+      });
+    });
+  });
+
+  describe('greaterThanEquals', () => {
+    it('should return null when min < max', () => {
+      const validator = FimsValidators.greaterThanEquals('min', 'max');
+      const group = new FormGroup({
+        min: new FormControl(1),
+        max: new FormControl(2)
+      });
+
+      const result = validator(group);
+
+      expect(result).toBeNull();
+    });
+
+    it('should return null when min = max', () => {
+      const validator = FimsValidators.greaterThanEquals('min', 'max');
+      const group = new FormGroup({
+        min: new FormControl(2),
+        max: new FormControl(2)
+      });
+
+      const result = validator(group);
+
+      expect(result).toBeNull();
+    });
+
+    it('should return error when min > max', () => {
+      const validator = FimsValidators.greaterThanEquals('min', 'max');
+      const group = new FormGroup({
+        min: new FormControl(2),
+        max: new FormControl(1)
+      });
+
+      const result = validator(group);
+
+      expect(result).toEqual({
+        greaterThanEquals: true
+      });
+    });
+  });
+
+  describe('beforeToday', () => {
+    it('should return null when date before today', () => {
+      const date = new Date();
+      date.setDate(date.getDate() - 1);
+
+      const result = FimsValidators.beforeToday(new FormControl(dateAsISOString(date)));
+
+      expect(result).toBeNull();
+    });
+
+    it('should return error when date after today', () => {
+      const date = new Date();
+      date.setDate(date.getDate() + 1);
+
+      const result = FimsValidators.beforeToday(new FormControl(dateAsISOString(date)));
+
+      expect(result).toEqual({
+        beforeToday: true
+      });
+    });
+
+    it('should return error when date equals today', () => {
+      const date = new Date();
+
+      const result = FimsValidators.beforeToday(new FormControl(dateAsISOString(date)));
+
+      expect(result).toEqual({
+        beforeToday: true
+      });
+    });
+  });
+});
diff --git a/src/app/common/validator/validators.ts b/src/app/common/validator/validators.ts
new file mode 100644
index 0000000..c6774fe
--- /dev/null
+++ b/src/app/common/validator/validators.ts
@@ -0,0 +1,274 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 {AbstractControl, FormGroup, ValidationErrors, ValidatorFn} from '@angular/forms';
+import {todayAsISOString} from '../../services/domain/date.converter';
+
+// tslint:disable-next-line:max-line-length
+const EMAIL_REGEXP =
+  /^(?=.{1,254}$)(?=.{1,64}@)[-!#$%&'*+/0-9=?A-Z^_`a-z{|}~]+(\.[-!#$%&'*+/0-9=?A-Z^_`a-z{|}~]+)*@[A-Za-z0-9]([A-Za-z0-9-]{0,61}[A-Za-z0-9])?(\.[A-Za-z0-9]([A-Za-z0-9-]{0,61}[A-Za-z0-9])?)*$/;
+
+export function isEmptyInputValue(value: any): boolean {
+  return value == null || value.length === 0;
+}
+
+export function isString(value: any): boolean {
+  return typeof value === 'string';
+}
+
+export class FimsValidators {
+
+  static urlSafe(control: AbstractControl): ValidationErrors | null {
+    const notAllowed: string[] = [
+      '!',
+      '\'',
+      '(',
+      ')',
+      '~',
+    ];
+
+    const foundNotAllowed = notAllowed.find(char => control.value.indexOf(char) > -1);
+
+    if (control.value && (encodeURIComponent(control.value) !== control.value || !!foundNotAllowed)) {
+      return {
+        urlSafe: true
+      };
+    }
+    return null;
+  }
+
+  static isNumber(control: AbstractControl): ValidationErrors | null {
+    if (control.value && isNaN(control.value)) {
+      return {
+        isNumber: true
+      };
+    }
+    return null;
+  }
+
+  static scale(scale: number): ValidatorFn {
+    return (c: AbstractControl): ValidationErrors | null => {
+      if (!isEmptyInputValue(c.value)) {
+        const stringValue = String(c.value);
+
+        const valueChunks = stringValue.split('.');
+
+        if (valueChunks.length === 1 && scale === 0) {
+          return null;
+        }
+
+        if (valueChunks.length === 2 && valueChunks[1].length === scale) {
+          return null;
+        }
+
+        return {
+          scale: {
+            valid: false,
+            value: scale
+          }
+        };
+
+      }
+      return null;
+    };
+  }
+
+  static maxScale(scale: number): ValidatorFn {
+    return (c: AbstractControl): ValidationErrors | null => {
+      if (!isEmptyInputValue(c.value)) {
+        const stringValue = String(c.value);
+        const valueChunks = stringValue.split('.');
+
+        if (valueChunks.length === 2 && valueChunks[1].length > scale) {
+          return {
+            maxScale: {
+              valid: false,
+              value: scale
+            }
+          };
+        }
+      }
+      return null;
+    };
+  }
+
+  static minValue(minValue: number): ValidatorFn {
+    return (c: AbstractControl): ValidationErrors | null => {
+      if (!isEmptyInputValue(c.value) && (c.value < minValue)) {
+        return {
+          minValue: {
+            valid: false,
+            value: minValue
+          }
+        };
+      }
+      return null;
+    };
+  }
+
+  static maxValue(maxValue: number): ValidatorFn {
+    return (c: AbstractControl): ValidationErrors | null => {
+      if (!isEmptyInputValue(c.value) != null && (c.value > maxValue)) {
+        return {
+          maxValue: {
+            valid: false,
+            value: maxValue
+          }
+        };
+      }
+      return null;
+    };
+  }
+
+  static greaterThanValue(greaterThanValue: number): ValidatorFn {
+    return (c: AbstractControl): ValidationErrors | null => {
+      if (!isEmptyInputValue(c.value) && (c.value <= greaterThanValue)) {
+        return {
+          greaterThanValue: {
+            valid: false,
+            value: greaterThanValue
+          }
+        };
+      }
+      return null;
+    };
+  }
+
+  static greaterThan(firstValue: string, secondValue: string) {
+    return (group: FormGroup): ValidationErrors | null => {
+      const firstNumber: number = Number(group.controls[firstValue].value);
+      const secondNumber: number = Number(group.controls[secondValue].value);
+
+      if (firstNumber == null || secondNumber == null) {
+        return null;
+      }
+
+      if (firstNumber >= secondNumber) {
+        return {
+          greaterThan: true
+        };
+      }
+
+      return null;
+    };
+  }
+
+  static greaterThanEquals(firstValue: string, secondValue: string) {
+    return (group: FormGroup): ValidationErrors | null => {
+      const firstNumber: number = Number(group.controls[firstValue].value);
+      const secondNumber: number = Number(group.controls[secondValue].value);
+
+      if (firstNumber == null || secondNumber == null) {
+        return null;
+      }
+
+      if (firstNumber > secondNumber) {
+        return {
+          greaterThanEquals: true
+        };
+      }
+
+      return null;
+    };
+  }
+
+  static matchValues(firstValue: string, secondValue: string) {
+    return (group: FormGroup): ValidationErrors | null => {
+      const val1 = group.controls[firstValue];
+      const val2 = group.controls[secondValue];
+
+      if (val1.value !== val2.value) {
+        return {
+          mismatch: true
+        };
+      }
+
+      return null;
+    };
+  }
+
+  static matchRange(firstValue: string, secondValue: string) {
+    return (group: FormGroup): ValidationErrors | null => {
+      const val1: AbstractControl = group.controls[firstValue];
+      const val2: AbstractControl = group.controls[secondValue];
+
+      const dateStart: number = Date.parse(val1.value);
+      const dateEnd: number = Date.parse(val2.value);
+
+      if (dateStart > dateEnd) {
+        return {
+          rangeInvalid: true
+        };
+      }
+
+      return null;
+    };
+  }
+
+  static email(control: AbstractControl): ValidationErrors | null {
+    if (isEmptyInputValue(control.value)) {
+      return null;
+    }
+
+    return EMAIL_REGEXP.test(control.value) ? null : {'email': true};
+  }
+
+  static maxFileSize(maxSizeInKB: number) {
+    return (c: AbstractControl): ValidationErrors | null => {
+      const bytes: number = maxSizeInKB * 1024;
+      if (!isEmptyInputValue(c.value) && (c.value.size > bytes)) {
+        return {
+          maxFileSize: {
+            value: maxSizeInKB
+          }
+        };
+      }
+      return null;
+    };
+  }
+
+  static requiredNotEmpty(control: AbstractControl): ValidationErrors | null {
+    return isEmptyInputValue(control.value) || (isString(control.value) && control.value.trim() === '') ? {'required': true} : null;
+  }
+
+  static beforeToday(control: AbstractControl): ValidationErrors | null {
+    const date = new Date(Date.parse(control.value));
+
+    const today = new Date(Date.parse(todayAsISOString()));
+
+    if (date >= today) {
+      return {
+        beforeToday: true
+      };
+    }
+    return null;
+  }
+
+  static afterToday(control: AbstractControl): ValidationErrors | null {
+    const date = new Date(Date.parse(control.value));
+
+    const today = new Date(Date.parse(todayAsISOString()));
+
+    if (date <= today) {
+      return {
+        afterToday: true
+      };
+    }
+    return null;
+  }
+}
diff --git a/src/app/customer/add-member/add-member.component.html b/src/app/customer/add-member/add-member.component.html
deleted file mode 100644
index 98c10eb..0000000
--- a/src/app/customer/add-member/add-member.component.html
+++ /dev/null
@@ -1,109 +0,0 @@
-<div class="main-div mat-elevation-z2">
-  <h3 class="heading">Add Member</h3>
-  <mat-divider></mat-divider>
-<form class="fineract-form">
-  <mat-form-field>
-    <input matInput placeholder="Account">
-  </mat-form-field>
-<br/>
-  <mat-form-field>
-    <input matInput placeholder="Firstname">
-  </mat-form-field>
-<br/>
-  <mat-form-field>
-    <input matInput placeholder="Middlename">
-  </mat-form-field>
-  <br/>
-  <mat-form-field>
-    <input matInput placeholder="Lastname">
-  </mat-form-field>
-  <br/>
-  <mat-form-field>
-    <input matInput [matDatepicker]="picker" placeholder="Choose a date">
-    <mat-datepicker-toggle matSuffix [for]="picker"></mat-datepicker-toggle>
-    <mat-datepicker #picker></mat-datepicker>
-  </mat-form-field>
-  </form>
-  <span>ADDRESS</span>
-  <form class="fineract-form">
-  <mat-form-field>
-    <input matInput placeholder="Street">
-  </mat-form-field>
-  <br/>
-  <mat-form-field>
-    <input matInput placeholder="City">
-  </mat-form-field>
-  <br/>
-  <mat-form-field>
-    <input matInput placeholder="Postal code">
-  </mat-form-field>
-  <br/>
-  <mat-form-field>
-    <input matInput placeholder="Country">
-  </mat-form-field>
-  <br/>
-  <mat-form-field>
-    <input matInput placeholder="Region">
-  </mat-form-field>
-  </form>
-<span>CONTACT</span>
-<form class="fineract-form">
-<mat-form-field>
-  <input matInput placeholder="Email">
-</mat-form-field>
-<br/>
-<mat-form-field>
-  <input matInput placeholder="Phone">
-</mat-form-field>
-<br/>
-<mat-form-field>
-  <input matInput placeholder="Mobile">
-</mat-form-field>
-</form>
-<span>ASSIGN MEMBER TO OFFICE(OPTIONAL)</span>
-<form class="fineract-form">
-  
-  <mat-form-field>
-    <mat-select placeholder="Assigned office">
-      <mat-option *ngFor="let office of offices" [value]="office.value">
-        {{ office.viewValue }}
-      </mat-option>
-    </mat-select>
-  </mat-form-field>
-  <br/>
-<mat-form-field>
-  <input matInput placeholder="Search office">
-</mat-form-field>
-<button mat-raised-button color="primary">search</button>
-</form>
-<span>ASSIGN MEMBER TO EMPLOYEE(OPTIONAL)</span>
-<form class="fineract-form">
-  
-  <mat-form-field>
-    <mat-select placeholder="Assigned employee">
-      <mat-option *ngFor="let employee of employees" [value]="employee.value">
-        {{ employee.viewValue }}
-      </mat-option>
-    </mat-select>
-  </mat-form-field>
-  <br/>
-<mat-form-field>
-  <input matInput placeholder="Search employee">
-</mat-form-field>
-<button mat-raised-button color="primary">search</button>
-</form>
-<span>CUSTOM FIELDS</span>
-<form class="fineract-form">
-<mat-form-field>
-  <input matInput placeholder="Sample customer field">
-</mat-form-field>
-<br/>
-<mat-form-field>
-  <input matInput placeholder="employer">
-</mat-form-field>
-</form>
-  <div>
-  <button mat-raised-button class="button1" color="primary">Submit</button>
-  <button mat-raised-button class="button2" color="warn">Cancel</button>
-  </div>
-</div>
\ No newline at end of file
diff --git a/src/app/customer/add-member/add-member.component.scss b/src/app/customer/add-member/add-member.component.scss
deleted file mode 100644
index 7109ed8..0000000
--- a/src/app/customer/add-member/add-member.component.scss
+++ /dev/null
@@ -1,13 +0,0 @@
-.fineract-form {
-    min-width: 150px;
-    max-width: 500px;
-    width: 100%;
-  }
-  .button1{
-      margin-left: 50%;
-      
-      }
-  .button1, .button2{
-      width:20px;
-      display: inline-block;
-  }
\ No newline at end of file
diff --git a/src/app/customer/add-member/add-member.component.spec.ts b/src/app/customer/add-member/add-member.component.spec.ts
deleted file mode 100644
index 5b4e410..0000000
--- a/src/app/customer/add-member/add-member.component.spec.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-import { async, ComponentFixture, TestBed } from '@angular/core/testing';
-
-import { AddMemberComponent } from './add-member.component';
-
-describe('AddMemberComponent', () => {
-  let component: AddMemberComponent;
-  let fixture: ComponentFixture<AddMemberComponent>;
-
-  beforeEach(async(() => {
-    TestBed.configureTestingModule({
-      declarations: [ AddMemberComponent ]
-    })
-    .compileComponents();
-  }));
-
-  beforeEach(() => {
-    fixture = TestBed.createComponent(AddMemberComponent);
-    component = fixture.componentInstance;
-    fixture.detectChanges();
-  });
-
-  it('should create', () => {
-    expect(component).toBeTruthy();
-  });
-});
diff --git a/src/app/customer/add-member/add-member.component.ts b/src/app/customer/add-member/add-member.component.ts
deleted file mode 100644
index 4355a48..0000000
--- a/src/app/customer/add-member/add-member.component.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-import { Component, OnInit } from '@angular/core';
-
-@Component({
-  selector: 'app-add-member',
-  templateUrl: './add-member.component.html',
-  styleUrls: ['./add-member.component.scss']
-})
-export class AddMemberComponent implements OnInit {
-
-  offices = [
-    {value: 'office-1', viewValue: 'Head office'},
-   
-  ];
-
-  employees = [
-    {value: '', viewValue: ''},
-   
-  ];
-
-  constructor() { }
-
-  ngOnInit() {
-  }
-
-}
diff --git a/src/app/customer/manage-members/manage-members.component.html b/src/app/customer/manage-members/manage-members.component.html
deleted file mode 100644
index d846c2d..0000000
--- a/src/app/customer/manage-members/manage-members.component.html
+++ /dev/null
@@ -1,47 +0,0 @@
-<div class="main-div mat-elevation-z2">
-  <h3 class="heading">Manage Members</h3>
-  <mat-divider></mat-divider>
-  <div class="fineract-button">
-  <a mat-raised-button  color="primary" [routerLink]="['/navbar/add_member']">
-    <mat-icon>add</mat-icon>Add Member</a>
-    </div>
-  <mat-form-field>
-    <input matInput (keyup)="applyFilter($event.target.value)" placeholder="Filter by firstName/lastName">
-  </mat-form-field>
-  
-<br>
-<mat-divider></mat-divider>
-
-<mat-table #table [dataSource]="dataSource">
-
-  <!-- Position Column -->
-  <ng-container matColumnDef="id">
-    <mat-header-cell *matHeaderCellDef>Code</mat-header-cell>
-    <mat-cell *matCellDef="let element">
-      <a routerLink="{{element.id}}">{{element.id}}</a>
-    </mat-cell>
-  </ng-container>
-
-  <!-- Name Column -->
-  <ng-container matColumnDef="firstName">
-    <mat-header-cell *matHeaderCellDef>First Name</mat-header-cell>
-    <mat-cell *matCellDef="let element"> {{element.firstName}} </mat-cell>
-  </ng-container>
-
-  <ng-container matColumnDef="lastName">
-      <mat-header-cell *matHeaderCellDef>Last Name</mat-header-cell>
-      <mat-cell *matCellDef="let element"> {{element.lastName}} </mat-cell>
-    </ng-container>
-
-    <ng-container matColumnDef="status">
-      <mat-header-cell *matHeaderCellDef>Current Status</mat-header-cell>
-      <mat-cell *matCellDef="let element"> {{element.status}} </mat-cell>
-    </ng-container>
-
-  <mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
-  <mat-row *matRowDef="let row; columns: displayedColumns;"></mat-row>
-</mat-table>
-  
-  
-</div>
-  
diff --git a/src/app/customer/manage-members/manage-members.component.scss b/src/app/customer/manage-members/manage-members.component.scss
deleted file mode 100644
index 87b71f6..0000000
--- a/src/app/customer/manage-members/manage-members.component.scss
+++ /dev/null
@@ -1,16 +0,0 @@
-.my-div{
-    margin-left: 2%;
-    margin-right:2%;
-    border-radius: 5px 5px 5px 5px;
-    background-color: #e6e6ff;
-    min-height: 100%;
-   
-  }
-  table {
-      width: 100%;
-    }
-    
-    .mat-form-field {
-      font-size: 14px;
-      width: 100%;
-    }
\ No newline at end of file
diff --git a/src/app/customer/manage-members/manage-members.component.spec.ts b/src/app/customer/manage-members/manage-members.component.spec.ts
deleted file mode 100644
index a4107e3..0000000
--- a/src/app/customer/manage-members/manage-members.component.spec.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-import { async, ComponentFixture, TestBed } from '@angular/core/testing';
-
-import { ManageMembersComponent } from './manage-members.component';
-
-describe('ManageMembersComponent', () => {
-  let component: ManageMembersComponent;
-  let fixture: ComponentFixture<ManageMembersComponent>;
-
-  beforeEach(async(() => {
-    TestBed.configureTestingModule({
-      declarations: [ ManageMembersComponent ]
-    })
-    .compileComponents();
-  }));
-
-  beforeEach(() => {
-    fixture = TestBed.createComponent(ManageMembersComponent);
-    component = fixture.componentInstance;
-    fixture.detectChanges();
-  });
-
-  it('should create', () => {
-    expect(component).toBeTruthy();
-  });
-});
diff --git a/src/app/customer/manage-members/manage-members.component.ts b/src/app/customer/manage-members/manage-members.component.ts
deleted file mode 100644
index dc9c822..0000000
--- a/src/app/customer/manage-members/manage-members.component.ts
+++ /dev/null
@@ -1,35 +0,0 @@
-import { Component, OnInit } from '@angular/core';
-import {MatTableDataSource} from '@angular/material';
-
-@Component({
-  selector: 'app-manage-members',
-  templateUrl: './manage-members.component.html',
-  styleUrls: ['./manage-members.component.scss']
-})
-export class ManageMembersComponent implements OnInit {
-  displayedColumns = ['id','firstName','lastName','status'];
-  dataSource = new MatTableDataSource(ELEMENT_DATA);
-
-  applyFilter(filterValue: string) {
-    filterValue = filterValue.trim(); // Remove whitespace
-    filterValue = filterValue.toLowerCase(); // MatTableDataSource defaults to lowercase matches
-    this.dataSource.filter = filterValue;
-  }
-
-  constructor() { }
-
-  ngOnInit() {
-  }
-
-}
-export interface Element {
-  id: number;
-  firstName: string;
-  lastName: string;
-  status: boolean;
-  
-}
-
-const ELEMENT_DATA: Element[] = [
-  
-];
diff --git a/src/app/customers/cases/case-exists.guard.ts b/src/app/customers/cases/case-exists.guard.ts
new file mode 100644
index 0000000..d2d3a93
--- /dev/null
+++ b/src/app/customers/cases/case-exists.guard.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 {ActivatedRouteSnapshot, CanActivate, RouterStateSnapshot} from '@angular/router';
+import {Injectable} from '@angular/core';
+import * as fromCases from './store';
+import {Observable} from 'rxjs/Observable';
+import {of} from 'rxjs/observable/of';
+import {CasesStore} from './store/index';
+import {PortfolioService} from '../../services/portfolio/portfolio.service';
+import {LoadAction} from './store/case.actions';
+import {ExistsGuardService} from '../../common/guards/exists-guard';
+
+@Injectable()
+export class CaseExistsGuard implements CanActivate {
+
+  constructor(private store: CasesStore,
+              private portfolioService: PortfolioService,
+              private existsGuardService: ExistsGuardService) {}
+
+  hasCaseInStore(id: string): Observable<boolean> {
+    const timestamp$ = this.store.select(fromCases.getCasesLoadedAt)
+      .map(loadedAt => loadedAt[id]);
+
+    return this.existsGuardService.isWithinExpiry(timestamp$);
+  }
+
+  hasCaseInApi(productId: string, caseId: string): Observable<boolean> {
+    const getCase$ = this.portfolioService.getCase(productId, caseId)
+      .map(caseEntity => new LoadAction({
+        resource: caseEntity
+      }))
+      .do((action: LoadAction) => this.store.dispatch(action))
+      .map(caseEntity => !!caseEntity);
+
+    return this.existsGuardService.routeTo404OnError(getCase$);
+  }
+
+  hasCase(productId: string, caseId: string): Observable<boolean> {
+    return this.hasCaseInStore(caseId)
+      .switchMap(inStore => {
+        if (inStore) {
+          return of(inStore);
+        }
+
+        return this.hasCaseInApi(productId, caseId);
+      });
+  }
+
+  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
+    return this.hasCase(route.params['productId'], route.params['caseId']);
+  }
+}
diff --git a/src/app/customers/cases/case.detail.component.html b/src/app/customers/cases/case.detail.component.html
new file mode 100644
index 0000000..6f6111b
--- /dev/null
+++ b/src/app/customers/cases/case.detail.component.html
@@ -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.
+-->
+
+<fims-layout-card-over [title]="caseInstance.identifier" *ngIf="caseInstance$ | async as caseInstance" [navigateBackTo]="['../../../../../../../../']">
+  <td-message *ngIf="caseInstance.currentState === 'APPROVED'" label="{{'Member loan approved' | translate }}" sublabel="{{'To activate this loan you need to disburse' | translate }}" color="accent" icon="error">
+    <button td-message-actions mat-button *hasPermission="{ id: 'portfolio_cases', accessLevel: 'CHANGE'}" [routerLink]="['tasks']" translate>GO TO TASKS</button>
+  </td-message>
+  <fims-two-column-layout>
+    <mat-nav-list left>
+      <h3 mat-subheader translate>Management</h3>
+      <a mat-list-item [routerLink]="['./payments']">
+        <mat-icon matListAvatar>payment</mat-icon>
+        <h3 matLine translate>Planned Payments</h3>
+        <p matLine translate>View payments</p>
+      </a>
+      <a mat-list-item [routerLink]="['./tasks']" *hasPermission="{ id: 'portfolio_cases', accessLevel: 'CHANGE'}">
+        <mat-icon matListAvatar>playlist_add_check</mat-icon>
+        <h3 matLine translate>Tasks</h3>
+        <p matLine translate>Change the status of the member loan</p>
+      </a>
+      <a mat-list-item [routerLink]="['./debtIncome']">
+        <mat-icon matListAvatar>show_chart</mat-icon>
+        <h3 matLine translate>Debt Income Report</h3>
+        <p matLine translate>View debt income report</p>
+      </a>
+      <a mat-list-item [routerLink]="['./documents']" *hasPermission="{ id: 'portfolio_documents', accessLevel: 'READ'}">
+        <mat-icon matListAvatar>content_paste</mat-icon>
+        <h3 matLine translate>Loan documents</h3>
+        <p matLine translate>Manage loan documents</p>
+      </a>
+    </mat-nav-list>
+    <mat-list right>
+      <h3 mat-subheader translate>Current status</h3>
+      <fims-state-display [state]="caseInstance.currentState"></fims-state-display>
+      <h3 mat-subheader translate>Details</h3>
+      <mat-list-item>
+        <h3 matLine translate>Loan product id</h3>
+        <p matLine>{{caseInstance.productIdentifier}}</p>
+      </mat-list-item>
+      <mat-list-item>
+        <h3 matLine translate>Principal amount</h3>
+        <p matLine>{{caseInstance.parameters.maximumBalance | number:numberFormat}}</p>
+      </mat-list-item>
+      <mat-list-item>
+        <h3 matLine translate>Interest</h3>
+        <p matLine>{{caseInstance.interest | number:numberFormat}}</p>
+      </mat-list-item>
+      <fims-case-detail-payment-cycle [paymentCycle]="caseInstance.parameters.paymentCycle"></fims-case-detail-payment-cycle>
+      <mat-list-item>
+        <h3 matLine translate>Term</h3>
+        <p matLine>{{caseInstance.parameters.termRange.maximum | number}} {{caseInstance.parameters.termRange.temporalUnit}}</p>
+      </mat-list-item>
+      <mat-list-item>
+        <h3 matLine translate>Member deposit account</h3>
+        <p matLine>{{caseInstance.depositAccountIdentifier}}</p>
+      </mat-list-item>
+      <mat-list-item>
+        <h3 matLine translate>Created by</h3>
+        <p matLine>{{caseInstance.createdBy}} - {{caseInstance.createdOn | date:'medium'}}</p>
+      </mat-list-item>
+      <mat-list-item>
+        <h3 matLine translate>Last modified by</h3>
+        <p matLine>{{caseInstance.lastModifiedBy}} - {{caseInstance.lastModifiedOn | date:'medium'}}</p>
+      </mat-list-item>
+    </mat-list>
+  </fims-two-column-layout>
+</fims-layout-card-over>
+<fims-fab-button title="{{'Edit member loan' | translate}}" icon="mode_edit" [link]="['edit']" *ngIf="canEdit$ | async"></fims-fab-button>
diff --git a/src/app/customers/cases/case.detail.component.ts b/src/app/customers/cases/case.detail.component.ts
new file mode 100644
index 0000000..caa4ab3
--- /dev/null
+++ b/src/app/customers/cases/case.detail.component.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 {Component, OnInit} from '@angular/core';
+import * as fromCases from './store/index';
+import {CasesStore} from './store/index';
+import * as fromRoot from '../../store';
+import {Observable} from 'rxjs/Observable';
+import {FimsPermission} from '../../services/security/authz/fims-permission.model';
+import {FimsCase} from '../../services/portfolio/domain/fims-case.model';
+
+@Component({
+  templateUrl: './case.detail.component.html'
+})
+export class CaseDetailComponent implements OnInit {
+
+  numberFormat = '1.2-2';
+
+  caseInstance$: Observable<FimsCase>;
+
+  canEdit$: Observable<boolean>;
+
+  constructor(private casesStore: CasesStore) {}
+
+  ngOnInit(): void {
+    this.caseInstance$ = this.casesStore.select(fromCases.getSelectedCase);
+
+    this.canEdit$ = Observable.combineLatest(
+      this.casesStore.select(fromRoot.getPermissions),
+      this.caseInstance$,
+      (permissions, caseInstance: FimsCase) => ({
+        hasPermission: this.hasChangePermission(permissions),
+        isCreatedOrPending: caseInstance.currentState === 'PENDING' || caseInstance.currentState === 'CREATED'
+      }))
+      .map(result => result.hasPermission && result.isCreatedOrPending);
+  }
+
+  private hasChangePermission(permissions: FimsPermission[]): boolean {
+    return permissions.filter(permission =>
+        permission.id === 'portfolio_cases' &&
+        permission.accessLevel === 'CHANGE'
+      ).length > 0;
+  }
+
+}
diff --git a/src/app/customers/cases/case.index.component.html b/src/app/customers/cases/case.index.component.html
new file mode 100644
index 0000000..ca721b3
--- /dev/null
+++ b/src/app/customers/cases/case.index.component.html
@@ -0,0 +1,18 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<router-outlet></router-outlet>
diff --git a/src/app/customers/cases/case.index.component.ts b/src/app/customers/cases/case.index.component.ts
new file mode 100644
index 0000000..3d85488
--- /dev/null
+++ b/src/app/customers/cases/case.index.component.ts
@@ -0,0 +1,44 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, OnDestroy, OnInit} from '@angular/core';
+import {ActivatedRoute} from '@angular/router';
+import {Subscription} from 'rxjs/Subscription';
+import {CasesStore} from './store/index';
+import {SelectAction} from './store/case.actions';
+
+@Component({
+  templateUrl: './case.index.component.html'
+})
+export class CaseIndexComponent implements OnInit, OnDestroy {
+
+  private actionsSubscription: Subscription;
+
+  constructor(private route: ActivatedRoute, private casesStore: CasesStore) {}
+
+  ngOnInit(): void {
+    this.actionsSubscription = this.route.params
+      .map(params => new SelectAction(params['caseId']))
+      .subscribe(this.casesStore);
+  }
+
+  ngOnDestroy(): void {
+    this.actionsSubscription.unsubscribe();
+  }
+
+}
diff --git a/src/app/customers/cases/case.list.component.html b/src/app/customers/cases/case.list.component.html
new file mode 100644
index 0000000..0b5dbe6
--- /dev/null
+++ b/src/app/customers/cases/case.list.component.html
@@ -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.
+-->
+
+<fims-layout-card-over title="{{'Manage member loans' | translate}}" [navigateBackTo]="['../../../../']">
+  <fims-data-table flex
+                   (onFetch)="fetchCases($event)"
+                   (onActionCellClick)="rowSelect($event)"
+                   [columns]="columns"
+                   [pageable]="true"
+                   [data]="casesData$ | async">
+  </fims-data-table>
+</fims-layout-card-over>
+<fims-fab-button title="{{'Create new loan for member ' | translate}}" icon="add" [link]="['create']" *ngIf="canAdd$ | async"></fims-fab-button>
diff --git a/src/app/customers/cases/case.list.component.spec.ts b/src/app/customers/cases/case.list.component.spec.ts
new file mode 100644
index 0000000..174bbb2
--- /dev/null
+++ b/src/app/customers/cases/case.list.component.spec.ts
@@ -0,0 +1,136 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, TestBed} from '@angular/core/testing';
+import {CaseListComponent} from './case.list.component';
+import {ActivatedRouteStub, RouterLinkStubDirective, RouterStub} from '../../common/testing/router-stubs';
+import {ActivatedRoute, Router} from '@angular/router';
+import {TranslateModule} from '@ngx-translate/core';
+import {Observable} from 'rxjs/Observable';
+import * as fromCases from './store/index';
+import {CasesStore} from './store/index';
+import * as fromCustomers from '../store';
+import * as fromRoot from '../../store';
+import {By} from '@angular/platform-browser';
+import {CUSTOM_ELEMENTS_SCHEMA, DebugElement} from '@angular/core';
+import {Customer} from '../../services/customer/domain/customer.model';
+import {CustomerState} from '../../services/customer/domain/customer-state.model';
+import {FimsPermission} from '../../services/security/authz/fims-permission.model';
+
+describe('Test case list component', () => {
+
+  let component: CaseListComponent;
+  let fixture: ComponentFixture<CaseListComponent>;
+
+  beforeEach(() => {
+    const activatedRoute = new ActivatedRouteStub();
+    const routerStub = new RouterStub();
+
+    TestBed.configureTestingModule({
+      imports: [
+        TranslateModule.forRoot()
+      ],
+      declarations: [
+        RouterLinkStubDirective,
+        CaseListComponent
+      ],
+      providers: [
+        { provide: ActivatedRoute, useValue: activatedRoute },
+        { provide: Router, useValue: routerStub },
+        { provide: CasesStore, useValue: jasmine.createSpyObj('casesStore', ['select', 'dispatch']) }
+      ],
+      schemas: [CUSTOM_ELEMENTS_SCHEMA]
+    });
+
+    fixture = TestBed.createComponent(CaseListComponent);
+
+    component = fixture.componentInstance;
+  });
+
+  function setup(currentState: CustomerState, hasChangePermission: boolean) {
+    const customer: Customer = {
+      identifier: 'test',
+      type: 'BUSINESS',
+      givenName: 'test',
+      surname: 'test',
+      dateOfBirth: undefined,
+      address: undefined,
+      customValues: [],
+      member: false,
+      currentState
+    };
+
+    const permissions: FimsPermission[] = [];
+
+    if (hasChangePermission) {
+      permissions.push({
+        id: 'portfolio_cases',
+        accessLevel: 'CHANGE'
+      });
+    }
+
+    const casesStore = TestBed.get(CasesStore);
+
+    casesStore.select.and.callFake(selector => {
+      if (selector === fromCases.getCaseSearchResults) {
+        return Observable.of({});
+      }
+      if (selector === fromCustomers.getSelectedCustomer) {
+        return Observable.of(customer);
+      }
+      if (selector === fromRoot.getPermissions) {
+        return Observable.of(permissions);
+      }
+    });
+  }
+
+  function getCreateButton(): DebugElement {
+    return fixture.debugElement.query(By.css('fims-fab-button'));
+  }
+
+  it('should not display add button when customer is not active but has change permission', () => {
+    setup('PENDING', true);
+
+    fixture.detectChanges();
+
+    const button = getCreateButton();
+
+    expect(button).toBeNull();
+  });
+
+  it('should not display add button when customer is active but has no change permission', () => {
+    setup('ACTIVE', false);
+
+    fixture.detectChanges();
+
+    const button = getCreateButton();
+
+    expect(button).toBeNull();
+  });
+
+  it('should display add button when customer is active and has change permission', () => {
+    setup('ACTIVE', true);
+
+    fixture.detectChanges();
+
+    const button = getCreateButton();
+
+    expect(button).not.toBeNull();
+  });
+
+});
diff --git a/src/app/customers/cases/case.list.component.ts b/src/app/customers/cases/case.list.component.ts
new file mode 100644
index 0000000..428221d
--- /dev/null
+++ b/src/app/customers/cases/case.list.component.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 {Component, OnDestroy, OnInit} from '@angular/core';
+import {ActivatedRoute, Router} from '@angular/router';
+import {TableData} from '../../common/data-table/data-table.component';
+import {FetchRequest} from '../../services/domain/paging/fetch-request.model';
+import {Case} from '../../services/portfolio/domain/case.model';
+import {Customer} from '../../services/customer/domain/customer.model';
+import * as fromCases from './store/index';
+import {CasesStore} from './store/index';
+import * as fromCustomers from '../store';
+import * as fromRoot from '../../store';
+import {Observable} from 'rxjs/Observable';
+import {Subscription} from 'rxjs/Subscription';
+import {SEARCH} from './store/case.actions';
+import {FimsPermission} from '../../services/security/authz/fims-permission.model';
+
+@Component({
+  templateUrl: './case.list.component.html'
+})
+export class CaseListComponent implements OnInit, OnDestroy {
+
+  private customerSubscription: Subscription;
+
+  private customer: Customer;
+
+  casesData$: Observable<TableData>;
+
+  canAdd$: Observable<boolean>;
+
+  columns: any[] = [
+    { name: 'identifier', label: 'Id' },
+    { name: 'productIdentifier', label: 'Loan product id' },
+    { name: 'parameters', label: 'Principal', format: v => v.maximumBalance },
+    { name: 'interest', label: 'Interest' },
+    { name: 'currentState', label: 'Current status' }
+  ];
+
+  constructor(private router: Router, private route: ActivatedRoute, private casesStore: CasesStore) {}
+
+  ngOnInit(): void {
+    this.casesData$ = this.casesStore.select(fromCases.getCaseSearchResults)
+      .map(casePage => ({
+        totalElements: casePage.totalElements,
+        totalPages: casePage.totalPages,
+        data: casePage.cases
+      }));
+
+    const customer$ = this.casesStore.select(fromCustomers.getSelectedCustomer)
+      .filter(customer => !!customer);
+
+    this.customerSubscription = customer$
+      .subscribe(customer => {
+        this.customer = customer;
+        this.fetchCases();
+      });
+
+    this.canAdd$ = Observable.combineLatest(
+      this.casesStore.select(fromRoot.getPermissions),
+      customer$,
+      (permissions, customer: Customer) => ({
+        hasPermission: this.hasChangePermission(permissions),
+        isCustomerActive: customer.currentState === 'ACTIVE'
+      }))
+      .map(result => result.hasPermission && result.isCustomerActive);
+  }
+
+  ngOnDestroy(): void {
+    this.customerSubscription.unsubscribe();
+  }
+
+  fetchCases(fetchRequest?: FetchRequest): void {
+    this.casesStore.dispatch({ type: SEARCH, payload: {
+      customerId: this.customer.identifier,
+      fetchRequest
+    }});
+  }
+
+  rowSelect(caseInstance: Case): void {
+    this.router.navigate(['products', caseInstance.productIdentifier, 'detail', caseInstance.identifier], { relativeTo: this.route });
+  }
+
+  private hasChangePermission(permissions: FimsPermission[]): boolean {
+    return permissions.filter(permission =>
+        permission.id === 'portfolio_cases' &&
+        permission.accessLevel === 'CHANGE'
+      ).length > 0;
+  }
+
+}
diff --git a/src/app/customers/cases/case.module.ts b/src/app/customers/cases/case.module.ts
new file mode 100644
index 0000000..0a9a3f3
--- /dev/null
+++ b/src/app/customers/cases/case.module.ts
@@ -0,0 +1,170 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 {FimsSharedModule} from '../../common/common.module';
+import {CaseRoutes} from './case.routes';
+import {NgModule} from '@angular/core';
+import {RouterModule} from '@angular/router';
+import {CasePaymentsComponent} from './payments/payments.component';
+import {CaseDetailFormComponent} from './form/detail/detail.component';
+import {CaseCreateComponent} from './form/create.component';
+import {CaseFormComponent} from './form/form.component';
+import {CaseListComponent} from './case.list.component';
+import {CaseDetailComponent} from './case.detail.component';
+import {CaseEditComponent} from './form/edit.component';
+import {CasesStore, caseStoreFactory} from './store/index';
+import {Store} from '@ngrx/store';
+import {CaseExistsGuard} from './case-exists.guard';
+import {CaseDetailPaymentCycleComponent} from './payment-cycle/payment-cycle.component';
+import {CasePaymentsApiEffects} from './store/payments/effects/service.effects';
+import {EffectsModule} from '@ngrx/effects';
+import {CaseTasksApiEffects} from './store/tasks/effects/service.effects';
+import {CaseNotificationEffects} from './store/effects/notification.effects';
+import {CaseRouteEffects} from './store/effects/route.effects';
+import {CaseApiEffects} from './store/effects/service.effects';
+import {TranslateModule} from '@ngx-translate/core';
+import {CommonModule} from '@angular/common';
+import {ReactiveFormsModule} from '@angular/forms';
+import {
+  MatAutocompleteModule,
+  MatButtonModule,
+  MatCardModule,
+  MatCheckboxModule,
+  MatIconModule,
+  MatInputModule,
+  MatListModule,
+  MatOptionModule,
+  MatRadioModule,
+  MatSelectModule,
+  MatTabsModule,
+  MatToolbarModule,
+  MatTooltipModule
+} from '@angular/material';
+import {
+  CovalentCommonModule,
+  CovalentDataTableModule,
+  CovalentFileModule,
+  CovalentMessageModule,
+  CovalentNotificationsModule,
+  CovalentStepsModule
+} from '@covalent/core';
+import {CaseStatusComponent} from './status/status.component';
+import {CaseCreditFactorFormComponent} from './form/components/credit-factor.component';
+import {CaseCoSignerFormComponent} from './form/co-signer/co-signer.component';
+import {CaseDebtToIncomeFormComponent} from './form/debt-to-income/debt-to-income.component';
+import {CaseDebtIncomeComponent} from './debt-income/debt-income.component';
+import {CaseTasksComponent} from './status/tasks.component';
+import {CaseCommandComponent} from './status/command.component';
+import {CaseTasksNotificationEffects} from './store/tasks/effects/notification.effects';
+import {CaseCommandConfirmationComponent} from './status/confirmation/confirmation.component';
+import {CaseCommandConfirmationFormComponent} from './status/confirmation/form.component';
+import {CaseTaskComponent} from './status/task.component';
+import {CaseIndexComponent} from './case.index.component';
+import {FeeService} from './status/services/fee.service';
+import {CaseDocumentComponent} from './documents/documents.component';
+import {CaseDocumentFormComponent} from './documents/form/form.component';
+import {CaseDocumentEditComponent} from './documents/form/edit.component';
+import {CaseDocumentCreateComponent} from './documents/form/create.component';
+import {CaseDocumentIndexComponent} from './documents/document.index.component';
+import {CaseDocumentDetailComponent} from './documents/document.detail.component';
+import {DocumentExistsGuard} from './documents/document-exists.guard';
+import {DocumentsService} from './store/documents/effects/services/documents.service';
+import {CaseDocumentApiEffects} from './store/documents/effects/service.effects';
+import {CaseDocumentRouteEffects} from './store/documents/effects/route.effects';
+import {CaseDocumentNotificationEffects} from './store/documents/effects/notification.effects';
+import {UploadPageFormComponent} from './documents/form/upload/upload-page.form.component';
+import {CreateDocumentPageComponent} from './documents/form/upload/create.form.component';
+
+@NgModule({
+  imports: [
+    RouterModule.forChild(CaseRoutes),
+    FimsSharedModule,
+    TranslateModule,
+    CommonModule,
+    ReactiveFormsModule,
+    MatTooltipModule,
+    MatTabsModule,
+    MatIconModule,
+    MatListModule,
+    MatToolbarModule,
+    MatInputModule,
+    MatButtonModule,
+    MatOptionModule,
+    MatSelectModule,
+    MatRadioModule,
+    MatCardModule,
+    MatCheckboxModule,
+    MatAutocompleteModule,
+    CovalentCommonModule,
+    CovalentStepsModule,
+    CovalentDataTableModule,
+    CovalentMessageModule,
+    CovalentFileModule,
+    CovalentNotificationsModule,
+
+    EffectsModule.run(CaseApiEffects),
+    EffectsModule.run(CaseRouteEffects),
+    EffectsModule.run(CaseNotificationEffects),
+
+    EffectsModule.run(CaseTasksApiEffects),
+    EffectsModule.run(CaseTasksNotificationEffects),
+    EffectsModule.run(CasePaymentsApiEffects),
+
+    EffectsModule.run(CaseDocumentApiEffects),
+    EffectsModule.run(CaseDocumentRouteEffects),
+    EffectsModule.run(CaseDocumentNotificationEffects),
+
+  ],
+  declarations: [
+    CaseListComponent,
+    CaseIndexComponent,
+    CaseFormComponent,
+    CaseCreateComponent,
+    CaseEditComponent,
+    CaseDetailFormComponent,
+    CaseDetailComponent,
+    CaseDetailPaymentCycleComponent,
+    CasePaymentsComponent,
+    CaseCommandComponent,
+    CaseTasksComponent,
+    CaseTaskComponent,
+    CaseCommandConfirmationComponent,
+    CaseCommandConfirmationFormComponent,
+    CaseStatusComponent,
+    CaseDebtToIncomeFormComponent,
+    CaseCreditFactorFormComponent,
+    CaseCoSignerFormComponent,
+    CaseDebtIncomeComponent,
+    CaseDocumentComponent,
+    CaseDocumentIndexComponent,
+    CaseDocumentDetailComponent,
+    CaseDocumentFormComponent,
+    CaseDocumentCreateComponent,
+    CaseDocumentEditComponent,
+    CreateDocumentPageComponent,
+    UploadPageFormComponent
+  ],
+  providers: [
+    CaseExistsGuard,
+    DocumentExistsGuard,
+    FeeService,
+    DocumentsService,
+    { provide: CasesStore, useFactory: caseStoreFactory, deps: [Store]}
+  ]
+})
+export class CaseModule {}
diff --git a/src/app/customers/cases/case.routes.ts b/src/app/customers/cases/case.routes.ts
new file mode 100644
index 0000000..b2c1586
--- /dev/null
+++ b/src/app/customers/cases/case.routes.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 {Routes} from '@angular/router';
+import {CasePaymentsComponent} from './payments/payments.component';
+import {CaseDetailComponent} from './case.detail.component';
+import {CaseCreateComponent} from './form/create.component';
+import {CaseListComponent} from './case.list.component';
+import {CaseEditComponent} from './form/edit.component';
+import {CaseExistsGuard} from './case-exists.guard';
+import {CaseStatusComponent} from './status/status.component';
+import {CaseDebtIncomeComponent} from './debt-income/debt-income.component';
+import {CaseCommandConfirmationComponent} from './status/confirmation/confirmation.component';
+import {CaseIndexComponent} from './case.index.component';
+import {CaseDocumentComponent} from './documents/documents.component';
+import {CaseDocumentIndexComponent} from './documents/document.index.component';
+import {CaseDocumentDetailComponent} from './documents/document.detail.component';
+import {CaseDocumentCreateComponent} from './documents/form/create.component';
+import {CaseDocumentEditComponent} from './documents/form/edit.component';
+import {DocumentExistsGuard} from './documents/document-exists.guard';
+import {CreateDocumentPageComponent} from './documents/form/upload/create.form.component';
+
+export const CaseRoutes: Routes = [
+  {
+    path: '',
+    component: CaseListComponent,
+    data: {
+      hasPermission: {id: 'portfolio_cases', accessLevel: 'READ'}
+    }
+  },
+  {
+    path: 'create',
+    component: CaseCreateComponent,
+    data: {
+      hasPermission: {id: 'portfolio_cases', accessLevel: 'CHANGE'}
+    }
+  },
+  {
+    path: 'products/:productId/detail/:caseId',
+    component: CaseIndexComponent,
+    canActivate: [CaseExistsGuard],
+    data: {
+      hasPermission: {id: 'portfolio_cases', accessLevel: 'READ'}
+    },
+    children: [
+      {
+        path: '', component: CaseDetailComponent
+      },
+      {
+        path: 'edit',
+        component: CaseEditComponent,
+        data: {
+          hasPermission: {id: 'portfolio_cases', accessLevel: 'CHANGE'}
+        }
+      },
+      {
+        path: 'payments', component: CasePaymentsComponent
+      },
+      {
+        path: 'tasks',
+        component: CaseStatusComponent,
+        data: {
+          hasPermission: {id: 'portfolio_cases', accessLevel: 'CHANGE'}
+        }
+      },
+      {
+        path: 'tasks/:action/confirmation',
+        component: CaseCommandConfirmationComponent,
+        data: {
+          hasPermission: {id: 'portfolio_cases', accessLevel: 'CHANGE'}
+        }
+      },
+      {
+        path: 'debtIncome',
+        component: CaseDebtIncomeComponent
+      },
+      {
+        path: 'documents',
+        component: CaseDocumentComponent,
+        data: {
+          hasPermission: {id: 'portfolio_documents', accessLevel: 'READ'}
+        }
+      },
+      {
+        path: 'documents/detail/:documentId',
+        component: CaseDocumentIndexComponent,
+        canActivate: [DocumentExistsGuard],
+        data: {
+          hasPermission: {id: 'portfolio_documents', accessLevel: 'READ'}
+        },
+        children: [
+          {
+            path: '',
+            component: CaseDocumentDetailComponent
+          },
+          {
+            path: 'edit',
+            component: CaseDocumentEditComponent,
+            data: {
+              hasPermission: {id: 'portfolio_documents', accessLevel: 'CHANGE'}
+            }
+          },
+          {
+            path: 'upload',
+            component: CreateDocumentPageComponent,
+            data: {
+              hasPermission: {id: 'portfolio_documents', accessLevel: 'CHANGE'}
+            }
+          }
+        ]
+      },
+      {
+        path: 'documents/create',
+        component: CaseDocumentCreateComponent,
+        data: {
+          hasPermission: {id: 'portfolio_documents', accessLevel: 'CHANGE'}
+        }
+      }
+    ]
+  }
+
+];
diff --git a/src/app/customers/cases/debt-income/debt-income.component.html b/src/app/customers/cases/debt-income/debt-income.component.html
new file mode 100644
index 0000000..6092c36
--- /dev/null
+++ b/src/app/customers/cases/debt-income/debt-income.component.html
@@ -0,0 +1,53 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<fims-layout-card-over title="{{'View debt income ratio' | translate}}" [navigateBackTo]="['../']">
+  <mat-tab-group>
+    <mat-tab label="Member(Ratio: {{customerOverview.ratio ? (customerOverview.ratio | number:numberFormat) : '-'}})" *ngIf="customerOverview$ | async as customerOverview">
+      <div class="mat-content inset" flex>
+        <h5>Debts(Total: {{customerOverview.debtTotal | number:numberFormat}})</h5>
+        <fims-data-table flex
+                         [columns]="columns"
+                         [data]="customerOverview.debtTableData"
+                         [actionColumn]="false">
+        </fims-data-table>
+        <h5>Income(Total: {{customerOverview.incomeTotal | number:numberFormat}})</h5>
+        <fims-data-table flex
+                         [columns]="columns"
+                         [data]="customerOverview.incomeTableData"
+                         [actionColumn]="false">
+        </fims-data-table>
+      </div>
+    </mat-tab>
+    <mat-tab label="Co-signer(Ratio: {{cosignerOverview.ratio ? (cosignerOverview.ratio | number:numberFormat) : '-'}})" *ngIf="cosignerOverview$ | async as cosignerOverview">
+      <div class="mat-content inset" flex>
+        <h5>Debts(Total: {{cosignerOverview.debtTotal | number:numberFormat}})</h5>
+        <fims-data-table flex
+                         [columns]="columns"
+                         [data]="cosignerOverview.debtTableData"
+                         [actionColumn]="false">
+        </fims-data-table>
+        <h5>Income(Total: {{cosignerOverview.incomeTotal | number:numberFormat}})</h5>
+        <fims-data-table flex
+                         [columns]="columns"
+                         [data]="cosignerOverview.incomeTableData"
+                         [actionColumn]="false">
+        </fims-data-table>
+      </div>
+    </mat-tab>
+  </mat-tab-group>
+</fims-layout-card-over>
diff --git a/src/app/customers/cases/debt-income/debt-income.component.ts b/src/app/customers/cases/debt-income/debt-income.component.ts
new file mode 100644
index 0000000..5909198
--- /dev/null
+++ b/src/app/customers/cases/debt-income/debt-income.component.ts
@@ -0,0 +1,120 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, OnInit} from '@angular/core';
+import * as fromCases from '../store/index';
+import {CasesStore} from '../store/index';
+import {Observable} from 'rxjs/Observable';
+import {CreditWorthinessFactor} from '../../../services/portfolio/domain/individuallending/credit-worthiness-factor.model';
+import {CreditWorthinessSnapshot} from '../../../services/portfolio/domain/individuallending/credit-worthiness-snapshot.model';
+import * as fromCustomers from '../../store/index';
+import {Customer} from '../../../services/customer/domain/customer.model';
+import {TableData} from '../../../common/data-table/data-table.component';
+import {FimsCase} from '../../../services/portfolio/domain/fims-case.model';
+
+interface IncomeDebtOverview {
+  debtTableData: TableData;
+  incomeTableData: TableData;
+  ratio: number;
+  debtTotal: number;
+  incomeTotal: number;
+}
+
+@Component({
+  templateUrl: './debt-income.component.html'
+})
+export class CaseDebtIncomeComponent implements OnInit {
+
+  numberFormat = '2.2-2';
+
+  columns: any[] = [
+    { name: 'description', label: 'Description' },
+    { name: 'amount', label: 'Amount' }
+  ];
+
+  customerOverview$: Observable<IncomeDebtOverview>;
+
+  cosignerOverview$: Observable<IncomeDebtOverview>;
+
+  constructor(private store: CasesStore) {}
+
+  ngOnInit(): void {
+    const selectedCustomer$: Observable<Customer> = this.store.select(fromCustomers.getSelectedCustomer);
+
+    const snapshots$: Observable<CreditWorthinessSnapshot[]> = this.store.select(fromCases.getSelectedCase)
+      .map((fimsCase: FimsCase) => fimsCase.parameters.creditWorthinessSnapshots);
+
+    const combined$ = Observable.combineLatest(selectedCustomer$, snapshots$, (customer, snapshots) => ({
+      customer,
+      snapshots
+    }));
+
+    this.customerOverview$ = combined$
+      .map(result => result.snapshots.find(snapshot => snapshot.forCustomer === result.customer.identifier))
+      .map(snapshot => snapshot ? snapshot : this.mapEmptySnapshot())
+      .map(snapshot => this.mapToOverview(snapshot));
+
+    this.cosignerOverview$ = combined$
+      .map(result => result.snapshots.find(snapshot => snapshot.forCustomer !== result.customer.identifier))
+      .map(snapshot => snapshot ? snapshot : this.mapEmptySnapshot())
+      .map(snapshot => this.mapToOverview(snapshot));
+  }
+
+  private mapEmptySnapshot(): CreditWorthinessSnapshot {
+    return {
+      forCustomer: '',
+      incomeSources: [],
+      debts: [],
+      assets: []
+    };
+  }
+
+  private mapToOverview(snapshot: CreditWorthinessSnapshot): IncomeDebtOverview {
+    const debtTotal = this.sum(snapshot.debts);
+    const incomeTotal = this.sum(snapshot.incomeSources);
+    const ratio = this.divideIfNotZero(debtTotal, incomeTotal);
+    return {
+      debtTableData: this.mapToTableData(snapshot.debts),
+      incomeTableData: this.mapToTableData(snapshot.incomeSources),
+      debtTotal,
+      incomeTotal,
+      ratio
+    };
+  }
+
+  mapToTableData(data: CreditWorthinessFactor[]): TableData {
+    return {
+      data,
+      totalPages: 1,
+      totalElements: data.length
+    };
+  }
+
+  divideIfNotZero(numerator, denominator): number {
+    if (denominator === 0 || isNaN(denominator)) {
+      return null;
+    } else {
+      return numerator / denominator;
+    }
+  }
+
+  sum(factors: CreditWorthinessFactor[]): number {
+    return factors.reduce((acc, val) => acc + parseFloat(val.amount), 0);
+  }
+
+}
diff --git a/src/app/customers/cases/documents/document-exists.guard.ts b/src/app/customers/cases/documents/document-exists.guard.ts
new file mode 100644
index 0000000..8c0cdab
--- /dev/null
+++ b/src/app/customers/cases/documents/document-exists.guard.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 {ActivatedRouteSnapshot, CanActivate, RouterStateSnapshot} from '@angular/router';
+import {CasesStore} from '../store/index';
+import {ExistsGuardService} from '../../../common/guards/exists-guard';
+import {PortfolioService} from '../../../services/portfolio/portfolio.service';
+import {Observable} from 'rxjs/Observable';
+import * as fromCases from '../store';
+import {LoadAction} from '../store/documents/document.actions';
+import {of} from 'rxjs/observable/of';
+import {CustomerService} from '../../../services/customer/customer.service';
+import {CaseCustomerDocuments, Document} from '../../../services/portfolio/domain/case-customer-documents.model';
+import {CustomerDocument} from '../../../services/customer/domain/customer-document.model';
+
+@Injectable()
+export class DocumentExistsGuard implements CanActivate {
+
+  constructor(private store: CasesStore,
+              private customerService: CustomerService,
+              private portfolioService: PortfolioService,
+              private existsGuardService: ExistsGuardService) {}
+
+  hasDocumentInStore(id: string): Observable<boolean> {
+    const timestamp$ = this.store.select(fromCases.getCaseDocumentLoadedAt)
+      .map(loadedAt => loadedAt[id]);
+
+    return this.existsGuardService.isWithinExpiry(timestamp$);
+  }
+
+  hasDocumentInApi(productId: string, caseId: string, documentId: string): Observable<boolean> {
+    const getDocument$ = this.portfolioService.getCaseDocuments(productId, caseId)
+      .switchMap((documents: CaseCustomerDocuments) => this.findDocument(documentId, documents.documents))
+      .switchMap((document: Document) => this.customerService.getDocument(document.customerId, document.documentId))
+      .map((customerDocument: CustomerDocument) => new LoadAction({
+        resource: customerDocument
+      }))
+      .do((action: LoadAction) => this.store.dispatch(action))
+      .map(document => !!document);
+
+    return this.existsGuardService.routeTo404OnError(getDocument$);
+  }
+
+  private findDocument(documentId: string, documents: Document[]): Observable<Document> {
+    const foundDocument = documents.find(document => document.documentId === documentId);
+
+    if (!foundDocument) {
+      return Observable.throw(new Error('Document not found'));
+    }
+
+    return Observable.of(foundDocument);
+  }
+
+  hasDocument(productId: string, caseId: string, documentId: string): Observable<boolean> {
+    return this.hasDocumentInStore(documentId)
+      .switchMap(inStore => {
+        if (inStore) {
+          return of(inStore);
+        }
+
+        return this.hasDocumentInApi(productId, caseId, documentId);
+      });
+  }
+
+  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
+    return this.hasDocument(route.parent.params['productId'], route.parent.params['caseId'], route.params['documentId']);
+  }
+}
diff --git a/src/app/customers/cases/documents/document.detail.component.html b/src/app/customers/cases/documents/document.detail.component.html
new file mode 100644
index 0000000..957278f
--- /dev/null
+++ b/src/app/customers/cases/documents/document.detail.component.html
@@ -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.
+-->
+
+<ng-container *ngIf="customerDocument$ | async as customerDocument">
+  <fims-layout-card-over title="{{ 'Document' | translate }}" [navigateBackTo]="['../../../../../../../../../../']" *ngIf="currentSelection$ | async as currentSelection">
+    <fims-layout-card-over-header-menu layout="row" layout-align="end center">
+      <a mat-icon-button title="{{'Edit document' | translate}}" [routerLink]="['edit']" *ngIf="canEdit$ | async">
+        <mat-icon>mode_edit</mat-icon>
+      </a>
+      <button mat-icon-button (click)="deleteDocument(currentSelection, customerDocument)" title="{{'Delete document' | translate}}" *ngIf="canEdit$ | async">
+        <mat-icon>delete</mat-icon>
+      </button>
+    </fims-layout-card-over-header-menu>
+    <td-message *ngIf="canEdit$ | async" label="{{'Document not locked' | translate }}"
+                sublabel="{{'You can lock this document' | translate }}"
+                color="accent" icon="lock_open">
+      <button td-message-actions mat-button (click)="lock(currentSelection, customerDocument)" translate>
+        LOCK
+      </button>
+    </td-message>
+    <td-message *ngIf="customerDocument.completed" label="{{'Document locked' | translate }}" color="warn" icon="lock">
+    </td-message>
+    <div class="mat-content inset" flex>
+      <mat-list>
+        <mat-list-item>
+          <h3 matLine translate>Description</h3>
+          <p matLine>{{customerDocument.description}}</p>
+        </mat-list-item>
+        <mat-list-item>
+          <h3 matLine translate>Created by</h3>
+          <p matLine>{{customerDocument.createdBy}} - {{customerDocument.createdOn | date:'medium'}}</p>
+        </mat-list-item>
+      </mat-list>
+      <mat-list *ngIf="pageNumbers$ | async as pageNumbers">
+        <h3 mat-subheader translate>Pages uploaded</h3>
+        <mat-list-item *ngFor="let pageNumber of pageNumbers">
+          <mat-icon mat-list-icon>content_paste</mat-icon>
+          <h4 matLine>{{pageNumber}}</h4>
+          <button mat-button color="warn" (click)="deletePage(currentSelection, customerDocument, pageNumber)" *ngIf="canEdit$ | async" translate>
+            Delete
+          </button>
+          <button mat-raised-button (click)="viewPage(currentSelection, customerDocument, pageNumber)" translate>
+            View
+          </button>
+        </mat-list-item>
+      </mat-list>
+    </div>
+  </fims-layout-card-over>
+  <fims-fab-button title="{{'Upload page' | translate}}" icon="content_paste" [link]="['upload']" *ngIf="canEdit$ | async"></fims-fab-button>
+</ng-container>
diff --git a/src/app/customers/cases/documents/document.detail.component.ts b/src/app/customers/cases/documents/document.detail.component.ts
new file mode 100644
index 0000000..104a596
--- /dev/null
+++ b/src/app/customers/cases/documents/document.detail.component.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 {Component, OnDestroy, OnInit} from '@angular/core';
+import {CasesStore} from '../store/index';
+import * as fromRoot from '../../../store';
+import * as fromCases from '../store';
+import {Observable} from 'rxjs/Observable';
+import {DeleteDocumentAction, DeletePageAction, LoadAllPagesAction, LockDocumentAction} from '../store/documents/document.actions';
+import {TranslateService} from '@ngx-translate/core';
+import {TdDialogService} from '@covalent/core';
+import {CaseSelection} from '../store/model/case-selection.model';
+import {ActivatedRoute} from '@angular/router';
+import {Subscription} from 'rxjs/Subscription';
+import {CustomerDocument} from '../../../services/customer/domain/customer-document.model';
+import {CustomerService} from '../../../services/customer/customer.service';
+import {ImageComponent} from '../../../common/image/image.component';
+import {FimsPermission} from '../../../services/security/authz/fims-permission.model';
+
+@Component({
+  templateUrl: './document.detail.component.html'
+})
+export class CaseDocumentDetailComponent implements OnInit, OnDestroy {
+
+  private actionsSubscription: Subscription;
+
+  currentSelection$: Observable<CaseSelection>;
+
+  customerDocument$: Observable<CustomerDocument>;
+
+  pageNumbers$: Observable<number[]>;
+
+  canEdit$: Observable<boolean>;
+
+  constructor(private route: ActivatedRoute, private casesStore: CasesStore,
+              private translate: TranslateService, private dialogService: TdDialogService, private customerService: CustomerService) {}
+
+  ngOnInit(): void {
+    this.customerDocument$ = this.casesStore.select(fromCases.getSelectedCaseDocument)
+      .filter(document => !!document);
+    this.currentSelection$ = this.casesStore.select(fromCases.getCaseSelection);
+    this.pageNumbers$ = this.casesStore.select(fromCases.getAllDocumentPages);
+
+    this.actionsSubscription = Observable.combineLatest(
+      this.customerDocument$,
+      this.currentSelection$,
+      (document, selection) => ({
+        document,
+        selection
+      })
+    ).map(result => new LoadAllPagesAction({
+      customerId: result.selection.customerId,
+      documentId: result.document.identifier
+      })
+    ).subscribe(this.casesStore);
+
+    this.canEdit$ = Observable.combineLatest(
+      this.casesStore.select(fromRoot.getPermissions),
+      this.customerDocument$,
+      (permissions, document: CustomerDocument) => ({
+        hasPermission: this.hasChangePermission(permissions),
+        isLocked: document.completed
+      }))
+      .map(result => result.hasPermission && !result.isLocked);
+  }
+
+  ngOnDestroy(): void {
+    this.actionsSubscription.unsubscribe();
+  }
+
+  private showTranslatedDialog(title: string, message: string, button: string): Observable<boolean> {
+    return this.translate.get([title, message, button])
+      .mergeMap(result =>
+        this.dialogService.openConfirm({
+          message: result[message],
+          title: result[title],
+          acceptButton: result[button]
+        }).afterClosed()
+      );
+  }
+
+  confirmDeletePage(): Observable<boolean> {
+    const message = 'Do you want to delete this page?';
+    const title = 'Confirm deletion';
+    const button = 'DELETE PAGE';
+
+    return this.showTranslatedDialog(title, message, button);
+  }
+
+  deletePage(selection: CaseSelection, document: CustomerDocument, pageNumber: number): void {
+    this.confirmDeletePage()
+      .filter(accept => accept)
+      .subscribe(() => {
+        const action = new DeletePageAction({
+          customerId: selection.customerId,
+          documentId: document.identifier,
+          pageNumber
+        });
+
+        this.casesStore.dispatch(action);
+      });
+  }
+
+  viewPage(selection: CaseSelection, document: CustomerDocument, pageNumber: number): void {
+    this.customerService.getDocumentPage(selection.customerId, document.identifier, pageNumber)
+      .subscribe(blob => {
+        this.dialogService.open(ImageComponent, {
+          data: blob
+        });
+      });
+  }
+
+  confirmDeleteDocument(): Observable<boolean> {
+    const message = 'Do you want to delete this document?';
+    const title = 'Confirm deletion';
+    const button = 'DELETE DOCUMENT';
+
+    return this.showTranslatedDialog(title, message, button);
+  }
+
+  deleteDocument(selection: CaseSelection, document: CustomerDocument): void {
+    this.confirmDeleteDocument()
+      .filter(accept => accept)
+      .subscribe(() => {
+        const action = new DeleteDocumentAction({
+          customerId: selection.customerId,
+          productId: selection.productId,
+          caseId: selection.caseId,
+          document,
+          activatedRoute: this.route
+        });
+
+        this.casesStore.dispatch(action);
+      });
+  }
+
+  lock(selection: CaseSelection, document: CustomerDocument): void {
+    const action = new LockDocumentAction({
+      customerId: selection.customerId,
+      documentId: document.identifier
+    });
+
+    this.casesStore.dispatch(action);
+  }
+
+  private hasChangePermission(permissions: FimsPermission[]): boolean {
+    return permissions.filter(permission =>
+      permission.id === 'portfolio_documents' &&
+      permission.accessLevel === 'CHANGE'
+    ).length > 0;
+  }
+
+}
diff --git a/src/app/customers/cases/documents/document.index.component.html b/src/app/customers/cases/documents/document.index.component.html
new file mode 100644
index 0000000..ca721b3
--- /dev/null
+++ b/src/app/customers/cases/documents/document.index.component.html
@@ -0,0 +1,18 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<router-outlet></router-outlet>
diff --git a/src/app/customers/cases/documents/document.index.component.ts b/src/app/customers/cases/documents/document.index.component.ts
new file mode 100644
index 0000000..53a9b54
--- /dev/null
+++ b/src/app/customers/cases/documents/document.index.component.ts
@@ -0,0 +1,44 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, OnDestroy, OnInit} from '@angular/core';
+import {Subscription} from 'rxjs/Subscription';
+import {ActivatedRoute} from '@angular/router';
+import {CasesStore} from '../store/index';
+import {SelectAction} from '../store/documents/document.actions';
+
+@Component({
+  templateUrl: './document.index.component.html'
+})
+export class CaseDocumentIndexComponent implements OnInit, OnDestroy {
+
+  private actionsSubscription: Subscription;
+
+  constructor(private route: ActivatedRoute, private casesStore: CasesStore) {}
+
+  ngOnInit(): void {
+    this.actionsSubscription = this.route.params
+      .map(params => new SelectAction(params['documentId']))
+      .subscribe(this.casesStore);
+  }
+
+  ngOnDestroy(): void {
+    this.actionsSubscription.unsubscribe();
+  }
+
+}
diff --git a/src/app/customers/cases/documents/documents.component.html b/src/app/customers/cases/documents/documents.component.html
new file mode 100644
index 0000000..6b1cfb9
--- /dev/null
+++ b/src/app/customers/cases/documents/documents.component.html
@@ -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.
+-->
+
+<fims-layout-card-over title="{{'Manage documents' | translate}}" [navigateBackTo]="['../']">
+  <fims-data-table flex
+                   (onActionCellClick)="rowSelect($event)"
+                   [columns]="columns"
+                   [pageable]="false"
+                   [data]="documentData$ | async">
+  </fims-data-table>
+</fims-layout-card-over>
+<fims-fab-button title="{{'Create new document' | translate}}" icon="add" [link]="['create']"></fims-fab-button>
diff --git a/src/app/customers/cases/documents/documents.component.ts b/src/app/customers/cases/documents/documents.component.ts
new file mode 100644
index 0000000..d0bfcc6
--- /dev/null
+++ b/src/app/customers/cases/documents/documents.component.ts
@@ -0,0 +1,79 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, OnDestroy, OnInit} from '@angular/core';
+import {Observable} from 'rxjs/Observable';
+import {TableData} from '../../../common/data-table/data-table.component';
+import {CasesStore} from '../store/index';
+import * as fromCases from '../store';
+import {LoadAllAction} from '../store/documents/document.actions';
+import {CaseSelection} from '../store/model/case-selection.model';
+import {Subscription} from 'rxjs/Subscription';
+import {CustomerDocument} from '../../../services/customer/domain/customer-document.model';
+import {ActivatedRoute, Router} from '@angular/router';
+import {DatePipe} from '@angular/common';
+
+@Component({
+  templateUrl: './documents.component.html',
+  providers: [DatePipe]
+})
+export class CaseDocumentComponent implements OnInit, OnDestroy {
+
+  private actionsSubscription: Subscription;
+
+  currentSelection$: Observable<CaseSelection>;
+
+  documentData$: Observable<TableData>;
+
+  columns: any[] = [
+    { name: 'description', label: 'Description' },
+    { name: 'createdBy', label: 'Created by' },
+    { name: 'createdOn', label: 'Created on', format: value => this.datePipe.transform(value, 'short') }
+  ];
+
+  constructor(private casesStore: CasesStore, private router: Router, private route: ActivatedRoute, private datePipe: DatePipe) {
+    this.currentSelection$ = casesStore.select(fromCases.getCaseSelection);
+
+    this.documentData$ = casesStore.select(fromCases.getAllCaseDocumentEntities)
+      .map(entities => ({
+        data: entities,
+        totalElements: entities.length,
+        totalPages: 1
+      }))
+  }
+
+  ngOnInit(): void {
+    this.actionsSubscription = this.currentSelection$
+      .map(selection => new LoadAllAction({
+        customerId: selection.customerId,
+        productId: selection.productId,
+        caseId: selection.caseId
+      }))
+      .subscribe(this.casesStore);
+  }
+
+  ngOnDestroy(): void {
+    if (this.actionsSubscription) {
+      this.actionsSubscription.unsubscribe();
+    }
+  }
+
+  rowSelect(document: CustomerDocument): void {
+    this.router.navigate(['detail', document.identifier], { relativeTo: this.route });
+  }
+}
diff --git a/src/app/customers/cases/documents/form/create.component.html b/src/app/customers/cases/documents/form/create.component.html
new file mode 100644
index 0000000..970823b
--- /dev/null
+++ b/src/app/customers/cases/documents/form/create.component.html
@@ -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.
+-->
+
+<fims-layout-card-over title="{{'Create document' | translate }}" *ngIf="currentSelection$ | async as currentSelection">
+  <fims-case-document-form #form
+                           [document]="document"
+                           (onSave)="onSave(currentSelection, $event)"
+                           (onCancel)="onCancel()"
+                           [editMode]="false">
+  </fims-case-document-form>
+</fims-layout-card-over>
diff --git a/src/app/customers/cases/documents/form/create.component.ts b/src/app/customers/cases/documents/form/create.component.ts
new file mode 100644
index 0000000..535acfc
--- /dev/null
+++ b/src/app/customers/cases/documents/form/create.component.ts
@@ -0,0 +1,59 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 {CustomerDocument} from '../../../../services/customer/domain/customer-document.model';
+import {ActivatedRoute, Router} from '@angular/router';
+import {CasesStore} from '../../store/index';
+import {CREATE, CreateDocumentAction} from '../../store/documents/document.actions';
+import * as fromCases from '../../store';
+import {Observable} from 'rxjs/Observable';
+import {CaseSelection} from '../../store/model/case-selection.model';
+
+@Component({
+  templateUrl: './create.component.html'
+})
+export class CaseDocumentCreateComponent {
+
+  currentSelection$: Observable<CaseSelection>;
+
+  document: CustomerDocument = {
+    identifier: ''
+  };
+
+  constructor(private router: Router, private route: ActivatedRoute, private casesStore: CasesStore) {
+    this.currentSelection$ = casesStore.select(fromCases.getCaseSelection);
+  }
+
+  onSave(selection: CaseSelection, document: CustomerDocument) {
+    const action = new CreateDocumentAction({
+      productId: selection.productId,
+      caseId: selection.caseId,
+      customerId: selection.customerId,
+      document,
+      activatedRoute: this.route
+    });
+
+    this.casesStore.dispatch(action);
+  }
+
+  onCancel() {
+    this.router.navigate(['../'], { relativeTo: this.route });
+  }
+
+}
diff --git a/src/app/customers/cases/documents/form/edit.component.html b/src/app/customers/cases/documents/form/edit.component.html
new file mode 100644
index 0000000..bcd5746
--- /dev/null
+++ b/src/app/customers/cases/documents/form/edit.component.html
@@ -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.
+-->
+
+<fims-layout-card-over title="{{'Edit document' | translate }}" *ngIf="currentSelection$ | async as currentSelection">
+  <fims-case-document-form #form
+                           [document]="document$ | async"
+                           (onSave)="onSave(currentSelection, $event)"
+                           (onCancel)="onCancel()"
+                           [editMode]="true">
+  </fims-case-document-form>
+</fims-layout-card-over>
diff --git a/src/app/customers/cases/documents/form/edit.component.ts b/src/app/customers/cases/documents/form/edit.component.ts
new file mode 100644
index 0000000..22db4a4
--- /dev/null
+++ b/src/app/customers/cases/documents/form/edit.component.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 {Component, OnDestroy} from '@angular/core';
+import {CustomerDocument} from '../../../../services/customer/domain/customer-document.model';
+import {Observable} from 'rxjs/Observable';
+import {ActivatedRoute, Router} from '@angular/router';
+import {CaseSelection} from '../../store/model/case-selection.model';
+import {CasesStore} from '../../store/index';
+import * as fromCases from '../../store';
+import {Subscription} from 'rxjs/Subscription';
+import {UpdateDocumentAction} from '../../store/documents/document.actions';
+
+@Component({
+  templateUrl: './edit.component.html'
+})
+export class CaseDocumentEditComponent {
+
+  currentSelection$: Observable<CaseSelection>;
+
+  document$: Observable<CustomerDocument>;
+
+  constructor(private casesStore: CasesStore, private router: Router, private route: ActivatedRoute) {
+    this.currentSelection$ = casesStore.select(fromCases.getCaseSelection);
+    this.document$ = casesStore.select(fromCases.getSelectedCaseDocument);
+  }
+
+  onSave(selection: CaseSelection, document: CustomerDocument) {
+    const action = new UpdateDocumentAction({
+      customerId: selection.customerId,
+      productId: selection.productId,
+      caseId: selection.caseId,
+      document,
+      activatedRoute: this.route
+    });
+
+    this.casesStore.dispatch(action)
+  }
+
+  onCancel() {
+    this.router.navigate(['../'], { relativeTo: this.route });
+  }
+}
diff --git a/src/app/customers/cases/documents/form/form.component.html b/src/app/customers/cases/documents/form/form.component.html
new file mode 100644
index 0000000..944b9a0
--- /dev/null
+++ b/src/app/customers/cases/documents/form/form.component.html
@@ -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.
+-->
+
+<td-steps mode="'vertical'">
+  <td-step #detailsStep label="{{'Details' | translate}}" [active]="true">
+    <div layout="row" [formGroup]="form">
+      <mat-form-field layout-margin flex>
+        <textarea matInput placeholder="{{'Description(Optional)' | translate}}" formControlName="description"></textarea>
+        <mat-error *ngIf="form.get('description').hasError('maxlength')">
+          {{ 'Only characters allowed.' | translate:{ value: form.get('description').getError('maxlength')['requiredLength']} }}
+        </mat-error>
+      </mat-form-field>
+    </div>
+  </td-step>
+  <td-step label="{{'Final step' | translate}}" [state]="'complete'">
+    <ng-template td-step-summary>
+      <fims-form-final-action
+        [resourceName]="'DOCUMENT'"
+        [editMode]="editMode"
+        [disabled]="form.invalid"
+        (onCancel)="cancel()"
+        (onSave)="save()">
+      </fims-form-final-action>
+    </ng-template>
+  </td-step>
+</td-steps>
diff --git a/src/app/customers/cases/documents/form/form.component.ts b/src/app/customers/cases/documents/form/form.component.ts
new file mode 100644
index 0000000..7730f1b
--- /dev/null
+++ b/src/app/customers/cases/documents/form/form.component.ts
@@ -0,0 +1,66 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, OnChanges, OnInit, Output, SimpleChanges, ViewChild} from '@angular/core';
+import {CustomerDocument} from '../../../../services/customer/domain/customer-document.model';
+import {FormBuilder, FormGroup, Validators} from '@angular/forms';
+import {TdStepComponent} from '@covalent/core';
+
+@Component({
+  selector: 'fims-case-document-form',
+  templateUrl: './form.component.html'
+})
+export class CaseDocumentFormComponent implements OnChanges {
+
+  form: FormGroup;
+
+  @Input() document: CustomerDocument;
+
+  @Input() editMode: boolean;
+
+  @Output('onSave') onSave = new EventEmitter<CustomerDocument>();
+
+  @Output('onCancel') onCancel = new EventEmitter<void>();
+
+  constructor(private formBuilder: FormBuilder) {
+    this.form = formBuilder.group({
+      description: ['', Validators.maxLength(4096)]
+    })
+  }
+
+  ngOnChanges(changes: SimpleChanges): void {
+    if (changes.document) {
+      this.form.reset({
+        description: this.document.description
+      })
+    }
+  }
+
+  save(): void {
+    const document: CustomerDocument = Object.assign({}, this.document, {
+      description: this.form.get('description').value
+    });
+
+    this.onSave.emit(document);
+  }
+
+  cancel(): void {
+    this.onCancel.emit();
+  }
+
+}
diff --git a/src/app/customers/cases/documents/form/upload/create.form.component.html b/src/app/customers/cases/documents/form/upload/create.form.component.html
new file mode 100644
index 0000000..c6f7542
--- /dev/null
+++ b/src/app/customers/cases/documents/form/upload/create.form.component.html
@@ -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.
+-->
+
+<fims-layout-card-over title="{{'Upload new page' | translate}}">
+  <fims-upload-page-form #form
+                         (onSave)="onSave($event)"
+                         (onCancel)="onCancel()">
+  </fims-upload-page-form>
+</fims-layout-card-over>
diff --git a/src/app/customers/cases/documents/form/upload/create.form.component.ts b/src/app/customers/cases/documents/form/upload/create.form.component.ts
new file mode 100644
index 0000000..3393bb3
--- /dev/null
+++ b/src/app/customers/cases/documents/form/upload/create.form.component.ts
@@ -0,0 +1,75 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, OnDestroy} from '@angular/core';
+import {Observable} from 'rxjs/Observable';
+import {UploadPageFormData} from './upload-page.form.component';
+import {UploadPageAction} from '../../../store/documents/document.actions';
+import {CasesStore} from '../../../store/index';
+import {ActivatedRoute, Router} from '@angular/router';
+import {Subscription} from 'rxjs/Subscription';
+import {CaseSelection} from '../../../store/model/case-selection.model';
+import * as fromCases from '../../../store';
+import {CustomerDocument} from '../../../../../services/customer/domain/customer-document.model';
+
+@Component({
+  templateUrl: './create.form.component.html'
+})
+export class CreateDocumentPageComponent implements OnDestroy {
+
+  private actionsSubscription: Subscription;
+
+  private currentSelection$: Observable<CaseSelection>;
+
+  private document$: Observable<CustomerDocument>;
+
+  constructor(private router: Router, private route: ActivatedRoute, private casesStore: CasesStore) {
+    this.currentSelection$ = casesStore.select(fromCases.getCaseSelection);
+    this.document$ = casesStore.select(fromCases.getSelectedCaseDocument);
+  }
+
+  ngOnDestroy(): void {
+    if (this.actionsSubscription) {
+      this.actionsSubscription.unsubscribe();
+    }
+  }
+
+  onSave(formData: UploadPageFormData): void {
+    this.actionsSubscription = Observable.combineLatest(
+      this.currentSelection$,
+      this.document$,
+      (selection, document) => ({
+        selection,
+        document
+      })
+    ).map(result => new UploadPageAction({
+        customerId: result.selection.customerId,
+        documentId: result.document.identifier,
+        page: formData.file,
+        pageNumber: formData.pageNumber,
+        activatedRoute: this.route
+      })
+    ).subscribe(this.casesStore);
+  }
+
+  onCancel(): void {
+    this.router.navigate(['../'], { relativeTo: this.route });
+  }
+
+}
diff --git a/src/app/customers/cases/documents/form/upload/upload-page.form.component.html b/src/app/customers/cases/documents/form/upload/upload-page.form.component.html
new file mode 100644
index 0000000..24f1cf0
--- /dev/null
+++ b/src/app/customers/cases/documents/form/upload/upload-page.form.component.html
@@ -0,0 +1,66 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<td-steps mode="'vertical'">
+  <td-step #detailsStep label="{{'Upload new page' | translate}}"
+           [state]="form.valid ? 'complete' : form.pristine ? 'none' : 'required'" [active]="true">
+    <form [formGroup]="form" layout="column">
+      <mat-form-field flex layout-margin>
+        <input matInput type="number" placeholder="{{'Page number' | translate}}" formControlName="pageNumber"/>
+        <mat-error *ngIf="form.get('pageNumber').hasError('required')" translate>
+          Required
+        </mat-error>
+        <mat-error *ngIf="form.get('pageNumber').hasError('minValue')">
+          {{ 'Value must be greater than or equal to' | translate:{ value: form.get('pageNumber').getError('minValue').value} }}
+        </mat-error>
+      </mat-form-field>
+      <div layout="row">
+        <mat-form-field tdFileDrop
+                        [disabled]="true"
+                        flex layout-margin>
+          <input matInput
+                 placeholder="{{'Selected file' | translate }}"
+                 [value]="form.get('file').value ? form.get('file').value.name : ''"
+                 [disabled]="true"
+                 readonly/>
+          <mat-hint class="tc-red-500" *ngIf="form.get('file').hasError('maxFileSize')">
+            {{ 'Max file size' | translate:{ value: form.get('file').getError('maxFileSize')['value']} }}
+          </mat-hint>
+        </mat-form-field>
+        <button mat-icon-button *ngIf="form.get('file').value" (click)="fileInput.clear()"
+                (keyup.enter)="fileInput.clear()">
+          <mat-icon>cancel</mat-icon>
+        </button>
+        <td-file-input class="push-left-sm push-right-sm" #fileInput formControlName="file" accept=".jpg,.png">
+          <mat-icon>folder</mat-icon>
+          <span class="text-upper" translate>Browse...</span>
+        </td-file-input>
+      </div>
+    </form>
+  </td-step>
+  <td-step label="{{'Final step' | translate}}" [state]="'complete'">
+    <ng-template td-step-summary>
+      <fims-form-final-action
+        [resourceName]="'PAGE'"
+        [editMode]="false"
+        [disabled]="!form.valid"
+        (onCancel)="cancel()"
+        (onSave)="save()">
+      </fims-form-final-action>
+    </ng-template>
+  </td-step>
+</td-steps>
diff --git a/src/app/customers/cases/documents/form/upload/upload-page.form.component.ts b/src/app/customers/cases/documents/form/upload/upload-page.form.component.ts
new file mode 100644
index 0000000..36e4a40
--- /dev/null
+++ b/src/app/customers/cases/documents/form/upload/upload-page.form.component.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 {Component, EventEmitter, Input, OnInit, Output} from '@angular/core';
+import {FormBuilder, FormGroup, Validators} from '@angular/forms';
+import {FimsValidators} from '../../../../../common/validator/validators';
+
+export interface UploadPageFormData {
+  pageNumber: number;
+  file: File;
+}
+
+@Component({
+  selector: 'fims-upload-page-form',
+  templateUrl: './upload-page.form.component.html'
+})
+export class UploadPageFormComponent implements OnInit {
+
+  form: FormGroup;
+
+  @Output() onSave = new EventEmitter<UploadPageFormData>();
+
+  @Output() onCancel = new EventEmitter<void>();
+
+  constructor(private formBuilder: FormBuilder) {}
+
+  ngOnInit() {
+    this.form = this.formBuilder.group({
+      pageNumber: ['', [Validators.required, FimsValidators.minValue(0)]],
+      file: ['', [Validators.required, FimsValidators.maxFileSize(512)]]
+    });
+  }
+
+  save(): void {
+    this.onSave.emit(this.form.getRawValue());
+  }
+
+  cancel(): void {
+    this.onCancel.emit();
+  }
+}
diff --git a/src/app/customers/cases/form/co-signer/co-signer.component.html b/src/app/customers/cases/form/co-signer/co-signer.component.html
new file mode 100644
index 0000000..bbae2cb
--- /dev/null
+++ b/src/app/customers/cases/form/co-signer/co-signer.component.html
@@ -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.
+-->
+
+<form [formGroup]="form">
+  <fims-customer-select title="{{'Member' | translate}}" formControlName="customerIdentifier">
+    <ng-container *ngIf="form.get('customerIdentifier').hasError('invalidCustomer')" translate>
+      Invalid member
+    </ng-container>
+  </fims-customer-select>
+  <fims-case-debt-to-income-form #debtToIncomeForm [formData]="debtToIncomeFormData"></fims-case-debt-to-income-form>
+</form>
diff --git a/src/app/customers/cases/form/co-signer/co-signer.component.ts b/src/app/customers/cases/form/co-signer/co-signer.component.ts
new file mode 100644
index 0000000..700a91f
--- /dev/null
+++ b/src/app/customers/cases/form/co-signer/co-signer.component.ts
@@ -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.
+ */
+import {Component, Input, OnInit, ViewChild} from '@angular/core';
+import {FormComponent} from '../../../../common/forms/form.component';
+import {CreditWorthinessFactor} from '../../../../services/portfolio/domain/individuallending/credit-worthiness-factor.model';
+import {CaseDebtToIncomeFormComponent, DebtToIncomeFormData} from '../debt-to-income/debt-to-income.component';
+import {FormBuilder} from '@angular/forms';
+import {customerExists} from '../../../../common/validator/customer-exists.validator';
+import {CustomerService} from '../../../../services/customer/customer.service';
+
+export interface CoSignerFormData {
+  customerId: string;
+  incomeSources: CreditWorthinessFactor[];
+  debts: CreditWorthinessFactor[];
+}
+
+@Component({
+  selector: 'fims-case-co-signer-form',
+  templateUrl: './co-signer.component.html'
+})
+export class CaseCoSignerFormComponent extends FormComponent<CoSignerFormData> implements OnInit {
+
+  private _formData: CoSignerFormData;
+
+  @Input('formData') set formData(formData: CoSignerFormData) {
+    this._formData = formData;
+  }
+
+  @ViewChild('debtToIncomeForm') debtToIncomeForm: CaseDebtToIncomeFormComponent;
+  debtToIncomeFormData: DebtToIncomeFormData;
+
+  constructor(private formBuilder: FormBuilder, private customerService: CustomerService) {
+    super();
+  }
+
+  ngOnInit(): void {
+    this.form = this.formBuilder.group({
+      customerIdentifier: [this._formData.customerId, [], customerExists(this.customerService)]
+    });
+
+    this.debtToIncomeFormData = {
+      incomeSources: this._formData.incomeSources,
+      debts: this._formData.debts
+    };
+  }
+
+  get formData(): CoSignerFormData {
+    return {
+      customerId: this.form.get('customerIdentifier').value,
+      incomeSources: this.debtToIncomeForm.formData.incomeSources,
+      debts: this.debtToIncomeForm.formData.debts
+    };
+  }
+
+  get valid(): boolean {
+    return this.form.valid && this.debtToIncomeForm.valid;
+  }
+}
diff --git a/src/app/customers/cases/form/components/credit-factor.component.html b/src/app/customers/cases/form/components/credit-factor.component.html
new file mode 100644
index 0000000..dd5e0b0
--- /dev/null
+++ b/src/app/customers/cases/form/components/credit-factor.component.html
@@ -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.
+-->
+
+<form [formGroup]="form">
+  <div layout-gt-xs="column" layout-margin formArrayName="factors">
+    <mat-card *ngFor="let factor of factorControls; let i=index" [formGroupName]="i">
+      <mat-card-content>
+        <div layout="row">
+          <fims-text-input [form]="factor" controlName="description" placeholder="{{'Description' | translate}}"></fims-text-input>
+          <fims-text-input type="number" [form]="factor" controlName="amount" placeholder="{{'Amount' | translate}}"></fims-text-input>
+          <button mat-button (click)="removeFactor(i)">{{'REMOVE ' + factorName | translate}}</button>
+        </div>
+      </mat-card-content>
+    </mat-card>
+    <div layout="row">
+      <button flex mat-button mat-raised-button (click)="addFactor()">{{'ADD ' + factorName | translate}}</button>
+    </div>
+  </div>
+</form>
diff --git a/src/app/customers/cases/form/components/credit-factor.component.ts b/src/app/customers/cases/form/components/credit-factor.component.ts
new file mode 100644
index 0000000..22a7f6d
--- /dev/null
+++ b/src/app/customers/cases/form/components/credit-factor.component.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 {Component, Input, OnInit} from '@angular/core';
+import {CreditWorthinessFactor} from '../../../../services/portfolio/domain/individuallending/credit-worthiness-factor.model';
+import {AbstractControl, FormArray, FormBuilder, FormGroup, Validators} from '@angular/forms';
+import {FimsValidators} from '../../../../common/validator/validators';
+import {FormComponent} from '../../../../common/forms/form.component';
+
+
+@Component({
+  selector: 'fims-case-credit-factor-form',
+  templateUrl: './credit-factor.component.html'
+})
+export class CaseCreditFactorFormComponent extends FormComponent<CreditWorthinessFactor[]> implements OnInit {
+
+  @Input() factors: CreditWorthinessFactor[];
+
+  @Input() factorName: string;
+
+  form: FormGroup;
+
+  constructor(private formBuilder: FormBuilder) {
+    super();
+  }
+
+  ngOnInit(): void {
+    this.form = this.formBuilder.group({
+      factors: this.initFactors(this.factors)
+    });
+  }
+
+  get formData(): CreditWorthinessFactor[] {
+    return this.formFactors.value;
+  }
+
+  private initFactors(factors: CreditWorthinessFactor[]): FormArray {
+    const formControls: FormGroup[] = [];
+    factors.forEach(factor => formControls.push(this.initFactor(factor)));
+    return this.formBuilder.array(formControls);
+  }
+
+  private initFactor(factor?: CreditWorthinessFactor): FormGroup {
+    return this.formBuilder.group({
+      description: [factor ? factor.description : ''],
+      amount: [factor ? factor.amount : 0, [ Validators.required, FimsValidators.minValue(0)] ]
+    });
+  }
+
+  get formFactors(): FormArray {
+    return this.form.get('factors') as FormArray;
+  }
+
+  addFactor(): void {
+    this.formFactors.push(this.initFactor());
+  }
+
+  removeFactor(index: number): void {
+    this.formFactors.removeAt(index);
+  }
+
+  get factorControls(): AbstractControl[] {
+    return this.formFactors.controls;
+  }
+}
diff --git a/src/app/customers/cases/form/create.component.html b/src/app/customers/cases/form/create.component.html
new file mode 100644
index 0000000..63a9ebd
--- /dev/null
+++ b/src/app/customers/cases/form/create.component.html
@@ -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.
+-->
+
+<fims-layout-card-over title="{{'Create new loan for member' | translate:{value: fullName} }}">
+  <fims-case-form-component #form
+                                 (onSave)="onSave($event)"
+                                 (onCancel)="onCancel()"
+                                 [products]="products$ | async"
+                                 [productInstances]="productsInstances$ | async"
+                                 [case]="caseInstance"
+                                 [customerId]="(customer$ | async).identifier"
+                                 [editMode]="false">
+  </fims-case-form-component>
+</fims-layout-card-over>
diff --git a/src/app/customers/cases/form/create.component.ts b/src/app/customers/cases/form/create.component.ts
new file mode 100644
index 0000000..bacacef
--- /dev/null
+++ b/src/app/customers/cases/form/create.component.ts
@@ -0,0 +1,115 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, OnDestroy, OnInit, ViewChild} from '@angular/core';
+import {ActivatedRoute, Router} from '@angular/router';
+import {CaseFormComponent} from './form.component';
+import * as fromCases from '../store/index';
+import {CasesStore} from '../store/index';
+import * as fromCustomers from '../../store/index';
+import {Subscription} from 'rxjs/Subscription';
+import {CREATE, RESET_FORM} from '../store/case.actions';
+import {Error} from '../../../services/domain/error.model';
+import {Product} from '../../../services/portfolio/domain/product.model';
+import {PortfolioService} from '../../../services/portfolio/portfolio.service';
+import {Observable} from 'rxjs/Observable';
+import {DepositAccountService} from '../../../services/depositAccount/deposit-account.service';
+import {ProductInstance} from '../../../services/depositAccount/domain/instance/product-instance.model';
+import {FimsCase} from '../../../services/portfolio/domain/fims-case.model';
+import {Customer} from '../../../services/customer/domain/customer.model';
+
+@Component({
+  templateUrl: './create.component.html'
+})
+export class CaseCreateComponent implements OnInit, OnDestroy {
+
+  private formStateSubscription: Subscription;
+
+  fullName: string;
+
+  @ViewChild('form') formComponent: CaseFormComponent;
+
+  products$: Observable<Product[]>;
+
+  productsInstances$: Observable<ProductInstance[]>;
+
+  customer$: Observable<Customer>;
+
+  caseInstance: FimsCase = {
+    currentState: 'CREATED',
+    identifier: '',
+    productIdentifier: '',
+    parameters: {
+      customerIdentifier: '',
+      maximumBalance: 0,
+      paymentCycle: {
+        alignmentDay: null,
+        alignmentMonth: null,
+        alignmentWeek: null,
+        period: 1,
+        temporalUnit: 'MONTHS',
+      },
+      termRange: {
+        temporalUnit: 'MONTHS',
+        maximum: 1
+      },
+      creditWorthinessSnapshots: []
+    },
+    interest: 0,
+    depositAccountIdentifier: ''
+  };
+
+  constructor(private router: Router, private route: ActivatedRoute, private casesStore: CasesStore,
+              private portfolioService: PortfolioService, private depositService: DepositAccountService) {}
+
+  ngOnInit(): void {
+    this.customer$ = this.casesStore.select(fromCustomers.getSelectedCustomer)
+      .filter(customer => !!customer)
+      .do(customer => this.fullName = `${customer.givenName} ${customer.surname}`);
+
+    this.formStateSubscription = this.casesStore.select(fromCases.getCaseFormError)
+      .filter((error: Error) => !!error)
+      .subscribe((error: Error) => this.formComponent.showIdentifierValidationError());
+
+    this.products$ = this.portfolioService.findAllProducts(false)
+      .map(productPage => productPage.elements);
+
+    this.productsInstances$ = this.customer$
+      .switchMap(customer => this.depositService.fetchProductInstances(customer.identifier))
+      .map((instances: ProductInstance[]) => instances.filter(instance => instance.state === 'ACTIVE'));
+  }
+
+  ngOnDestroy(): void {
+    this.formStateSubscription.unsubscribe();
+
+    this.casesStore.dispatch({ type: RESET_FORM });
+  }
+
+  onSave(caseInstance: FimsCase): void {
+    this.casesStore.dispatch({ type: CREATE, payload: {
+      productId: caseInstance.productIdentifier,
+      caseInstance,
+      activatedRoute: this.route
+    }});
+  }
+
+  onCancel(): void {
+    this.router.navigate(['../'], { relativeTo: this.route });
+  }
+
+}
diff --git a/src/app/customers/cases/form/debt-to-income/debt-to-income.component.html b/src/app/customers/cases/form/debt-to-income/debt-to-income.component.html
new file mode 100644
index 0000000..652dd93
--- /dev/null
+++ b/src/app/customers/cases/form/debt-to-income/debt-to-income.component.html
@@ -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.
+-->
+
+<h4>Debts(Total: {{debtTotal | number:numberFormat}})</h4>
+<fims-case-credit-factor-form #debtForm [factorName]="'DEBT'" [factors]="debts"></fims-case-credit-factor-form>
+<h4>Incomes(Total: {{incomeTotal | number:numberFormat}})</h4>
+<fims-case-credit-factor-form #incomeForm [factorName]="'INCOME'" [factors]="incomeSources"></fims-case-credit-factor-form>
+<h3>Ratio</h3>
+{{ratio | number:numberFormat}}
diff --git a/src/app/customers/cases/form/debt-to-income/debt-to-income.component.ts b/src/app/customers/cases/form/debt-to-income/debt-to-income.component.ts
new file mode 100644
index 0000000..fa98efd
--- /dev/null
+++ b/src/app/customers/cases/form/debt-to-income/debt-to-income.component.ts
@@ -0,0 +1,88 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, Input, ViewChild} from '@angular/core';
+import {CaseCreditFactorFormComponent} from '../components/credit-factor.component';
+import {CreditWorthinessFactor} from '../../../../services/portfolio/domain/individuallending/credit-worthiness-factor.model';
+
+export interface DebtToIncomeFormData {
+  incomeSources: CreditWorthinessFactor[];
+  debts: CreditWorthinessFactor[];
+}
+
+@Component({
+  selector: 'fims-case-debt-to-income-form',
+  templateUrl: './debt-to-income.component.html'
+})
+export class CaseDebtToIncomeFormComponent {
+
+  numberFormat = '2.2-2';
+
+  @ViewChild('incomeForm') incomeFactorComponent: CaseCreditFactorFormComponent;
+  incomeSources: CreditWorthinessFactor[] = [];
+
+  @ViewChild('debtForm') debtsFactorComponent: CaseCreditFactorFormComponent;
+  debts: CreditWorthinessFactor[] = [];
+
+  @Input('formData') set formData(formData: DebtToIncomeFormData) {
+    this.incomeSources = formData.incomeSources;
+    this.debts = formData.debts;
+  }
+
+  constructor() {}
+
+  get formData(): DebtToIncomeFormData {
+    return {
+      incomeSources: this.incomeFactorComponent.formData,
+      debts: this.debtsFactorComponent.formData
+    };
+  }
+
+  get valid(): boolean {
+    return this.incomeFactorComponent.valid && this.debtsFactorComponent.valid;
+  }
+
+  get pristine(): boolean {
+    return this.incomeFactorComponent.pristine || this.debtsFactorComponent.pristine;
+  }
+
+  get ratio(): number {
+    return this.divideIfNotZero(this.debtTotal, this.incomeTotal);
+  }
+
+  divideIfNotZero(numerator, denominator): number {
+    if (denominator === 0 || isNaN(denominator)) {
+      return null;
+    } else {
+      return numerator / denominator;
+    }
+  }
+
+  get incomeTotal(): number {
+    return this.sum(this.formData.incomeSources);
+  }
+
+  get debtTotal(): number {
+    return this.sum(this.formData.debts);
+  }
+
+  private sum(factors: CreditWorthinessFactor[]): number {
+    return factors.reduce((acc, val) => acc + parseFloat(val.amount), 0);
+  }
+
+}
diff --git a/src/app/customers/cases/form/detail/detail.component.html b/src/app/customers/cases/form/detail/detail.component.html
new file mode 100644
index 0000000..d8231cc
--- /dev/null
+++ b/src/app/customers/cases/form/detail/detail.component.html
@@ -0,0 +1,127 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<form [formGroup]="form" layout="column">
+  <mat-form-field layout-margin>
+    <mat-select placeholder="{{'Choose product' | translate}}" formControlName="productIdentifier">
+      <mat-option *ngFor="let product of products" [value]="product.identifier" matTooltip="{{product.description}}">{{ product.name }}</mat-option>
+    </mat-select>
+  </mat-form-field>
+  <fims-id-input placeholder="{{'Short name' | translate}}" [form]="form" controlName="identifier" [readonly]="editMode"></fims-id-input>
+  <fims-number-input
+    [form]="form"
+    controlName="principalAmount"
+    placeholder="{{'Principal amount' | translate}}"
+    [decimalLimit]="product?.minorCurrencyUnitDigits"
+    hint="{{product?.balanceRange.minimum | number:numberFormat}} - {{product?.balanceRange.maximum | number:numberFormat}}">
+  </fims-number-input>
+  <fims-number-input
+    [form]="form"
+    controlName="interest"
+    placeholder="{{'Interest rate' | translate}}"
+    [decimalLimit]="product?.minorCurrencyUnitDigits"
+    hint="{{product?.interestRange.minimum | number:numberFormat}} - {{product?.interestRange.maximum | number:numberFormat}}">
+  </fims-number-input>
+  <div layout="column" layout-margin>
+    <div layout="row" layout-align="start center">
+      <fims-text-input type="number" [form]="form" controlName="term" placeholder="{{'Term' | translate}}"></fims-text-input>
+      <mat-form-field>
+        <mat-select formControlName="termTemporalUnit">
+          <mat-option *ngFor="let basis of temporalOptions" [value]="basis.type">
+            {{basis.label | translate}}
+          </mat-option>
+        </mat-select>
+      </mat-form-field>
+    </div>
+    <p *ngIf="form.hasError('maxTerm')" class="tc-red-600 text-sm" layout-margin translate>
+      Invalid term. Maximum allowed are {{form.getError('maxTerm').maxWeeks}} week(s), {{form.getError('maxTerm').maxMonths}} month(s) or {{form.getError('maxTerm').maxYears}} year(s).
+    </p>
+  </div>
+  <div layout="column" layout-margin>
+    <div layout="row" layout-align="start center">
+      <fims-text-input type="number" [form]="form" controlName="paymentPeriod" placeholder="{{'Repay every' | translate}}"></fims-text-input>
+      <mat-form-field>
+        <mat-select formControlName="paymentTemporalUnit">
+          <mat-option *ngFor="let alignment of temporalOptions" [value]="alignment.type">
+            {{alignment.label | translate}}
+          </mat-option>
+        </mat-select>
+      </mat-form-field>
+    </div>
+    <p *ngIf="form.hasError('maxPayment')" class="tc-red-600 text-sm" layout-margin translate>
+      Invalid payment period. Maximum allowed are {{form.getError('maxPayment').maxWeeks}} week(s), {{form.getError('maxPayment').maxMonths}} month(s) or {{form.getError('maxPayment').maxYears}} year(s).
+    </p>
+  </div>
+  <!-- Days in weeks -->
+  <div layout="row" *ngIf="displayDaysInWeek">
+    <mat-form-field layout-margin>
+      <mat-select formControlName="dayInWeek" placeholder="{{'on' | translate}}">
+        <mat-option *ngFor="let day of weekDays" [value]="day.type">
+          {{day.label | translate}}
+        </mat-option>
+      </mat-select>
+    </mat-form-field>
+  </div>
+  <!-- Month setting -->
+  <div layout="column" *ngIf="displayMonthSetting">
+    <mat-radio-group formControlName="monthSetting">
+      <div layout="row" layout-align="start center">
+        <mat-radio-button layout-margin value="DAY"></mat-radio-button>
+        <mat-form-field layout-margin>
+          <mat-select formControlName="monthSettingDay" placeholder="{{'on the' | translate}}">
+            <mat-option *ngFor="let day of monthDays" [value]="day.type">
+              {{day.label}}
+            </mat-option>
+          </mat-select>
+        </mat-form-field>
+        <p translate>Day</p>
+      </div>
+      <div layout="row" layout-align="start center">
+        <mat-radio-button layout-margin value="WEEK_AND_DAY"></mat-radio-button>
+        <mat-form-field layout-margin>
+          <mat-select formControlName="monthSettingWeek" placeholder="{{'on the' | translate}}">
+            <mat-option *ngFor="let alignment of weekAlignments" [value]="alignment.type">
+              {{alignment.label | translate}}
+            </mat-option>
+          </mat-select>
+        </mat-form-field>
+        <mat-form-field layout-margin>
+          <mat-select formControlName="monthSettingDayInWeek">
+            <mat-option *ngFor="let weekDay of weekDays" [value]="weekDay.type">
+              {{weekDay.label | translate}}
+            </mat-option>
+          </mat-select>
+        </mat-form-field>
+      </div>
+    </mat-radio-group>
+  </div>
+  <!-- Months -->
+  <mat-form-field layout-margin *ngIf="displayMonths">
+    <mat-select formControlName="month" placeholder="{{'in' | translate}}">
+      <mat-option *ngFor="let month of months" [value]="month.type">
+        {{month.label | translate}}
+      </mat-option>
+    </mat-select>
+  </mat-form-field>
+  <mat-form-field layout-margin>
+    <mat-select formControlName="depositAccountIdentifier" placeholder="{{ 'Deposit account used for fees and disbursal' | translate }}">
+      <mat-option *ngFor="let instance of productInstances" [value]="instance.accountIdentifier">
+        {{instance.accountIdentifier}}({{instance.productIdentifier}})
+      </mat-option>
+    </mat-select>
+  </mat-form-field>
+</form>
diff --git a/src/app/customers/cases/form/detail/detail.component.spec.ts b/src/app/customers/cases/form/detail/detail.component.spec.ts
new file mode 100644
index 0000000..17773ca
--- /dev/null
+++ b/src/app/customers/cases/form/detail/detail.component.spec.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 {ComponentFixture, TestBed} from '@angular/core/testing';
+import {TranslateModule} from '@ngx-translate/core';
+import {Component, ViewChild} from '@angular/core';
+import {ReactiveFormsModule} from '@angular/forms';
+import {NoopAnimationsModule} from '@angular/platform-browser/animations';
+import {CaseDetailFormComponent, DetailFormData} from './detail.component';
+import {FimsSharedModule} from '../../../../common/common.module';
+import {Product} from '../../../../services/portfolio/domain/product.model';
+import {ProductInstance} from '../../../../services/depositAccount/domain/instance/product-instance.model';
+import {MatInputModule, MatOptionModule, MatRadioModule, MatSelectModule, MatTooltipModule} from '@angular/material';
+
+describe('Test case detail form component', () => {
+
+  const products: Product[] = [
+    {
+      identifier: 'productIdentifier',
+      name: 'product',
+      termRange: {
+        temporalUnit: 'WEEKS',
+        maximum: 2
+      },
+      balanceRange: {
+        minimum: 1,
+        maximum: 2
+      },
+      interestRange: {
+        minimum: 1,
+        maximum: 2
+      },
+      interestBasis: 'CURRENT_BALANCE',
+      patternPackage: '',
+      description: '',
+      accountAssignments: [],
+      parameters: null,
+      currencyCode: '',
+      minorCurrencyUnitDigits: 2
+    }
+  ];
+
+  const productInstances: ProductInstance[] = [
+    {
+      customerIdentifier: 'customerIdentifier',
+      accountIdentifier: 'depositAccountIdentifier',
+      productIdentifier: 'productIdentifier'
+    }
+  ];
+
+  const validFormData: DetailFormData = {
+    identifier: 'identifier',
+    productIdentifier: 'productIdentifier',
+    interest: '1.00',
+    principalAmount: '1.00',
+    term: 1,
+    termTemporalUnit: 'WEEKS',
+    paymentTemporalUnit: 'WEEKS',
+    paymentPeriod: 1,
+    paymentAlignmentDay: 1,
+    paymentAlignmentWeek: undefined,
+    paymentAlignmentMonth: undefined,
+    depositAccountIdentifier: 'depositAccountIdentifier'
+  };
+
+  let component: TestComponent;
+  let fixture: ComponentFixture<TestComponent>;
+
+  beforeEach(() => {
+    TestBed.configureTestingModule({
+      imports: [
+        TranslateModule.forRoot(),
+        ReactiveFormsModule,
+        MatRadioModule,
+        MatInputModule,
+        MatSelectModule,
+        MatOptionModule,
+        MatTooltipModule,
+        FimsSharedModule,
+        NoopAnimationsModule
+      ],
+      declarations: [
+        TestComponent,
+        CaseDetailFormComponent
+      ]
+    });
+
+    fixture = TestBed.createComponent(TestComponent);
+
+    component = fixture.componentInstance;
+
+    component.products = products;
+    component.productInstances = productInstances;
+  });
+
+  it('should return same form data', () => {
+    component.formData = validFormData;
+    fixture.detectChanges();
+    expect(component.form.formData).toEqual(component.formData);
+  });
+
+  it('should mark form as valid', () => {
+    component.formData = validFormData;
+    fixture.detectChanges();
+    expect(component.form.valid).toBeTruthy();
+  });
+
+  it('should mark form as invalid when principal amount is invalid', () => {
+    component.formData = Object.assign({}, validFormData, {
+      principalAmount: '3'
+    });
+    fixture.detectChanges();
+    expect(component.form.valid).toBeFalsy();
+  });
+
+  it('should mark form as invalid when interest is invalid', () => {
+    component.formData = Object.assign({}, validFormData, {
+      interest: '3'
+    });
+    fixture.detectChanges();
+    expect(component.form.valid).toBeFalsy();
+  });
+
+  it('should mark form as invalid when term range is invalid', () => {
+    component.formData = Object.assign({}, validFormData, {
+      term: 3
+    });
+    fixture.detectChanges();
+    expect(component.form.valid).toBeFalsy();
+  });
+
+  describe('weeks selection', () => {
+    it('should mark form as invalid when no week day is given', () => {
+      component.formData = Object.assign({}, validFormData, {
+        paymentTemporalUnit: 'WEEKS',
+        paymentAlignmentDay: undefined
+      });
+      fixture.detectChanges();
+      expect(component.form.valid).toBeFalsy();
+    });
+  });
+
+  describe('months selection', () => {
+    it('should mark form as invalid when no week day is given on DAY', () => {
+      component.formData = Object.assign({}, validFormData, {
+        paymentTemporalUnit: 'MONTHS',
+        paymentAlignmentDay: undefined,
+        paymentAlignmentWeek: undefined,
+        paymentAlignmentMonth: undefined
+      });
+      fixture.detectChanges();
+      expect(component.form.valid).toBeFalsy();
+    });
+
+    it('should mark form as invalid when no week day is given on WEEK_AND_DAY', () => {
+      component.formData = Object.assign({}, validFormData, {
+        paymentTemporalUnit: 'MONTHS',
+        paymentAlignmentDay: undefined,
+        paymentAlignmentWeek: 1,
+        paymentAlignmentMonth: undefined
+      });
+      fixture.detectChanges();
+      expect(component.form.valid).toBeFalsy();
+    });
+
+  });
+
+  describe('years selection', () => {
+    it('should mark form as invalid when no month is given', () => {
+      component.formData = Object.assign({}, validFormData, {
+        paymentTemporalUnit: 'YEARS',
+        paymentAlignmentDay: 1,
+        paymentAlignmentWeek: 1,
+        paymentAlignmentMonth: undefined,
+      });
+      fixture.detectChanges();
+      expect(component.form.valid).toBeFalsy();
+    });
+  });
+
+});
+
+@Component({
+  template: `
+    <fims-case-detail-form #form [formData]="formData" [products]="products" [productInstances]="productInstances">
+    </fims-case-detail-form>`
+})
+class TestComponent {
+
+  @ViewChild('form') form: CaseDetailFormComponent;
+
+  formData: DetailFormData;
+
+  products: Product[];
+
+  productInstances: ProductInstance[];
+}
diff --git a/src/app/customers/cases/form/detail/detail.component.ts b/src/app/customers/cases/form/detail/detail.component.ts
new file mode 100644
index 0000000..68e8760
--- /dev/null
+++ b/src/app/customers/cases/form/detail/detail.component.ts
@@ -0,0 +1,311 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, Input, OnChanges, OnInit, SimpleChanges} from '@angular/core';
+import {FormComponent} from '../../../../common/forms/form.component';
+import {FormBuilder, FormControl, Validators} from '@angular/forms';
+import {FimsValidators} from '../../../../common/validator/validators';
+import {ChronoUnit} from '../../../../services/portfolio/domain/chrono-unit.model';
+import {alignmentOptions} from '../../../../common/domain/alignment.model';
+import {weekDayOptions} from '../../../../common/domain/week-days.model';
+import {monthOptions} from '../../../../common/domain/months.model';
+import {temporalOptionList} from '../../../../common/domain/temporal.domain';
+import {Product} from '../../../../services/portfolio/domain/product.model';
+import {ProductInstance} from '../../../../services/depositAccount/domain/instance/product-instance.model';
+import {maxPayment, maxTerm} from './validator/max-term.validators';
+
+export interface DetailFormData {
+  identifier: string;
+  productIdentifier: string;
+  interest: string;
+  principalAmount: string;
+  term: number;
+  termTemporalUnit: ChronoUnit;
+  paymentTemporalUnit: ChronoUnit;
+  paymentPeriod: number;
+  paymentAlignmentDay: number;
+  paymentAlignmentWeek: number;
+  paymentAlignmentMonth: number;
+  depositAccountIdentifier: string;
+}
+
+type MonthSetting = 'DAY' | 'WEEK_AND_DAY';
+
+@Component({
+  selector: 'fims-case-detail-form',
+  templateUrl: './detail.component.html'
+})
+export class CaseDetailFormComponent extends FormComponent<DetailFormData> implements OnInit, OnChanges {
+
+  private _formData: DetailFormData;
+
+  numberFormat = '1.2-2';
+
+  @Input() editMode: boolean;
+
+  @Input() products: Product[];
+
+  @Input() productInstances: ProductInstance[];
+
+  @Input() set formData(formData: DetailFormData) {
+    this._formData = formData;
+  };
+
+  product: Product;
+
+  weekAlignments: any[] = alignmentOptions;
+
+  monthDays: any[] = [];
+
+  weekDays: any[] = weekDayOptions;
+
+  months: any[] = monthOptions;
+
+  temporalOptions = temporalOptionList;
+
+  displayDaysInWeek: boolean;
+
+  displayMonthSetting: boolean;
+
+  displayMonths: boolean;
+
+  constructor(private formBuilder: FormBuilder) {
+    super();
+
+    this.form = this.formBuilder.group({
+      identifier: ['', [Validators.required, Validators.minLength(3), Validators.maxLength(32), FimsValidators.urlSafe]],
+      productIdentifier: ['', [Validators.required]],
+      interest: ['', [Validators.required]],
+      principalAmount: [''],
+      term: ['', [ Validators.required, FimsValidators.minValue(1), FimsValidators.maxScale(0)]],
+      termTemporalUnit: ['', Validators.required],
+      paymentTemporalUnit: ['', [ Validators.required ]],
+      paymentPeriod: ['', [ Validators.required, FimsValidators.minValue(1), FimsValidators.maxScale(0)]],
+
+      dayInWeek: ['', Validators.required],
+
+      monthSetting: ['', Validators.required],
+
+      monthSettingDay: ['', Validators.required],
+      monthSettingDayInWeek: ['', Validators.required],
+      monthSettingWeek: ['', Validators.required],
+
+      month: ['', Validators.required],
+
+      depositAccountIdentifier: ['', Validators.required]
+    });
+
+    this.form.get('productIdentifier').valueChanges
+      .filter(() => !!this.products)
+      .map(identifier => this.products.find(product => product.identifier === identifier))
+      .subscribe(product => this.toggleProduct(product));
+
+    this.form.get('paymentTemporalUnit').valueChanges
+      .subscribe(unit => this.toggleTemporalUnit(unit));
+
+    this.form.get('monthSetting').valueChanges
+      .subscribe(setting => this.toggleMonthSetting(setting));
+  }
+
+  ngOnInit(): void {
+    for (let i = 0; i < 30; i++) {
+      this.monthDays.push({
+        type: i, label: `${i + 1}.`
+      });
+    }
+  }
+
+  ngOnChanges(changes: SimpleChanges): void {
+    if (changes.formData) {
+      this.form.reset({
+        identifier: this._formData.identifier,
+        productIdentifier: this._formData.productIdentifier,
+        interest: this._formData.interest,
+        principalAmount: this._formData.principalAmount,
+        term: this._formData.term,
+        termTemporalUnit: this._formData.termTemporalUnit,
+        paymentTemporalUnit: this._formData.paymentTemporalUnit,
+        paymentPeriod: this._formData.paymentPeriod,
+
+        dayInWeek: this._formData.paymentAlignmentDay,
+
+        monthSetting: this._formData.paymentAlignmentWeek != null ? 'WEEK_AND_DAY' : 'DAY',
+
+        monthSettingDay: this._formData.paymentAlignmentDay,
+        monthSettingDayInWeek: this._formData.paymentAlignmentDay,
+        monthSettingWeek: this._formData.paymentAlignmentWeek,
+
+        month: this._formData.paymentAlignmentMonth,
+
+        depositAccountIdentifier: this._formData.depositAccountIdentifier
+      });
+    }
+
+    if (changes.products && changes.products.currentValue && this._formData.productIdentifier) {
+      const foundProduct = this.products.find(product => product.identifier === this._formData.productIdentifier);
+      this.toggleProduct(foundProduct);
+    }
+  }
+
+  private toggleProduct(product: Product) {
+    this.product = product;
+
+    // Override validator with product constraints
+    const principalAmount = this.form.get('principalAmount') as FormControl;
+    this.toggleDisabledState(principalAmount, product.balanceRange.minimum, product.balanceRange.maximum, product.minorCurrencyUnitDigits);
+
+    const interest: FormControl = this.form.get('interest') as FormControl;
+    this.toggleDisabledState(interest, product.interestRange.minimum, product.interestRange.maximum, product.minorCurrencyUnitDigits);
+
+    this.form.setValidators([maxTerm(product.termRange), maxPayment()]);
+  }
+
+  private toggleDisabledState(formControl: FormControl, minimum: number, maximum: number, maxScale: number): void {
+    const hasRange: boolean = minimum !== maximum;
+
+    if (hasRange) {
+      formControl.enable();
+      formControl.setValidators([
+        Validators.required,
+        FimsValidators.minValue(minimum),
+        FimsValidators.maxValue(maximum)
+      ]);
+    } else {
+      formControl.setValue(minimum.toFixed(maxScale));
+      formControl.disable();
+    }
+
+    formControl.updateValueAndValidity();
+  }
+
+  toggleTemporalUnit(chronoUnit: ChronoUnit): void {
+    const dayInWeek = this.form.get('dayInWeek');
+    const month = this.form.get('month');
+
+    if (chronoUnit === 'WEEKS') {
+      this.enableMonthSetting(false);
+      dayInWeek.enable();
+      month.disable();
+
+      this.displayDaysInWeek = true;
+      this.displayMonths = false;
+    } else if (chronoUnit === 'MONTHS') {
+      this.enableMonthSetting(true);
+      dayInWeek.disable();
+      month.disable();
+
+      this.displayDaysInWeek = false;
+      this.displayMonths = false;
+    } else {
+      this.enableMonthSetting(true);
+      dayInWeek.disable();
+      month.enable();
+
+      this.displayDaysInWeek = false;
+      this.displayMonths = true;
+    }
+  }
+
+  enableMonthSetting(enable: boolean): void {
+    const monthSetting = this.form.get('monthSetting');
+    const monthSettingDay = this.form.get('monthSettingDay');
+    const monthSettingDayInWeek = this.form.get('monthSettingDayInWeek');
+    const monthSettingWeek = this.form.get('monthSettingWeek');
+
+    if (enable) {
+      this.displayMonthSetting = true;
+      monthSetting.enable();
+    } else {
+      this.displayMonthSetting = false;
+      monthSetting.disable();
+      monthSettingDay.disable();
+      monthSettingDayInWeek.disable();
+      monthSettingWeek.disable();
+    }
+  }
+
+  toggleMonthSetting(setting: MonthSetting): void {
+    const monthSettingDay = this.form.get('monthSettingDay');
+    const monthSettingDayInWeek = this.form.get('monthSettingDayInWeek');
+    const monthSettingWeek = this.form.get('monthSettingWeek');
+
+    if (setting === 'DAY') {
+      monthSettingDay.enable();
+      monthSettingDayInWeek.disable();
+      monthSettingWeek.disable();
+    } else {
+      monthSettingDay.disable();
+      monthSettingDayInWeek.enable();
+      monthSettingWeek.enable();
+    }
+  }
+
+  get formData(): DetailFormData {
+    const paymentTemporalUnit = this.form.get('paymentTemporalUnit').value;
+    const dayInWeek = this.form.get('dayInWeek').value;
+
+    const monthSetting: MonthSetting = this.form.get('monthSetting').value;
+    const monthSettingDay = this.form.get('monthSettingDay').value;
+    const monthSettingDayInWeek = this.form.get('monthSettingDayInWeek').value;
+    const monthSettingWeek = this.form.get('monthSettingWeek').value;
+    const month = this.form.get('month').value;
+
+    let paymentAlignmentDay: number;
+    let paymentAlignmentWeek: number;
+    let paymentAlignmentMonth: number;
+
+    if (paymentTemporalUnit === 'WEEKS') {
+      paymentAlignmentDay = dayInWeek;
+    }
+
+    if (paymentTemporalUnit === 'MONTHS' || paymentTemporalUnit === 'YEARS') {
+      if (monthSetting === 'DAY') {
+        paymentAlignmentDay = monthSettingDay;
+      } else {
+        paymentAlignmentDay = monthSettingDayInWeek;
+        paymentAlignmentWeek = monthSettingWeek;
+      }
+    }
+
+    if (paymentTemporalUnit === 'YEARS') {
+      paymentAlignmentMonth = month;
+    }
+
+    const formData: DetailFormData = {
+      identifier: this.form.get('identifier').value,
+      productIdentifier: this.form.get('productIdentifier').value,
+      interest: this.form.get('interest').value,
+      principalAmount: this.form.get('principalAmount').value,
+      term: this.form.get('term').value,
+      termTemporalUnit: this.form.get('termTemporalUnit').value,
+      paymentTemporalUnit,
+      paymentPeriod: this.form.get('paymentPeriod').value,
+      paymentAlignmentDay,
+      paymentAlignmentWeek,
+      paymentAlignmentMonth,
+      depositAccountIdentifier: this.form.get('depositAccountIdentifier').value
+    };
+
+    return formData;
+  }
+
+  showIdentifierValidationError(): void {
+    this.setError('identifier', 'unique', true);
+  }
+
+}
diff --git a/src/app/customers/cases/form/detail/validator/max-term.validators.ts b/src/app/customers/cases/form/detail/validator/max-term.validators.ts
new file mode 100644
index 0000000..1d4f98b
--- /dev/null
+++ b/src/app/customers/cases/form/detail/validator/max-term.validators.ts
@@ -0,0 +1,151 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 {FormGroup, ValidationErrors, ValidatorFn} from '@angular/forms';
+import {isEmptyInputValue} from '../../../../../common/validator/validators';
+import {TermRange} from '../../../../../services/portfolio/domain/term-range.model';
+import {ChronoUnit} from '../../../../../services/portfolio/domain/chrono-unit.model';
+
+interface MaxValues {
+  maxWeeks: number;
+  maxMonths: number;
+  maxYears: number;
+}
+
+export function maxTerm(termRange: TermRange): ValidatorFn {
+  return (group: FormGroup): ValidationErrors | null => {
+    const term: number = parseInt(group.get('term').value, 10);
+
+    if (isEmptyInputValue(term)) {
+      return null;
+    }
+
+    const maxValues = getMaxValues(termRange.temporalUnit, termRange.maximum);
+
+    const termTemporalUnit = group.get('termTemporalUnit').value;
+
+    if (!isValid(term, termTemporalUnit, maxValues)) {
+      return {
+        maxTerm: {
+          maxWeeks: maxValues.maxWeeks,
+          maxMonths: maxValues.maxMonths,
+          maxYears: maxValues.maxYears
+        },
+      };
+    }
+
+    return null;
+  };
+}
+
+export function maxPayment(): ValidatorFn {
+  return (group: FormGroup): ValidationErrors | null => {
+    const term: number = parseInt(group.get('term').value, 10);
+    const termTemporalUnit = group.get('termTemporalUnit').value;
+
+    const paymentPeriod: number = parseInt(group.get('paymentPeriod').value, 10);
+    const paymentTemporalUnit = group.get('paymentTemporalUnit').value;
+
+    if (isEmptyInputValue(term) || isEmptyInputValue(paymentPeriod)) {
+      return null;
+    }
+
+    const maxValues = getMaxValues(termTemporalUnit, term);
+
+    if (!isValid(paymentPeriod, paymentTemporalUnit, maxValues)) {
+      return {
+        maxPayment: {
+          maxWeeks: maxValues.maxWeeks,
+          maxMonths: maxValues.maxMonths,
+          maxYears: maxValues.maxYears
+        },
+      };
+    }
+
+    return null;
+  };
+}
+
+function isValid(term: number, temporalUnit: ChronoUnit, maxValues: MaxValues): boolean {
+
+  let valid = false;
+
+  switch (temporalUnit) {
+    case 'WEEKS': {
+      valid = term <= maxValues.maxWeeks;
+      break;
+    }
+
+    case 'MONTHS': {
+      valid = term <= maxValues.maxMonths;
+      break;
+    }
+
+    case 'YEARS': {
+      valid = term <= maxValues.maxYears;
+      break;
+    }
+
+    default:
+      break;
+  }
+
+  return valid;
+}
+
+function getMaxValues(temporalUnit: ChronoUnit, maximum: number): MaxValues {
+
+  const weekBase = 52;
+  const monthBase = 12;
+
+  let maxWeeks = 0;
+  let maxMonths = 0;
+  let maxYears = 0;
+
+  switch (temporalUnit) {
+    case 'WEEKS': {
+      maxWeeks = maximum;
+      maxMonths = (maximum * monthBase) / weekBase;
+      maxYears = maximum / weekBase;
+      break;
+    }
+
+    case 'MONTHS': {
+      maxWeeks = (maximum * weekBase) / monthBase;
+      maxMonths = maximum;
+      maxYears = maximum / monthBase;
+      break;
+    }
+
+    case 'YEARS': {
+      maxWeeks = maximum * weekBase;
+      maxMonths = maximum * monthBase;
+      maxYears = maximum;
+      break;
+    }
+
+    default:
+      break;
+  }
+
+  return {
+    maxWeeks: Math.floor(maxWeeks),
+    maxMonths: Math.floor(maxMonths),
+    maxYears: Math.floor(maxYears)
+  };
+}
diff --git a/src/app/customers/cases/form/edit.component.html b/src/app/customers/cases/form/edit.component.html
new file mode 100644
index 0000000..1ecfc6b
--- /dev/null
+++ b/src/app/customers/cases/form/edit.component.html
@@ -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.
+-->
+
+<fims-layout-card-over title="{{'Edit loan for member' | translate:{value: fullName} }}">
+  <fims-case-form-component #form
+                              (onSave)="onSave($event)"
+                              (onCancel)="onCancel()"
+                              [products]="products$ | async"
+                              [productInstances]="productsInstances$ | async"
+                              [case]="caseInstance$ | async"
+                              [customerId]="(customer$ | async).identifier"
+                              [editMode]="true">
+  </fims-case-form-component>
+</fims-layout-card-over>
diff --git a/src/app/customers/cases/form/edit.component.ts b/src/app/customers/cases/form/edit.component.ts
new file mode 100644
index 0000000..05ade01
--- /dev/null
+++ b/src/app/customers/cases/form/edit.component.ts
@@ -0,0 +1,85 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, OnInit} from '@angular/core';
+import {ActivatedRoute, Router} from '@angular/router';
+import {Customer} from '../../../services/customer/domain/customer.model';
+import * as fromCases from '../store/index';
+import {CasesStore} from '../store/index';
+import {UPDATE} from '../store/case.actions';
+import * as fromCustomers from '../../store/index';
+import {Product} from '../../../services/portfolio/domain/product.model';
+import {Observable} from 'rxjs/Observable';
+import {PortfolioService} from '../../../services/portfolio/portfolio.service';
+import {ProductInstance} from '../../../services/depositAccount/domain/instance/product-instance.model';
+import {DepositAccountService} from '../../../services/depositAccount/deposit-account.service';
+import {FimsCase} from '../../../services/portfolio/domain/fims-case.model';
+
+@Component({
+  templateUrl: './edit.component.html'
+})
+export class CaseEditComponent implements OnInit {
+
+  private productId: string;
+
+  fullName: string;
+
+  products$: Observable<Product[]>;
+
+  productsInstances$: Observable<ProductInstance[]>;
+
+  customer$: Observable<Customer>;
+
+  caseInstance$: Observable<FimsCase>;
+
+  constructor(private router: Router, private route: ActivatedRoute, private casesStore: CasesStore,
+              private portfolioService: PortfolioService, private depositService: DepositAccountService) {}
+
+  ngOnInit() {
+    this.route.parent.params.subscribe(params => {
+      this.productId = params['productId'];
+    });
+
+    this.caseInstance$ = this.casesStore.select(fromCases.getSelectedCase)
+      .filter(caseInstance => !!caseInstance);
+
+    this.customer$ = this.casesStore.select(fromCustomers.getSelectedCustomer)
+      .filter(customer => !!customer)
+      .do(customer => this.fullName = `${customer.givenName} ${customer.surname}`);
+
+    this.products$ = this.portfolioService.findAllProducts(false)
+      .map(productPage => productPage.elements);
+
+    this.productsInstances$ = this.customer$
+      .switchMap(customer => this.depositService.fetchProductInstances(customer.identifier))
+      .map((instances: ProductInstance[]) => instances.filter(instance => instance.state === 'ACTIVE'));
+  }
+
+  onSave(caseInstance: FimsCase) {
+    this.casesStore.dispatch({ type: UPDATE, payload: {
+      productId: this.productId,
+      caseInstance,
+      activatedRoute: this.route
+    }});
+  }
+
+  onCancel() {
+    this.router.navigate(['../'], { relativeTo: this.route });
+  }
+
+}
diff --git a/src/app/customers/cases/form/form.component.html b/src/app/customers/cases/form/form.component.html
new file mode 100644
index 0000000..714bd57
--- /dev/null
+++ b/src/app/customers/cases/form/form.component.html
@@ -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.
+-->
+
+<td-steps mode="'vertical'">
+  <td-step #detailsStep label="{{'Loan details' | translate}}" [state]="detailFormState">
+    <fims-case-detail-form #detailForm [formData]="detailFormData" [products]="products" [productInstances]="productInstances"></fims-case-detail-form>
+  </td-step>
+  <td-step #debtToIncomeStep label="{{'Debt to income ratio' | translate}}" [state]="debtToIncomeFormState">
+    <fims-case-debt-to-income-form #debtToIncomeForm [formData]="debtToIncomeFormData"></fims-case-debt-to-income-form>
+  </td-step>
+  <td-step #coSignerStep label="{{'Co-signer' | translate}}" [state]="coSignerFormState">
+    <fims-case-co-signer-form #coSignerForm [formData]="coSignerFormData"></fims-case-co-signer-form>
+  </td-step>
+  <td-step label="{{'Final step' | translate}}" [state]="'complete'">
+    <ng-template td-step-summary>
+      <fims-form-final-action
+        [resourceName]="'MEMBER LOAN'"
+        [editMode]="editMode"
+        [disabled]="!isValid"
+        (onCancel)="cancel()"
+        (onSave)="save()">
+      </fims-form-final-action>
+    </ng-template>
+  </td-step>
+</td-steps>
diff --git a/src/app/customers/cases/form/form.component.ts b/src/app/customers/cases/form/form.component.ts
new file mode 100644
index 0000000..5c7861d
--- /dev/null
+++ b/src/app/customers/cases/form/form.component.ts
@@ -0,0 +1,202 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, OnInit, Output, ViewChild} from '@angular/core';
+import {TdStepComponent} from '@covalent/core';
+import {CaseParameters} from '../../../services/portfolio/domain/individuallending/case-parameters.model';
+import {CaseDetailFormComponent, DetailFormData} from './detail/detail.component';
+import {CreditWorthinessSnapshot} from '../../../services/portfolio/domain/individuallending/credit-worthiness-snapshot.model';
+import {CaseDebtToIncomeFormComponent, DebtToIncomeFormData} from './debt-to-income/debt-to-income.component';
+import {CaseCoSignerFormComponent, CoSignerFormData} from './co-signer/co-signer.component';
+import {Product} from '../../../services/portfolio/domain/product.model';
+import {ProductInstance} from '../../../services/depositAccount/domain/instance/product-instance.model';
+import {FimsCase} from '../../../services/portfolio/domain/fims-case.model';
+
+@Component({
+  selector: 'fims-case-form-component',
+  templateUrl: './form.component.html'
+})
+export class CaseFormComponent implements OnInit {
+
+  private _caseInstance: FimsCase;
+
+  @ViewChild('detailsStep') detailsStep: TdStepComponent;
+
+  @ViewChild('detailForm') detailForm: CaseDetailFormComponent;
+  detailFormData: DetailFormData;
+
+  @ViewChild('debtToIncomeForm') debtToIncomeForm: CaseDebtToIncomeFormComponent;
+  debtToIncomeFormData: DebtToIncomeFormData;
+
+  @ViewChild('coSignerForm') coSignerForm: CaseCoSignerFormComponent;
+  coSignerFormData: CoSignerFormData;
+
+  @Input('products') products: Product[];
+
+  @Input('productInstances') productInstances: ProductInstance[];
+
+  @Input('editMode') editMode: boolean;
+
+  @Input('customerId') customerId: string;
+
+  @Input('case') set caseInstance(caseInstance: FimsCase) {
+    this._caseInstance = caseInstance;
+
+    this.prepareDetailForm(caseInstance);
+    this.prepareDeptToIncomeForm(caseInstance.parameters.creditWorthinessSnapshots);
+    this.prepareCosignerForm(caseInstance.parameters.creditWorthinessSnapshots);
+  };
+
+  get caseInstance(): FimsCase {
+    return this._caseInstance;
+  }
+
+  @Output('onSave') onSave = new EventEmitter<FimsCase>();
+  @Output('onCancel') onCancel = new EventEmitter<void>();
+
+  constructor() {}
+
+  ngOnInit(): void {
+    this.detailsStep.open();
+  }
+
+  private prepareDetailForm(caseInstance: FimsCase): void {
+    this.detailFormData = {
+      identifier: caseInstance.identifier,
+      productIdentifier: caseInstance.productIdentifier,
+      interest: caseInstance.interest.toFixed(2),
+      principalAmount: caseInstance.parameters.maximumBalance.toFixed(2),
+      term: caseInstance.parameters.termRange.maximum,
+      termTemporalUnit: caseInstance.parameters.termRange.temporalUnit,
+      paymentTemporalUnit: caseInstance.parameters.paymentCycle.temporalUnit,
+      paymentPeriod: caseInstance.parameters.paymentCycle.period,
+      paymentAlignmentDay: caseInstance.parameters.paymentCycle.alignmentDay,
+      paymentAlignmentWeek: caseInstance.parameters.paymentCycle.alignmentWeek,
+      paymentAlignmentMonth: caseInstance.parameters.paymentCycle.alignmentMonth,
+      depositAccountIdentifier: caseInstance.depositAccountIdentifier
+    };
+  }
+
+  private prepareDeptToIncomeForm(snapshots: CreditWorthinessSnapshot[]): void {
+    const foundSnapshot: CreditWorthinessSnapshot = snapshots.find(snapshot => snapshot.forCustomer === this.customerId);
+    if (foundSnapshot) {
+      this.debtToIncomeFormData = {
+        incomeSources: foundSnapshot.incomeSources,
+        debts: foundSnapshot.debts
+      };
+    } else {
+      this.debtToIncomeFormData = {
+        incomeSources: [],
+        debts: []
+      };
+    }
+  }
+
+  private prepareCosignerForm(snapshots: CreditWorthinessSnapshot[]): void {
+    const foundSnapshot: CreditWorthinessSnapshot = snapshots.find(snapshot => snapshot.forCustomer !== this.customerId);
+    if (foundSnapshot) {
+      this.coSignerFormData = {
+        customerId: foundSnapshot.forCustomer,
+        incomeSources: foundSnapshot.incomeSources,
+        debts: foundSnapshot.debts
+      };
+    } else {
+      this.coSignerFormData = {
+        customerId: null,
+        incomeSources: [],
+        debts: []
+      };
+    }
+  }
+
+  get isValid(): boolean {
+    return this.detailForm.valid &&
+      this.debtToIncomeForm.valid &&
+      this.coSignerForm.valid;
+  }
+
+  save(): void {
+    const customerSnapshot: CreditWorthinessSnapshot = {
+      forCustomer: this.customerId,
+      incomeSources: this.debtToIncomeForm.formData.incomeSources,
+      debts: this.debtToIncomeForm.formData.debts,
+      assets: []
+    };
+
+    const cosignerSnapshot: CreditWorthinessSnapshot = {
+      forCustomer: this.coSignerForm.formData.customerId,
+      incomeSources: this.coSignerForm.formData.incomeSources,
+      debts: this.coSignerForm.formData.debts,
+      assets: []
+    };
+
+    const creditWorthinessSnapshots = [customerSnapshot];
+    if (cosignerSnapshot.forCustomer) {
+      creditWorthinessSnapshots.push(cosignerSnapshot);
+    }
+
+    const caseParameters: CaseParameters = {
+      customerIdentifier: this.customerId,
+      maximumBalance: parseFloat(this.detailForm.formData.principalAmount),
+      paymentCycle: {
+        alignmentDay: this.detailForm.formData.paymentAlignmentDay,
+        alignmentMonth: this.detailForm.formData.paymentAlignmentMonth,
+        alignmentWeek: this.detailForm.formData.paymentAlignmentWeek,
+        period: this.detailForm.formData.paymentPeriod,
+        temporalUnit: this.detailForm.formData.paymentTemporalUnit
+      },
+      termRange: {
+        temporalUnit: this.detailForm.formData.termTemporalUnit,
+        maximum: this.detailForm.formData.term
+      },
+      creditWorthinessSnapshots
+    };
+
+    const caseToSave: FimsCase = {
+      currentState: this.caseInstance.currentState,
+      identifier: this.detailForm.formData.identifier,
+      productIdentifier: this.detailForm.formData.productIdentifier,
+      interest: parseFloat(this.detailForm.formData.interest),
+      parameters: caseParameters,
+      depositAccountIdentifier: this.detailForm.formData.depositAccountIdentifier
+    };
+
+    this.onSave.emit(caseToSave);
+  }
+
+  cancel(): void {
+    this.onCancel.emit();
+  }
+
+  get detailFormState(): string {
+    return this.detailForm.valid ? 'complete' : this.detailForm.pristine ? 'none' : 'required';
+  }
+
+  get debtToIncomeFormState(): string {
+    return this.debtToIncomeForm.valid ? 'complete' : this.debtToIncomeForm.pristine ? 'none' : 'required';
+  }
+
+  get coSignerFormState(): string {
+    return this.coSignerForm.valid ? 'complete' : this.coSignerForm.pristine ? 'none' : 'required';
+  }
+
+  showIdentifierValidationError(): void {
+    this.detailForm.showIdentifierValidationError();
+    this.detailsStep.open();
+  }
+}
diff --git a/src/app/customers/cases/payment-cycle/payment-cycle.component.html b/src/app/customers/cases/payment-cycle/payment-cycle.component.html
new file mode 100644
index 0000000..d7dee2f
--- /dev/null
+++ b/src/app/customers/cases/payment-cycle/payment-cycle.component.html
@@ -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.
+-->
+
+<mat-list-item>
+  <h3 matLine translate>Payment cycle</h3>
+  <p matLine>Repay every {{paymentCycle.period + ' ' + getTemporalOption(paymentCycle.temporalUnit)?.label}}</p>
+  <p matLine *ngIf="paymentCycle.temporalUnit === 'WEEKS'">
+    on {{getWeekDayOption(paymentCycle.alignmentDay)?.label}}
+  </p>
+  <p matLine *ngIf="paymentCycle.temporalUnit !== 'WEEKS' && alignmentDaySetting === 'fixed'">
+    on the {{paymentCycle.alignmentDay +1}}. day
+  </p>
+  <p matLine *ngIf="paymentCycle.temporalUnit !== 'WEEKS' && alignmentDaySetting === 'relative'">
+    on the {{getAlignment(paymentCycle.alignmentWeek)?.label + ' ' + getWeekDayOption(paymentCycle.alignmentDay)?.label}}
+  </p>
+  <p matLine *ngIf="paymentCycle.temporalUnit === 'YEARS'">
+    in {{getMonthOption(paymentCycle.alignmentMonth)?.label}}
+  </p>
+</mat-list-item>
diff --git a/src/app/customers/cases/payment-cycle/payment-cycle.component.ts b/src/app/customers/cases/payment-cycle/payment-cycle.component.ts
new file mode 100644
index 0000000..c9b78ea
--- /dev/null
+++ b/src/app/customers/cases/payment-cycle/payment-cycle.component.ts
@@ -0,0 +1,53 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, Input} from '@angular/core';
+import {temporalOptionList} from '../../../common/domain/temporal.domain';
+import {weekDayOptions} from '../../../common/domain/week-days.model';
+import {alignmentOptions} from '../../../common/domain/alignment.model';
+import {PaymentCycle} from '../../../services/portfolio/domain/payment-cycle.model';
+import {monthOptions} from '../../../common/domain/months.model';
+
+@Component({
+  selector: 'fims-case-detail-payment-cycle',
+  templateUrl: './payment-cycle.component.html'
+})
+export class CaseDetailPaymentCycleComponent {
+
+  @Input() paymentCycle: PaymentCycle;
+
+  get alignmentDaySetting(): string {
+    return this.paymentCycle.alignmentWeek ? 'relative' : 'fixed';
+  }
+
+  getAlignment(id: number): any {
+    return alignmentOptions.find(alignment => alignment.type === id);
+  }
+
+  getWeekDayOption(id: number): any {
+    return weekDayOptions.find(weekDayOption => weekDayOption.type === id);
+  }
+
+  getMonthOption(id: number): any {
+    return monthOptions.find(monthOption => monthOption.type === id);
+  }
+
+  getTemporalOption(id: string): any {
+    return temporalOptionList.find(monthOption => monthOption.type === id);
+  }
+}
diff --git a/src/app/customers/cases/payments/payments.component.html b/src/app/customers/cases/payments/payments.component.html
new file mode 100644
index 0000000..de52528
--- /dev/null
+++ b/src/app/customers/cases/payments/payments.component.html
@@ -0,0 +1,69 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<fims-layout-card-over title="{{'Planned payments' | translate}}" [navigateBackTo]="['../']">
+  <div layout="row" layout-margin>
+    <mat-form-field layout-margin>
+      <input matInput type="date" placeholder="{{'Start date' | translate}}" [(ngModel)]="startDate">
+    </mat-form-field>
+    <span>
+      <button layout-margin mat-button mat-icon-button (click)="fetchPayments(startDate)"><mat-icon>search</mat-icon></button>
+    </span>
+  </div>
+  <div layout="row" class="mat-content">
+    <table td-data-table>
+      <thead>
+        <tr td-data-table-column-row>
+          <th td-data-table-column [active]="true" translate>
+            Date
+          </th>
+          <th td-data-table-column [active]="true" [numeric]="true" translate>
+            Payment
+          </th>
+          <th td-data-table-column [active]="true" [numeric]="true" translate>
+            Interest
+          </th>
+          <th td-data-table-column [active]="true" [numeric]="true" translate>
+            Principal
+          </th>
+          <th td-data-table-column [active]="true" [numeric]="true" translate>
+            Balance
+          </th>
+        </tr>
+      </thead>
+      <tbody>
+        <tr td-data-table-row *ngFor="let row of (rows | async)">
+          <td td-data-table-cell>
+            {{row.date | date}}
+          </td>
+          <td td-data-table-cell [numeric]="true">
+            {{row.payment | number}}
+          </td>
+          <td td-data-table-cell [numeric]="true">
+            {{row.interest | number}}
+          </td>
+          <td td-data-table-cell [numeric]="true">
+            {{row.principal | number}}
+          </td>
+          <td td-data-table-cell [numeric]="true">
+            {{row.balance | number}}
+          </td>
+        </tr>
+      </tbody>
+    </table>
+  </div>
+</fims-layout-card-over>
diff --git a/src/app/customers/cases/payments/payments.component.ts b/src/app/customers/cases/payments/payments.component.ts
new file mode 100644
index 0000000..19cc421
--- /dev/null
+++ b/src/app/customers/cases/payments/payments.component.ts
@@ -0,0 +1,125 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, OnDestroy, OnInit} from '@angular/core';
+import {PlannedPaymentPage} from '../../../services/portfolio/domain/individuallending/planned-payment-page.model';
+import * as fromCases from '../store/index';
+import {CasesStore} from '../store/index';
+import {Observable} from 'rxjs/Observable';
+import {Subscription} from 'rxjs/Subscription';
+import {SEARCH} from '../store/payments/payment.actions';
+import {PlannedPayment} from '../../../services/portfolio/domain/individuallending/planned-payment.model';
+import {CostComponent} from '../../../services/portfolio/domain/cost-component.model';
+import {ChargeName} from '../../../services/portfolio/domain/individuallending/charge-name.model';
+import {todayAsISOString} from '../../../services/domain/date.converter';
+import {FimsCase} from '../../../services/portfolio/domain/fims-case.model';
+
+interface CostComponents {
+  [id: string]: CostComponent;
+}
+
+interface PaymentRow {
+  date?: string;
+  payment?: number;
+  interest?: number;
+  principal?: number;
+  balance: number;
+}
+
+@Component({
+  templateUrl: './payments.component.html'
+})
+export class CasePaymentsComponent implements OnInit, OnDestroy {
+
+  private caseSubscription: Subscription;
+
+  startDate: string = todayAsISOString();
+
+  caseInstance: FimsCase;
+
+  rows: Observable<PaymentRow[]>;
+
+  columns: Observable<ChargeName[]>;
+
+  constructor(private casesStore: CasesStore) {}
+
+  private createRows(plannedPayments: PlannedPayment[]): PaymentRow[] {
+    const rows: PaymentRow[] = [];
+
+    plannedPayments.forEach((plannedPayment, index) => {
+      const interest = this.getChargeAmount(plannedPayment.payment.costComponents, 'repay-interest');
+      const principal = this.getChargeAmount(plannedPayment.payment.costComponents, 'repay-principal');
+      const payment = plannedPayment.payment.balanceAdjustments.ey * -1;
+      const balance = plannedPayment.balances.clp;
+
+      if (index === 0) {
+        rows.push({
+          balance
+        });
+
+        return;
+      }
+
+      rows.push({
+        date: plannedPayment.payment.date,
+        payment,
+        interest,
+        principal,
+        balance
+      });
+    });
+
+    return rows;
+  }
+
+  private getChargeAmount(costComponents: CostComponent[], chargeIdentifier: string): number {
+    const foundComponent = costComponents.find(component => component.chargeIdentifier === chargeIdentifier);
+
+    if (foundComponent) {
+      return foundComponent.amount;
+    }
+
+    return 0;
+  };
+
+  ngOnInit(): void {
+    this.columns = this.casesStore.select(fromCases.getSearchCasePaymentPage)
+      .map((page: PlannedPaymentPage) => page.chargeNames);
+
+    this.rows = this.casesStore.select(fromCases.getSearchCasePaymentPage)
+      .map((page: PlannedPaymentPage) => this.createRows(page.elements));
+
+    this.caseSubscription = this.casesStore.select(fromCases.getSelectedCase)
+      .subscribe(caseInstance => {
+        this.caseInstance = caseInstance;
+        this.fetchPayments();
+      });
+  }
+
+  ngOnDestroy(): void {
+    this.caseSubscription.unsubscribe();
+  }
+
+  fetchPayments(startDate?: string): void {
+    this.casesStore.dispatch({ type: SEARCH, payload: {
+      productIdentifier: this.caseInstance.productIdentifier,
+      caseIdentifier: this.caseInstance.identifier,
+      initialDisbursalDate: startDate
+    }});
+  }
+}
diff --git a/src/app/customers/cases/status/command.component.html b/src/app/customers/cases/status/command.component.html
new file mode 100644
index 0000000..b6ba481
--- /dev/null
+++ b/src/app/customers/cases/status/command.component.html
@@ -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.
+-->
+
+<fims-case-tasks
+  [caseCreator]="caseCreator"
+  [currentUser]="currentUser"
+  [action]="statusCommand.action"
+  [productId]="productId"
+  [caseId]="caseId"
+  [tasks]="statusCommand.tasks"
+  (onExecuteTask)="executeTask($event)">
+</fims-case-tasks>
+<div layout="row" layout-align="end center" layout-margin>
+  <button mat-raised-button style="margin-left: 10px;" color="accent" (click)="executeCommand()" [disabled]="disabled">{{statusCommand.action}}</button>
+</div>
diff --git a/src/app/customers/cases/status/command.component.ts b/src/app/customers/cases/status/command.component.ts
new file mode 100644
index 0000000..a1ce1ca
--- /dev/null
+++ b/src/app/customers/cases/status/command.component.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 {Component, EventEmitter, Input, OnInit, Output} from '@angular/core';
+import {StatusCommand} from '../store/model/fims-command.model';
+import {ExecuteTaskEvent} from './tasks.component';
+import {WorkflowAction} from '../../../services/portfolio/domain/individuallending/workflow-action.model';
+
+@Component({
+  selector: 'fims-case-command',
+  templateUrl: './command.component.html'
+})
+
+export class CaseCommandComponent implements OnInit {
+
+  @Input() caseId: string;
+
+  @Input() caseCreator: string;
+
+  @Input() currentUser: string;
+
+  @Input() productId: string;
+
+  @Input() statusCommand: StatusCommand;
+
+  @Output() onExecuteTask = new EventEmitter<ExecuteTaskEvent>();
+
+  @Output() onExecuteCommand = new EventEmitter<WorkflowAction>();
+
+  constructor() {
+  }
+
+  ngOnInit() {}
+
+  executeTask(event: ExecuteTaskEvent): void {
+    this.onExecuteTask.emit(event);
+  }
+
+  executeCommand(): void {
+    this.onExecuteCommand.emit(this.statusCommand.action);
+  }
+
+  get disabled(): boolean {
+    const notExecuted = this.statusCommand.tasks.filter(task => task.taskDefinition.mandatory && !task.executedOn);
+    return notExecuted.length > 0;
+  }
+}
diff --git a/src/app/customers/cases/status/confirmation/confirmation.component.html b/src/app/customers/cases/status/confirmation/confirmation.component.html
new file mode 100644
index 0000000..4b52ffd
--- /dev/null
+++ b/src/app/customers/cases/status/confirmation/confirmation.component.html
@@ -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.
+-->
+
+<fims-layout-card-over title="{{ 'Confirm action' | translate }}">
+  <fims-case-command-confirmation-form
+    [fimsCase]="fimsCase$ | async"
+    [fees]="fees$ | async"
+    [action]="(params$ | async)?.action"
+    (onExecuteCommand)="executeCommand($event)"
+    (onCancel)="cancel()">
+  </fims-case-command-confirmation-form>
+</fims-layout-card-over>
diff --git a/src/app/customers/cases/status/confirmation/confirmation.component.ts b/src/app/customers/cases/status/confirmation/confirmation.component.ts
new file mode 100644
index 0000000..e5f1820
--- /dev/null
+++ b/src/app/customers/cases/status/confirmation/confirmation.component.ts
@@ -0,0 +1,95 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, OnInit} from '@angular/core';
+import {Observable} from 'rxjs/Observable';
+import {ActivatedRoute, Router} from '@angular/router';
+import * as fromCases from '../../store/index';
+import {CasesStore} from '../../store/index';
+import {EXECUTE_COMMAND} from '../../store/case.actions';
+import {ExecuteCommandEvent} from './form.component';
+import {CaseCommand} from '../../../../services/portfolio/domain/case-command.model';
+import {FimsCase} from '../../../../services/portfolio/domain/fims-case.model';
+import {Fee} from '../services/domain/fee.model';
+import {FeeService} from '../services/fee.service';
+
+interface Parameter {
+  productId: string;
+  caseId: string;
+  action: string;
+}
+
+@Component({
+  templateUrl: './confirmation.component.html'
+})
+export class CaseCommandConfirmationComponent implements OnInit {
+
+  fimsCase$: Observable<FimsCase>;
+
+  fees$: Observable<Fee[]>;
+
+  params$: Observable<Parameter>;
+
+  constructor(private router: Router, private route: ActivatedRoute, private casesStore: CasesStore,
+              private feeService: FeeService) {}
+
+  ngOnInit() {
+    const parentParams$ = this.route.parent.params
+      .map(params => ({
+        caseId: params['caseId'],
+        productId: params['productId']
+      }));
+
+    this.params$ = this.route.params
+      .map(params => ({
+        action: params['action']
+      }));
+
+    this.fees$ = Observable.combineLatest(
+      parentParams$,
+      this.params$,
+      (parentParams, params) => ({
+        productId: parentParams.productId,
+        caseId: parentParams.caseId,
+        action: params.action
+      }))
+      .switchMap(params => this.feeService.getFees(params.productId, params.caseId, params.action));
+
+    this.fimsCase$ = this.casesStore.select(fromCases.getSelectedCase);
+  }
+
+  executeCommand(event: ExecuteCommandEvent): void {
+    const command: CaseCommand = {
+      note: event.note,
+      paymentSize: event.paymentSize,
+      createdOn: event.createdOn
+    };
+
+    this.casesStore.dispatch({ type: EXECUTE_COMMAND, payload: {
+      productId: event.productId,
+      caseId: event.caseId,
+      action: event.action,
+      command: command,
+      activatedRoute: this.route
+    } });
+  }
+
+  cancel(): void {
+    this.router.navigate(['../../'], { relativeTo: this.route });
+  }
+}
diff --git a/src/app/customers/cases/status/confirmation/form.component.html b/src/app/customers/cases/status/confirmation/form.component.html
new file mode 100644
index 0000000..f240562
--- /dev/null
+++ b/src/app/customers/cases/status/confirmation/form.component.html
@@ -0,0 +1,69 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<td-steps mode="'vertical'">
+  <td-step #confirmationStep label="{{'Confirmation' | translate}}" [active]="true">
+    <div layout="row" [formGroup]="formGroup">
+      <mat-form-field layout-margin flex>
+        <textarea matInput placeholder="{{'Note(optional)' | translate}}" formControlName="note"></textarea>
+      </mat-form-field>
+    </div>
+    <div layout-gt-xs="column">
+      <h4 translate>Principal amount: {{fimsCase?.parameters?.maximumBalance | number:numberFormat}}</h4>
+      <h3 translate>Costs</h3>
+      <table td-data-table>
+        <thead>
+          <tr td-data-table-column-row>
+            <th td-data-table-column>
+              <span translate>Name</span>
+            </th>
+            <th td-data-table-column>
+              <span translate>Description</span>
+            </th>
+            <th td-data-table-column>
+              <span translate>Amount</span>
+            </th>
+          </tr>
+        </thead>
+        <tbody>
+          <tr td-data-table-row *ngFor="let row of fees">
+            <td td-data-table-cell>
+              {{row['name']}}
+            </td>
+            <td td-data-table-cell>
+              {{row['description']}}
+            </td>
+            <td td-data-table-cell>
+              {{row['amount'] | number:numberFormat}}
+            </td>
+          </tr>
+          <tr td-data-table-row>
+            <td td-data-table-cell translate colspan="2">Total</td>
+            <td td-data-table-cell>{{totalAmount | number:numberFormat}}</td>
+          </tr>
+        </tbody>
+      </table>
+    </div>
+  </td-step>
+  <td-step label="{{'Final step' | translate}}" [state]="'complete'">
+    <ng-template td-step-summary>
+      <button mat-raised-button color="primary" (click)="executeCommand()">{{action | translate}}</button>
+      <span flex></span>
+      <button mat-button (click)="cancel()">{{'CANCEL' | translate}}</button>
+    </ng-template>
+  </td-step>
+</td-steps>
diff --git a/src/app/customers/cases/status/confirmation/form.component.ts b/src/app/customers/cases/status/confirmation/form.component.ts
new file mode 100644
index 0000000..95a3b75
--- /dev/null
+++ b/src/app/customers/cases/status/confirmation/form.component.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 {Component, EventEmitter, Input, OnInit, Output} from '@angular/core';
+import {WorkflowAction} from '../../../../services/portfolio/domain/individuallending/workflow-action.model';
+import {FormBuilder, FormGroup} from '@angular/forms';
+import {FimsCase} from '../../../../services/portfolio/domain/fims-case.model';
+import {Fee} from '../services/domain/fee.model';
+
+export interface ExecuteCommandEvent {
+  productId: string;
+  caseId: string;
+  action: WorkflowAction;
+  note: string;
+  paymentSize: number;
+  createdOn: string;
+}
+
+@Component({
+  selector: 'fims-case-command-confirmation-form',
+  templateUrl: './form.component.html'
+})
+export class CaseCommandConfirmationFormComponent implements OnInit {
+
+  numberFormat = '2.2-2';
+
+  formGroup: FormGroup;
+
+  @Input() fees: Fee[];
+
+  @Input() action: WorkflowAction;
+
+  @Input() fimsCase: FimsCase;
+
+  @Output() onExecuteCommand = new EventEmitter<ExecuteCommandEvent>();
+
+  @Output() onCancel = new EventEmitter<void>();
+
+  constructor(private formBuilder: FormBuilder) {}
+
+  ngOnInit() {
+    this.formGroup = this.formBuilder.group({
+      note: ['']
+    });
+  }
+
+  get totalAmount(): number {
+    if (!this.fees) {
+      return 0;
+    }
+    return this.fees.reduce((acc, val) => acc + val.amount, 0);
+  }
+
+  executeCommand(): void {
+    this.onExecuteCommand.emit({
+      caseId: this.fimsCase.identifier,
+      productId: this.fimsCase.productIdentifier,
+      note: this.formGroup.get('note').value,
+      action: this.action,
+      paymentSize: this.action === 'DISBURSE' ? this.fimsCase.parameters.maximumBalance : 0,
+      createdOn: new Date().toISOString()
+    });
+  }
+
+  cancel(): void {
+    this.onCancel.emit();
+  }
+}
diff --git a/src/app/customers/cases/status/services/domain/fee.model.ts b/src/app/customers/cases/status/services/domain/fee.model.ts
new file mode 100644
index 0000000..1327ab1
--- /dev/null
+++ b/src/app/customers/cases/status/services/domain/fee.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 Fee {
+  name: string;
+  description: string;
+  amount: number;
+}
diff --git a/src/app/customers/cases/status/services/fee.service.ts b/src/app/customers/cases/status/services/fee.service.ts
new file mode 100644
index 0000000..2ec5db5
--- /dev/null
+++ b/src/app/customers/cases/status/services/fee.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 {Injectable} from '@angular/core';
+import {Observable} from 'rxjs/Observable';
+import {PortfolioService} from '../../../../services/portfolio/portfolio.service';
+import {Payment} from '../../../../services/portfolio/domain/payment.model';
+import {ChargeDefinition} from '../../../../services/portfolio/domain/charge-definition.model';
+import {Fee} from './domain/fee.model';
+
+@Injectable()
+export class FeeService {
+
+  constructor(private portfolioService: PortfolioService) {}
+
+  getFees(productIdentifier: string, caseIdentifier: string, action: string): Observable<Fee[]> {
+    return Observable.combineLatest(
+      this.portfolioService.getCostComponentsForAction(productIdentifier, caseIdentifier, action),
+      this.portfolioService.findAllChargeDefinitionsForProduct(productIdentifier),
+      (payment, chargeDefinitions) => ({
+        payment,
+        chargeDefinitions
+      }))
+      .map(result => this.map(result.payment, result.chargeDefinitions));
+  }
+
+  private map(payment: Payment, chargeDefinitions: ChargeDefinition[]): Fee[] {
+    return payment.costComponents.reduce((fees, component) => {
+      const foundDefinition = chargeDefinitions.find(definition => definition.identifier === component.chargeIdentifier);
+
+      if (foundDefinition) {
+        return fees.concat({
+          name: foundDefinition.name,
+          description: foundDefinition.description,
+          amount: component.amount
+        });
+      }
+
+      return fees;
+    }, []);
+  }
+
+}
diff --git a/src/app/customers/cases/status/status.component.html b/src/app/customers/cases/status/status.component.html
new file mode 100644
index 0000000..2626f4e
--- /dev/null
+++ b/src/app/customers/cases/status/status.component.html
@@ -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.
+-->
+
+<fims-layout-card-over title="{{'Tasks' | translate}}" *ngIf="caseInstance$ | async as caseInstance" [navigateBackTo]="['../']">
+  <div layout="row" layout-align="start start" layout-margin>
+    <div layout="column" flex="100">
+      <div layout="row" layout-align="start start" *ngFor="let command of statusCommands$ | async">
+        <div layout="column" flex="100" *ngIf="command.preStates.indexOf(caseInstance.currentState) > -1">
+          <fims-case-command
+            [caseCreator]="caseInstance.createdBy"
+            [currentUser]="currentUser$ | async"
+            [productId]="productId$ | async"
+            [caseId]="caseInstance.identifier"
+            [statusCommand]="command"
+            (onExecuteTask)="executeTask($event)"
+            (onExecuteCommand)="executeCommand($event)">
+          </fims-case-command>
+        </div>
+        <mat-divider></mat-divider>
+      </div>
+    </div>
+  </div>
+</fims-layout-card-over>
diff --git a/src/app/customers/cases/status/status.component.ts b/src/app/customers/cases/status/status.component.ts
new file mode 100644
index 0000000..863be34
--- /dev/null
+++ b/src/app/customers/cases/status/status.component.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 {Component, OnDestroy, OnInit} from '@angular/core';
+import {Subscription} from 'rxjs/Subscription';
+import {ActivatedRoute, Router} from '@angular/router';
+import * as fromCases from '../store/index';
+import {CasesStore} from '../store/index';
+import * as fromRoot from '../../../store';
+import {Observable} from 'rxjs/Observable';
+import {EXECUTE_TASK, LoadAllAction} from '../store/tasks/task.actions';
+import {ExecuteTaskEvent} from './tasks.component';
+import {StatusCommand} from '../store/model/fims-command.model';
+import {WorkflowAction} from '../../../services/portfolio/domain/individuallending/workflow-action.model';
+import {FimsCase} from '../../../services/portfolio/domain/fims-case.model';
+
+@Component({
+  templateUrl: './status.component.html'
+})
+export class CaseStatusComponent implements OnInit, OnDestroy {
+
+  private actionSubscription: Subscription;
+
+  currentUser$: Observable<string>;
+
+  productId$: Observable<string>;
+
+  caseInstance$: Observable<FimsCase>;
+
+  statusCommands$: Observable<StatusCommand[]>;
+
+  constructor(private router: Router, private route: ActivatedRoute, private casesStore: CasesStore) {}
+
+  ngOnInit(): void {
+    this.productId$ = this.route.parent.params
+      .map(params => params['productId']);
+
+    this.currentUser$ = this.casesStore.select(fromRoot.getUsername);
+
+    this.caseInstance$ = this.casesStore.select(fromCases.getSelectedCase);
+
+    this.statusCommands$ = this.casesStore.select(fromCases.getCaseCommands);
+
+    this.actionSubscription = Observable.combineLatest(
+      this.productId$,
+      this.caseInstance$,
+      (productId, caseInstance) => ({
+        productId,
+        caseId: caseInstance.identifier
+      })
+    ).map(({ productId, caseId }) => new LoadAllAction({
+      productId,
+      caseId
+    })).subscribe(this.casesStore);
+  }
+
+  ngOnDestroy(): void {
+    this.actionSubscription.unsubscribe();
+  }
+
+  executeCommand(action: WorkflowAction): void {
+    this.router.navigate([action, 'confirmation'], {
+      relativeTo: this.route
+    });
+  }
+
+  executeTask(event: ExecuteTaskEvent): void {
+    this.casesStore.dispatch({
+      type: EXECUTE_TASK,
+      payload: event
+    });
+  }
+
+}
diff --git a/src/app/customers/cases/status/task.component.html b/src/app/customers/cases/status/task.component.html
new file mode 100644
index 0000000..4585ed4
--- /dev/null
+++ b/src/app/customers/cases/status/task.component.html
@@ -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.
+-->
+
+<mat-list-item [matTooltip]="disabled ? 'This is a four eyes task and must be executed by somebody else than you.' : ''" [matTooltipPosition]="'after'">
+  <mat-icon matListAvatar *ngIf="task.taskDefinition.fourEyes">people</mat-icon>
+  <mat-icon matListAvatar *ngIf="!task.taskDefinition.fourEyes">person</mat-icon>
+  <h3 matLine>{{task.taskDefinition.name}}</h3>
+  <h4 matLine>{{task.taskDefinition.description}}</h4>
+  <p matLine>{{task.executedBy}}, {{task.executedOn | date:'medium'}}</p>
+  <mat-checkbox class="list-checkbox" title="{{'Execute task' | translate}}" [checked]="!!task.executedOn" (change)="selectTask($event)" [disabled]="disabled"></mat-checkbox>
+</mat-list-item>
diff --git a/src/app/customers/cases/status/task.component.ts b/src/app/customers/cases/status/task.component.ts
new file mode 100644
index 0000000..c55e296
--- /dev/null
+++ b/src/app/customers/cases/status/task.component.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 {Component, EventEmitter, Input, OnInit, Output} from '@angular/core';
+import {FimsTaskInstance} from '../store/model/fims-task-instance.model';
+import {MatCheckboxChange} from '@angular/material';
+
+export interface SelectTaskEvent {
+  taskIdentifier: string;
+  checked: boolean;
+}
+
+@Component({
+  selector: 'fims-case-task',
+  templateUrl: './task.component.html'
+})
+export class CaseTaskComponent implements OnInit {
+
+  @Input() task: FimsTaskInstance;
+
+  @Input() disabled: boolean;
+
+  @Output() onSelectTask = new EventEmitter<SelectTaskEvent>();
+
+  constructor() {}
+
+  ngOnInit() {}
+
+  selectTask(change: MatCheckboxChange): void {
+    this.onSelectTask.emit({
+      taskIdentifier: this.task.taskDefinition.identifier,
+      checked: change.checked
+    });
+  }
+}
diff --git a/src/app/customers/cases/status/tasks.component.html b/src/app/customers/cases/status/tasks.component.html
new file mode 100644
index 0000000..73764bd
--- /dev/null
+++ b/src/app/customers/cases/status/tasks.component.html
@@ -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.
+-->
+
+<mat-list *ngIf="tasks?.length > 0">
+  <ng-container *ngIf="mandatoryTasks && mandatoryTasks.length > 0">
+    <h3 mat-subheader>Mandatory tasks(These tasks must be executed)</h3>
+    <fims-case-task *ngFor="let task of mandatoryTasks" [task]="task" [disabled]="taskDisabled(task)" (onSelectTask)="executeTask($event)"></fims-case-task>
+  </ng-container>
+
+  <ng-container *ngIf="optionalTasks && optionalTasks.length > 0">
+    <h3 mat-subheader translate>Optional tasks</h3>
+    <fims-case-task *ngFor="let task of optionalTasks" [task]="task" [disabled]="taskDisabled(task)" (onSelectTask)="executeTask($event)"></fims-case-task>
+  </ng-container>
+</mat-list>
diff --git a/src/app/customers/cases/status/tasks.component.ts b/src/app/customers/cases/status/tasks.component.ts
new file mode 100644
index 0000000..29ce17e
--- /dev/null
+++ b/src/app/customers/cases/status/tasks.component.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 {Component, EventEmitter, Input, OnInit, Output} from '@angular/core';
+import {FimsTaskInstance} from '../store/model/fims-task-instance.model';
+import {SelectTaskEvent} from './task.component';
+
+export interface ExecuteTaskEvent {
+  action: string;
+  productIdentifier: string;
+  caseIdentifier: string;
+  taskIdentifier: string;
+  executedBy: string;
+  executed: boolean;
+}
+
+@Component({
+  selector: 'fims-case-tasks',
+  templateUrl: './tasks.component.html'
+})
+export class CaseTasksComponent implements OnInit {
+
+  @Input() tasks: FimsTaskInstance[];
+
+  @Input() action: string;
+
+  @Input() productId: string;
+
+  @Input() caseId: string;
+
+  @Input() caseCreator: string;
+
+  @Input() currentUser: string;
+
+  @Output() onExecuteTask = new EventEmitter<ExecuteTaskEvent>();
+
+  constructor() {}
+
+  ngOnInit() {}
+
+  executeTask(event: SelectTaskEvent): void {
+    this.onExecuteTask.emit({
+      action: this.action,
+      productIdentifier: this.productId,
+      caseIdentifier: this.caseId,
+      taskIdentifier: event.taskIdentifier,
+      executedBy: this.currentUser,
+      executed: event.checked
+    });
+  }
+
+  taskDisabled(task: FimsTaskInstance): boolean {
+    return task.taskDefinition.fourEyes ? this.caseCreator === this.currentUser : false;
+  }
+
+  get mandatoryTasks(): FimsTaskInstance[] {
+    return this.tasks.filter(task => task.taskDefinition.mandatory);
+  }
+
+  get optionalTasks(): FimsTaskInstance[] {
+    return this.tasks.filter(task => !task.taskDefinition.mandatory);
+  }
+
+  get sameUser(): boolean {
+    return this.caseCreator === this.currentUser;
+  }
+}
diff --git a/src/app/customers/cases/store/case.actions.ts b/src/app/customers/cases/store/case.actions.ts
new file mode 100644
index 0000000..638601b
--- /dev/null
+++ b/src/app/customers/cases/store/case.actions.ts
@@ -0,0 +1,214 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 {Error} from '../../../services/domain/error.model';
+import {type} from '../../../store/util';
+import {RoutePayload} from '../../../common/store/route-payload';
+import {FetchRequest} from '../../../services/domain/paging/fetch-request.model';
+import {Product} from '../../../services/portfolio/domain/product.model';
+import {SearchResult} from '../../../common/store/search.reducer';
+import {
+  CreateResourceSuccessPayload,
+  LoadResourcePayload,
+  SelectResourcePayload,
+  UpdateResourceSuccessPayload
+} from '../../../common/store/resource.reducer';
+import {CaseCommand} from '../../../services/portfolio/domain/case-command.model';
+import {WorkflowAction} from '../../../services/portfolio/domain/individuallending/workflow-action.model';
+import {FimsCase} from '../../../services/portfolio/domain/fims-case.model';
+
+export const SEARCH = type('[Case] Search');
+export const SEARCH_COMPLETE = type('[Case] Search Complete');
+
+export const LOAD = type('[Case] Load');
+export const SELECT = type('[Case] Select');
+
+export const CREATE = type('[Case] Create');
+export const CREATE_SUCCESS = type('[Case] Create Success');
+export const CREATE_FAIL = type('[Case] Create Fail');
+
+export const UPDATE = type('[Case] Update');
+export const UPDATE_SUCCESS = type('[Case] Update Success');
+export const UPDATE_FAIL = type('[Case] Update Fail');
+
+export const LOAD_PRODUCT = type('[Case] Form Load Product');
+export const LOAD_PRODUCT_SUCCESS = type('[Case] Form Load Product Success');
+export const LOAD_PRODUCT_FAIL = type('[Case] Form Load Product Fail');
+
+export const UNLOAD_PRODUCT = type('[Case] Form Unload Product');
+export const RESET_FORM = type('[Case] Reset Form');
+
+export const EXECUTE_COMMAND = type('[Case] Execute Command');
+export const EXECUTE_COMMAND_SUCCESS = type('[Case] Execute Command Success');
+export const EXECUTE_COMMAND_FAIL = type('[Case] Execute Command Fail');
+
+export const LOAD_ALL_COST_COMPONENTS = type('[Case] Load All Cost Components');
+export const LOAD_ALL_COST_COMPONENTS_SUCCESS = type('[Case] Load All Cost Components Success');
+export const LOAD_ALL_COST_COMPONENTS_FAIL = type('[Case] Load All Cost Components Fail');
+
+export interface SearchCasePayload {
+  customerId: string;
+  fetchRequest: FetchRequest;
+}
+
+export interface CaseRoutePayload extends RoutePayload {
+  productId: string;
+  caseInstance: FimsCase;
+}
+
+export interface ExecuteCommandPayload extends RoutePayload {
+  productId: string;
+  caseId: string;
+  action: WorkflowAction;
+  command: CaseCommand;
+}
+
+export interface LoadAllCostComponentsPayload {
+  productId: string;
+  caseId: string;
+  action: WorkflowAction;
+}
+
+export class SearchAction implements Action {
+  readonly type = SEARCH;
+
+  constructor(public payload: SearchCasePayload) { }
+}
+
+export class SearchCompleteAction implements Action {
+  readonly type = SEARCH_COMPLETE;
+
+  constructor(public payload: SearchResult) { }
+}
+
+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 CreateCaseAction implements Action {
+  readonly type = CREATE;
+
+  constructor(public payload: CaseRoutePayload) { }
+}
+
+export class CreateCaseSuccessAction implements Action {
+  readonly type = CREATE_SUCCESS;
+
+  constructor(public payload: CreateResourceSuccessPayload) { }
+}
+
+export class CreateCaseFailAction implements Action {
+  readonly type = CREATE_FAIL;
+
+  constructor(public payload: Error) { }
+}
+
+export class UpdateCaseAction implements Action {
+  readonly type = UPDATE;
+
+  constructor(public payload: CaseRoutePayload) { }
+}
+
+export class UpdateCaseSuccessAction implements Action {
+  readonly type = UPDATE_SUCCESS;
+
+  constructor(public payload: UpdateResourceSuccessPayload) { }
+}
+
+export class UpdateCaseFailAction implements Action {
+  readonly type = UPDATE_FAIL;
+
+  constructor(public payload: Error) { }
+}
+
+export class LoadProductAction implements Action {
+  readonly type = LOAD_PRODUCT;
+
+  constructor(public payload: string) { }
+}
+
+export class LoadProductSuccessAction implements Action {
+  readonly type = LOAD_PRODUCT_SUCCESS;
+
+  constructor(public payload: Product) { }
+}
+
+export class LoadProductFailAction implements Action {
+  readonly type = LOAD_PRODUCT_FAIL;
+
+  constructor(public payload: Error) { }
+}
+
+export class UnloadProductAction implements Action {
+  readonly type = UNLOAD_PRODUCT;
+
+  constructor() { }
+}
+
+export class ResetCaseFormAction implements Action {
+  readonly type = RESET_FORM;
+
+  constructor() {}
+}
+
+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: Error) { }
+}
+
+export type Actions
+  = SearchAction
+  | SearchCompleteAction
+  | LoadAction
+  | SelectAction
+  | CreateCaseAction
+  | CreateCaseSuccessAction
+  | CreateCaseFailAction
+  | UpdateCaseAction
+  | UpdateCaseSuccessAction
+  | UpdateCaseFailAction
+  | LoadProductAction
+  | LoadProductSuccessAction
+  | LoadProductFailAction
+  | UnloadProductAction
+  | ResetCaseFormAction
+  | ExecuteCommandAction
+  | ExecuteCommandSuccessAction
+  | ExecuteCommandFailAction;
diff --git a/src/app/customers/cases/store/cases.reducer.ts b/src/app/customers/cases/store/cases.reducer.ts
new file mode 100644
index 0000000..2bca9c8
--- /dev/null
+++ b/src/app/customers/cases/store/cases.reducer.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 {ResourceState} from '../../../common/store/resource.reducer';
+import * as caseActions from './case.actions';
+import {CaseState} from '../../../services/portfolio/domain/case-state.model';
+
+export const initialState: ResourceState = {
+  ids: [],
+  entities: {},
+  loadedAt: {},
+  selectedId: null,
+};
+
+export function reducer(state = initialState, action: caseActions.Actions): ResourceState {
+
+  switch (action.type) {
+
+    case caseActions.EXECUTE_COMMAND_SUCCESS: {
+      const payload = action.payload;
+
+      const caseId = payload.caseId;
+      const commandAction: string = payload.action;
+
+      const caseInstance = state.entities[caseId];
+
+      let caseState: CaseState = null;
+
+      if (commandAction === 'OPEN') {
+        caseState = 'PENDING';
+      }else if (commandAction === 'APPROVE') {
+        caseState = 'APPROVED';
+      }else if (commandAction === 'DENY') {
+        caseState = 'CLOSED';
+      }else if (commandAction === 'CLOSE') {
+        caseState = 'CLOSED';
+      }else if (commandAction === 'DISBURSE') {
+        caseState = 'ACTIVE';
+      }
+
+      caseInstance.currentState = caseState;
+
+      return {
+        ids: [ ...state.ids ],
+        entities: Object.assign({}, state.entities, {
+          [caseInstance.identifier]: caseInstance
+        }),
+        loadedAt: state.loadedAt,
+        selectedId: state.selectedId
+      };
+    }
+
+    default: {
+      return state;
+    }
+  }
+}
diff --git a/src/app/customers/cases/store/documents/document.actions.ts b/src/app/customers/cases/store/documents/document.actions.ts
new file mode 100644
index 0000000..d545e77
--- /dev/null
+++ b/src/app/customers/cases/store/documents/document.actions.ts
@@ -0,0 +1,274 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 {RoutePayload} from '../../../../common/store/route-payload';
+import {CustomerDocument} from '../../../../services/customer/domain/customer-document.model';
+import {Action} from '@ngrx/store';
+import {
+  CreateResourceSuccessPayload, DeleteResourceSuccessPayload, LoadResourcePayload, SelectResourcePayload,
+  UpdateResourceSuccessPayload
+} from '../../../../common/store/resource.reducer';
+
+export const LOAD_ALL = type('[Case Document] Load All');
+export const LOAD_ALL_COMPLETE = type('[Case Document] Load All Complete');
+
+export const LOAD = type('[Case Document] Load');
+export const SELECT = type('[Case Document] Select');
+
+export const CREATE = type('[Case Document] Create');
+export const CREATE_SUCCESS = type('[Case Document] Create Success');
+export const CREATE_FAIL = type('[Case Document] Create Fail');
+
+export const UPDATE = type('[Case Document] Update');
+export const UPDATE_SUCCESS = type('[Case Document] Update Success');
+export const UPDATE_FAIL = type('[Case Document] Update Fail');
+
+export const DELETE = type('[Case Document] Delete');
+export const DELETE_SUCCESS = type('[Case Document] Delete Success');
+export const DELETE_FAIL = type('[Case Document] Delete Fail');
+
+export const LOCK = type('[Case Document] Lock');
+export const LOCK_SUCCESS = type('[Case Document] Lock Success');
+export const LOCK_FAIL = type('[Case Document] Lock Fail');
+
+export const LOAD_ALL_PAGES = type('[Case Document] Load All Pages');
+export const LOAD_ALL_PAGES_COMPLETE = type('[Case Document] Load All Pages Complete');
+
+export const UPLOAD_PAGE = type('[Case Document] Upload Page');
+export const UPLOAD_PAGE_SUCCESS = type('[Case Document] Upload Page Success');
+export const UPLOAD_PAGE_FAIL = type('[Case Document] Upload Page Fail');
+
+export const DELETE_PAGE = type('[Case Document] Delete Page');
+export const DELETE_PAGE_SUCCESS = type('[Case Document] Delete Page Success');
+export const DELETE_PAGE_FAIL = type('[Case Document] Delete Page Fail');
+
+export const RESET_PAGE_FORM = type('[Case Document] Reset Page Form');
+
+export interface LoadAllPayload {
+  customerId: string;
+  productId: string;
+  caseId: string;
+}
+
+export interface LoadAllPagesPayload {
+  customerId: string;
+  documentId: string;
+}
+
+export interface DocumentPayload extends RoutePayload {
+  productId: string;
+  caseId: string;
+  customerId: string;
+  document: CustomerDocument;
+}
+
+export interface DeletePagePayload {
+  customerId: string;
+  documentId: string;
+  pageNumber: number;
+}
+
+export interface UploadPagePayload extends RoutePayload {
+  customerId: string;
+  documentId: string;
+  pageNumber: number;
+  page: File;
+}
+
+export interface LockPayload {
+  customerId: string;
+  documentId: string;
+}
+
+export class LoadAllAction implements Action {
+  readonly type = LOAD_ALL;
+
+  constructor(public payload: LoadAllPayload) { }
+}
+
+export class LoadAllCompleteAction implements Action {
+  readonly type = LOAD_ALL_COMPLETE;
+
+  constructor(public payload: CustomerDocument[]) { }
+}
+
+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 CreateDocumentAction implements Action {
+  readonly type = CREATE;
+
+  constructor(public payload: DocumentPayload) { }
+}
+
+export class CreateDocumentSuccessAction implements Action {
+  readonly type = CREATE_SUCCESS;
+
+  constructor(public payload: CreateResourceSuccessPayload) { }
+}
+
+export class CreateDocumentFailAction implements Action {
+  readonly type = CREATE_FAIL;
+
+  constructor(public payload: Error) { }
+}
+
+export class UpdateDocumentAction implements Action {
+  readonly type = UPDATE;
+
+  constructor(public payload: DocumentPayload) { }
+}
+
+export class UpdateDocumentSuccessAction implements Action {
+  readonly type = UPDATE_SUCCESS;
+
+  constructor(public payload: UpdateResourceSuccessPayload) { }
+}
+
+export class UpdateDocumentFailAction implements Action {
+  readonly type = UPDATE_FAIL;
+
+  constructor(public payload: Error) { }
+}
+
+export class DeleteDocumentAction implements Action {
+  readonly type = DELETE;
+
+  constructor(public payload: DocumentPayload) { }
+}
+
+export class DeleteDocumentSuccessAction implements Action {
+  readonly type = DELETE_SUCCESS;
+
+  constructor(public payload: DeleteResourceSuccessPayload) { }
+}
+
+export class DeleteDocumentFailAction implements Action {
+  readonly type = DELETE_FAIL;
+
+  constructor(public payload: Error) { }
+}
+
+export class LockDocumentAction implements Action {
+  readonly type = LOCK;
+
+  constructor(public payload: LockPayload) { }
+}
+
+export class LockDocumentSuccessAction implements Action {
+  readonly type = LOCK_SUCCESS;
+
+  constructor(public payload: LockPayload) { }
+}
+
+export class LockDocumentFailAction implements Action {
+  readonly type = LOCK_FAIL;
+
+  constructor(public payload: Error) { }
+}
+
+export class LoadAllPagesAction implements Action {
+  readonly type = LOAD_ALL_PAGES;
+
+  constructor(public payload: LoadAllPagesPayload) { }
+}
+
+export class LoadAllPagesCompleteAction implements Action {
+  readonly type = LOAD_ALL_PAGES_COMPLETE;
+
+  constructor(public payload: number[]) { }
+}
+
+export class UploadPageAction implements Action {
+  readonly type = UPLOAD_PAGE;
+
+  constructor(public payload: UploadPagePayload) { }
+}
+
+export class UploadPageSuccessAction implements Action {
+  readonly type = UPLOAD_PAGE_SUCCESS;
+
+  constructor(public payload: UploadPagePayload) { }
+}
+
+export class UploadPageFailAction implements Action {
+  readonly type = UPLOAD_PAGE_FAIL;
+
+  constructor(public payload: Error) { }
+}
+
+export class DeletePageAction implements Action {
+  readonly type = DELETE_PAGE;
+
+  constructor(public payload: DeletePagePayload) { }
+}
+
+export class DeletePageSuccessAction implements Action {
+  readonly type = DELETE_PAGE_SUCCESS;
+
+  constructor(public payload: DeletePagePayload) { }
+}
+
+export class DeletePageFailAction implements Action {
+  readonly type = DELETE_PAGE_FAIL;
+
+  constructor(public payload: Error) { }
+}
+
+export class ResetDocumentPageForm implements Action {
+  readonly type = RESET_PAGE_FORM;
+
+  constructor() {}
+}
+
+export type Actions
+  = LoadAllAction
+  | LoadAllCompleteAction
+  | LoadAction
+  | SelectAction
+  | CreateDocumentAction
+  | CreateDocumentSuccessAction
+  | CreateDocumentFailAction
+  | UpdateDocumentAction
+  | UpdateDocumentSuccessAction
+  | UpdateDocumentFailAction
+  | DeleteDocumentAction
+  | DeleteDocumentSuccessAction
+  | DeleteDocumentFailAction
+  | ResetDocumentPageForm
+  | LockDocumentAction
+  | LockDocumentSuccessAction
+  | LockDocumentFailAction
+  | LoadAllPagesAction
+  | LoadAllPagesCompleteAction
+  | UploadPageAction
+  | UploadPageSuccessAction
+  | UploadPageFailAction
+  | DeletePageAction
+  | DeletePageSuccessAction
+  | DeletePageFailAction;
diff --git a/src/app/customers/cases/store/documents/documents.reducer.ts b/src/app/customers/cases/store/documents/documents.reducer.ts
new file mode 100644
index 0000000..15f1f53
--- /dev/null
+++ b/src/app/customers/cases/store/documents/documents.reducer.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 * as documentActions from './document.actions';
+import {ResourceState} from '../../../../common/store/resource.reducer';
+import {CustomerDocument} from '../../../../services/customer/domain/customer-document.model';
+import {idsToHashWithCurrentTimestamp, resourcesToHash} from '../../../../common/store/reducer.helper';
+import {DeletePagePayload, LockPayload, UploadPagePayload} from './document.actions';
+
+export const initialState: ResourceState = {
+  ids: [],
+  entities: {},
+  loadedAt: {},
+  selectedId: null,
+};
+
+export function reducer(state = initialState, action: documentActions.Actions): ResourceState {
+
+  switch (action.type) {
+
+    case documentActions.LOAD_ALL: {
+      return initialState;
+    }
+
+    case documentActions.LOAD_ALL_COMPLETE: {
+      const documents: CustomerDocument[] = action.payload;
+
+      const ids = documents.map(document => document.identifier);
+
+      const entities = resourcesToHash(documents);
+
+      const loadedAt = idsToHashWithCurrentTimestamp(ids);
+
+      return {
+        ids: [ ...ids ],
+        entities: entities,
+        loadedAt: loadedAt,
+        selectedId: state.selectedId
+      };
+    }
+
+    case documentActions.LOCK_SUCCESS: {
+      const payload: LockPayload = action.payload;
+
+      const document = state.entities[payload.documentId];
+
+      const entities = Object.assign({}, state.entities, {
+        [document.identifier]: Object.assign({}, document, {
+          completed: true
+        })
+      });
+
+      return Object.assign({}, state, {
+        entities
+      })
+    }
+
+    default: {
+      return state;
+    }
+  }
+}
diff --git a/src/app/customers/cases/store/documents/effects/notification.effects.ts b/src/app/customers/cases/store/documents/effects/notification.effects.ts
new file mode 100644
index 0000000..9db85f8
--- /dev/null
+++ b/src/app/customers/cases/store/documents/effects/notification.effects.ts
@@ -0,0 +1,87 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 * as documents from '../document.actions';
+import {Action} from '@ngrx/store';
+import {NotificationService, NotificationType} from '../../../../../services/notification/notification.service';
+
+@Injectable()
+export class CaseDocumentNotificationEffects {
+
+  @Effect({ dispatch: false })
+  createDocumentSuccess$: Observable<Action> = this.actions$
+    .ofType(documents.CREATE_SUCCESS)
+    .do(() => this.notificationService.send({
+      type: NotificationType.MESSAGE,
+      message: 'Document is going to be saved'
+    }));
+
+  @Effect({ dispatch: false })
+  deleteDocumentSuccess$: Observable<Action> = this.actions$
+    .ofType(documents.DELETE_SUCCESS)
+    .do(() => this.notificationService.send({
+      type: NotificationType.MESSAGE,
+      message: 'Document is going to be deleted'
+    }));
+
+  @Effect({ dispatch: false })
+  uploadPageSuccess$: Observable<Action> = this.actions$
+    .ofType(documents.UPLOAD_PAGE_SUCCESS)
+    .do(() => this.notificationService.send({
+      type: NotificationType.MESSAGE,
+      message: 'Page is going to be uploaded'
+    }));
+
+  @Effect({ dispatch: false })
+  uploadPageFail$: Observable<Action> = this.actions$
+    .ofType(documents.UPLOAD_PAGE_FAIL)
+    .do(() => this.notificationService.send({
+      type: NotificationType.ALERT,
+      message: 'Please choose a different page number'
+    }));
+
+  @Effect({ dispatch: false })
+  deletePageSuccess$: Observable<Action> = this.actions$
+    .ofType(documents.DELETE_PAGE_SUCCESS)
+    .do(() => this.notificationService.send({
+      type: NotificationType.MESSAGE,
+      message: 'Page is going to be deleted'
+    }));
+
+  @Effect({ dispatch: false })
+  lockDocumentSuccess$: Observable<Action> = this.actions$
+    .ofType(documents.LOCK_SUCCESS)
+    .do(() => this.notificationService.send({
+      type: NotificationType.MESSAGE,
+      message: 'Document locked'
+    }));
+
+  @Effect({ dispatch: false })
+  lockDocumentFail$: Observable<Action> = this.actions$
+    .ofType(documents.LOCK_FAIL)
+    .do(() => this.notificationService.send({
+      type: NotificationType.ALERT,
+      title: 'Document could not be locked',
+      message: 'Please make sure all pages are uploaded and are in sequence'
+    }));
+
+  constructor(private actions$: Actions, private notificationService: NotificationService) {}
+}
diff --git a/src/app/customers/cases/store/documents/effects/route.effects.ts b/src/app/customers/cases/store/documents/effects/route.effects.ts
new file mode 100644
index 0000000..3000bd8
--- /dev/null
+++ b/src/app/customers/cases/store/documents/effects/route.effects.ts
@@ -0,0 +1,53 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 {Actions, Effect} from '@ngrx/effects';
+import {Action} from '@ngrx/store';
+import {Router} from '@angular/router';
+import * as documents from '../document.actions';
+import {
+  CreateDocumentSuccessAction, DeleteDocumentSuccessAction, UpdateDocumentSuccessAction,
+  UploadPageSuccessAction
+} from '../document.actions';
+
+@Injectable()
+export class CaseDocumentRouteEffects {
+
+  @Effect({ dispatch: false })
+  createUpdateSuccess$: Observable<Action> = this.actions$
+    .ofType(
+      documents.CREATE_SUCCESS,
+      documents.UPDATE_SUCCESS,
+      documents.UPLOAD_PAGE_SUCCESS
+    )
+    .do((action: CreateDocumentSuccessAction | UpdateDocumentSuccessAction | UploadPageSuccessAction) =>
+      this.router.navigate(['../'], { relativeTo: action.payload.activatedRoute} )
+    );
+
+  @Effect({ dispatch: false })
+  deleteSuccess$: Observable<Action> = this.actions$
+    .ofType(documents.DELETE_SUCCESS)
+    .do((action: DeleteDocumentSuccessAction) =>
+      this.router.navigate(['../../../../../../../../../../'], { relativeTo: action.payload.activatedRoute} )
+    );
+
+  constructor(private actions$: Actions, private router: Router) { }
+
+}
diff --git a/src/app/customers/cases/store/documents/effects/service.effects.ts b/src/app/customers/cases/store/documents/effects/service.effects.ts
new file mode 100644
index 0000000..cf34cf6
--- /dev/null
+++ b/src/app/customers/cases/store/documents/effects/service.effects.ts
@@ -0,0 +1,122 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 documents from '../document.actions';
+import {DeletePagePayload, DocumentPayload, LoadAllPagesPayload, LoadAllPayload, LockPayload, UploadPagePayload} from '../document.actions';
+import {of} from 'rxjs/observable/of';
+import {DocumentsService} from './services/documents.service';
+
+@Injectable()
+export class CaseDocumentApiEffects {
+
+  @Effect()
+  loadAllDocuments$: Observable<Action> = this.actions$
+    .ofType(documents.LOAD_ALL)
+    .map((action: documents.LoadAllAction) => action.payload)
+    .switchMap((payload: LoadAllPayload) =>
+      this.documentsService.getCustomerDocuments(payload.customerId, payload.productId, payload.caseId)
+        .map(customerDocuments => new documents.LoadAllCompleteAction(customerDocuments))
+        .catch(() => of(new documents.LoadAllCompleteAction([])))
+    );
+
+  @Effect()
+  createDocument$: Observable<Action> = this.actions$
+    .ofType(documents.CREATE)
+    .map((action: documents.CreateDocumentAction) => action.payload)
+    .mergeMap((payload: DocumentPayload) =>
+      this.documentsService.createDocument(payload.productId, payload.caseId, payload.customerId, payload.document)
+        .map(() => new documents.CreateDocumentSuccessAction({
+          resource: payload.document,
+          activatedRoute: payload.activatedRoute
+        }))
+        .catch((error) => of(new documents.CreateDocumentFailAction(error)))
+    );
+
+  @Effect()
+  updateDocument$: Observable<Action> = this.actions$
+    .ofType(documents.UPDATE)
+    .map((action: documents.UpdateDocumentAction) => action.payload)
+    .mergeMap((payload: DocumentPayload) =>
+      this.documentsService.updateDocument(payload.customerId, payload.document)
+        .map(() => new documents.UpdateDocumentSuccessAction({
+          resource: payload.document,
+          activatedRoute: payload.activatedRoute
+        }))
+        .catch((error) => of(new documents.UpdateDocumentFailAction(error)))
+    );
+
+  @Effect()
+  deleteDocument$: Observable<Action> = this.actions$
+    .ofType(documents.DELETE)
+    .map((action: documents.DeleteDocumentAction) => action.payload)
+    .mergeMap((payload: DocumentPayload) =>
+      this.documentsService.deleteDocument(payload.productId, payload.caseId, payload.customerId, payload.document)
+        .map(() => new documents.DeleteDocumentSuccessAction({
+          resource: payload.document,
+          activatedRoute: payload.activatedRoute
+        }))
+        .catch((error) => of(new documents.DeleteDocumentFailAction(error)))
+    );
+
+  @Effect()
+  lockDocument$: Observable<Action> = this.actions$
+    .ofType(documents.LOCK)
+    .map((action: documents.LockDocumentAction) => action.payload)
+    .mergeMap((payload: LockPayload) =>
+      this.documentsService.lockDocument(payload.customerId, payload.documentId)
+        .map(() => new documents.LockDocumentSuccessAction(payload))
+        .catch((error) => of(new documents.LockDocumentFailAction(error)))
+    );
+
+  @Effect()
+  loadAllPages$: Observable<Action> = this.actions$
+    .ofType(documents.LOAD_ALL_PAGES)
+    .map((action: documents.LoadAllPagesAction) => action.payload)
+    .switchMap((payload: LoadAllPagesPayload) =>
+      this.documentsService.getDocumentPageNumbers(payload.customerId, payload.documentId)
+        .map(pageNumbers => new documents.LoadAllPagesCompleteAction(pageNumbers))
+        .catch(() => of(new documents.LoadAllPagesCompleteAction([])))
+    );
+
+  @Effect()
+  uploadDocumentPage$: Observable<Action> = this.actions$
+    .ofType(documents.UPLOAD_PAGE)
+    .map((action: documents.UploadPageAction) => action.payload)
+    .mergeMap((payload: UploadPagePayload) =>
+      this.documentsService.uploadPage(payload.customerId, payload.documentId, payload.pageNumber, payload.page)
+        .map(() => new documents.UploadPageSuccessAction(payload))
+        .catch((error) => of(new documents.UploadPageFailAction(error)))
+    );
+
+  @Effect()
+  deleteDocumentPage$: Observable<Action> = this.actions$
+    .ofType(documents.DELETE_PAGE)
+    .map((action: documents.DeletePageAction) => action.payload)
+    .mergeMap((payload: DeletePagePayload) =>
+      this.documentsService.deletePage(payload.customerId, payload.documentId, payload.pageNumber)
+        .map(() => new documents.DeletePageSuccessAction(payload))
+        .catch((error) => of(new documents.DeletePageFailAction(error)))
+    );
+
+  constructor(private actions$: Actions, private documentsService: DocumentsService) {}
+
+}
diff --git a/src/app/customers/cases/store/documents/effects/services/documents.service.ts b/src/app/customers/cases/store/documents/effects/services/documents.service.ts
new file mode 100644
index 0000000..f88f030
--- /dev/null
+++ b/src/app/customers/cases/store/documents/effects/services/documents.service.ts
@@ -0,0 +1,115 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 {PortfolioService} from '../../../../../../services/portfolio/portfolio.service';
+import {CustomerService} from '../../../../../../services/customer/customer.service';
+import {Observable} from 'rxjs/Observable';
+import {CustomerDocument} from '../../../../../../services/customer/domain/customer-document.model';
+import {CaseCustomerDocuments} from '../../../../../../services/portfolio/domain/case-customer-documents.model';
+
+@Injectable()
+export class DocumentsService {
+
+  constructor(private customerService: CustomerService, private portfolioService: PortfolioService) {
+  }
+
+  public createDocument(productId: string, caseId: string, customerId: string, customerDocument: CustomerDocument): Observable<void> {
+    return this.getNextDocumentId(customerId, caseId)
+      .map((nextId: string) => Object.assign({}, customerDocument, {identifier: nextId}))
+      .mergeMap((document: CustomerDocument) => this.customerService.createDocument(customerId, document)
+        .mergeMap(() => this.portfolioService.getCaseDocuments(productId, caseId))
+        .map((documents: CaseCustomerDocuments) => this.addDocument(documents, customerId, document.identifier))
+        .mergeMap((documents: CaseCustomerDocuments) => this.portfolioService.changeCaseDocuments(productId, caseId, documents))
+      )
+  }
+
+  /**
+   * Fetches the next available document identifier from the customer service.
+   * The document identifier follows the pattern 'caseId_increment' e.g. 'myCase_12'
+   */
+  private getNextDocumentId(customerId: string, caseId: string): Observable<string> {
+    return this.customerService.getDocuments(customerId)
+      .map((documents: CustomerDocument[]) => documents.filter(document => document.identifier.startsWith(caseId)))
+      .map((documents: CustomerDocument[]) => documents.map(document =>
+        document.identifier.substr(caseId.length + 1, document.identifier.length))
+      )
+      .map((documentIds: string[]) => documentIds.map(id => parseInt(id, 10)))
+      .map((documentIds: number[]) => documentIds.length > 0 ? Math.max(...documentIds) + 1 : 1)
+      .map((nextId: number) => `${caseId}_${nextId}`);
+  }
+
+  public getCustomerDocuments(customerId: string, productId: string, caseId: string): Observable<CustomerDocument[]> {
+    return Observable.combineLatest(
+      this.portfolioService.getCaseDocuments(productId, caseId),
+      this.customerService.getDocuments(customerId),
+      (caseDocuments, customerDocuments) => ({
+        caseDocuments: caseDocuments.documents,
+        customerDocuments
+      })
+    ).map(result => result.customerDocuments.filter(document => {
+        const foundCaseDocument = result.caseDocuments.find(caseDocument => caseDocument.documentId === document.identifier);
+        return !!foundCaseDocument;
+      })
+    );
+  }
+
+  private addDocument(documents: CaseCustomerDocuments, customerId: string, documentId: string): CaseCustomerDocuments {
+    return Object.assign({}, documents, {
+      documents: documents.documents.concat({
+        customerId,
+        documentId
+      })
+    })
+  }
+
+  public updateDocument(customerId: string, customerDocument: CustomerDocument): Observable<void> {
+    return this.customerService.updateDocument(customerId, customerDocument);
+  }
+
+  public deleteDocument(productId: string, caseId: string, customerId: string, customerDocument: CustomerDocument): Observable<void> {
+    return this.customerService.deleteDocument(customerId, customerDocument)
+      .mergeMap(() => this.portfolioService.getCaseDocuments(productId, caseId))
+      .map((documents: CaseCustomerDocuments) => this.removeDocument(documents, customerDocument.identifier))
+      .mergeMap((documents: CaseCustomerDocuments) => this.portfolioService.changeCaseDocuments(productId, caseId, documents))
+  }
+
+  private removeDocument(documents: CaseCustomerDocuments, documentId: string): CaseCustomerDocuments {
+    return Object.assign({}, documents, {
+      documents: documents.documents.filter(document => document.documentId !== documentId)
+    })
+  }
+
+  public getDocumentPageNumbers(customerId: string, documentId: string): Observable<number[]> {
+    return this.customerService.getDocumentPageNumbers(customerId, documentId);
+  }
+
+  public uploadPage(customerId: string, documentId: string, pageNumber: number, file: File): Observable<void> {
+    return this.customerService.createDocumentPage(customerId, documentId, pageNumber, file);
+  }
+
+  public deletePage(customerId: string, documentId: string, pageNumber: number): Observable<void> {
+    return this.customerService.deleteDocumentPage(customerId, documentId, pageNumber);
+  }
+
+  public lockDocument(customerId: string, documentId: string): Observable<void> {
+    return this.customerService.completeDocument(customerId, documentId, true);
+  }
+
+
+}
diff --git a/src/app/customers/cases/store/documents/pageNumber.reducer.ts b/src/app/customers/cases/store/documents/pageNumber.reducer.ts
new file mode 100644
index 0000000..6c4d52c
--- /dev/null
+++ b/src/app/customers/cases/store/documents/pageNumber.reducer.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 * as documentActions from './document.actions';
+import {DeletePagePayload, UploadPagePayload} from './document.actions';
+
+export interface State {
+  pageNumbers: number[]
+}
+
+export const initialState: State = {
+  pageNumbers: [],
+};
+
+export function reducer(state = initialState, action: documentActions.Actions): State {
+
+  switch (action.type) {
+
+    case documentActions.LOAD_ALL_PAGES: {
+      return initialState;
+    }
+
+    case documentActions.LOAD_ALL_PAGES_COMPLETE: {
+      const pageNumbers: number[] = action.payload;
+
+      return {
+        pageNumbers
+      };
+    }
+
+    case documentActions.UPLOAD_PAGE_SUCCESS: {
+      const payload: UploadPagePayload = action.payload;
+
+      return {
+        pageNumbers: state.pageNumbers.concat(payload.pageNumber)
+      }
+    }
+
+    case documentActions.DELETE_PAGE_SUCCESS: {
+      const payload: DeletePagePayload = action.payload;
+
+      return {
+        pageNumbers: state.pageNumbers.filter(pageNumber => pageNumber !== payload.pageNumber)
+      }
+    }
+
+    default: {
+      return state;
+    }
+  }
+}
+
+export const getPageNumbers = (state: State) => state.pageNumbers;
diff --git a/src/app/customers/cases/store/effects/notification.effects.ts b/src/app/customers/cases/store/effects/notification.effects.ts
new file mode 100644
index 0000000..498e424
--- /dev/null
+++ b/src/app/customers/cases/store/effects/notification.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 {Observable} from 'rxjs/Observable';
+import {Action} from '@ngrx/store';
+import * as caseActions from '../case.actions';
+import {NotificationService, NotificationType} from '../../../../services/notification/notification.service';
+
+@Injectable()
+export class CaseNotificationEffects {
+
+  @Effect({ dispatch: false })
+  createCaseSuccess$: Observable<Action> = this.actions$
+    .ofType(caseActions.CREATE_SUCCESS, caseActions.UPDATE_SUCCESS)
+    .do(() => this.notificationService.send({
+      type: NotificationType.MESSAGE,
+      message: 'Case is going to be saved'
+    }));
+
+  @Effect({ dispatch: false })
+  executeCommandSuccess$: Observable<Action> = this.actions$
+    .ofType(caseActions.EXECUTE_COMMAND_SUCCESS)
+    .do(() => this.notificationService.send({
+      type: NotificationType.MESSAGE,
+      message: 'Case is going to be updated'
+    }));
+
+  constructor(private actions$: Actions, private notificationService: NotificationService) {}
+}
diff --git a/src/app/customers/cases/store/effects/route.effects.ts b/src/app/customers/cases/store/effects/route.effects.ts
new file mode 100644
index 0000000..ad42d8b
--- /dev/null
+++ b/src/app/customers/cases/store/effects/route.effects.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 {Action} from '@ngrx/store';
+import {Observable} from 'rxjs/Observable';
+import {Actions, Effect} from '@ngrx/effects';
+import {Router} from '@angular/router';
+import * as caseActions from '../case.actions';
+import {Injectable} from '@angular/core';
+
+@Injectable()
+export class CaseRouteEffects {
+
+  @Effect({ dispatch: false })
+  createCaseSuccess$: Observable<Action> = this.actions$
+    .ofType(caseActions.CREATE_SUCCESS, caseActions.UPDATE_SUCCESS)
+    .map(action => action.payload)
+    .do(payload => this.router.navigate(['../'], { relativeTo: payload.activatedRoute} ));
+
+  @Effect({ dispatch: false })
+  executeCommandSuccess$: Observable<Action> = this.actions$
+    .ofType(caseActions.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/customers/cases/store/effects/service.effects.ts b/src/app/customers/cases/store/effects/service.effects.ts
new file mode 100644
index 0000000..6edddb5
--- /dev/null
+++ b/src/app/customers/cases/store/effects/service.effects.ts
@@ -0,0 +1,98 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 caseActions from '../case.actions';
+import {PortfolioService} from '../../../../services/portfolio/portfolio.service';
+import {Product} from '../../../../services/portfolio/domain/product.model';
+
+@Injectable()
+export class CaseApiEffects {
+
+  @Effect()
+  search$: Observable<Action> = this.actions$
+    .ofType(caseActions.SEARCH)
+    .debounceTime(300)
+    .map((action: caseActions.SearchAction) => action.payload)
+    .switchMap(payload => {
+      const nextSearch$ = this.actions$.ofType(caseActions.SEARCH).skip(1);
+
+      return this.portfolioService.getAllCasesForCustomer(payload.customerId, payload.fetchRequest)
+        .takeUntil(nextSearch$)
+        .map(fimsCasePage => new caseActions.SearchCompleteAction(fimsCasePage))
+        .catch(() => of(new caseActions.SearchCompleteAction({
+          totalElements: 0,
+          totalPages: 0,
+          elements: []
+        })));
+    });
+
+  @Effect()
+  createCase$: Observable<Action> = this.actions$
+    .ofType(caseActions.CREATE)
+    .map((action: caseActions.CreateCaseAction) => action.payload)
+    .mergeMap(payload =>
+      this.portfolioService.createCase(payload.productId, payload.caseInstance)
+        .map(() => new caseActions.CreateCaseSuccessAction({
+          resource: payload.caseInstance,
+          activatedRoute: payload.activatedRoute
+        }))
+        .catch((error) => of(new caseActions.CreateCaseFailAction(error)))
+    );
+
+
+  @Effect()
+  updateCase$: Observable<Action> = this.actions$
+    .ofType(caseActions.UPDATE)
+    .map((action: caseActions.UpdateCaseAction) => action.payload)
+    .mergeMap(payload =>
+        this.portfolioService.changeCase(payload.productId, payload.caseInstance)
+          .map(() => new caseActions.UpdateCaseSuccessAction({
+            resource: payload.caseInstance,
+            activatedRoute: payload.activatedRoute
+          }))
+          .catch((error) => of(new caseActions.UpdateCaseFailAction(error)))
+    );
+
+  @Effect()
+  loadProduct$: Observable<Action> = this.actions$
+    .ofType(caseActions.LOAD_PRODUCT)
+    .map((action: caseActions.LoadProductAction) => action.payload)
+    .mergeMap(productId =>
+      this.portfolioService.getProduct(productId)
+        .map((product: Product) => new caseActions.LoadProductSuccessAction(product))
+        .catch((error) => of(new caseActions.LoadProductFailAction(error)))
+    );
+
+  @Effect()
+  executeCommand$: Observable<Action> = this.actions$
+    .ofType(caseActions.EXECUTE_COMMAND)
+    .map((action: caseActions.ExecuteCommandAction) => action.payload)
+    .mergeMap(payload =>
+      this.portfolioService.executeCaseCommand(payload.productId, payload.caseId, payload.action, payload.command)
+        .map(() => new caseActions.ExecuteCommandSuccessAction(payload))
+        .catch((error) => of(new caseActions.ExecuteCommandFailAction(error)))
+    );
+
+  constructor(private actions$: Actions, private portfolioService: PortfolioService) { }
+
+}
diff --git a/src/app/customers/cases/store/form.reducer.ts b/src/app/customers/cases/store/form.reducer.ts
new file mode 100644
index 0000000..a647e76
--- /dev/null
+++ b/src/app/customers/cases/store/form.reducer.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 * as caseActions from './case.actions';
+import {Product} from '../../../services/portfolio/domain/product.model';
+import {FormState} from '../../../common/store/form.reducer';
+
+export interface State extends FormState {
+  product: Product;
+}
+
+export const initialState: State = {
+  error: null,
+  product: null
+};
+
+export function reducer(state = initialState, action: caseActions.Actions): State {
+  switch (action.type) {
+
+    case caseActions.LOAD_PRODUCT_SUCCESS: {
+      const product: Product = action.payload;
+      return {
+        error: state.error,
+        product: product
+      };
+    }
+
+    default:
+      return state;
+
+  }
+}
+
+export const getFormProduct = (state: State) => state.product;
diff --git a/src/app/customers/cases/store/index.ts b/src/app/customers/cases/store/index.ts
new file mode 100644
index 0000000..a3b34ca
--- /dev/null
+++ b/src/app/customers/cases/store/index.ts
@@ -0,0 +1,138 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 fromCustomer from '../../store';
+import * as fromCases from './cases.reducer';
+import * as fromCaseForm from './form.reducer';
+import * as fromCaseTasks from './tasks/tasks.reducer';
+import * as fromCasePayments from './payments/search.reducer';
+import * as fromCaseDocuments from './documents/documents.reducer'
+import * as fromCaseDocumentPages from './documents/pageNumber.reducer'
+
+import {ActionReducer, Store} from '@ngrx/store';
+import {createReducer} from '../../../store/index';
+import {createSelector} from 'reselect';
+import {
+  createResourceReducer,
+  getResourceAll,
+  getResourceEntities,
+  getResourceIds,
+  getResourceLoadedAt,
+  getResourceSelected,
+  getResourceSelectedId,
+  ResourceState
+} from '../../../common/store/resource.reducer';
+import {
+  createSearchReducer,
+  getSearchEntities,
+  getSearchTotalElements,
+  getSearchTotalPages,
+  SearchState
+} from '../../../common/store/search.reducer';
+import {createFormReducer, getFormError} from '../../../common/store/form.reducer';
+
+export interface State extends fromCustomer.State {
+  cases: ResourceState;
+  caseForm: fromCaseForm.State;
+  caseSearch: SearchState;
+  caseTasks: fromCaseTasks.State;
+  casePayments: fromCasePayments.State;
+  caseDocuments: ResourceState;
+  caseDocumentPages: fromCaseDocumentPages.State;
+}
+
+const reducers = {
+  cases: createResourceReducer('Case', fromCases.reducer),
+  caseForm: createFormReducer('Case', fromCaseForm.reducer),
+  caseSearch: createSearchReducer('Case'),
+  caseTasks: fromCaseTasks.reducer,
+  casePayments: fromCasePayments.reducer,
+  caseDocuments: createResourceReducer('Case Document', fromCaseDocuments.reducer),
+  caseDocumentPages: fromCaseDocumentPages.reducer
+};
+
+export const caseModuleReducer: ActionReducer<State> = createReducer(reducers);
+
+export class CasesStore extends Store<State> {}
+
+export function caseStoreFactory(appStore: Store<fromCustomer.State>) {
+  appStore.replaceReducer(caseModuleReducer);
+  return appStore;
+}
+
+export const getCaseSearchState = (state: State) => state.caseSearch;
+
+export const getSearchCases = createSelector(getCaseSearchState, getSearchEntities);
+export const getCaseSearchTotalElements = createSelector(getCaseSearchState, getSearchTotalElements);
+export const getCaseSearchTotalPages = createSelector(getCaseSearchState, getSearchTotalPages);
+
+export const getCaseSearchResults = createSelector(getSearchCases, getCaseSearchTotalPages, getCaseSearchTotalElements,
+  (cases, totalPages, totalElements) => {
+  return {
+    cases: cases,
+    totalPages: totalPages,
+    totalElements: totalElements
+  };
+});
+
+export const getCasesState = (state: State) => state.cases;
+
+export const getCaseEntities = createSelector(getCasesState, getResourceEntities);
+export const getCasesLoadedAt = createSelector(getCasesState, getResourceLoadedAt);
+export const getCaseIds = createSelector(getCasesState, getResourceIds);
+export const getSelectedCaseId = createSelector(getCasesState, getResourceSelectedId);
+export const getSelectedCase = createSelector(getCasesState, getResourceSelected);
+
+export const getCaseTasksState = (state: State) => state.caseTasks;
+
+export const getCaseCommands = createSelector(getCaseTasksState, fromCaseTasks.getCommands);
+
+
+export const getCasePaymentsSearchState = (state: State) => state.casePayments;
+
+export const getSearchCasePaymentPage = createSelector(getCasePaymentsSearchState, fromCasePayments.getPaymentPage);
+
+export const getCaseFormState = (state: State) => state.caseForm;
+export const getCaseFormError = createSelector(getCaseFormState, getFormError);
+
+export const getCaseFormProduct = createSelector(getCaseFormState, fromCaseForm.getFormProduct);
+
+export const getCaseSelection = createSelector(getSelectedCase, fromCustomer.getSelectedCustomer, (caseInstance, customer) => {
+  return {
+    customerId: customer.identifier,
+    productId: caseInstance.productIdentifier,
+    caseId: caseInstance.identifier
+  }
+});
+
+/**
+ * Case Document Selectors
+ */
+export const getCaseDocumentsState = (state: State) => state.caseDocuments;
+
+export const getAllCaseDocumentEntities = createSelector(getCaseDocumentsState, getResourceAll);
+
+export const getCaseDocumentLoadedAt = createSelector(getCaseDocumentsState, getResourceLoadedAt);
+export const getSelectedCaseDocument = createSelector(getCaseDocumentsState, getResourceSelected);
+
+/**
+ * Case Document Selectors
+ */
+export const getDocumentPagesState = (state: State) => state.caseDocumentPages;
+
+export const getAllDocumentPages = createSelector(getDocumentPagesState, fromCaseDocumentPages.getPageNumbers);
diff --git a/src/app/customers/cases/store/model/case-selection.model.ts b/src/app/customers/cases/store/model/case-selection.model.ts
new file mode 100644
index 0000000..c5bc084
--- /dev/null
+++ b/src/app/customers/cases/store/model/case-selection.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 CaseSelection {
+  customerId: string,
+  productId: string,
+  caseId: string,
+}
diff --git a/src/app/customers/cases/store/model/fims-command.model.ts b/src/app/customers/cases/store/model/fims-command.model.ts
new file mode 100644
index 0000000..95f229a
--- /dev/null
+++ b/src/app/customers/cases/store/model/fims-command.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 {CaseState} from '../../../../services/portfolio/domain/case-state.model';
+import {WorkflowAction} from '../../../../services/portfolio/domain/individuallending/workflow-action.model';
+import {FimsTaskInstance} from './fims-task-instance.model';
+
+export interface StatusCommand {
+  action: WorkflowAction;
+  preStates: CaseState[];
+  tasks: FimsTaskInstance[];
+  note?: string;
+}
diff --git a/src/app/customers/cases/store/model/fims-task-instance.model.ts b/src/app/customers/cases/store/model/fims-task-instance.model.ts
new file mode 100644
index 0000000..7bba822
--- /dev/null
+++ b/src/app/customers/cases/store/model/fims-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.
+ */
+import {TaskDefinition} from '../../../../services/portfolio/domain/task-definition.model';
+
+export interface FimsTaskInstance {
+  taskDefinition: TaskDefinition;
+  comment: string;
+  executedOn: string;
+  executedBy: string;
+}
diff --git a/src/app/customers/cases/store/payments/effects/service.effects.ts b/src/app/customers/cases/store/payments/effects/service.effects.ts
new file mode 100644
index 0000000..143e6b1
--- /dev/null
+++ b/src/app/customers/cases/store/payments/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 {Actions, Effect} from '@ngrx/effects';
+import {Injectable} from '@angular/core';
+import {Action} from '@ngrx/store';
+import {Observable} from 'rxjs/Observable';
+import * as paymentActions from '../payment.actions';
+import {of} from 'rxjs/observable/of';
+import {PortfolioService} from '../../../../../services/portfolio/portfolio.service';
+
+@Injectable()
+export class CasePaymentsApiEffects {
+
+  @Effect()
+  search$: Observable<Action> = this.actions$
+    .ofType(paymentActions.SEARCH)
+    .debounceTime(300)
+    .map((action: paymentActions.SearchAction) => action.payload)
+    .switchMap(payload => {
+      const nextSearch$ = this.actions$.ofType(paymentActions.SEARCH).skip(1);
+
+      return this.portfolioService.getPaymentScheduleForCase(payload.productIdentifier, payload.caseIdentifier,
+        payload.initialDisbursalDate)
+        .takeUntil(nextSearch$)
+        .map(paymentsPage => new paymentActions.SearchCompleteAction(paymentsPage))
+        .catch(() => of(new paymentActions.SearchCompleteAction({
+          totalElements: 0,
+          totalPages: 0,
+          elements: [],
+          chargeNames: []
+        })));
+    });
+
+  constructor(private actions$: Actions, private portfolioService: PortfolioService) {}
+}
diff --git a/src/app/customers/cases/store/payments/payment.actions.ts b/src/app/customers/cases/store/payments/payment.actions.ts
new file mode 100644
index 0000000..370a1e0
--- /dev/null
+++ b/src/app/customers/cases/store/payments/payment.actions.ts
@@ -0,0 +1,47 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 {PlannedPaymentPage} from '../../../../services/portfolio/domain/individuallending/planned-payment-page.model';
+import {Action} from '@ngrx/store';
+
+export const SEARCH = type('[Case Payments] Search');
+export const SEARCH_COMPLETE = type('[Case Payments] Search Complete');
+
+export interface SearchPaymentsPayload {
+  productIdentifier: string;
+  caseIdentifier: string;
+  initialDisbursalDate?: string;
+}
+
+export class SearchAction implements Action {
+  readonly type = SEARCH;
+
+  constructor(public payload: SearchPaymentsPayload) { }
+}
+
+export class SearchCompleteAction implements Action {
+  readonly type = SEARCH_COMPLETE;
+
+  constructor(public payload: PlannedPaymentPage) { }
+}
+
+export type Actions
+  = SearchAction
+  | SearchCompleteAction;
+
diff --git a/src/app/customers/cases/store/payments/search.reducer.ts b/src/app/customers/cases/store/payments/search.reducer.ts
new file mode 100644
index 0000000..511ede5
--- /dev/null
+++ b/src/app/customers/cases/store/payments/search.reducer.ts
@@ -0,0 +1,73 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 paymentActions from './payment.actions';
+import {SearchPaymentsPayload} from './payment.actions';
+import {PlannedPaymentPage} from '../../../../services/portfolio/domain/individuallending/planned-payment-page.model';
+
+export interface State {
+  paymentPage: PlannedPaymentPage;
+  loading: boolean;
+  initialDisbursalDate: string;
+}
+
+const initialState: State = {
+  paymentPage: {
+    chargeNames: [],
+    elements: [],
+    totalElements: 0,
+    totalPages: 0
+  },
+  loading: false,
+  initialDisbursalDate: null
+};
+
+export function reducer(state = initialState, action: paymentActions.Actions): State {
+
+  switch (action.type) {
+
+    case paymentActions.SEARCH: {
+      const payload: SearchPaymentsPayload = action.payload;
+
+      return Object.assign({}, state, {
+        paymentPage: initialState.paymentPage,
+        initialDisbursalDate: payload.initialDisbursalDate,
+        loading: true
+      });
+    }
+
+    case paymentActions.SEARCH_COMPLETE: {
+      const paymentsPage: PlannedPaymentPage = action.payload;
+
+      return {
+        paymentPage: paymentsPage,
+        loading: false,
+        initialDisbursalDate: state.initialDisbursalDate
+      };
+    }
+
+    default: {
+      return state;
+    }
+  }
+}
+
+
+export const getPaymentPage = (state: State) => state.paymentPage;
+
+export const getLoading = (state: State) => state.loading;
diff --git a/src/app/customers/cases/store/search.reducer.ts b/src/app/customers/cases/store/search.reducer.ts
new file mode 100644
index 0000000..c96d053
--- /dev/null
+++ b/src/app/customers/cases/store/search.reducer.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 * as caseActions from './case.actions';
+import {SearchCasePayload} from './case.actions';
+import {FetchRequest} from '../../../services/domain/paging/fetch-request.model';
+import {FimsCase} from '../../../services/portfolio/domain/fims-case.model';
+
+export interface State {
+  cases: FimsCase[];
+  totalPages: number;
+  totalElements: number;
+  loading: boolean;
+  fetchRequest: FetchRequest;
+}
+
+const initialState: State = {
+  cases: [],
+  totalPages: 0,
+  totalElements: 0,
+  loading: false,
+  fetchRequest: null
+};
+
+export function reducer(state = initialState, action: caseActions.Actions): State {
+
+  switch (action.type) {
+
+    case caseActions.SEARCH: {
+      const payload: SearchCasePayload = action.payload;
+
+      return Object.assign({}, state, {
+        fetchRequest: payload.fetchRequest,
+        loading: true
+      });
+    }
+
+    case caseActions.SEARCH_COMPLETE: {
+      const casePage = action.payload;
+
+      return {
+        cases: casePage.elements,
+        loading: false,
+        fetchRequest: state.fetchRequest,
+        totalElements: casePage.totalElements,
+        totalPages: casePage.totalPages
+      };
+    }
+
+    default: {
+      return state;
+    }
+  }
+}
+
+
+export const getCases = (state: State) => state.cases;
+
+export const getFetchRequest = (state: State) => state.fetchRequest;
+
+export const getLoading = (state: State) => state.loading;
+
+export const getTotalPages = (state: State) => state.totalPages;
+
+export const getTotalElements = (state: State) => state.totalElements;
diff --git a/src/app/customers/cases/store/tasks/effects/notification.effects.ts b/src/app/customers/cases/store/tasks/effects/notification.effects.ts
new file mode 100644
index 0000000..05107df
--- /dev/null
+++ b/src/app/customers/cases/store/tasks/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 {NotificationService, NotificationType} from '../../../../../services/notification/notification.service';
+import {Observable} from 'rxjs/Observable';
+import {Action} from '@ngrx/store';
+import * as taskActions from '../task.actions';
+
+@Injectable()
+export class CaseTasksNotificationEffects {
+
+  @Effect({dispatch: false})
+  createTaskSuccess$: Observable<Action> = this.actions$
+    .ofType(taskActions.EXECUTE_TASK_SUCCESS)
+    .do(() => this.notificationService.send({
+      type: NotificationType.MESSAGE,
+      message: 'Task executed successfully'
+    }));
+
+  @Effect({dispatch: false})
+  executeCommandSuccess$: Observable<Action> = this.actions$
+    .ofType(taskActions.EXECUTE_TASK_FAIL)
+    .do(() => this.notificationService.send({
+      type: NotificationType.ALERT,
+      message: 'Task execution failed'
+    }));
+
+  constructor(private actions$: Actions, private notificationService: NotificationService) {
+  }
+
+}
diff --git a/src/app/customers/cases/store/tasks/effects/service.effects.ts b/src/app/customers/cases/store/tasks/effects/service.effects.ts
new file mode 100644
index 0000000..1b9cca2
--- /dev/null
+++ b/src/app/customers/cases/store/tasks/effects/service.effects.ts
@@ -0,0 +1,69 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 {Actions, Effect} from '@ngrx/effects';
+import {Injectable} from '@angular/core';
+import {Action} from '@ngrx/store';
+import {Observable} from 'rxjs/Observable';
+import * as taskActions from '../task.actions';
+import {of} from 'rxjs/observable/of';
+import {PortfolioService} from '../../../../../services/portfolio/portfolio.service';
+import {TaskInstance} from '../../../../../services/portfolio/domain/task-instance.model';
+import {TaskDefinition} from '../../../../../services/portfolio/domain/task-definition.model';
+import {FimsTaskInstance} from '../../model/fims-task-instance.model';
+
+@Injectable()
+export class CaseTasksApiEffects {
+
+  @Effect()
+  loadAll$: Observable<Action> = this.actions$
+    .ofType(taskActions.LOAD_ALL)
+    .map((action: taskActions.LoadAllAction) => action.payload)
+    .switchMap(payload => {
+      return Observable.combineLatest(
+        this.portfolioService.findAllTaskDefinitionsForProduct(payload.productId),
+        this.portfolioService.findAllTasksForCase(payload.productId, payload.caseId, true),
+        (definitions, tasks) => ({
+          definitions,
+          tasks
+        })
+      ).map(result => this.mapTaskInstances(result.tasks, result.definitions))
+       .map(taskInstances => new taskActions.LoadAllCompleteAction(taskInstances))
+       .catch(() => of(new taskActions.LoadAllCompleteAction([])));
+    });
+
+  @Effect()
+  executeTask$: Observable<Action> = this.actions$
+    .ofType(taskActions.EXECUTE_TASK)
+    .map((action: taskActions.ExecuteTaskAction) => action.payload)
+    .mergeMap(payload => this.portfolioService.taskForCaseExecuted(payload.productIdentifier, payload.caseIdentifier,
+      payload.taskIdentifier, payload.executed)
+      .map(() => new taskActions.ExecuteTaskActionSuccess(payload))
+      .catch(error => of(new taskActions.ExecuteTaskActionFail(error))));
+
+  private mapTaskInstances(taskInstances: TaskInstance[], taskDefinitions: TaskDefinition[]): FimsTaskInstance[] {
+    return taskInstances.map(instance => ({
+      taskDefinition: taskDefinitions.find(definition => definition.identifier === instance.taskIdentifier),
+      comment: instance.comment,
+      executedOn: instance.executedOn,
+      executedBy: instance.executedBy
+    }));
+  }
+
+  constructor(private actions$: Actions, private portfolioService: PortfolioService) {}
+}
diff --git a/src/app/customers/cases/store/tasks/task.actions.ts b/src/app/customers/cases/store/tasks/task.actions.ts
new file mode 100644
index 0000000..45b55e3
--- /dev/null
+++ b/src/app/customers/cases/store/tasks/task.actions.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 {type} from '../../../../store/util';
+import {Action} from '@ngrx/store';
+import {Error} from '../../../../services/domain/error.model';
+import {FimsTaskInstance} from '../model/fims-task-instance.model';
+
+export const LOAD_ALL = type('[Case Task] Load All');
+export const LOAD_ALL_COMPLETE = type('[Case Task] Load All Complete');
+
+export const EXECUTE_TASK = type('[Case Task] Execute');
+export const EXECUTE_TASK_SUCCESS = type('[Case Task] Execute Success');
+export const EXECUTE_TASK_FAIL = type('[Case Task] Execute Fail');
+
+export interface LoadAllTasksPayload {
+  caseId: string;
+  productId: string;
+}
+
+export interface ExecuteTaskPayload {
+  action: string;
+  productIdentifier: string;
+  caseIdentifier: string;
+  taskIdentifier: string;
+  executedBy: string;
+  executed: boolean;
+}
+
+export class LoadAllAction implements Action {
+  readonly type = LOAD_ALL;
+
+  constructor(public payload: LoadAllTasksPayload) { }
+}
+
+export class LoadAllCompleteAction implements Action {
+  readonly type = LOAD_ALL_COMPLETE;
+
+  constructor(public payload: FimsTaskInstance[]) { }
+}
+
+export class ExecuteTaskAction implements Action {
+  readonly type = EXECUTE_TASK;
+
+  constructor(public payload: ExecuteTaskPayload) {}
+}
+
+export class ExecuteTaskActionSuccess implements Action {
+  readonly type = EXECUTE_TASK_SUCCESS;
+
+  constructor(public payload: ExecuteTaskPayload) {}
+}
+
+export class ExecuteTaskActionFail implements Action {
+  readonly type = EXECUTE_TASK_FAIL;
+
+  constructor(public payload: Error) {}
+}
+
+export type Actions
+  = LoadAllAction
+  | LoadAllCompleteAction
+  | ExecuteTaskAction
+  | ExecuteTaskActionSuccess
+  | ExecuteTaskActionFail;
diff --git a/src/app/customers/cases/store/tasks/tasks.reducer.ts b/src/app/customers/cases/store/tasks/tasks.reducer.ts
new file mode 100644
index 0000000..6e4101a
--- /dev/null
+++ b/src/app/customers/cases/store/tasks/tasks.reducer.ts
@@ -0,0 +1,91 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 task from './task.actions';
+import {StatusCommand} from '../model/fims-command.model';
+
+export interface State {
+  commands: StatusCommand[];
+}
+
+export const initialState: State = {
+  commands: [
+    { action: 'OPEN', preStates: ['CREATED'], tasks: []},
+    { action: 'APPROVE', preStates: ['PENDING'], tasks: []},
+    { action: 'DENY', preStates: ['PENDING'], tasks: []},
+    { action: 'CLOSE', preStates: ['APPROVED', 'ACTIVE'], tasks: []},
+    { action: 'DISBURSE', preStates: ['APPROVED'], tasks: []}
+  ]
+};
+
+export function reducer(state = initialState, action: task.Actions): State {
+
+  switch (action.type) {
+
+    case task.LOAD_ALL: {
+      return initialState;
+    }
+
+    case task.LOAD_ALL_COMPLETE: {
+      const entities = action.payload;
+
+      const commands = state.commands.map(command => {
+        return Object.assign({}, command, {
+          tasks: entities.filter(instance => instance.taskDefinition.actions.indexOf(command.action) > -1)
+        });
+      });
+
+      return {
+        commands
+      };
+    }
+
+    case task.EXECUTE_TASK_SUCCESS: {
+      const payload = action.payload;
+
+      const commands = state.commands.map(command => {
+        if (command.action !== payload.action) {
+          return command;
+        }
+
+        return Object.assign({}, command, {
+          tasks: command.tasks.map(task => {
+            if (task.taskDefinition.identifier !== payload.taskIdentifier) {
+              return task;
+            }
+
+            return Object.assign({}, task, {
+              executedOn: payload.executed ? new Date().toISOString() : undefined,
+              executedBy: payload.executedBy
+            });
+          })
+        });
+      });
+
+      return {
+        commands
+      };
+    }
+
+    default: {
+      return state;
+    }
+  }
+}
+
+export const getCommands = (state: State) => state.commands;
diff --git a/src/app/customers/contact.helper.ts b/src/app/customers/contact.helper.ts
new file mode 100644
index 0000000..f72a1e7
--- /dev/null
+++ b/src/app/customers/contact.helper.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 {ContactDetail} from '../services/domain/contact/contact-detail.model';
+
+export function getContactDetailValueByType(contactDetails: ContactDetail[], type: string): string {
+  const items = contactDetails.filter(contact => contact.type === type);
+  return items.length ? items[0].value : '';
+}
diff --git a/src/app/customers/customFields/catalog-exists.guard.ts b/src/app/customers/customFields/catalog-exists.guard.ts
new file mode 100644
index 0000000..be567db
--- /dev/null
+++ b/src/app/customers/customFields/catalog-exists.guard.ts
@@ -0,0 +1,66 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, RouterStateSnapshot} from '@angular/router';
+import * as fromCustomers from '../store/index';
+import {CustomersStore} from '../store/index';
+import {CatalogService} from '../../services/catalog/catalog.service';
+import {ExistsGuardService} from '../../common/guards/exists-guard';
+import {Observable} from 'rxjs/Observable';
+import {LoadAction} from '../store/catalogs/catalog.actions';
+import {of} from 'rxjs/observable/of';
+
+@Injectable()
+export class CatalogExistsGuard implements CanActivate {
+
+  constructor(private store: CustomersStore,
+              private catalogService: CatalogService,
+              private existsGuardService: ExistsGuardService) {
+  }
+
+  hasCatalogInStore(): Observable<boolean> {
+    const timestamp$: Observable<number> = this.store.select(fromCustomers.getCustomerCatalogLoadedAt);
+
+    return this.existsGuardService.isWithinExpiry(timestamp$);
+  }
+
+  hasCatalogInApi(id: string): Observable<boolean> {
+    return this.catalogService.findCatalog(id, true)
+      .catch(() => {
+        return Observable.of(null);
+      })
+      .map(catalogEntity => new LoadAction(catalogEntity))
+      .do((action: LoadAction) => this.store.dispatch(action))
+      .map(catalog => !!catalog);
+  }
+
+  hasCatalog(id: string): Observable<boolean> {
+    return this.hasCatalogInStore()
+      .switchMap(inStore => {
+        if (inStore) {
+          return of(inStore);
+        }
+        return this.hasCatalogInApi(id);
+      });
+  }
+
+  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
+    return this.hasCatalog('customers');
+  }
+}
diff --git a/src/app/customers/customFields/catalog.detail.component.html b/src/app/customers/customFields/catalog.detail.component.html
new file mode 100644
index 0000000..963fc23
--- /dev/null
+++ b/src/app/customers/customFields/catalog.detail.component.html
@@ -0,0 +1,63 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<fims-layout-card-over title="{{'Custom fields' | translate }}" [navigateBackTo]="['../../../']">
+  <fims-layout-card-over-header-menu *ngIf="(catalog$ | async) as catalog">
+    <button mat-icon-button (click)="deleteCatalog(catalog)" title="{{'Delete this catalog' | translate}}">
+      <mat-icon>delete</mat-icon>
+    </button>
+  </fims-layout-card-over-header-menu>
+  <ng-container *ngIf="(catalog$ | async) as catalog; else elseBlock">
+    <div class="mat-content inset" flex>
+      <div layout="row">
+        <mat-list>
+          <mat-list-item>
+            <h3 matLine translate>Name</h3>
+            <p matLine>{{catalog.name}}</p>
+          </mat-list-item>
+          <mat-list-item>
+            <h3 matLine translate>Description</h3>
+            <p matLine>{{catalog.description}}</p>
+          </mat-list-item>
+          <mat-list-item>
+            <h3 matLine translate>Created by</h3>
+            <p matLine>{{catalog.createdBy}} - {{catalog.createdOn | date:'medium'}}</p>
+          </mat-list-item>
+          <mat-list-item>
+            <h3 matLine translate>Last modified by</h3>
+            <p matLine>{{catalog.lastModifiedBy}} - {{catalog.lastModifiedOn | date:'medium'}}</p>
+          </mat-list-item>
+        </mat-list>
+      </div>
+      <fims-data-table flex
+                       [columns]="columns"
+                       [data]="fieldData$ | async"
+                       (onActionCellClick)="rowSelect($event)">
+      </fims-data-table>
+    </div>
+  </ng-container>
+  <ng-template #elseBlock>
+    <td-message label="{{'No custom fields found' | translate }}"
+                color="warn" icon="error">
+      <button td-message-actions mat-button [routerLink]="['edit']"
+              *hasPermission="{ id: 'catalog_catalogs', accessLevel: 'CHANGE'}" translate>CREATE CUSTOM FIELDS
+      </button>
+    </td-message>
+  </ng-template>
+</fims-layout-card-over>
+
+
diff --git a/src/app/customers/customFields/catalog.detail.component.ts b/src/app/customers/customFields/catalog.detail.component.ts
new file mode 100644
index 0000000..bc12679
--- /dev/null
+++ b/src/app/customers/customFields/catalog.detail.component.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 {Component} from '@angular/core';
+import {Catalog} from '../../services/catalog/domain/catalog.model';
+import {Observable} from 'rxjs/Observable';
+import {CustomersStore} from '../store/index';
+import * as fromCustomers from '../store';
+import {TableData} from '../../common/data-table/data-table.component';
+import {dataTypes} from './domain/datatype-types.model';
+import {TdDialogService} from '@covalent/core';
+import {DELETE} from '../store/catalogs/catalog.actions';
+import {ActivatedRoute, Router} from '@angular/router';
+import {Field} from '../../services/catalog/domain/field.model';
+
+@Component({
+  templateUrl: './catalog.detail.component.html'
+})
+export class CatalogDetailComponent {
+
+  catalog$: Observable<Catalog>;
+
+  fieldData$: Observable<TableData>;
+
+  columns: any[] = [
+    { name: 'identifier', label: 'Identifier' },
+    { name: 'dataType', label: 'Data type', format: value => dataTypes.find(type => type.type === value).label },
+    { name: 'label', label: 'Label' },
+    { name: 'hint', label: 'Hint' },
+    { name: 'description', label: 'Description' },
+    { name: 'mandatory', label: 'Mandatory' }
+  ];
+
+  constructor(private store: CustomersStore, private dialogService: TdDialogService, private route: ActivatedRoute,
+              private router: Router) {
+    this.catalog$ = store.select(fromCustomers.getCustomerCatalog)
+      .filter(catalog => !!catalog);
+
+    this.fieldData$ = this.catalog$
+      .map(catalog => ({
+        data: catalog.fields,
+        totalElements: catalog.fields.length,
+        totalPages: 1
+      }));
+  }
+
+  rowSelect(field: Field): void {
+    this.router.navigate(['field/detail', field.identifier], { relativeTo: this.route });
+  }
+
+  confirmDeletion(): Observable<boolean> {
+    return this.dialogService.openConfirm({
+      message: 'Do you want to delete this catalog?',
+      title: 'Confirm deletion',
+      acceptButton: 'DELETE CATALOG',
+    }).afterClosed();
+  }
+
+  deleteCatalog(catalog: Catalog): void {
+    this.confirmDeletion()
+      .filter(accept => accept)
+      .subscribe(() => {
+        this.store.dispatch({ type: DELETE, payload: {
+          catalog,
+          activatedRoute: this.route
+        } });
+      });
+  }
+}
diff --git a/src/app/customers/customFields/components/field.component.html b/src/app/customers/customFields/components/field.component.html
new file mode 100644
index 0000000..258d4c6
--- /dev/null
+++ b/src/app/customers/customFields/components/field.component.html
@@ -0,0 +1,53 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<ng-container [formGroup]="form">
+  <div layout="row">
+    <fims-id-input [form]="form" controlName="identifier"></fims-id-input>
+    <fims-text-input [form]="form" controlName="label" placeholder="{{'Label' | translate}}"></fims-text-input>
+  </div>
+  <fims-text-input [form]="form" controlName="hint" placeholder="{{'Hint(Optional)' | translate}}"></fims-text-input>
+  <div layout="row">
+    <mat-form-field layout-margin flex>
+      <textarea matInput placeholder="{{'Description(Optional)' | translate}}" formControlName="description"></textarea>
+    </mat-form-field>
+  </div>
+  <mat-checkbox layout-margin formControlName="mandatory" translate>Mandatory?</mat-checkbox>
+  <mat-radio-group formControlName="dataType">
+    <mat-radio-button *ngFor="let type of dataTypeOptions" [value]="type.type" layout-margin>
+      {{type.label}}
+    </mat-radio-button>
+  </mat-radio-group>
+  <div layout="row">
+    <fims-text-input [hideIfDisabled]="true" type="number" [form]="form" controlName="length" placeholder="{{'Length' | translate}}"></fims-text-input>
+    <fims-text-input [hideIfDisabled]="true" type="number" [form]="form" controlName="precision" placeholder="{{'Max digits' | translate}}"></fims-text-input>
+    <fims-text-input [hideIfDisabled]="true" type="number" [form]="form" controlName="minValue" placeholder="{{'Min value' | translate}}"></fims-text-input>
+    <fims-text-input [hideIfDisabled]="true" type="number" [form]="form" controlName="maxValue" placeholder="{{'Max value' | translate}}"></fims-text-input>
+  </div>
+  <div layout-gt-xs="column" layout-margin formArrayName="options" *ngIf="options.enabled">
+    <h4 translate>Options</h4>
+    <div *ngFor="let option of options.controls; let i=index" [formGroupName]="i" layout="row">
+      <fims-text-input [form]="option" controlName="label" placeholder="{{'Label' | translate}}"></fims-text-input>
+      <fims-text-input type="number" [form]="option" controlName="value" placeholder="{{'Value' | translate}}"></fims-text-input>
+      <button mat-button (click)="removeOption(i)">{{'Remove option' | translate}}</button>
+    </div>
+    <p *ngIf="form.get('options').hasError('optionValueUnique')" class="tc-red-600" translate>
+      Values can't overlap with other values.
+    </p>
+    <button mat-button (click)="addOption()">{{'Add option' | translate}}</button>
+  </div>
+</ng-container>
diff --git a/src/app/customers/customFields/components/field.component.ts b/src/app/customers/customFields/components/field.component.ts
new file mode 100644
index 0000000..70a4e22
--- /dev/null
+++ b/src/app/customers/customFields/components/field.component.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 {FormArray, FormGroup} from '@angular/forms';
+import {Component, EventEmitter, Input, OnChanges, Output, SimpleChanges} from '@angular/core';
+import {DataTypeOption, dataTypes} from '../domain/datatype-types.model';
+
+@Component({
+  selector: 'fims-custom-field-form',
+  templateUrl: './field.component.html'
+})
+export class FieldFormComponent implements OnChanges {
+
+  @Input() form: FormGroup;
+
+  @Input() editMode: boolean;
+
+  @Output('onAddOption') onAddOption = new EventEmitter<void>();
+
+  @Output('onRemoveOption') onRemoveOption = new EventEmitter<number>();
+
+  dataTypeOptions: DataTypeOption[] = dataTypes;
+
+  constructor() {
+  }
+
+  ngOnChanges(changes: SimpleChanges): void {
+    if (changes.form) {
+      this.form.get('dataType').valueChanges
+        .subscribe(dataType => this.toggleDataType());
+
+      this.toggleDataType();
+    }
+  }
+
+  private toggleDataType(): void {
+    const dataType = this.form.get('dataType').value;
+
+    const lengthControl = this.form.get('length');
+    const precisionControl = this.form.get('precision');
+    const minValueControl = this.form.get('minValue');
+    const maxValueControl = this.form.get('maxValue');
+    const optionsControl = this.form.get('options');
+
+    lengthControl.disable();
+    precisionControl.disable();
+    minValueControl.disable();
+    maxValueControl.disable();
+    optionsControl.disable();
+
+    switch (dataType) {
+      case 'TEXT': {
+        if (!this.editMode) {
+          lengthControl.enable();
+        }
+        break;
+      }
+
+      case 'NUMBER': {
+        if (!this.editMode) {
+          precisionControl.enable();
+          minValueControl.enable();
+          maxValueControl.enable();
+        }
+        break;
+      }
+
+      case 'SINGLE_SELECTION':
+      case 'MULTI_SELECTION': {
+        optionsControl.enable();
+        break;
+      }
+
+      default:
+        break;
+    }
+  }
+
+  addOption(): void {
+    this.onAddOption.emit();
+  }
+
+  removeOption(index: number): void {
+    this.onRemoveOption.emit(index);
+  }
+
+  get options(): FormArray {
+    return this.form.get('options') as FormArray;
+  }
+
+}
diff --git a/src/app/customers/customFields/components/value.component.html b/src/app/customers/customFields/components/value.component.html
new file mode 100644
index 0000000..5b3dc2c
--- /dev/null
+++ b/src/app/customers/customFields/components/value.component.html
@@ -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.
+-->
+
+<mat-list-item *ngFor="let field of customCatalog?.fields" [ngSwitch]="field.dataType">
+  <h3 matLine translate>{{field.label}}</h3>
+  <p matLine *ngSwitchCase="'TEXT'">{{field.value}}</p>
+  <p matLine *ngSwitchCase="'NUMBER'">{{field.value | number}}</p>
+  <p matLine *ngSwitchCase="'DATE'">{{field.value | date:'medium'}}</p>
+  <p matLine *ngSwitchCase="'SINGLE_SELECTION'">{{field.value}}</p>
+  <p matLine *ngSwitchCase="'MULTI_SELECTION'">{{field.value}}</p>
+</mat-list-item>
diff --git a/src/app/customers/customFields/components/value.component.ts b/src/app/customers/customFields/components/value.component.ts
new file mode 100644
index 0000000..bd7bcaf
--- /dev/null
+++ b/src/app/customers/customFields/components/value.component.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 {Component, Input, OnChanges, SimpleChanges} from '@angular/core';
+import {Catalog} from '../../../services/catalog/domain/catalog.model';
+import {Value} from '../../../services/catalog/domain/value.model';
+import {Field, FieldDataType} from '../../../services/catalog/domain/field.model';
+import {Option} from '../../../services/catalog/domain/option.model';
+
+interface CustomCatalog {
+  label: string;
+  fields: CustomDetailField[];
+}
+
+interface CustomDetailField {
+  label: string;
+  value: string;
+  dataType: FieldDataType;
+}
+
+@Component({
+  selector: 'fims-customer-custom-values',
+  templateUrl: './value.component.html'
+})
+export class CustomerCustomValuesComponent implements OnChanges {
+
+  @Input() catalog: Catalog;
+
+  @Input() values: Value[];
+
+  customCatalog: CustomCatalog;
+
+  ngOnChanges(changes: SimpleChanges): void {
+    this.customCatalog = this.buildCustomCatalogs(this.values, this.catalog);
+  }
+
+  private buildCustomCatalogs(values: Value[], catalog: Catalog): CustomCatalog {
+    if (!values || !catalog || !catalog.fields) {
+      return;
+    }
+
+    const customCatalog: CustomCatalog = {
+      label: catalog.name,
+      fields: []
+    };
+
+    if (values) {
+      for (const value of values) {
+        const foundField: Field = catalog.fields.find((field: Field) => field.identifier === value.fieldIdentifier );
+
+        let valueString: string = value.value;
+
+        switch (foundField.dataType) {
+          case 'SINGLE_SELECTION': {
+            const foundOption = foundField.options.find((option: Option) => option.value === Number(valueString));
+            valueString = foundOption.label;
+            break;
+          }
+
+          case 'MULTI_SELECTION': {
+            const optionValues = valueString ? valueString.split(',').map(optionValue => Number(optionValue)) : [];
+            const foundOptions = foundField.options
+              .filter((option: Option) => optionValues.indexOf(option.value) > -1)
+              .map((option: Option) => option.label);
+            valueString = foundOptions.join(', ');
+            break;
+          }
+
+          default: {
+            break;
+          }
+        }
+
+        const customField: CustomDetailField = {
+          label: foundField.label,
+          value: valueString,
+          dataType: foundField.dataType
+        };
+
+        customCatalog.fields.push(customField);
+      }
+    }
+
+    return customCatalog;
+  };
+}
diff --git a/src/app/customers/customFields/domain/datatype-types.model.ts b/src/app/customers/customFields/domain/datatype-types.model.ts
new file mode 100644
index 0000000..69d3d6f
--- /dev/null
+++ b/src/app/customers/customFields/domain/datatype-types.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 {FieldDataType} from '../../../services/catalog/domain/field.model';
+
+export interface DataTypeOption {
+  type: FieldDataType;
+  label: string;
+}
+
+export const dataTypes: DataTypeOption[] = [
+  { type: 'TEXT', label: 'Text' },
+  { type: 'NUMBER', label: 'Number' },
+  { type: 'DATE', label: 'Date' },
+  { type: 'SINGLE_SELECTION', label: 'Single selection' },
+  { type: 'MULTI_SELECTION', label: 'Multi selection' }
+];
diff --git a/src/app/customers/customFields/fields/field-exists.guard.ts b/src/app/customers/customFields/fields/field-exists.guard.ts
new file mode 100644
index 0000000..fe3de8f
--- /dev/null
+++ b/src/app/customers/customFields/fields/field-exists.guard.ts
@@ -0,0 +1,44 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, CanActivate, RouterStateSnapshot} from '@angular/router';
+import {Injectable} from '@angular/core';
+import {CustomersStore} from '../../store/index';
+import {ExistsGuardService} from '../../../common/guards/exists-guard';
+import {Observable} from 'rxjs/Observable';
+import * as fromCustomers from '../../store';
+
+@Injectable()
+export class FieldExistsGuard implements CanActivate {
+
+  constructor(private store: CustomersStore,
+              private existsGuardService: ExistsGuardService) {
+  }
+
+  hasFieldInCatalog(id: string): Observable<boolean> {
+    const getField$ = this.store.select(fromCustomers.getCustomerCatalog)
+      .map(catalog => catalog.fields.find(field => field.identifier === id))
+      .map(field => !!field);
+
+    return this.existsGuardService.routeTo404OnError(getField$);
+  }
+
+  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
+    return this.hasFieldInCatalog(route.params['fieldId']);
+  }
+}
diff --git a/src/app/customers/customFields/fields/field.detail.component.html b/src/app/customers/customFields/fields/field.detail.component.html
new file mode 100644
index 0000000..87fd872
--- /dev/null
+++ b/src/app/customers/customFields/fields/field.detail.component.html
@@ -0,0 +1,73 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<fims-layout-card-over title="{{'Custom field' | translate }}" [navigateBackTo]="['../../../../../../']" *ngIf="(field$ | async) as field">
+  <fims-layout-card-over-header-menu *ngIf="(catalog$ | async) as catalog">
+    <button mat-icon-button (click)="deleteField(catalog.identifier, field)" title="{{'Delete this field' | translate}}">
+      <mat-icon>delete</mat-icon>
+    </button>
+  </fims-layout-card-over-header-menu>
+  <div class="mat-content inset" flex>
+    <div layout="row">
+      <mat-list>
+        <mat-list-item>
+          <h3 matLine translate>Data type</h3>
+          <p matLine>{{field.dataType}}</p>
+        </mat-list-item>
+        <mat-list-item>
+          <h3 matLine translate>Label</h3>
+          <p matLine>{{field.label}}</p>
+        </mat-list-item>
+        <mat-list-item>
+          <h3 matLine translate>Hint</h3>
+          <p matLine>{{field.hint}}</p>
+        </mat-list-item>
+        <mat-list-item>
+          <h3 matLine translate>Description</h3>
+          <p matLine>{{field.description}}</p>
+        </mat-list-item>
+        <mat-list-item>
+          <h3 matLine translate>Mandatory</h3>
+          <p matLine>{{field.mandatory}}</p>
+        </mat-list-item>
+        <mat-list-item>
+          <h3 matLine translate>Length</h3>
+          <p matLine>{{field.length}}</p>
+        </mat-list-item>
+        <mat-list-item>
+          <h3 matLine translate>Max digits</h3>
+          <p matLine>{{field.precision}}</p>
+        </mat-list-item>
+        <mat-list-item>
+          <h3 matLine translate>Min value</h3>
+          <p matLine>{{field.minValue}}</p>
+        </mat-list-item>
+        <mat-list-item>
+          <h3 matLine translate>Max value</h3>
+          <p matLine>{{field.maxValue}}</p>
+        </mat-list-item>
+        <mat-list-item>
+          <h3 matLine translate>Created by</h3>
+          <p matLine>{{field.createdBy}} - {{field.createdOn | date:'medium'}}</p>
+        </mat-list-item>
+      </mat-list>
+    </div>
+  </div>
+</fims-layout-card-over>
+<fims-fab-button title="{{'Edit field' | translate}}" icon="mode_edit" [link]="['edit']"
+                 [permission]="{ id: 'catalog_catalogs', accessLevel: 'CHANGE'}">
+</fims-fab-button>
diff --git a/src/app/customers/customFields/fields/field.detail.component.ts b/src/app/customers/customFields/fields/field.detail.component.ts
new file mode 100644
index 0000000..ec13f20
--- /dev/null
+++ b/src/app/customers/customFields/fields/field.detail.component.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 {Component} from '@angular/core';
+import {Observable} from 'rxjs/Observable';
+import {DELETE_FIELD} from '../../store/catalogs/catalog.actions';
+import {Field} from '../../../services/catalog/domain/field.model';
+import {CustomersStore} from '../../store/index';
+import {TdDialogService} from '@covalent/core';
+import * as fromCustomers from '../../store';
+import {ActivatedRoute} from '@angular/router';
+import {Catalog} from '../../../services/catalog/domain/catalog.model';
+
+@Component({
+  templateUrl: './field.detail.component.html'
+})
+export class FieldDetailComponent {
+
+  catalog$: Observable<Catalog>;
+
+  field$: Observable<Field>;
+
+  constructor(private store: CustomersStore, private dialogService: TdDialogService, private route: ActivatedRoute) {
+    this.catalog$ = store.select(fromCustomers.getCustomerCatalog);
+    this.field$ = store.select(fromCustomers.getSelectedField);
+  }
+
+  confirmDeletion(): Observable<boolean> {
+    return this.dialogService.openConfirm({
+      message: 'Do you want to delete this field?',
+      title: 'Confirm deletion',
+      acceptButton: 'DELETE FIELD',
+    }).afterClosed();
+  }
+
+  deleteField(catalogIdentifier: string, field: Field): void {
+    this.confirmDeletion()
+      .filter(accept => accept)
+      .subscribe(() => {
+        this.store.dispatch({ type: DELETE_FIELD, payload: {
+          catalogIdentifier,
+          field,
+          activatedRoute: this.route
+        } });
+      });
+  }
+}
diff --git a/src/app/customers/customFields/fields/field.index.component.html b/src/app/customers/customFields/fields/field.index.component.html
new file mode 100644
index 0000000..ca721b3
--- /dev/null
+++ b/src/app/customers/customFields/fields/field.index.component.html
@@ -0,0 +1,18 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<router-outlet></router-outlet>
diff --git a/src/app/customers/customFields/fields/field.index.component.ts b/src/app/customers/customFields/fields/field.index.component.ts
new file mode 100644
index 0000000..c1f4b1b
--- /dev/null
+++ b/src/app/customers/customFields/fields/field.index.component.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 {Component, OnDestroy, OnInit} from '@angular/core';
+import {CustomersStore} from '../../store/index';
+import {ActivatedRoute} from '@angular/router';
+import {Subscription} from 'rxjs/Subscription';
+import {SelectFieldAction} from '../../store/catalogs/catalog.actions';
+
+@Component({
+  templateUrl: './field.index.component.html'
+})
+export class FieldIndexComponent implements OnInit, OnDestroy {
+
+  private actionsSubscription: Subscription;
+
+  constructor(private route: ActivatedRoute, private store: CustomersStore) {}
+
+  ngOnInit(): void {
+    this.actionsSubscription = this.route.params
+      .map(params => new SelectFieldAction(params['fieldId']))
+      .subscribe(this.store);
+  }
+
+  ngOnDestroy(): void {
+    this.actionsSubscription.unsubscribe();
+  }
+}
diff --git a/src/app/customers/customFields/fields/form/edit.form.component.html b/src/app/customers/customFields/fields/form/edit.form.component.html
new file mode 100644
index 0000000..37e08f7
--- /dev/null
+++ b/src/app/customers/customFields/fields/form/edit.form.component.html
@@ -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.
+-->
+
+<fims-layout-card-over title="{{'Change custom field' | translate}}" [navigateBackTo]="['../']" *ngIf="(catalog$ | async) as catalog">
+  <fims-catalog-custom-field-form
+    [field]="field$ | async"
+    (onSave)="onSave(catalog.identifier, $event)"
+    (onCancel)="onCancel()">
+  </fims-catalog-custom-field-form>
+</fims-layout-card-over>
diff --git a/src/app/customers/customFields/fields/form/edit.form.component.ts b/src/app/customers/customFields/fields/form/edit.form.component.ts
new file mode 100644
index 0000000..79cc7c1
--- /dev/null
+++ b/src/app/customers/customFields/fields/form/edit.form.component.ts
@@ -0,0 +1,56 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 * as fromCustomers from '../../../store/index';
+import {CustomersStore} from '../../../store/index';
+import {ActivatedRoute, Router} from '@angular/router';
+import {Observable} from 'rxjs/Observable';
+import {Field} from '../../../../services/catalog/domain/field.model';
+import {UPDATE_FIELD} from '../../../store/catalogs/catalog.actions';
+import {Catalog} from '../../../../services/catalog/domain/catalog.model';
+
+@Component({
+  templateUrl: './edit.form.component.html'
+})
+export class EditCatalogFieldFormComponent {
+
+  catalog$: Observable<Catalog>;
+
+  field$: Observable<Field>;
+
+  constructor(private store: CustomersStore, private router: Router, private route: ActivatedRoute) {
+    this.catalog$ = store.select(fromCustomers.getCustomerCatalog);
+    this.field$ = store.select(fromCustomers.getSelectedField);
+  }
+
+  onSave(catalogIdentifier: string, field: Field): void {
+    this.store.dispatch({
+      type: UPDATE_FIELD,
+      payload: {
+        catalogIdentifier,
+        field,
+        activatedRoute: this.route
+      }
+    });
+  }
+
+  onCancel(): void {
+    this.router.navigate(['../'], { relativeTo: this.route });
+  }
+}
diff --git a/src/app/customers/customFields/fields/form/form.component.html b/src/app/customers/customFields/fields/form/form.component.html
new file mode 100644
index 0000000..e3c0c12
--- /dev/null
+++ b/src/app/customers/customFields/fields/form/form.component.html
@@ -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.
+-->
+
+<td-steps mode="'vertical'">
+  <td-step #detailsStep label="{{'Update custom field' | translate}}" [state]="form.valid ? 'complete' : form.pristine ? 'none' : 'required'">
+    <fims-custom-field-form
+      [editMode]="true"
+      [form]="form"
+      (onAddOption)="addOption()"
+      (onRemoveOption)="removeOption($event)">
+    </fims-custom-field-form>
+    <ng-template td-step-actions>
+      <fims-form-final-action
+        [resourceName]="'CUSTOM FIELD'"
+        [editMode]="true"
+        [disabled]="!form.valid"
+        (onCancel)="cancel()"
+        (onSave)="save()">
+      </fims-form-final-action>
+    </ng-template>
+  </td-step>
+</td-steps>
diff --git a/src/app/customers/customFields/fields/form/form.component.ts b/src/app/customers/customFields/fields/form/form.component.ts
new file mode 100644
index 0000000..40f894c
--- /dev/null
+++ b/src/app/customers/customFields/fields/form/form.component.ts
@@ -0,0 +1,87 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, OnChanges, OnInit, Output, SimpleChanges, ViewChild} from '@angular/core';
+import {FormGroup} from '@angular/forms';
+import {TdStepComponent} from '@covalent/core';
+import {Field} from '../../../../services/catalog/domain/field.model';
+import {Option} from '../../../../services/catalog/domain/option.model';
+import {FieldFormService} from '../../services/field-form.service';
+
+@Component({
+  selector: 'fims-catalog-custom-field-form',
+  templateUrl: './form.component.html'
+})
+export class CatalogFieldFormComponent implements OnInit, OnChanges {
+
+  form: FormGroup;
+
+  @ViewChild('detailsStep') detailsStep: TdStepComponent;
+
+  @Input('field') field: Field;
+
+  @Output('onSave') onSave = new EventEmitter<Field>();
+
+  @Output('onCancel') onCancel = new EventEmitter<void>();
+
+  constructor(private fieldFormService: FieldFormService) {
+    this.form = fieldFormService.buildForm();
+  }
+
+  ngOnInit(): void {
+    this.detailsStep.open();
+  }
+
+  ngOnChanges(changes: SimpleChanges): void {
+    if (changes.field) {
+      this.fieldFormService.resetForm(this.form, this.field);
+
+      this.form.get('identifier').disable();
+      this.form.get('dataType').disable();
+      this.form.get('length').disable();
+      this.form.get('precision').disable();
+      this.form.get('minValue').disable();
+      this.form.get('maxValue').disable();
+    }
+  }
+
+  save(): void {
+    const field: Field = Object.assign({}, this.field, {
+      label: this.form.get('label').value,
+      hint: this.form.get('hint').value,
+      description: this.form.get('description').value,
+      mandatory: this.form.get('mandatory').value,
+      options: this.form.get('options').value
+    });
+
+    this.onSave.emit(field);
+  }
+
+  cancel(): void {
+    this.onCancel.emit();
+  }
+
+  addOption(option?: Option): void {
+    this.fieldFormService.addOption(this.form, option);
+  }
+
+  removeOption(index: number): void {
+    this.fieldFormService.removeOption(this.form, index);
+  }
+
+}
diff --git a/src/app/customers/customFields/form/create.form.component.html b/src/app/customers/customFields/form/create.form.component.html
new file mode 100644
index 0000000..d21bae9
--- /dev/null
+++ b/src/app/customers/customFields/form/create.form.component.html
@@ -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.
+-->
+
+<fims-layout-card-over title="{{'Create custom fields' | translate}}" [navigateBackTo]="['../']">
+  <fims-customer-catalog-form
+    [catalog]="catalog"
+    (onSave)="onSave($event)"
+    (onCancel)="onCancel()">
+  </fims-customer-catalog-form>
+</fims-layout-card-over>
diff --git a/src/app/customers/customFields/form/create.form.component.ts b/src/app/customers/customFields/form/create.form.component.ts
new file mode 100644
index 0000000..ddc9d11
--- /dev/null
+++ b/src/app/customers/customFields/form/create.form.component.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 {Component} from '@angular/core';
+import {CustomersStore} from '../../store/index';
+import {ActivatedRoute, Router} from '@angular/router';
+import {Catalog} from '../../../services/catalog/domain/catalog.model';
+import {CREATE} from '../../store/catalogs/catalog.actions';
+
+@Component({
+  templateUrl: './create.form.component.html'
+})
+export class CreateCustomerCatalogFormComponent {
+
+  catalog: Catalog = {
+    identifier: 'customers',
+    name: '',
+    fields: []
+  };
+
+  constructor(private store: CustomersStore, private router: Router, private route: ActivatedRoute) {}
+
+  onSave(catalog: Catalog): void {
+    this.store.dispatch({
+      type: CREATE,
+      payload: {
+        catalog,
+        activatedRoute: this.route
+      }
+    });
+  }
+
+  onCancel(): void {
+    this.router.navigate(['../'], { relativeTo: this.route });
+  }
+}
diff --git a/src/app/customers/customFields/form/form.component.html b/src/app/customers/customFields/form/form.component.html
new file mode 100644
index 0000000..9151d2f
--- /dev/null
+++ b/src/app/customers/customFields/form/form.component.html
@@ -0,0 +1,56 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<td-steps mode="'vertical'">
+  <td-step #detailsStep label="{{'Create custom fields' | translate}}" [state]="form.valid ? 'complete' : form.pristine ? 'none' : 'required'">
+    <form [formGroup]="form">
+      <fims-text-input [form]="form" controlName="name" placeholder="{{'Name' | translate}}"></fims-text-input>
+      <div layout="row">
+        <mat-form-field layout-margin flex>
+          <textarea matInput placeholder="{{'Description(Optional)' | translate}}" formControlName="description"></textarea>
+        </mat-form-field>
+      </div>
+      <div layout-gt-xs="column" layout-margin formArrayName="fields">
+        <h4 translate>Fields</h4>
+        <mat-card *ngFor="let field of fields; let i=index" [formGroupName]="i">
+          <mat-card-content>
+            <fims-custom-field-form
+              [form]="field"
+              (onAddOption)="addOption(field)"
+              (onRemoveOption)="removeOption(field, $event)">
+            </fims-custom-field-form>
+          </mat-card-content>
+          <mat-card-actions>
+            <button mat-button (click)="removeField(i)">{{'Remove field' | translate}}</button>
+          </mat-card-actions>
+        </mat-card>
+        <div layout="row">
+          <button mat-button (click)="addField()">{{'Add field' | translate}}</button>
+        </div>
+      </div>
+    </form>
+    <ng-template td-step-actions>
+      <fims-form-final-action
+        [resourceName]="'CUSTOM FIELDS'"
+        [editMode]="false"
+        [disabled]="!form.valid"
+        (onCancel)="cancel()"
+        (onSave)="save()">
+      </fims-form-final-action>
+    </ng-template>
+  </td-step>
+</td-steps>
diff --git a/src/app/customers/customFields/form/form.component.ts b/src/app/customers/customFields/form/form.component.ts
new file mode 100644
index 0000000..d4f195d
--- /dev/null
+++ b/src/app/customers/customFields/form/form.component.ts
@@ -0,0 +1,116 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, OnChanges, OnInit, Output, SimpleChanges, ViewChild} from '@angular/core';
+import {AbstractControl, FormArray, FormBuilder, FormGroup, Validators} from '@angular/forms';
+import {TdStepComponent} from '@covalent/core';
+import {Catalog} from '../../../services/catalog/domain/catalog.model';
+import {Field} from '../../../services/catalog/domain/field.model';
+import {FieldFormService} from '../services/field-form.service';
+import {Option} from '../../../services/catalog/domain/option.model';
+
+@Component({
+  selector: 'fims-customer-catalog-form',
+  templateUrl: './form.component.html'
+})
+export class CustomerCatalogFormComponent implements OnInit, OnChanges {
+
+  form: FormGroup;
+
+  @ViewChild('detailsStep') detailsStep: TdStepComponent;
+
+  @Input('catalog') catalog: Catalog;
+
+  @Output('onSave') onSave = new EventEmitter<Catalog>();
+
+  @Output('onCancel') onCancel = new EventEmitter<void>();
+
+  constructor(private formBuilder: FormBuilder, private fieldFormService: FieldFormService) {
+    this.form = this.formBuilder.group({
+      name: ['', [Validators.required, Validators.maxLength(256)]],
+      description: ['', [Validators.maxLength(4096)]],
+      fields: this.initFields([])
+    });
+  }
+
+  ngOnInit(): void {
+    this.detailsStep.open();
+  }
+
+  ngOnChanges(changes: SimpleChanges): void {
+    if (changes.catalog) {
+      this.form.reset({
+        name: this.catalog.name,
+        description: this.catalog.description
+      });
+
+      this.catalog.fields.forEach(field => this.addField(field));
+    }
+  }
+
+  save(): void {
+    const catalog: Catalog = Object.assign({}, this.catalog, {
+      name: this.form.get('name').value,
+      description: this.form.get('description').value,
+      fields: this.form.get('fields').value
+    });
+
+    this.onSave.emit(catalog);
+  }
+
+  cancel(): void {
+    this.onCancel.emit();
+  }
+
+  private initFields(fields: Field[]): FormArray {
+    const formControls: FormGroup[] = [];
+    fields.forEach(allocation => formControls.push(this.initField(allocation)));
+    return this.formBuilder.array(formControls);
+  }
+
+  private initField(field?: Field): FormGroup {
+    const formGroup = this.fieldFormService.buildForm();
+
+    this.fieldFormService.resetForm(formGroup, field);
+
+    return formGroup;
+  }
+
+  addField(field?: Field): void {
+    const fields: FormArray = this.form.get('fields') as FormArray;
+    fields.push(this.initField(field));
+  }
+
+  removeField(index: number): void {
+    const fields: FormArray = this.form.get('fields') as FormArray;
+    fields.removeAt(index);
+  }
+
+  get fields(): AbstractControl[] {
+    const fields: FormArray = this.form.get('fields') as FormArray;
+    return fields.controls;
+  }
+
+  addOption(form: FormGroup, option?: Option): void {
+    this.fieldFormService.addOption(form, option);
+  }
+
+  removeOption(form: FormGroup, index: number): void {
+    this.fieldFormService.removeOption(form, index);
+  }
+}
diff --git a/src/app/customers/customFields/services/field-form.service.ts b/src/app/customers/customFields/services/field-form.service.ts
new file mode 100644
index 0000000..6fd0fd6
--- /dev/null
+++ b/src/app/customers/customFields/services/field-form.service.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 {FormArray, FormBuilder, FormGroup, Validators} from '@angular/forms';
+import {Injectable} from '@angular/core';
+import {Field} from '../../../services/catalog/domain/field.model';
+import {Option} from '../../../services/catalog/domain/option.model';
+import {FimsValidators} from '../../../common/validator/validators';
+import {optionValueUnique} from './option-value-unique.validator';
+
+@Injectable()
+export class FieldFormService {
+
+  constructor(private formBuilder: FormBuilder) {
+  }
+
+  buildForm(): FormGroup {
+    return this.formBuilder.group({
+      identifier: ['', [Validators.required]],
+      dataType: ['', [Validators.required]],
+      label: ['', [Validators.required, Validators.maxLength(256)]],
+      hint: ['', [Validators.maxLength(512)]],
+      description: ['', [Validators.maxLength(4096)]],
+      mandatory: [''],
+      length: ['', [FimsValidators.minValue(1), FimsValidators.maxScale(0)]],
+      precision: ['', [FimsValidators.minValue(0), FimsValidators.maxScale(0)]],
+      minValue: ['', [FimsValidators.minValue(0)]],
+      maxValue: ['', [FimsValidators.minValue(1)]],
+      options: this.formBuilder.array([], optionValueUnique)
+    });
+  }
+
+  resetForm(form: FormGroup, field: Field): void {
+    form.reset({
+      identifier: field ? field.identifier : '',
+      dataType: field ? field.dataType : 'TEXT',
+      label: field ? field.label : '',
+      hint: field ? field.hint : '',
+      description: field ? field.description : '',
+      mandatory: field ? field.mandatory : false,
+      length: field ? field.length : 1,
+      precision: field ? field.precision : 0,
+      minValue: field ? field.minValue : 0,
+      maxValue: field ? field.maxValue : 1
+    });
+
+    if (field && field.options) {
+      field.options.forEach(option => this.addOption(form, option));
+    }
+  }
+
+  initOption(option?: Option): FormGroup {
+    return this.formBuilder.group({
+      label: [option ? option.label : '', [Validators.required, Validators.maxLength(256)]],
+      value: [option ? option.value : '', [Validators.required, FimsValidators.minValue(0)]]
+    });
+  }
+
+  addOption(form: FormGroup, option?: Option): void {
+    const options: FormArray = form.get('options') as FormArray;
+    options.push(this.initOption(option));
+  }
+
+  removeOption(form: FormGroup, index: number): void {
+    const options: FormArray = form.get('options') as FormArray;
+    options.removeAt(index);
+  }
+}
diff --git a/src/app/customers/customFields/services/option-value-unique.validator.ts b/src/app/customers/customFields/services/option-value-unique.validator.ts
new file mode 100644
index 0000000..0fc3185
--- /dev/null
+++ b/src/app/customers/customFields/services/option-value-unique.validator.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 {FormArray, FormGroup, ValidationErrors} from '@angular/forms';
+
+export function optionValueUnique(array: FormArray): ValidationErrors | null {
+  const options: FormGroup[] = array.controls as FormGroup[];
+
+  const values = options
+    .map(optionGroup => parseInt(optionGroup.get('value').value, 10));
+
+  const set = new Set();
+
+  values.forEach(number => set.add(number));
+
+  if (set.size !== values.length) {
+    return {
+      optionValueUnique: true
+    };
+  }
+
+  return null;
+}
diff --git a/src/app/customers/customer-exists.guard.ts b/src/app/customers/customer-exists.guard.ts
new file mode 100644
index 0000000..cec8fc7
--- /dev/null
+++ b/src/app/customers/customer-exists.guard.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 {ActivatedRouteSnapshot, CanActivate, RouterStateSnapshot} from '@angular/router';
+import {Injectable} from '@angular/core';
+import * as fromCustomers from './store';
+import {Observable} from 'rxjs/Observable';
+import {LoadAction} from './store/customer.actions';
+import {of} from 'rxjs/observable/of';
+import {CustomerService} from '../services/customer/customer.service';
+import {CustomersStore} from './store/index';
+import {ExistsGuardService} from '../common/guards/exists-guard';
+
+@Injectable()
+export class CustomerExistsGuard implements CanActivate {
+
+  constructor(private store: CustomersStore,
+              private customerService: CustomerService,
+              private existsGuardService: ExistsGuardService) {
+  }
+
+  hasCustomerInStore(id: string): Observable<boolean> {
+    const timestamp$: Observable<number> = this.store.select(fromCustomers.getCustomerLoadedAt)
+      .map(loadedAt => loadedAt[id]);
+
+    return this.existsGuardService.isWithinExpiry(timestamp$);
+  }
+
+  hasCustomerInApi(id: string): Observable<boolean> {
+    const getCustomer$: Observable<any> = this.customerService.getCustomer(id)
+      .map(customerEntity => new LoadAction({
+        resource: customerEntity
+      }))
+      .do((action: LoadAction) => this.store.dispatch(action))
+      .map(customer => !!customer);
+
+    return this.existsGuardService.routeTo404OnError(getCustomer$);
+  }
+
+  hasCustomer(id: string): Observable<boolean> {
+    return this.hasCustomerInStore(id)
+      .switchMap(inStore => {
+        if (inStore) {
+          return of(inStore);
+        }
+        return this.hasCustomerInApi(id);
+      });
+  }
+
+  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
+    return this.hasCustomer(route.params['id']);
+  }
+}
diff --git a/src/app/customers/customer.component.html b/src/app/customers/customer.component.html
new file mode 100644
index 0000000..7cd20a6
--- /dev/null
+++ b/src/app/customers/customer.component.html
@@ -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.
+-->
+
+<fims-layout-card-over title="{{'Manage members' | translate}}">
+  <fims-layout-card-over-header-menu>
+    <td-search-box #searchBox placeholder="{{'Search' | translate}}" (search)="search($event)" [alwaysVisible]="false"></td-search-box>
+  </fims-layout-card-over-header-menu>
+  <fims-two-column-layout>
+    <mat-nav-list left>
+      <h3 mat-subheader translate>Management</h3>
+      <a mat-list-item [routerLink]="['tasks']" *hasPermission="{ id: 'customer_tasks', accessLevel: 'READ' }">
+        <mat-icon matListAvatar>playlist_add_check</mat-icon>
+        <h3 matLine translate>Tasks</h3>
+        <p matLine translate>Manage tasks</p>
+      </a>
+      <a mat-list-item [routerLink]="['catalog/detail']" *hasPermission="{ id: 'catalog_catalogs', accessLevel: 'READ' }">
+        <mat-icon matListAvatar>format_list_numbered</mat-icon>
+        <h3 matLine translate>Custom fields</h3>
+        <p matLine translate>Manage custom fields</p>
+      </a>
+    </mat-nav-list>
+    <fims-data-table
+      right
+      (onFetch)="fetchCustomers($event)"
+      (onActionCellClick)="rowSelect($event)"
+      [columns]="columns"
+      [data]="customerData$ | async"
+      [loading]="loading$ | async"
+      [sortable]="true"
+      [pageable]="true">
+    </fims-data-table>
+  </fims-two-column-layout>
+
+</fims-layout-card-over>
+<fims-fab-button title="{{'Create new member ' | translate}}" icon="add" [link]="['create']" [permission]="{ id: 'customer_customers', accessLevel: 'CHANGE'}"></fims-fab-button>
diff --git a/src/app/customers/customer.component.ts b/src/app/customers/customer.component.ts
new file mode 100644
index 0000000..5c52edb
--- /dev/null
+++ b/src/app/customers/customer.component.ts
@@ -0,0 +1,88 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, OnInit} from '@angular/core';
+import {ActivatedRoute, Params, Router} from '@angular/router';
+import {FetchRequest} from '../services/domain/paging/fetch-request.model';
+import {TableData, TableFetchRequest} from '../common/data-table/data-table.component';
+import {Customer} from '../services/customer/domain/customer.model';
+import {Observable} from 'rxjs/Observable';
+import * as fromRoot from '../store';
+import {SEARCH} from '../store/customer/customer.actions';
+import {CustomersStore} from './store/index';
+
+@Component({
+  templateUrl: './customer.component.html'
+})
+export class CustomerComponent implements OnInit {
+
+  customerData$: Observable<TableData>;
+
+  loading$: Observable<boolean>;
+
+  columns: any[] = [
+    { name: 'identifier', label: 'Id' },
+    { name: 'givenName', label: 'First Name' },
+    { name: 'surname', label: 'Last Name' },
+    { name: 'currentState', label: 'Current status' }
+  ];
+
+  private searchTerm: string;
+
+  private lastFetchRequest: FetchRequest = {};
+
+  constructor(private router: Router, private route: ActivatedRoute, private store: CustomersStore) {}
+
+  ngOnInit(): void {
+    this.customerData$ = this.store.select(fromRoot.getCustomerSearchResults)
+      .map(customerPage => ({
+        data: customerPage.customers,
+        totalElements: customerPage.totalElements,
+        totalPages: customerPage.totalPages
+      }));
+
+    this.loading$ = this.store.select(fromRoot.getCustomerSearchLoading);
+
+    this.route.queryParams.subscribe((params: Params) => {
+      this.search(params['term']);
+    });
+  }
+
+  search(searchTerm: string): void {
+    this.searchTerm = searchTerm;
+    this.fetchCustomers();
+  }
+
+  rowSelect(customer: Customer): void {
+    this.router.navigate(['detail', customer.identifier], { relativeTo: this.route });
+  }
+
+  fetchCustomers(fetchRequest?: TableFetchRequest): void {
+    if (fetchRequest) {
+      this.lastFetchRequest = fetchRequest;
+    }
+
+    this.lastFetchRequest.searchTerm = this.searchTerm;
+
+    this.store.dispatch({ type: SEARCH, payload: this.lastFetchRequest });
+  }
+
+  goToTasks(): void {
+    this.router.navigate(['tasks'], { relativeTo: this.route });
+  }
+}
diff --git a/src/app/customers/customer.module.ts b/src/app/customers/customer.module.ts
new file mode 100644
index 0000000..0b58c5f
--- /dev/null
+++ b/src/app/customers/customer.module.ts
@@ -0,0 +1,190 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 {RouterModule} from '@angular/router';
+import {CustomerRoutes} from './customer.routing';
+import {NgModule} from '@angular/core';
+import {CustomerComponent} from './customer.component';
+import {CustomerFormComponent} from './form/form.component';
+import {CreateCustomerFormComponent} from './form/create/create.form.component';
+import {FimsSharedModule} from '../common/common.module';
+import {CustomerDetailComponent} from './detail/customer.detail.component';
+import {CustomerDetailFormComponent} from './form/detail/detail.component';
+import {CustomerOfficesComponent} from './form/offices/offices.component';
+import {CustomerEmployeesComponent} from './form/employees/employees.component';
+import {CustomerContactFormComponent} from './form/contact/contact.component';
+import {EditCustomerFormComponent} from './form/edit/edit.form.component';
+import {CustomerCustomFieldsComponent} from './form/customFields/custom-fields.component';
+import {CustomerStatusComponent} from './detail/status/status.component';
+import {CustomerActivityComponent} from './detail/activity/activity.component';
+import {CustomerIndexComponent} from './detail/customer.index.component';
+import {CustomerExistsGuard} from './customer-exists.guard';
+import {CustomersStore, customerStoreFactory} from './store/index';
+import {Store} from '@ngrx/store';
+import {CustomerNotificationEffects} from './store/effects/notification.effects';
+import {CustomerRouteEffects} from './store/effects/route.effects';
+import {EffectsModule} from '@ngrx/effects';
+import {CustomerApiEffects} from './store/effects/service.effects';
+import {CustomerCommandApiEffects} from './store/commands/effects/service.effects';
+import {CustomerTasksNotificationEffects} from './store/customerTasks/effects/notification.effects';
+import {CustomerTasksApiEffects} from './store/customerTasks/effects/service.effects';
+import {CustomerTasksRouteEffects} from './store/customerTasks/effects/route.effects';
+import {CustomerPortraitComponent} from './detail/portrait/portrait.component';
+import {TranslateModule} from '@ngx-translate/core';
+import {CommonModule} from '@angular/common';
+import {FormsModule, ReactiveFormsModule} from '@angular/forms';
+import {
+  MatButtonModule,
+  MatCardModule,
+  MatCheckboxModule,
+  MatIconModule,
+  MatInputModule,
+  MatListModule,
+  MatOptionModule,
+  MatRadioModule,
+  MatSelectModule,
+  MatToolbarModule
+} from '@angular/material';
+import {CovalentChipsModule, CovalentFileModule, CovalentMessageModule, CovalentSearchModule, CovalentStepsModule} from '@covalent/core';
+import {TaskListComponent} from './tasks/task.list.component';
+import {TasksApiEffects} from './store/tasks/effects/service.effects';
+import {TasksRouteEffects} from './store/tasks/effects/route.effects';
+import {TasksNotificationEffects} from './store/tasks/effects/notification.effects';
+import {TaskCreateFormComponent} from './tasks/form/create.form.component';
+import {TaskEditFormComponent} from './tasks/form/edit.form.component';
+import {TaskFormComponent} from './tasks/form/form.component';
+import {TaskExistsGuard} from './tasks/task-exists.guard';
+import {TaskIndexComponent} from './tasks/task.index.component';
+import {TaskDetailComponent} from './tasks/task.detail.component';
+import {CustomerTaskComponent} from './detail/status/customer-task.component';
+import {CustomerPayrollFormComponent} from './detail/payroll/form/form.component';
+import {CustomerPayrollDetailComponent} from './detail/payroll/payroll.detail.component';
+import {CreateCustomerPayrollFormComponent} from './detail/payroll/form/create.form.component';
+import {PayrollExistsGuard} from './detail/payroll/payroll-exists.guard';
+import {CustomerPayrollApiEffects} from './store/payroll/effects/service.effects';
+import {CustomerPayrollRouteEffects} from './store/payroll/effects/route.effects';
+import {CustomerPayrollNotificationEffects} from './store/payroll/effects/notification.effects';
+import {CatalogExistsGuard} from './customFields/catalog-exists.guard';
+import {CreateCustomerCatalogFormComponent} from './customFields/form/create.form.component';
+import {CatalogDetailComponent} from './customFields/catalog.detail.component';
+import {CustomerCatalogFormComponent} from './customFields/form/form.component';
+import {FieldFormComponent} from './customFields/components/field.component';
+import {CatalogApiEffects} from './store/catalogs/effects/service.effects';
+import {CatalogRouteEffects} from './store/catalogs/effects/route.effects';
+import {CatalogNotificationEffects} from './store/catalogs/effects/notification.effects';
+import {FieldFormService} from './customFields/services/field-form.service';
+import {EditCatalogFieldFormComponent} from './customFields/fields/form/edit.form.component';
+import {FieldDetailComponent} from './customFields/fields/field.detail.component';
+import {FieldIndexComponent} from './customFields/fields/field.index.component';
+import {CatalogFieldFormComponent} from './customFields/fields/form/form.component';
+import {FieldExistsGuard} from './customFields/fields/field-exists.guard';
+import {CustomerCustomValuesComponent} from './customFields/components/value.component';
+
+@NgModule({
+  imports: [
+    RouterModule.forChild(CustomerRoutes),
+    FimsSharedModule,
+    TranslateModule,
+    CommonModule,
+    FormsModule,
+    ReactiveFormsModule,
+    MatCardModule,
+    MatIconModule,
+    MatListModule,
+    MatToolbarModule,
+    MatInputModule,
+    MatButtonModule,
+    MatRadioModule,
+    MatCheckboxModule,
+    MatOptionModule,
+    MatSelectModule,
+    CovalentSearchModule,
+    CovalentStepsModule,
+    CovalentFileModule,
+    CovalentMessageModule,
+    CovalentChipsModule,
+
+    EffectsModule.run(CustomerApiEffects),
+    EffectsModule.run(CustomerRouteEffects),
+    EffectsModule.run(CustomerNotificationEffects),
+
+    EffectsModule.run(TasksApiEffects),
+    EffectsModule.run(TasksRouteEffects),
+    EffectsModule.run(TasksNotificationEffects),
+
+    EffectsModule.run(CustomerTasksApiEffects),
+    EffectsModule.run(CustomerTasksRouteEffects),
+    EffectsModule.run(CustomerTasksNotificationEffects),
+    EffectsModule.run(CustomerCommandApiEffects),
+
+    EffectsModule.run(CustomerPayrollApiEffects),
+    EffectsModule.run(CustomerPayrollRouteEffects),
+    EffectsModule.run(CustomerPayrollNotificationEffects),
+
+    EffectsModule.run(CatalogApiEffects),
+    EffectsModule.run(CatalogRouteEffects),
+    EffectsModule.run(CatalogNotificationEffects),
+  ],
+  declarations: [
+    CustomerComponent,
+    CustomerDetailFormComponent,
+    CustomerContactFormComponent,
+    CustomerCustomFieldsComponent,
+    CustomerOfficesComponent,
+    CustomerEmployeesComponent,
+    CustomerFormComponent,
+    CreateCustomerFormComponent,
+    EditCustomerFormComponent,
+    CustomerIndexComponent,
+    CustomerDetailComponent,
+    CustomerStatusComponent,
+    CustomerActivityComponent,
+    CustomerPortraitComponent,
+    TaskListComponent,
+    TaskIndexComponent,
+    TaskCreateFormComponent,
+    TaskEditFormComponent,
+    TaskFormComponent,
+    TaskDetailComponent,
+    CustomerTaskComponent,
+
+    CustomerPayrollDetailComponent,
+    CreateCustomerPayrollFormComponent,
+    CustomerPayrollFormComponent,
+
+    CatalogDetailComponent,
+    CreateCustomerCatalogFormComponent,
+    CustomerCatalogFormComponent,
+    CatalogFieldFormComponent,
+    FieldFormComponent,
+    EditCatalogFieldFormComponent,
+    FieldIndexComponent,
+    FieldDetailComponent,
+    CustomerCustomValuesComponent
+  ],
+  providers: [
+    FieldFormService,
+    CustomerExistsGuard,
+    TaskExistsGuard,
+    PayrollExistsGuard,
+    CatalogExistsGuard,
+    FieldExistsGuard,
+    { provide: CustomersStore, useFactory: customerStoreFactory, deps: [Store]}
+  ]
+})
+export class CustomerModule {}
diff --git a/src/app/customers/customer.routing.ts b/src/app/customers/customer.routing.ts
new file mode 100644
index 0000000..a6b936d
--- /dev/null
+++ b/src/app/customers/customer.routing.ts
@@ -0,0 +1,192 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 {Routes} from '@angular/router';
+import {CustomerComponent} from './customer.component';
+import {CreateCustomerFormComponent} from './form/create/create.form.component';
+import {CustomerDetailComponent} from './detail/customer.detail.component';
+import {EditCustomerFormComponent} from './form/edit/edit.form.component';
+import {CustomerActivityComponent} from './detail/activity/activity.component';
+import {CustomerStatusComponent} from './detail/status/status.component';
+import {CustomerIndexComponent} from './detail/customer.index.component';
+import {CustomerExistsGuard} from './customer-exists.guard';
+import {CustomerPortraitComponent} from './detail/portrait/portrait.component';
+import {TaskListComponent} from './tasks/task.list.component';
+import {TaskExistsGuard} from './tasks/task-exists.guard';
+import {TaskEditFormComponent} from './tasks/form/edit.form.component';
+import {TaskCreateFormComponent} from './tasks/form/create.form.component';
+import {TaskIndexComponent} from './tasks/task.index.component';
+import {TaskDetailComponent} from './tasks/task.detail.component';
+import {PayrollExistsGuard} from './detail/payroll/payroll-exists.guard';
+import {CustomerPayrollDetailComponent} from './detail/payroll/payroll.detail.component';
+import {CreateCustomerPayrollFormComponent} from './detail/payroll/form/create.form.component';
+import {CatalogDetailComponent} from './customFields/catalog.detail.component';
+import {CatalogExistsGuard} from './customFields/catalog-exists.guard';
+import {CreateCustomerCatalogFormComponent} from './customFields/form/create.form.component';
+import {FieldIndexComponent} from './customFields/fields/field.index.component';
+import {FieldExistsGuard} from './customFields/fields/field-exists.guard';
+import {FieldDetailComponent} from './customFields/fields/field.detail.component';
+import {EditCatalogFieldFormComponent} from './customFields/fields/form/edit.form.component';
+
+export const CustomerRoutes: Routes = [
+  {
+    path: '',
+    component: CustomerComponent,
+    data: {title: 'Manage Customers', hasPermission: {id: 'customer_customers', accessLevel: 'READ'}},
+    canActivate: [ CatalogExistsGuard ]
+  },
+  {
+    path: 'create',
+    component: CreateCustomerFormComponent,
+    data: {title: 'Create Customer', hasPermission: { id: 'customer_customers', accessLevel: 'CHANGE' }}
+  },
+  {
+    path: 'detail/:id/edit',
+    component: EditCustomerFormComponent,
+    data: {title: 'Edit Customer', hasPermission: { id: 'customer_customers', accessLevel: 'CHANGE' }},
+    canActivate: [ CustomerExistsGuard ]
+  },
+  {
+    path: 'detail/:id',
+    component: CustomerIndexComponent,
+    data: {
+      hasPermission: { id: 'customer_customers', accessLevel: 'READ' }
+    },
+    canActivate: [ CustomerExistsGuard ],
+    children: [
+      {
+        path: '',
+        component: CustomerDetailComponent,
+        data: {title: 'View Customer'}
+      },
+      {
+        path: 'tasks',
+        component: CustomerStatusComponent,
+        data: {title: 'Manage Customer Tasks'},
+      },
+      {
+        path: 'activities',
+        component: CustomerActivityComponent,
+        data: {title: 'Manage Customer Tasks'}
+      },
+      {
+        path: 'portrait',
+        component: CustomerPortraitComponent,
+        data: {
+          title: 'Upload portrait',
+          hasPermission: { id: 'customer_portrait', accessLevel: 'READ' }
+        }
+      },
+      {path: 'identifications', loadChildren: './detail/identityCard/identity-card.module#IdentityCardModule'},
+      {path: 'loans', loadChildren: './cases/case.module#CaseModule'},
+      {path: 'deposits', loadChildren: './deposits/deposits.module#DepositsModule'},
+      {
+        path: 'payroll',
+        canActivate: [ PayrollExistsGuard ],
+        data: {
+          hasPermission: { id: 'payroll_configuration', accessLevel: 'READ' }
+        },
+        children: [
+          {
+            path: '',
+            component: CustomerPayrollDetailComponent
+          },
+          {
+            path: 'edit',
+            component: CreateCustomerPayrollFormComponent,
+            data: {
+              hasPermission: { id: 'payroll_configuration', accessLevel: 'CHANGE' }
+            }
+          }
+        ]
+      }
+    ]
+  },
+  {
+    path: 'tasks',
+    component: TaskListComponent,
+    data: {
+      hasPermission: { id: 'customer_tasks', accessLevel: 'READ' }
+    }
+  },
+  {
+    path: 'tasks/detail/:id',
+    canActivate: [ TaskExistsGuard ],
+    component: TaskIndexComponent,
+    data: {
+      hasPermission: { id: 'customer_tasks', accessLevel: 'READ' }
+    },
+    children: [
+      {
+        path: '',
+        component: TaskDetailComponent
+      },
+      {
+        path: 'edit',
+        component: TaskEditFormComponent,
+        data: {
+          hasPermission: { id: 'customer_tasks', accessLevel: 'CHANGE' }
+        }
+      }
+    ]
+  },
+  {
+    path: 'tasks/create',
+    component: TaskCreateFormComponent,
+    data: {
+      hasPermission: { id: 'customer_tasks', accessLevel: 'CHANGE' }
+    }
+  },
+  {
+    path: 'catalog/detail',
+    data: {
+      hasPermission: { id: 'catalog_catalogs', accessLevel: 'READ' }
+    },
+    children: [
+      {
+        path: '',
+        component: CatalogDetailComponent
+      },
+      {
+        path: 'edit',
+        component: CreateCustomerCatalogFormComponent,
+        data: {
+          hasPermission: { id: 'catalog_catalogs', accessLevel: 'CHANGE' }
+        }
+      },
+      {
+        path: 'field/detail/:fieldId',
+        component: FieldIndexComponent,
+        canActivate: [ FieldExistsGuard ],
+        children: [
+          {
+            path: '',
+            component: FieldDetailComponent
+          },
+          {
+            path: 'edit',
+            component: EditCatalogFieldFormComponent,
+            data: {
+              hasPermission: { id: 'catalog_catalogs', accessLevel: 'CHANGE' }
+            }
+          }
+        ]
+      }
+    ]
+  }
+];
diff --git a/src/app/customers/deposits/deposit-instance-exists.guard.ts b/src/app/customers/deposits/deposit-instance-exists.guard.ts
new file mode 100644
index 0000000..20bc2d7
--- /dev/null
+++ b/src/app/customers/deposits/deposit-instance-exists.guard.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 {ActivatedRouteSnapshot, CanActivate, RouterStateSnapshot} from '@angular/router';
+import {Injectable} from '@angular/core';
+import * as fromDeposits from './store';
+import {Observable} from 'rxjs/Observable';
+import {of} from 'rxjs/observable/of';
+import {DepositsStore} from './store/index';
+import {DepositAccountService} from '../../services/depositAccount/deposit-account.service';
+import {ExistsGuardService} from '../../common/guards/exists-guard';
+import {LoadAction} from './store/deposit.actions';
+
+@Injectable()
+export class DepositInstanceExistsGuard implements CanActivate {
+
+  constructor(private store: DepositsStore,
+              private depositService: DepositAccountService,
+              private existsGuardService: ExistsGuardService) {}
+
+  hasProductInstanceInStore(id: string): Observable<boolean> {
+    const timestamp$ = this.store.select(fromDeposits.getDepositsLoadedAt)
+      .map(loadedAt => loadedAt[id]);
+
+    return this.existsGuardService.isWithinExpiry(timestamp$);
+  }
+
+  hasProductInstanceInApi(id: string): Observable<boolean> {
+    const getProductInstance$ = this.depositService.findProductInstance(id)
+      .map(productInstance => new LoadAction({
+        resource: productInstance
+      }))
+      .do((action: LoadAction) => this.store.dispatch(action))
+      .map(productInstance => !!productInstance);
+
+    return this.existsGuardService.routeTo404OnError(getProductInstance$);
+  }
+
+  hasProductInstance(id: string): Observable<boolean> {
+    return this.hasProductInstanceInStore(id)
+      .switchMap(inStore => {
+        if (inStore) {
+          return of(inStore);
+        }
+
+        return this.hasProductInstanceInApi(id);
+      });
+  }
+
+  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
+    return this.hasProductInstance(route.params['id']);
+  }
+}
diff --git a/src/app/customers/deposits/deposits.list.component.html b/src/app/customers/deposits/deposits.list.component.html
new file mode 100644
index 0000000..b5cd64f
--- /dev/null
+++ b/src/app/customers/deposits/deposits.list.component.html
@@ -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.
+-->
+
+<fims-layout-card-over title="{{'Manage member deposit accounts' | translate}}" [navigateBackTo]="['../../../../']">
+  <fims-data-table flex
+                   (onFetch)="fetchProductInstances($event)"
+                   (onActionCellClick)="rowSelect($event)"
+                   [columns]="columns"
+                   [data]="productInstancesData$ | async">
+  </fims-data-table>
+</fims-layout-card-over>
+<fims-fab-button title="{{'Create new deposit account for member ' | translate}}" icon="add" [link]="['create']" [permission]="{ id: 'deposit_instances', accessLevel: 'CHANGE'}"></fims-fab-button>
diff --git a/src/app/customers/deposits/deposits.list.component.ts b/src/app/customers/deposits/deposits.list.component.ts
new file mode 100644
index 0000000..0f96fb1
--- /dev/null
+++ b/src/app/customers/deposits/deposits.list.component.ts
@@ -0,0 +1,95 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, OnDestroy, OnInit} from '@angular/core';
+import {Observable} from 'rxjs/Observable';
+import {Customer} from '../../services/customer/domain/customer.model';
+import {Subscription} from 'rxjs/Subscription';
+import {TableData} from '../../common/data-table/data-table.component';
+import {ActivatedRoute, Router} from '@angular/router';
+import * as fromDeposits from './store/index';
+import {DepositsStore} from './store/index';
+import {FetchRequest} from '../../services/domain/paging/fetch-request.model';
+import {SEARCH} from './store/deposit.actions';
+import {ProductInstance} from '../../services/depositAccount/domain/instance/product-instance.model';
+import * as fromCustomers from '../store';
+import {DatePipe} from '@angular/common';
+
+
+@Component({
+  templateUrl: './deposits.list.component.html',
+  providers: [DatePipe]
+})
+export class DepositsListComponent implements OnInit, OnDestroy {
+
+  private customerSubscription: Subscription;
+
+  private customer: Customer;
+
+  productInstancesData$: Observable<TableData>;
+
+  columns: any[] = [
+    { name: 'productIdentifier', label: 'Deposit product' },
+    { name: 'accountIdentifier', label: 'Account identifier' },
+    { name: 'balance', label: 'Balance', numeric: true, format: v => v.toFixed(2) },
+    { name: 'state', label: 'State' },
+    {
+      name: 'openedOn', label: 'Opened on', format: (v: any) => {
+        return this.datePipe.transform(v, 'shortDate');
+      }
+    },
+    {
+      name: 'lastTransactionDate', label: 'Last transaction', format: (v: any) => {
+      return this.datePipe.transform(v, 'short');
+    }
+    }
+  ];
+
+  constructor(private router: Router, private route: ActivatedRoute, private depositsStore: DepositsStore, private datePipe: DatePipe) {}
+
+  ngOnInit(): void {
+    this.productInstancesData$ = this.depositsStore.select(fromDeposits.getDepositSearchResults)
+      .map(depositsPage => ({
+        totalElements: depositsPage.totalElements,
+        totalPages: depositsPage.totalPages,
+        data: depositsPage.deposits
+      }));
+
+    this.customerSubscription = this.depositsStore.select(fromCustomers.getSelectedCustomer)
+      .filter(customer => !!customer)
+      .subscribe(customer => {
+        this.customer = customer;
+        this.fetchProductInstances();
+      });
+  }
+
+  ngOnDestroy(): void {
+    this.customerSubscription.unsubscribe();
+  }
+
+  fetchProductInstances(fetchRequest?: FetchRequest): void {
+    this.depositsStore.dispatch({ type: SEARCH, payload: {
+      customerId: this.customer.identifier,
+      fetchRequest: fetchRequest
+    }});
+  }
+
+  rowSelect(productInstance: ProductInstance): void {
+    this.router.navigate(['detail', productInstance.accountIdentifier], { relativeTo: this.route });
+  }
+}
diff --git a/src/app/customers/deposits/deposits.module.ts b/src/app/customers/deposits/deposits.module.ts
new file mode 100644
index 0000000..d5d24fa
--- /dev/null
+++ b/src/app/customers/deposits/deposits.module.ts
@@ -0,0 +1,93 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 {RouterModule} from '@angular/router';
+import {NgModule} from '@angular/core';
+import {FimsSharedModule} from '../../common/common.module';
+import {TranslateModule} from '@ngx-translate/core';
+import {CommonModule} from '@angular/common';
+import {ReactiveFormsModule} from '@angular/forms';
+import {
+  MatButtonModule,
+  MatCardModule,
+  MatIconModule,
+  MatInputModule,
+  MatListModule,
+  MatOptionModule,
+  MatRadioModule,
+  MatSelectModule,
+  MatToolbarModule
+} from '@angular/material';
+import {CovalentChipsModule, CovalentCommonModule, CovalentStepsModule} from '@covalent/core';
+import {DepositCreateComponent} from './form/create.component';
+import {DepositFormComponent} from './form/form.component';
+import {DepositsListComponent} from './deposits.list.component';
+import {Store} from '@ngrx/store';
+import {DepositsStore, depositsStoreFactory} from './store/index';
+import {DepositRoutes} from './deposits.routes';
+import {EffectsModule} from '@ngrx/effects';
+import {DepositProductInstanceApiEffects} from './store/effects/service.effects';
+import {DepositProductInstanceRouteEffects} from './store/effects/route.effects';
+import {DepositProductInstanceNotificationEffects} from './store/effects/notification.effects';
+import {DepositIndexComponent} from './detail/deposit.index.component';
+import {DepositDetailComponent} from './detail/deposit.detail.component';
+import {DepositInstanceExistsGuard} from './deposit-instance-exists.guard';
+import {DepositEditComponent} from './form/edit.component';
+import {IssueChequesFormComponent} from './detail/cheques/form.component';
+import {IssueChequeComponent} from './detail/cheques/cheques.component';
+
+@NgModule({
+  imports: [
+    RouterModule.forChild(DepositRoutes),
+    FimsSharedModule,
+    TranslateModule,
+    CommonModule,
+    ReactiveFormsModule,
+    MatIconModule,
+    MatListModule,
+    MatToolbarModule,
+    MatInputModule,
+    MatButtonModule,
+    MatOptionModule,
+    MatSelectModule,
+    MatRadioModule,
+    MatCardModule,
+    CovalentCommonModule,
+    CovalentStepsModule,
+    CovalentChipsModule,
+
+    EffectsModule.run(DepositProductInstanceApiEffects),
+    EffectsModule.run(DepositProductInstanceRouteEffects),
+    EffectsModule.run(DepositProductInstanceNotificationEffects),
+  ],
+  declarations: [
+    DepositsListComponent,
+    DepositFormComponent,
+    DepositIndexComponent,
+    DepositCreateComponent,
+    DepositEditComponent,
+    DepositDetailComponent,
+    IssueChequeComponent,
+    IssueChequesFormComponent,
+  ],
+  providers: [
+    DepositInstanceExistsGuard,
+    { provide: DepositsStore, useFactory: depositsStoreFactory, deps: [Store] }
+  ]
+})
+export class DepositsModule {}
diff --git a/src/app/customers/deposits/deposits.routes.ts b/src/app/customers/deposits/deposits.routes.ts
new file mode 100644
index 0000000..bc1bdd8
--- /dev/null
+++ b/src/app/customers/deposits/deposits.routes.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 {DepositsListComponent} from './deposits.list.component';
+import {Routes} from '@angular/router';
+import {DepositCreateComponent} from './form/create.component';
+import {DepositIndexComponent} from './detail/deposit.index.component';
+import {DepositDetailComponent} from './detail/deposit.detail.component';
+import {DepositInstanceExistsGuard} from './deposit-instance-exists.guard';
+import {DepositEditComponent} from './form/edit.component';
+import {IssueChequeComponent} from './detail/cheques/cheques.component';
+
+export const DepositRoutes: Routes = [
+  {
+    path: '',
+    component: DepositsListComponent,
+    data: {
+      hasPermission: {id: 'deposit_instances', accessLevel: 'READ'}
+    }
+  },
+  {
+    path: 'detail/:id',
+    component: DepositIndexComponent,
+    canActivate: [DepositInstanceExistsGuard],
+    data: {
+      hasPermission: {id: 'deposit_instances', accessLevel: 'READ'}
+    },
+    children: [
+      {
+        path: '',
+        component: DepositDetailComponent
+      },
+      {
+        path: 'edit',
+        component: DepositEditComponent,
+        data: {
+          hasPermission: {id: 'deposit_instances', accessLevel: 'CHANGE'}
+        }
+      },
+      {
+        path: 'cheques',
+        component: IssueChequeComponent,
+        data: {
+          hasPermission: {id: 'cheque_management', accessLevel: 'CHANGE'}
+        }
+      }
+    ]
+  },
+  {
+    path: 'create',
+    component: DepositCreateComponent,
+    data: {
+      hasPermission: {id: 'deposit_instances', accessLevel: 'CHANGE'}
+    }
+  }
+];
diff --git a/src/app/customers/deposits/detail/cheques/cheques.component.html b/src/app/customers/deposits/detail/cheques/cheques.component.html
new file mode 100644
index 0000000..a03cb2e
--- /dev/null
+++ b/src/app/customers/deposits/detail/cheques/cheques.component.html
@@ -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.
+-->
+
+<fims-layout-card-over title="{{'Issue cheques' | translate}}">
+  <fims-issue-cheque-form
+    [accountIdentifier]="(depositInstance$ | async).accountIdentifier"
+    (onSave)="issueCheques($event)"
+    (onCancel)="cancel()">
+  </fims-issue-cheque-form>
+</fims-layout-card-over>
diff --git a/src/app/customers/deposits/detail/cheques/cheques.component.ts b/src/app/customers/deposits/detail/cheques/cheques.component.ts
new file mode 100644
index 0000000..34fea7e
--- /dev/null
+++ b/src/app/customers/deposits/detail/cheques/cheques.component.ts
@@ -0,0 +1,55 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, OnInit} from '@angular/core';
+import * as fromDeposits from '../../store/index';
+import {DepositsStore} from '../../store/index';
+import {Observable} from 'rxjs/Observable';
+import {ProductInstance} from '../../../../services/depositAccount/domain/instance/product-instance.model';
+import {IssuingCount} from '../../../../services/cheque/domain/issuing-count.model';
+import {ISSUE_CHEQUES} from '../../store/deposit.actions';
+import {ActivatedRoute, Router} from '@angular/router';
+
+@Component({
+  templateUrl: './cheques.component.html'
+})
+export class IssueChequeComponent implements OnInit {
+
+  depositInstance$: Observable<ProductInstance>;
+
+  constructor(private store: DepositsStore, private router: Router, private route: ActivatedRoute) {}
+
+  ngOnInit(): void {
+    this.depositInstance$ = this.store.select(fromDeposits.getSelectedDepositInstance);
+  }
+
+  issueCheques(issuingCount: IssuingCount): void {
+    this.store.dispatch({
+      type: ISSUE_CHEQUES,
+      payload: {
+        issuingCount,
+        activatedRoute: this.route
+      }
+    });
+  }
+
+  cancel(): void {
+    this.router.navigate(['../'], { relativeTo: this.route });
+  }
+
+}
diff --git a/src/app/customers/deposits/detail/cheques/form.component.html b/src/app/customers/deposits/detail/cheques/form.component.html
new file mode 100644
index 0000000..ca62023
--- /dev/null
+++ b/src/app/customers/deposits/detail/cheques/form.component.html
@@ -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.
+-->
+
+<td-steps mode="'vertical'">
+  <td-step #detailsStep label="{{'Issue cheques' | translate}}" [state]="form.valid ? 'complete' : form.pristine ? 'none' : 'required'">
+    <form [formGroup]="form" layout="column">
+      <fims-text-input type="number" [form]="form" controlName="start" placeholder="{{'Start at(optional)' | translate}}"></fims-text-input>
+      <fims-text-input type="number" [form]="form" controlName="amount" placeholder="{{'Cheque amount' | translate}}"></fims-text-input>
+    </form>
+    <ng-template td-step-actions>
+      <button mat-raised-button color="primary" (click)="save()" [disabled]="form.invalid">{{'ISSUE CHEQUES' | translate}}</button>
+      <span flex></span>
+      <button mat-button (click)="cancel()">{{'CANCEL' | translate}}</button>
+    </ng-template>
+  </td-step>
+</td-steps>
diff --git a/src/app/customers/deposits/detail/cheques/form.component.ts b/src/app/customers/deposits/detail/cheques/form.component.ts
new file mode 100644
index 0000000..86edbc0
--- /dev/null
+++ b/src/app/customers/deposits/detail/cheques/form.component.ts
@@ -0,0 +1,66 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, OnInit, Output, ViewChild} from '@angular/core';
+import {TdStepComponent} from '@covalent/core';
+import {IssuingCount} from '../../../../services/cheque/domain/issuing-count.model';
+import {FormBuilder, FormGroup, Validators} from '@angular/forms';
+import {FimsValidators} from '../../../../common/validator/validators';
+
+@Component({
+  selector: 'fims-issue-cheque-form',
+  templateUrl: './form.component.html'
+})
+export class IssueChequesFormComponent implements OnInit {
+
+  form: FormGroup;
+
+  @ViewChild('detailsStep') detailsStep: TdStepComponent;
+
+  @Input('accountIdentifier') accountIdentifier: string;
+
+  @Output('onSave') onSave = new EventEmitter<IssuingCount>();
+
+  @Output('onCancel') onCancel = new EventEmitter<void>();
+
+  constructor(private formBuilder: FormBuilder) {}
+
+  ngOnInit(): void {
+    this.form = this.formBuilder.group({
+      start: [1, [FimsValidators.minValue(1)]],
+      amount: [1, [Validators.required, FimsValidators.minValue(1)]]
+    });
+
+    this.detailsStep.open();
+  }
+
+  save(): void {
+    const issuingCount: IssuingCount = {
+      start: this.form.get('start').value,
+      amount: this.form.get('amount').value,
+      accountIdentifier: this.accountIdentifier
+    };
+
+    this.onSave.emit(issuingCount);
+  }
+
+  cancel(): void {
+    this.onCancel.emit();
+  }
+
+}
diff --git a/src/app/customers/deposits/detail/deposit.detail.component.html b/src/app/customers/deposits/detail/deposit.detail.component.html
new file mode 100644
index 0000000..5e01fe9
--- /dev/null
+++ b/src/app/customers/deposits/detail/deposit.detail.component.html
@@ -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.
+-->
+
+<fims-layout-card-over title="{{'Deposit account' | translate}}" [navigateBackTo]="['../../../../../../']">
+  <fims-layout-card-over-header-menu>
+    <button mat-icon-button (click)="issueCheques()" title="{{'Issue cheques' | translate}}"
+            *hasPermission="{ id: 'cheque_management', accessLevel: 'CHANGE'}">
+      <mat-icon>import_contacts</mat-icon>
+    </button>
+  </fims-layout-card-over-header-menu>
+  <div class="mat-content inset" flex>
+    <div layout="row">
+      <mat-list *ngIf="(depositInstance$ | async) as instance">
+        <h3 mat-subheader translate>Current status</h3>
+        <fims-state-display [state]="instance.state"></fims-state-display>
+        <h3 mat-subheader translate>Details</h3>
+        <mat-list-item>
+          <h3 matLine translate>Account</h3>
+          <p matLine>
+            <a [routerLink]="['/accounting/accounts/detail', instance.accountIdentifier, 'entries']">
+              {{instance.alternativeAccountNumber ? instance.alternativeAccountNumber : instance.accountNumber}}
+            </a>
+          </p>
+        </mat-list-item>
+        <mat-list-item>
+          <h3 matLine translate>Balance</h3>
+          <p matLine>{{instance.balance | number:'1.2-2'}}</p>
+        </mat-list-item>
+        <mat-list-item>
+          <h3 matLine translate>Beneficiaries</h3>
+          <p matLine>{{instance.beneficiaries?.join(', ')}}</p>
+        </mat-list-item>
+        <mat-list-item>
+          <h3 matLine translate>Opened on</h3>
+          <p matLine>{{instance.openedOn | date:'shortDate'}}</p>
+        </mat-list-item>
+        <mat-list-item>
+          <h3 matLine translate>Last transaction</h3>
+          <p matLine>{{instance.lastTransactionDate | date:'medium'}}</p>
+        </mat-list-item>
+      </mat-list>
+    </div>
+  </div>
+</fims-layout-card-over>
+<fims-fab-button title="{{'Edit deposit account' | translate}}" icon="mode_edit" [link]="['edit']"
+                 [permission]="{ id: 'deposit_instances', accessLevel: 'CHANGE'}"></fims-fab-button>
diff --git a/src/app/customers/deposits/detail/deposit.detail.component.ts b/src/app/customers/deposits/detail/deposit.detail.component.ts
new file mode 100644
index 0000000..fdb3928
--- /dev/null
+++ b/src/app/customers/deposits/detail/deposit.detail.component.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 {Component, OnInit} from '@angular/core';
+import {ProductInstance} from '../../../services/depositAccount/domain/instance/product-instance.model';
+import {Observable} from 'rxjs/Observable';
+import * as fromDeposits from '../store/index';
+import {DepositsStore} from '../store/index';
+import {ActivatedRoute, Router} from '@angular/router';
+
+@Component({
+  templateUrl: './deposit.detail.component.html'
+})
+export class DepositDetailComponent implements OnInit {
+
+  depositInstance$: Observable<ProductInstance>;
+
+  constructor(private router: Router, private route: ActivatedRoute, private store: DepositsStore) {}
+
+  ngOnInit(): void {
+    this.depositInstance$ = this.store.select(fromDeposits.getSelectedDepositInstance);
+  }
+
+  issueCheques(): void {
+    this.router.navigate(['cheques'], { relativeTo: this.route });
+  }
+}
diff --git a/src/app/customers/deposits/detail/deposit.index.component.html b/src/app/customers/deposits/detail/deposit.index.component.html
new file mode 100644
index 0000000..ca721b3
--- /dev/null
+++ b/src/app/customers/deposits/detail/deposit.index.component.html
@@ -0,0 +1,18 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<router-outlet></router-outlet>
diff --git a/src/app/customers/deposits/detail/deposit.index.component.ts b/src/app/customers/deposits/detail/deposit.index.component.ts
new file mode 100644
index 0000000..abfab7d
--- /dev/null
+++ b/src/app/customers/deposits/detail/deposit.index.component.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 {Component, OnDestroy, OnInit} from '@angular/core';
+import {Subscription} from 'rxjs/Subscription';
+import {ActivatedRoute} from '@angular/router';
+import {DepositsStore} from '../store/index';
+import {SelectAction} from '../store/deposit.actions';
+
+@Component({
+  templateUrl: './deposit.index.component.html'
+})
+export class DepositIndexComponent implements OnInit, OnDestroy {
+
+  private actionsSubscription: Subscription;
+
+  constructor(private route: ActivatedRoute, private store: DepositsStore) {}
+
+  ngOnInit(): void {
+    this.actionsSubscription = this.route.params
+      .map(params => new SelectAction(params['id']))
+      .subscribe(this.store);
+  }
+
+  ngOnDestroy(): void {
+    this.actionsSubscription.unsubscribe();
+  }
+}
diff --git a/src/app/customers/deposits/form/create.component.html b/src/app/customers/deposits/form/create.component.html
new file mode 100644
index 0000000..f4cfc51
--- /dev/null
+++ b/src/app/customers/deposits/form/create.component.html
@@ -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.
+-->
+
+<fims-layout-card-over title="{{'Create new deposit account for member ' | translate}}">
+  <fims-deposit-form-component #form
+                            (onSave)="onSave($event)"
+                            (onCancel)="onCancel()"
+                            [customerId]="(customer$ | async).identifier"
+                            [productInstance]="productInstance"
+                            [productDefinitions]="productDefinitions$ | async"
+                            [editMode]="false">
+  </fims-deposit-form-component>
+</fims-layout-card-over>
diff --git a/src/app/customers/deposits/form/create.component.ts b/src/app/customers/deposits/form/create.component.ts
new file mode 100644
index 0000000..16d579d
--- /dev/null
+++ b/src/app/customers/deposits/form/create.component.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 {Component, OnInit, ViewChild} from '@angular/core';
+import {DepositFormComponent} from './form.component';
+import {Customer} from '../../../services/customer/domain/customer.model';
+import {ProductInstance} from '../../../services/depositAccount/domain/instance/product-instance.model';
+import {ActivatedRoute, Router} from '@angular/router';
+import {DepositsStore} from '../store/index';
+import {CREATE} from '../store/deposit.actions';
+import * as fromCustomers from '../../store/index';
+import {DepositAccountService} from '../../../services/depositAccount/deposit-account.service';
+import {Observable} from 'rxjs/Observable';
+import {ProductDefinition} from '../../../services/depositAccount/domain/definition/product-definition.model';
+
+@Component({
+  templateUrl: './create.component.html'
+})
+export class DepositCreateComponent implements OnInit {
+
+  @ViewChild('form') formComponent: DepositFormComponent;
+
+  customer$: Observable<Customer>;
+
+  productInstance: ProductInstance = {
+    customerIdentifier: '',
+    productIdentifier: ''
+  };
+
+  productDefinitions$: Observable<ProductDefinition[]>;
+
+  constructor(private router: Router, private route: ActivatedRoute, private depositsStore: DepositsStore,
+              private depositService: DepositAccountService) {}
+
+  ngOnInit(): void {
+    this.customer$ = this.depositsStore.select(fromCustomers.getSelectedCustomer)
+      .filter(customer => !!customer);
+
+    this.productDefinitions$ = this.depositService.fetchProductDefinitions()
+      .map(productDefinitions => productDefinitions.filter(definition => definition.active));
+  }
+
+  onSave(productInstance: ProductInstance): void {
+    this.depositsStore.dispatch({ type: CREATE, payload: {
+      productInstance: productInstance,
+      activatedRoute: this.route
+    }});
+  }
+
+  onCancel(): void {
+    this.navigateAway();
+  }
+
+  navigateAway(): void {
+    this.router.navigate(['../'], { relativeTo: this.route });
+  }
+}
diff --git a/src/app/customers/deposits/form/edit.component.html b/src/app/customers/deposits/form/edit.component.html
new file mode 100644
index 0000000..2f22669
--- /dev/null
+++ b/src/app/customers/deposits/form/edit.component.html
@@ -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.
+-->
+
+<fims-layout-card-over title="{{'Edit deposit account for member ' | translate}}">
+  <fims-deposit-form-component #form
+                               (onSave)="onSave($event)"
+                               (onCancel)="onCancel()"
+                               [customerId]="(customer$ | async).identifier"
+                               [productInstance]="productInstance$ | async"
+                               [productDefinitions]="[]"
+                               [editMode]="true">
+  </fims-deposit-form-component>
+</fims-layout-card-over>
diff --git a/src/app/customers/deposits/form/edit.component.ts b/src/app/customers/deposits/form/edit.component.ts
new file mode 100644
index 0000000..3ea86b5
--- /dev/null
+++ b/src/app/customers/deposits/form/edit.component.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 {Component, OnInit, ViewChild} from '@angular/core';
+import {DepositFormComponent} from './form.component';
+import {Customer} from '../../../services/customer/domain/customer.model';
+import {ProductInstance} from '../../../services/depositAccount/domain/instance/product-instance.model';
+import {ActivatedRoute, Router} from '@angular/router';
+import * as fromDeposits from '../store/index';
+import {DepositsStore} from '../store/index';
+import * as fromCustomers from '../../store/index';
+import {Observable} from 'rxjs/Observable';
+import {UPDATE} from '../store/deposit.actions';
+
+@Component({
+  templateUrl: './edit.component.html'
+})
+export class DepositEditComponent implements OnInit {
+
+  @ViewChild('form') formComponent: DepositFormComponent;
+
+  customer$: Observable<Customer>;
+
+  productInstance$: Observable<ProductInstance>;
+
+  constructor(private router: Router, private route: ActivatedRoute, private depositsStore: DepositsStore) {}
+
+  ngOnInit(): void {
+    this.customer$ = this.depositsStore.select(fromCustomers.getSelectedCustomer);
+    this.productInstance$ = this.depositsStore.select(fromDeposits.getSelectedDepositInstance);
+  }
+
+  onSave(productInstance: ProductInstance): void {
+    this.depositsStore.dispatch({ type: UPDATE, payload: {
+      productInstance,
+      activatedRoute: this.route
+    }});
+  }
+
+  onCancel(): void {
+    this.navigateAway();
+  }
+
+  navigateAway(): void {
+    this.router.navigate(['../'], { relativeTo: this.route });
+  }
+}
diff --git a/src/app/customers/deposits/form/form.component.html b/src/app/customers/deposits/form/form.component.html
new file mode 100644
index 0000000..1879d9b
--- /dev/null
+++ b/src/app/customers/deposits/form/form.component.html
@@ -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.
+-->
+
+<td-steps mode="'vertical'">
+  <td-step #detailsStep label="{{'Assign product' | translate}}" [state]="detailForm.valid ? 'complete' : detailForm.pristine ? 'none' : 'required'">
+    <form [formGroup]="detailForm" layout="column">
+      <mat-form-field layout-margin *ngIf="!editMode">
+        <mat-select formControlName="productIdentifier" placeholder="{{ 'Select product' | translate }}">
+          <mat-option *ngFor="let definition of productDefinitions" [value]="definition.identifier">
+            {{definition.name}}
+          </mat-option>
+        </mat-select>
+      </mat-form-field>
+      <div layout="row">
+        <td-chips [items]="filteredCustomers | async"
+                  [debounce]="500"
+                  formControlName="beneficiaries"
+                  placeholder="{{'Search beneficiary' | translate }}"
+                  (inputChange)="filterAsync($event)"
+                  requireMatch>
+          <ng-template td-chip let-chip="chip">
+            <div class="tc-grey-100 bgc-teal-700" td-chip-avatar>{{chip.substring(0, 1).toUpperCase()}}</div> {{chip}}
+          </ng-template>
+        </td-chips>
+      </div>
+    </form>
+    <ng-template td-step-actions>
+      <fims-form-final-action
+        [resourceName]="'DEPOSIT ACCOUNT'"
+        [editMode]="editMode"
+        [disabled]="!isValid"
+        (onCancel)="cancel()"
+        (onSave)="save()">
+      </fims-form-final-action>
+    </ng-template>
+  </td-step>
+</td-steps>
diff --git a/src/app/customers/deposits/form/form.component.ts b/src/app/customers/deposits/form/form.component.ts
new file mode 100644
index 0000000..68c2399
--- /dev/null
+++ b/src/app/customers/deposits/form/form.component.ts
@@ -0,0 +1,86 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, OnInit, Output, ViewChild} from '@angular/core';
+import {FormBuilder, FormGroup, Validators} from '@angular/forms';
+import {TdStepComponent} from '@covalent/core';
+import {ProductInstance} from '../../../services/depositAccount/domain/instance/product-instance.model';
+import {ProductDefinition} from '../../../services/depositAccount/domain/definition/product-definition.model';
+import {Observable} from 'rxjs/Observable';
+import {CustomerService} from '../../../services/customer/customer.service';
+
+@Component({
+  selector: 'fims-deposit-form-component',
+  templateUrl: './form.component.html'
+})
+export class DepositFormComponent implements OnInit {
+
+  filteredCustomers: Observable<string[]>;
+
+  detailForm: FormGroup;
+
+  @ViewChild('detailsStep') detailsStep: TdStepComponent;
+
+  @Input('editMode') editMode: boolean;
+
+  @Input('customerId') customerId: string;
+
+  @Input('productDefinitions') productDefinitions: ProductDefinition[];
+
+  @Input('productInstance') productInstance: ProductInstance;
+
+  @Output('onSave') onSave = new EventEmitter<ProductInstance>();
+  @Output('onCancel') onCancel = new EventEmitter<void>();
+
+  constructor(private formBuilder: FormBuilder, private customerService: CustomerService) {}
+
+  ngOnInit(): void {
+    this.detailForm = this.formBuilder.group({
+      productIdentifier: [this.productInstance.productIdentifier, [Validators.required]],
+      beneficiaries: [this.productInstance.beneficiaries ? this.productInstance.beneficiaries : []]
+    });
+
+    this.detailsStep.open();
+  }
+
+  get isValid(): boolean {
+    return this.detailForm.valid;
+  }
+
+  save(): void {
+    const productInstance: ProductInstance = {
+      productIdentifier: this.detailForm.get('productIdentifier').value,
+      beneficiaries: this.detailForm.get('beneficiaries').value,
+      customerIdentifier: this.customerId,
+      accountIdentifier: this.productInstance.accountIdentifier,
+      state: this.productInstance.state
+    };
+
+    this.onSave.emit(productInstance);
+  }
+
+  cancel(): void {
+    this.onCancel.emit();
+  }
+
+  filterAsync(searchTerm: string): void {
+    this.filteredCustomers = this.customerService.fetchCustomers({
+      searchTerm
+    }).map(customerPage => customerPage.customers.map(customer => customer.identifier));
+  }
+}
diff --git a/src/app/customers/deposits/store/deposit.actions.ts b/src/app/customers/deposits/store/deposit.actions.ts
new file mode 100644
index 0000000..69fde58
--- /dev/null
+++ b/src/app/customers/deposits/store/deposit.actions.ts
@@ -0,0 +1,156 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 {FetchRequest} from '../../../services/domain/paging/fetch-request.model';
+import {RoutePayload} from '../../../common/store/route-payload';
+import {ProductInstance} from '../../../services/depositAccount/domain/instance/product-instance.model';
+import {
+  CreateResourceSuccessPayload,
+  LoadResourcePayload,
+  SelectResourcePayload,
+  UpdateResourceSuccessPayload
+} from '../../../common/store/resource.reducer';
+import {Action} from '@ngrx/store';
+import {SearchResult} from '../../../common/store/search.reducer';
+import {IssuingCount} from '../../../services/cheque/domain/issuing-count.model';
+
+export const SEARCH = type('[Deposit] Search');
+export const SEARCH_COMPLETE = type('[Deposit] Search Complete');
+
+export const LOAD = type('[Deposit] Load');
+export const SELECT = type('[Deposit] Select');
+
+export const CREATE = type('[Deposit] Create');
+export const CREATE_SUCCESS = type('[Deposit] Create Success');
+export const CREATE_FAIL = type('[Deposit] Create Fail');
+
+export const UPDATE = type('[Deposit] Update');
+export const UPDATE_SUCCESS = type('[Deposit] Update Success');
+export const UPDATE_FAIL = type('[Deposit] Update Fail');
+
+export const ISSUE_CHEQUES = type('[Deposit] Issue Cheques');
+export const ISSUE_CHEQUES_SUCCESS = type('[Deposit] Issue Cheques Success');
+export const ISSUE_CHEQUES_FAIL = type('[Deposit] Issue Cheques Fail');
+
+
+export interface SearchProductInstancePayload {
+  customerId: string;
+  fetchRequest: FetchRequest;
+}
+
+export interface DepositRoutePayload extends RoutePayload {
+  productInstance: ProductInstance;
+}
+
+export interface IssueChequePayload extends RoutePayload {
+  issuingCount: IssuingCount;
+}
+
+export class SearchAction implements Action {
+  readonly type = SEARCH;
+
+  constructor(public payload: SearchProductInstancePayload) { }
+}
+
+export class SearchCompleteAction implements Action {
+  readonly type = SEARCH_COMPLETE;
+
+  constructor(public payload: SearchResult) { }
+}
+
+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 CreateProductInstanceAction implements Action {
+  readonly type = CREATE;
+
+  constructor(public payload: DepositRoutePayload) { }
+}
+
+export class CreateProductInstanceSuccessAction implements Action {
+  readonly type = CREATE_SUCCESS;
+
+  constructor(public payload: CreateResourceSuccessPayload) { }
+}
+
+export class CreateProductInstanceFailAction implements Action {
+  readonly type = CREATE_FAIL;
+
+  constructor(public payload: Error) { }
+}
+
+export class UpdateProductInstanceAction implements Action {
+  readonly type = UPDATE;
+
+  constructor(public payload: DepositRoutePayload) { }
+}
+
+export class UpdateProductInstanceSuccessAction implements Action {
+  readonly type = UPDATE_SUCCESS;
+
+  constructor(public payload: UpdateResourceSuccessPayload) { }
+}
+
+export class UpdateProductInstanceFailAction implements Action {
+  readonly type = UPDATE_FAIL;
+
+  constructor(public payload: Error) { }
+}
+
+export class IssueChequesAction implements Action {
+  readonly type = ISSUE_CHEQUES;
+
+  constructor(public payload: IssueChequePayload) { }
+}
+
+export class IssueChequesSuccessAction implements Action {
+  readonly type = ISSUE_CHEQUES_SUCCESS;
+
+  constructor(public payload: IssueChequePayload) { }
+}
+
+export class IssueChequesFailAction implements Action {
+  readonly type = ISSUE_CHEQUES_FAIL;
+
+  constructor(public payload: Error) { }
+}
+
+export type Actions
+  = SearchAction
+  | SearchCompleteAction
+  | LoadAction
+  | SelectAction
+  | CreateProductInstanceAction
+  | CreateProductInstanceSuccessAction
+  | CreateProductInstanceFailAction
+  | UpdateProductInstanceAction
+  | UpdateProductInstanceSuccessAction
+  | UpdateProductInstanceFailAction
+  | IssueChequesAction
+  | IssueChequesSuccessAction
+  | IssueChequesFailAction;
diff --git a/src/app/customers/deposits/store/effects/notification.effects.ts b/src/app/customers/deposits/store/effects/notification.effects.ts
new file mode 100644
index 0000000..4f58a38
--- /dev/null
+++ b/src/app/customers/deposits/store/effects/notification.effects.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 {NotificationService, NotificationType} from '../../../../services/notification/notification.service';
+import {Injectable} from '@angular/core';
+import {Actions, Effect} from '@ngrx/effects';
+import {Observable} from 'rxjs/Observable';
+import {Action} from '@ngrx/store';
+import * as instanceActions from '../deposit.actions';
+
+@Injectable()
+export class DepositProductInstanceNotificationEffects {
+
+  @Effect({dispatch: false})
+  createProductInstanceSuccess$: Observable<Action> = this.actions$
+    .ofType(instanceActions.CREATE_SUCCESS, instanceActions.UPDATE_SUCCESS)
+    .do(() => this.notificationService.send({
+      type: NotificationType.MESSAGE,
+      message: 'Deposit account is going to be saved'
+    }));
+
+  @Effect({dispatch: false})
+  issueChequesSuccess$: Observable<Action> = this.actions$
+    .ofType(instanceActions.ISSUE_CHEQUES_SUCCESS)
+    .do(() => this.notificationService.send({
+      type: NotificationType.MESSAGE,
+      message: 'Cheques are going to be issued'
+    }));
+
+  @Effect({dispatch: false})
+  issueChequesFail$: Observable<Action> = this.actions$
+    .ofType(instanceActions.ISSUE_CHEQUES_FAIL)
+    .do(() => this.notificationService.send({
+      type: NotificationType.ALERT,
+      message: 'There was an issue issuing cheques'
+    }));
+
+  constructor(private actions$: Actions, private notificationService: NotificationService) {}
+}
diff --git a/src/app/customers/deposits/store/effects/route.effects.ts b/src/app/customers/deposits/store/effects/route.effects.ts
new file mode 100644
index 0000000..b447699
--- /dev/null
+++ b/src/app/customers/deposits/store/effects/route.effects.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 {Injectable} from '@angular/core';
+import {Actions, Effect} from '@ngrx/effects';
+import {Router} from '@angular/router';
+import {Observable} from 'rxjs/Observable';
+import * as instanceActions from '../deposit.actions';
+import {Action} from '@ngrx/store';
+
+@Injectable()
+export class DepositProductInstanceRouteEffects {
+
+  @Effect({ dispatch: false })
+  createProductInstanceSuccess$: Observable<Action> = this.actions$
+    .ofType(instanceActions.CREATE_SUCCESS, instanceActions.UPDATE_SUCCESS)
+    .map(action => action.payload)
+    .do(payload => this.router.navigate(['../'], { relativeTo: payload.activatedRoute }));
+
+  @Effect({ dispatch: false })
+  issueChequesSuccess$: Observable<Action> = this.actions$
+    .ofType(instanceActions.ISSUE_CHEQUES_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/customers/deposits/store/effects/service.effects.ts b/src/app/customers/deposits/store/effects/service.effects.ts
new file mode 100644
index 0000000..f331d99
--- /dev/null
+++ b/src/app/customers/deposits/store/effects/service.effects.ts
@@ -0,0 +1,87 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 {of} from 'rxjs/observable/of';
+import {Action} from '@ngrx/store';
+import {Observable} from 'rxjs/Observable';
+import {Actions, Effect} from '@ngrx/effects';
+import {emptySearchResult} from '../../../../common/store/search.reducer';
+import {DepositAccountService} from '../../../../services/depositAccount/deposit-account.service';
+import {Injectable} from '@angular/core';
+import * as instanceActions from '../deposit.actions';
+import {ChequeService} from '../../../../services/cheque/cheque.service';
+
+@Injectable()
+export class DepositProductInstanceApiEffects {
+
+  @Effect()
+  search$: Observable<Action> = this.actions$
+    .ofType(instanceActions.SEARCH)
+    .debounceTime(300)
+    .map(action => action.payload)
+    .switchMap(payload => {
+      const nextSearch$ = this.actions$.ofType(instanceActions.SEARCH).skip(1);
+
+      return this.depositService.fetchProductInstances(payload.customerId)
+        .takeUntil(nextSearch$)
+        .map(productInstances => new instanceActions.SearchCompleteAction({
+          elements: productInstances,
+          totalElements: productInstances.length,
+          totalPages: 1
+        }))
+        .catch(() => of(new instanceActions.SearchCompleteAction(emptySearchResult())));
+    });
+
+  @Effect()
+  createProduct$: Observable<Action> = this.actions$
+    .ofType(instanceActions.CREATE)
+    .map((action: instanceActions.CreateProductInstanceAction) => action.payload)
+    .mergeMap(payload =>
+      this.depositService.createProductInstance(payload.productInstance)
+        .map(() => new instanceActions.CreateProductInstanceSuccessAction({
+          resource: payload.productInstance,
+          activatedRoute: payload.activatedRoute
+        }))
+        .catch((error) => of(new instanceActions.CreateProductInstanceFailAction(error)))
+    );
+
+  @Effect()
+  updateProduct$: Observable<Action> = this.actions$
+    .ofType(instanceActions.UPDATE)
+    .map((action: instanceActions.UpdateProductInstanceAction) => action.payload)
+    .mergeMap(payload =>
+      this.depositService.updateProductInstance(payload.productInstance)
+        .map(() => new instanceActions.UpdateProductInstanceSuccessAction({
+          resource: payload.productInstance,
+          activatedRoute: payload.activatedRoute
+        }))
+        .catch((error) => of(new instanceActions.UpdateProductInstanceFailAction(error)))
+    );
+
+  @Effect()
+  issueCheques$: Observable<Action> = this.actions$
+    .ofType(instanceActions.ISSUE_CHEQUES)
+    .map((action: instanceActions.IssueChequesAction) => action.payload)
+    .mergeMap(payload =>
+      this.chequeService.issue(payload.issuingCount)
+        .map(() => new instanceActions.IssueChequesSuccessAction(payload))
+        .catch((error) => of(new instanceActions.IssueChequesFailAction(error)))
+    );
+
+  constructor(private actions$: Actions, private depositService: DepositAccountService, private chequeService: ChequeService) { }
+}
diff --git a/src/app/customers/deposits/store/index.ts b/src/app/customers/deposits/store/index.ts
new file mode 100644
index 0000000..3de47e7
--- /dev/null
+++ b/src/app/customers/deposits/store/index.ts
@@ -0,0 +1,69 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, getResourceLoadedAt, getResourceSelected, ResourceState} from '../../../common/store/resource.reducer';
+import * as fromCustomer from '../../store';
+import {ActionReducer, Store} from '@ngrx/store';
+import {createReducer} from '../../../store/index';
+import {
+  createSearchReducer,
+  getSearchEntities,
+  getSearchTotalElements,
+  getSearchTotalPages,
+  SearchState
+} from '../../../common/store/search.reducer';
+import {createSelector} from 'reselect';
+
+export interface State extends fromCustomer.State {
+  deposits: ResourceState;
+  depositSearch: SearchState;
+}
+
+const reducers = {
+  deposits: createResourceReducer('Deposit', undefined, 'accountIdentifier'),
+  depositSearch: createSearchReducer('Deposit')
+};
+
+export const depositModuleReducer: ActionReducer<State> = createReducer(reducers);
+
+export class DepositsStore extends Store<State> {}
+
+export function depositsStoreFactory(appStore: Store<fromCustomer.State>) {
+  appStore.replaceReducer(depositModuleReducer);
+  return appStore;
+}
+
+export const getDepositSearchState = (state: State) => state.depositSearch;
+
+export const getSearchDeposits = createSelector(getDepositSearchState, getSearchEntities);
+export const getDepositSearchTotalElements = createSelector(getDepositSearchState, getSearchTotalElements);
+export const getDepositSearchTotalPages = createSelector(getDepositSearchState, getSearchTotalPages);
+
+export const getDepositSearchResults = createSelector(getSearchDeposits, getDepositSearchTotalPages, getDepositSearchTotalElements,
+  (deposits, totalPages, totalElements) => {
+    return {
+      deposits,
+      totalPages,
+      totalElements
+    };
+  });
+
+export const getDepositsState = (state: State) => state.deposits;
+
+export const getDepositsLoadedAt = createSelector(getDepositsState, getResourceLoadedAt);
+export const getSelectedDepositInstance = createSelector(getDepositsState, getResourceSelected);
diff --git a/src/app/customers/detail/activity/activity.component.html b/src/app/customers/detail/activity/activity.component.html
new file mode 100644
index 0000000..ae2f48d
--- /dev/null
+++ b/src/app/customers/detail/activity/activity.component.html
@@ -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.
+-->
+
+<fims-layout-card-over title="{{'Activities' | translate}}" [navigateBackTo]="['../']">
+  <mat-list>
+    <h3 mat-subheader translate>Latest activity</h3>
+    <ng-template let-item let-last="last" ngFor [ngForOf]="commands">
+      <fims-command-display [command]="item"></fims-command-display>
+      <mat-divider *ngIf="!last" mat-inset></mat-divider>
+    </ng-template>
+  </mat-list>
+</fims-layout-card-over>
diff --git a/src/app/customers/detail/activity/activity.component.ts b/src/app/customers/detail/activity/activity.component.ts
new file mode 100644
index 0000000..04ad93d
--- /dev/null
+++ b/src/app/customers/detail/activity/activity.component.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 {Component, OnDestroy, OnInit} from '@angular/core';
+import {Command} from '../../../services/customer/domain/command.model';
+import {CustomersStore} from '../../store/index';
+import {LOAD_ALL} from '../../store/commands/commands.actions';
+import * as fromCustomers from '../../store';
+import {Subscription} from 'rxjs/Subscription';
+
+@Component({
+  templateUrl: './activity.component.html'
+})
+export class CustomerActivityComponent implements OnInit, OnDestroy {
+
+  private commandsSubscription: Subscription;
+
+  private customerSubscription: Subscription;
+
+  commands: Command[];
+
+  constructor(private store: CustomersStore) {}
+
+  ngOnInit(): void {
+    this.customerSubscription = this.store.select(fromCustomers.getSelectedCustomer)
+      .subscribe(customer => this.store.dispatch({ type: LOAD_ALL, payload: customer.identifier }));
+
+    this.commandsSubscription = this.store.select(fromCustomers.getAllCustomerCommands)
+      .subscribe(commands => this.commands = commands);
+  }
+
+  ngOnDestroy(): void {
+    this.commandsSubscription.unsubscribe();
+    this.customerSubscription.unsubscribe();
+  }
+}
diff --git a/src/app/customers/detail/customer.detail.component.html b/src/app/customers/detail/customer.detail.component.html
new file mode 100644
index 0000000..5c60582
--- /dev/null
+++ b/src/app/customers/detail/customer.detail.component.html
@@ -0,0 +1,111 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<fims-layout-card-over title="{{customer.givenName}} {{customer.surname}}" [navigateBackTo]="['/customers']">
+  <fims-layout-card-over-header-menu>
+    <td-search-box #searchBox placeholder="{{'Search' | translate}}" (search)="searchCustomer($event)" [alwaysVisible]="false"></td-search-box>
+  </fims-layout-card-over-header-menu>
+  <td-message *ngIf="!isCustomerActive" label="{{'Member not active' | translate }}"
+              sublabel="{{'You can activate the member under tasks' | translate }}"
+              color="warn" icon="error">
+    <button td-message-actions mat-button (click)="goToTasks()"
+            *hasPermission="{ id: 'customer_customers', accessLevel: 'CHANGE'}" translate>GO TO TASKS
+    </button>
+  </td-message>
+  <fims-two-column-layout>
+    <ng-container left>
+      <fims-portrait (onClick)="changePortrait()" [tooltip]="'Change portrait'" [blob]="portrait"></fims-portrait>
+      <mat-nav-list>
+        <h3 mat-subheader translate>Financial products</h3>
+        <a mat-list-item [routerLink]="['loans']" *hasPermission="{ id: 'portfolio_cases', accessLevel: 'READ'}">
+          <mat-icon matListAvatar>credit_card</mat-icon>
+          <h3 matLine translate>Loan accounts</h3>
+          <p matLine translate>Manage loan accounts</p>
+        </a>
+        <a mat-list-item [routerLink]="['deposits']" *hasPermission="{ id: 'deposit_instances', accessLevel: 'READ'}">
+          <mat-icon matListAvatar>attach_money</mat-icon>
+          <h3 matLine translate>Deposit accounts</h3>
+          <p matLine translate>Manage deposit accounts</p>
+        </a>
+        <mat-divider></mat-divider>
+        <h3 mat-subheader translate>Management</h3>
+        <a mat-list-item [routerLink]="['identifications']" *hasPermission="{ id: 'customer_identifications', accessLevel: 'READ'}">
+          <mat-icon matListAvatar>perm_identity</mat-icon>
+          <h3 matLine translate>Identification cards</h3>
+          <p matLine translate>View identification cards</p>
+        </a>
+        <a mat-list-item [routerLink]="['tasks']">
+          <mat-icon matListAvatar>playlist_add_check</mat-icon>
+          <h3 matLine translate>Tasks</h3>
+          <p matLine translate>Change the status of the member </p>
+        </a>
+        <a mat-list-item [routerLink]="['activities']">
+          <mat-icon matListAvatar>event</mat-icon>
+          <h3 matLine translate>Activities</h3>
+          <p matLine translate>Recent activities</p>
+        </a>
+        <a mat-list-item [routerLink]="['payroll']" *hasPermission="{ id: 'payroll_configuration', accessLevel: 'READ'}">
+          <mat-icon matListAvatar>receipt</mat-icon>
+          <h3 matLine translate>Payroll</h3>
+          <p matLine translate>Manage payroll distributions</p>
+        </a>
+      </mat-nav-list>
+    </ng-container>
+    <mat-list right>
+      <h3 mat-subheader translate>Current status</h3>
+      <fims-state-display [state]="customer.currentState"></fims-state-display>
+      <h3 mat-subheader translate>Address</h3>
+      <mat-list-item>
+        <mat-icon matListAvatar>location_on</mat-icon>
+        <h3 matLine>{{customer.address?.street}}, {{customer.address?.city}}, {{customer.address?.postalCode}},
+          {{customer.address?.country}}</h3>
+      </mat-list-item>
+      <h3 mat-subheader translate>Contact information</h3>
+      <mat-list-item [ngSwitch]="detail.type" *ngFor="let detail of customer.contactDetails">
+        <mat-icon *ngSwitchCase="'EMAIL'" matListAvatar>email</mat-icon>
+        <mat-icon *ngSwitchCase="'PHONE'" matListAvatar>phone</mat-icon>
+        <mat-icon *ngSwitchCase="'MOBILE'" matListAvatar>smartphone</mat-icon>
+        <h3 matLine>{{detail.value}}</h3>
+      </mat-list-item>
+      <mat-list-item *ngIf="!customer.contactDetails?.length">
+        <h3 matLine translate>No contact details available</h3>
+      </mat-list-item>
+      <h3 mat-subheader translate>Birthday</h3>
+      <mat-list-item>
+        <mat-icon matListAvatar>cake</mat-icon>
+        <h3 matLine>{{customer.dateOfBirth | displayFimsDate}}</h3>
+      </mat-list-item>
+      <mat-list-item>
+        <h3 matLine translate>Application date</h3>
+        <h3 matLine>{{customer.applicationDate | date:'shortDate'}}</h3>
+      </mat-list-item>
+      <fims-customer-custom-values
+        [catalog]="catalog$ | async"
+        [values]="customer.customValues">
+      </fims-customer-custom-values>
+      <mat-list-item>
+        <h3 matLine translate>Created by</h3>
+        <p matLine>{{customer.createdBy}} - {{customer.createdOn | date:'medium'}}</p>
+      </mat-list-item>
+      <mat-list-item>
+        <h3 matLine translate>Last modified by</h3>
+        <p matLine>{{customer.lastModifiedBy}} - {{customer.lastModifiedOn | date:'medium'}}</p>
+      </mat-list-item>
+    </mat-list>
+  </fims-two-column-layout>
+</fims-layout-card-over>
+<fims-fab-button title="{{'Edit member ' | translate}}" icon="mode_edit" [link]="['edit']" [permission]="{ id: 'customer_customers', accessLevel: 'CHANGE'}"></fims-fab-button>
diff --git a/src/app/customers/detail/customer.detail.component.scss b/src/app/customers/detail/customer.detail.component.scss
new file mode 100644
index 0000000..aaaf060
--- /dev/null
+++ b/src/app/customers/detail/customer.detail.component.scss
@@ -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.
+ */
+
+.header-button-right {
+  float: right
+}
diff --git a/src/app/customers/detail/customer.detail.component.ts b/src/app/customers/detail/customer.detail.component.ts
new file mode 100644
index 0000000..7cdaa9a
--- /dev/null
+++ b/src/app/customers/detail/customer.detail.component.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 {ActivatedRoute, Router} from '@angular/router';
+import {Component, OnDestroy, OnInit} from '@angular/core';
+import {Customer} from '../../services/customer/domain/customer.model';
+import {Catalog} from '../../services/catalog/domain/catalog.model';
+import * as fromCustomers from '../store';
+import {Subscription} from 'rxjs/Subscription';
+import {CustomersStore} from '../store/index';
+import {CustomerService} from '../../services/customer/customer.service';
+import {Observable} from 'rxjs/Observable';
+
+
+interface CustomDetailField {
+  label: string;
+  value: string;
+}
+
+@Component({
+  templateUrl: './customer.detail.component.html',
+  styleUrls: ['./customer.detail.component.scss']
+})
+export class CustomerDetailComponent implements OnInit, OnDestroy {
+
+  portrait: Blob;
+
+  private customerSubscription: Subscription;
+
+  customer: Customer;
+
+  catalog$: Observable<Catalog>;
+
+  isCustomerActive: boolean;
+
+  constructor(private route: ActivatedRoute, private router: Router, private store: CustomersStore,
+              private customerService: CustomerService) {}
+
+  ngOnInit(): void {
+    this.customerSubscription = this.store.select(fromCustomers.getSelectedCustomer)
+      .filter(customer => !!customer)
+      .do(customer => this.customer = customer)
+      .do(customer => this.isCustomerActive = customer.currentState === 'ACTIVE')
+      .flatMap(customer => this.customerService.getPortrait(customer.identifier))
+      .subscribe(portrait => this.portrait = portrait);
+
+    this.catalog$ = this.store.select(fromCustomers.getCustomerCatalog);
+  }
+
+  ngOnDestroy(): void {
+    this.customerSubscription.unsubscribe();
+  }
+
+  searchCustomer(term): void {
+    if (term) {
+      this.router.navigate(['../../../'], { queryParams: { term: term }, relativeTo: this.route });
+    }
+  }
+
+  changePortrait(): void {
+    this.router.navigate(['portrait'], { relativeTo: this.route });
+  }
+
+  goToTasks(): void {
+    this.router.navigate(['tasks'], { relativeTo: this.route });
+  }
+
+}
diff --git a/src/app/customers/detail/customer.index.component.html b/src/app/customers/detail/customer.index.component.html
new file mode 100644
index 0000000..ca721b3
--- /dev/null
+++ b/src/app/customers/detail/customer.index.component.html
@@ -0,0 +1,18 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<router-outlet></router-outlet>
diff --git a/src/app/customers/detail/customer.index.component.ts b/src/app/customers/detail/customer.index.component.ts
new file mode 100644
index 0000000..1e7d89d
--- /dev/null
+++ b/src/app/customers/detail/customer.index.component.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 {Component, OnDestroy, OnInit} from '@angular/core';
+import {SelectAction} from '../store/customer.actions';
+import {CustomersStore} from '../store/index';
+import {Subscription} from 'rxjs/Subscription';
+import {ActivatedRoute} from '@angular/router';
+
+@Component({
+  templateUrl: './customer.index.component.html'
+})
+export class CustomerIndexComponent implements OnInit, OnDestroy {
+
+  private actionsSubscription: Subscription;
+
+  constructor(private route: ActivatedRoute, private customersStore: CustomersStore) {}
+
+  ngOnInit(): void {
+    this.actionsSubscription = this.route.params
+      .map(params => new SelectAction(params['id']))
+      .subscribe(this.customersStore);
+  }
+
+  ngOnDestroy(): void {
+    this.actionsSubscription.unsubscribe();
+  }
+}
diff --git a/src/app/customers/detail/identityCard/form/create.form.component.html b/src/app/customers/detail/identityCard/form/create.form.component.html
new file mode 100644
index 0000000..2a6cf3c
--- /dev/null
+++ b/src/app/customers/detail/identityCard/form/create.form.component.html
@@ -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.
+-->
+
+<fims-layout-card-over title="{{'Create new identification card' | translate}}">
+  <fims-identity-card-form #form
+                                (onSave)="onSave($event)"
+                                (onCancel)="onCancel()"
+                                [identificationCard]="identificationCard">
+  </fims-identity-card-form>
+</fims-layout-card-over>
diff --git a/src/app/customers/detail/identityCard/form/create.form.component.ts b/src/app/customers/detail/identityCard/form/create.form.component.ts
new file mode 100644
index 0000000..7cdfb8a
--- /dev/null
+++ b/src/app/customers/detail/identityCard/form/create.form.component.ts
@@ -0,0 +1,85 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, OnDestroy, OnInit, ViewChild} from '@angular/core';
+import {Subscription} from 'rxjs/Subscription';
+import {IdentificationCard} from '../../../../services/customer/domain/identification-card.model';
+import {IdentityCardFormComponent} from './identity-card-form.component';
+import {ActivatedRoute, Router} from '@angular/router';
+import * as fromCustomers from '../../../store/index';
+import {CustomersStore} from '../../../store/index';
+import {CREATE, RESET_FORM} from '../../../store/identityCards/identity-cards.actions';
+import {Error} from '../../../../services/domain/error.model';
+import {Customer} from '../../../../services/customer/domain/customer.model';
+
+@Component({
+  templateUrl: './create.form.component.html'
+})
+export class CreateCustomerIdentificationCardFormComponent implements OnInit, OnDestroy {
+
+  private formStateSubscription: Subscription;
+
+  private customerSubscription: Subscription;
+
+  private customer: Customer;
+
+  @ViewChild('form') formComponent: IdentityCardFormComponent;
+
+  identificationCard: IdentificationCard = {
+    type: '',
+    number: '',
+    expirationDate: null
+  };
+
+  constructor(private router: Router, private route: ActivatedRoute, private store: CustomersStore) {}
+
+  ngOnInit() {
+    this.customerSubscription = this.store.select(fromCustomers.getSelectedCustomer)
+      .subscribe(customer => this.customer = customer);
+
+    this.formStateSubscription = this.store.select(fromCustomers.getCustomerIdentificationCardFormError)
+      .filter((error: Error) => !!error)
+      .subscribe((error: Error) => {
+        this.formComponent.showNumberValidationError();
+      });
+  }
+
+  ngOnDestroy(): void {
+    this.formStateSubscription.unsubscribe();
+    this.customerSubscription.unsubscribe();
+
+    this.store.dispatch({ type: RESET_FORM });
+  }
+
+  onSave(identificationCard: IdentificationCard) {
+    const customerId = this.customer.identifier;
+    this.store.dispatch({ type: CREATE, payload: {
+      customerId,
+      identificationCard,
+      activatedRoute: this.route
+    } });
+  }
+
+  onCancel() {
+    this.navigateAway();
+  }
+
+  navigateAway(): void {
+    this.router.navigate(['../'], { relativeTo: this.route });
+  }
+}
diff --git a/src/app/customers/detail/identityCard/form/edit.form.component.html b/src/app/customers/detail/identityCard/form/edit.form.component.html
new file mode 100644
index 0000000..978b4e8
--- /dev/null
+++ b/src/app/customers/detail/identityCard/form/edit.form.component.html
@@ -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.
+-->
+
+<fims-layout-card-over title="{{'Edit identification card' | translate}}">
+  <fims-identity-card-form #form
+                           (onSave)="onSave($event)"
+                           (onCancel)="onCancel()"
+                           [identificationCard]="identificationCard$ | async"
+                           [editMode]="true">
+  </fims-identity-card-form>
+</fims-layout-card-over>
diff --git a/src/app/customers/detail/identityCard/form/edit.form.component.ts b/src/app/customers/detail/identityCard/form/edit.form.component.ts
new file mode 100644
index 0000000..bc773d6
--- /dev/null
+++ b/src/app/customers/detail/identityCard/form/edit.form.component.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 {Component, OnDestroy, OnInit} from '@angular/core';
+import {Subscription} from 'rxjs/Subscription';
+import {IdentificationCard} from '../../../../services/customer/domain/identification-card.model';
+import {Observable} from 'rxjs/Observable';
+import * as fromCustomers from '../../../store/index';
+import {CustomersStore} from '../../../store/index';
+import {ActivatedRoute, Router} from '@angular/router';
+import {UPDATE} from '../../../store/identityCards/identity-cards.actions';
+import {Customer} from '../../../../services/customer/domain/customer.model';
+
+@Component({
+  templateUrl: './edit.form.component.html'
+})
+export class EditCustomerIdentificationCardFormComponent implements OnInit, OnDestroy {
+
+  private customerSubscription: Subscription;
+
+  private customer: Customer;
+
+  identificationCard$: Observable<IdentificationCard>;
+
+  constructor(private router: Router, private route: ActivatedRoute, private customersStore: CustomersStore) {}
+
+  ngOnInit() {
+    this.customerSubscription = this.customersStore.select(fromCustomers.getSelectedCustomer)
+      .subscribe(customer => this.customer = customer);
+
+    this.identificationCard$ = this.customersStore.select(fromCustomers.getSelectedIdentificationCard);
+  }
+
+  ngOnDestroy(): void {
+    this.customerSubscription.unsubscribe();
+  }
+
+  onSave(identificationCard: IdentificationCard) {
+    const customerId = this.customer.identifier;
+
+    this.customersStore.dispatch({ type: UPDATE, payload: {
+      customerId,
+      identificationCard,
+      activatedRoute: this.route
+    } });
+  }
+
+  onCancel() {
+    this.navigateAway();
+  }
+
+  navigateAway(): void {
+    this.router.navigate(['../'], { relativeTo: this.route });
+  }
+}
diff --git a/src/app/customers/detail/identityCard/form/identity-card-form.component.html b/src/app/customers/detail/identityCard/form/identity-card-form.component.html
new file mode 100644
index 0000000..4b40a76
--- /dev/null
+++ b/src/app/customers/detail/identityCard/form/identity-card-form.component.html
@@ -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.
+-->
+
+<td-steps mode="'vertical'">
+  <td-step #detailsStep label="{{'Identification card' | translate}}" [state]="form.valid ? 'complete' : form.pristine ? 'none' : 'required'">
+    <form [formGroup]="form" layout="column">
+      <fims-id-input [form]="form" controlName="number" [placeholder]="'Number'" [readonly]="editMode"></fims-id-input>
+      <fims-text-input [form]="form" controlName="type" placeholder="{{'Type' | translate}}"></fims-text-input>
+      <fims-date-input [form]="form" controlName="expirationDate" placeholder="{{'Expiration date' | translate}}"></fims-date-input>
+      <fims-text-input [form]="form" controlName="issuer" placeholder="{{'Issuer' | translate}}"></fims-text-input>
+    </form>
+  </td-step>
+  <td-step label="{{'Final step' | translate}}" [state]="'complete'">
+    <ng-template td-step-summary>
+      <fims-form-final-action
+        [resourceName]="'IDENTIFICATION CARD'"
+        [editMode]="editMode"
+        [disabled]="!form.valid"
+        (onCancel)="cancel()"
+        (onSave)="save()">
+      </fims-form-final-action>
+    </ng-template>
+  </td-step>
+</td-steps>
diff --git a/src/app/customers/detail/identityCard/form/identity-card-form.component.spec.ts b/src/app/customers/detail/identityCard/form/identity-card-form.component.spec.ts
new file mode 100644
index 0000000..042f2bc
--- /dev/null
+++ b/src/app/customers/detail/identityCard/form/identity-card-form.component.spec.ts
@@ -0,0 +1,131 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 {async, ComponentFixture, TestBed} from '@angular/core/testing';
+import {IdentityCardFormComponent} from './identity-card-form.component';
+import {MatButtonModule, MatCardModule, MatIconModule, MatInputModule} from '@angular/material';
+import {CovalentFileModule, CovalentStepsModule} from '@covalent/core';
+import {ReactiveFormsModule} from '@angular/forms';
+import {setValueByCssSelector} from '../../../../common/testing/input-fields';
+import {By} from '@angular/platform-browser';
+import {Component, DebugElement} from '@angular/core';
+import {IdentificationCard} from '../../../../services/customer/domain/identification-card.model';
+import {TranslateModule} from '@ngx-translate/core';
+import {NoopAnimationsModule} from '@angular/platform-browser/animations';
+import {dateAsISOString, toFimsDate} from '../../../../services/domain/date.converter';
+import {FimsSharedModule} from '../../../../common/common.module';
+
+describe('Test identity card form component', () => {
+
+  let fixture: ComponentFixture<TestComponent>;
+
+  let formComponent: TestComponent;
+
+  let oneDayAhead: string;
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      declarations: [
+        TestComponent,
+        IdentityCardFormComponent
+      ],
+      imports: [
+        TranslateModule.forRoot(),
+        FimsSharedModule,
+        ReactiveFormsModule,
+        MatInputModule,
+        MatIconModule,
+        MatButtonModule,
+        MatCardModule,
+        CovalentStepsModule,
+        CovalentFileModule,
+        NoopAnimationsModule
+      ]
+    });
+
+    fixture = TestBed.createComponent(TestComponent);
+    formComponent = fixture.componentInstance;
+
+    fixture.detectChanges();
+  }));
+
+  beforeEach(() => {
+    const today = new Date();
+    today.setDate(today.getDate() + 1);
+    oneDayAhead = dateAsISOString(today);
+  });
+
+  function setValidValues(): void {
+    setValueByCssSelector(fixture, '#number', 'test');
+    setValueByCssSelector(fixture, '#type', 'test');
+    setValueByCssSelector(fixture, '#expirationDate', oneDayAhead);
+    setValueByCssSelector(fixture, '#issuer', 'test');
+  }
+
+  it('should disable/enable save button', () => {
+    const button: DebugElement = fixture.debugElement.query(By.css('button[color="primary"]'));
+
+    expect(button.properties['disabled']).toBeTruthy('Button should be disabled');
+
+    setValidValues();
+
+    fixture.detectChanges();
+
+    expect(button.properties['disabled']).toBeFalsy('Button should be enabled');
+  });
+
+  it('should set the correct form values', () => {
+    setValidValues();
+
+    fixture.detectChanges();
+
+    const button: DebugElement = fixture.debugElement.query(By.css('button[color="primary"]'));
+
+    button.nativeElement.click();
+
+    const expectedResult: IdentificationCard = {
+      type: 'test',
+      issuer: 'test',
+      expirationDate: toFimsDate(oneDayAhead),
+      number: 'test'
+    };
+
+    fixture.detectChanges();
+
+    expect(expectedResult).toEqual(fixture.componentInstance.output);
+  });
+
+});
+
+@Component({
+  template: '<fims-identity-card-form [identificationCard]="input" (onSave)="onSave($event)"></fims-identity-card-form>'
+})
+class TestComponent {
+
+  input: IdentificationCard = {
+    type: '',
+    number: '',
+    expirationDate: null
+  };
+
+  output: IdentificationCard;
+
+  onSave(identificationCard: IdentificationCard): void {
+    this.output = identificationCard;
+  }
+}
diff --git a/src/app/customers/detail/identityCard/form/identity-card-form.component.ts b/src/app/customers/detail/identityCard/form/identity-card-form.component.ts
new file mode 100644
index 0000000..02359a6
--- /dev/null
+++ b/src/app/customers/detail/identityCard/form/identity-card-form.component.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 {Component, EventEmitter, Input, OnInit, Output, ViewChild} from '@angular/core';
+import {FormComponent} from '../../../../common/forms/form.component';
+import {FormBuilder, Validators} from '@angular/forms';
+import {IdentificationCard} from '../../../../services/customer/domain/identification-card.model';
+import {ExpirationDate} from '../../../../services/customer/domain/expiration-date.model';
+import {FimsValidators} from '../../../../common/validator/validators';
+import {TdStepComponent} from '@covalent/core';
+
+@Component({
+  selector: 'fims-identity-card-form',
+  templateUrl: './identity-card-form.component.html'
+})
+export class IdentityCardFormComponent extends FormComponent<IdentificationCard> implements OnInit {
+
+  @ViewChild('detailsStep') step: TdStepComponent;
+
+  @Input() identificationCard: IdentificationCard;
+
+  @Input() editMode = false;
+
+  @Output('onSave') onSave = new EventEmitter<IdentificationCard>();
+
+  @Output('onCancel') onCancel = new EventEmitter<void>();
+
+  constructor(private formBuilder: FormBuilder) {
+    super();
+  }
+
+  ngOnInit(): void {
+    this.form = this.formBuilder.group({
+      number: [this.identificationCard.number, [Validators.required, Validators.minLength(3), Validators.maxLength(32),
+        FimsValidators.urlSafe]],
+      type: [this.identificationCard.type, [Validators.required, Validators.maxLength(128)]],
+      expirationDate: [this.formatDate(this.identificationCard.expirationDate), [Validators.required, FimsValidators.afterToday]],
+      issuer: [this.identificationCard.issuer, [Validators.required, Validators.maxLength(256)]]
+    });
+
+    this.step.open();
+  }
+
+  showNumberValidationError(): void {
+    this.setError('number', 'unique', true);
+  }
+
+  private formatDate(expirationDate: ExpirationDate): string {
+    if (!expirationDate) {
+      return '';
+    }
+    return `${expirationDate.year}-${this.addZero(expirationDate.month)}-${this.addZero(expirationDate.day)}`;
+  }
+
+  private addZero(value: number): string {
+    return ('0' + value).slice(-2);
+  }
+
+  get formData(): IdentificationCard {
+    // Not needed
+    return;
+  }
+
+  cancel(): void {
+    this.onCancel.emit();
+  }
+
+  save(): void {
+    const expirationDate: string = this.form.get('expirationDate').value;
+    const chunks: string[] = expirationDate.split('-');
+
+    const identificationCard: IdentificationCard = {
+      type: this.form.get('type').value,
+      number: this.form.get('number').value,
+      expirationDate: {
+        day: Number(chunks[2]),
+        month: Number(chunks[1]),
+        year: Number(chunks[0])
+      },
+      issuer: this.form.get('issuer').value
+    };
+
+    this.onSave.emit(identificationCard);
+  }
+
+}
diff --git a/src/app/customers/detail/identityCard/identity-card-exists.guard.ts b/src/app/customers/detail/identityCard/identity-card-exists.guard.ts
new file mode 100644
index 0000000..3e80d26
--- /dev/null
+++ b/src/app/customers/detail/identityCard/identity-card-exists.guard.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 {ActivatedRouteSnapshot, CanActivate, RouterStateSnapshot} from '@angular/router';
+import {Injectable} from '@angular/core';
+import * as fromCustomers from '../../store';
+import {Observable} from 'rxjs/Observable';
+import {of} from 'rxjs/observable/of';
+import {CustomersStore} from '../../store/index';
+import {CustomerService} from '../../../services/customer/customer.service';
+import {ExistsGuardService} from '../../../common/guards/exists-guard';
+import {LoadAction} from '../../store/identityCards/identity-cards.actions';
+
+@Injectable()
+export class IdentityCardExistsGuard implements CanActivate {
+
+  constructor(private store: CustomersStore,
+              private customerService: CustomerService,
+              private existsGuardService: ExistsGuardService) {
+  }
+
+  hasIdentificationCardInStore(number: string): Observable<boolean> {
+    const timestamp$: Observable<number> = this.store.select(fromCustomers.getIdentificationCardLoadedAt)
+      .map(loadedAt => loadedAt[number]);
+
+    return this.existsGuardService.isWithinExpiry(timestamp$);
+  }
+
+  hasIdentificationCardInApi(customerId: string, number: string): Observable<boolean> {
+    const getIdentificationCard: Observable<any> = this.customerService.getIdentificationCard(customerId, number)
+      .map(identificationCardEntity => new LoadAction({
+        resource: identificationCardEntity
+      }))
+      .do((action: LoadAction) => this.store.dispatch(action))
+      .map(identificationCard => !!identificationCard);
+
+    return this.existsGuardService.routeTo404OnError(getIdentificationCard);
+  }
+
+  hasIdentificationCard(customerId: string, number: string): Observable<boolean> {
+    return this.hasIdentificationCardInStore(number)
+      .switchMap(inStore => {
+        if (inStore) {
+          return of(inStore);
+        }
+        return this.hasIdentificationCardInApi(customerId, number);
+      });
+  }
+
+  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
+    return this.hasIdentificationCard(route.parent.params['id'], route.params['number']);
+  }
+}
diff --git a/src/app/customers/detail/identityCard/identity-card.detail.component.html b/src/app/customers/detail/identityCard/identity-card.detail.component.html
new file mode 100644
index 0000000..96627d0
--- /dev/null
+++ b/src/app/customers/detail/identityCard/identity-card.detail.component.html
@@ -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.
+-->
+
+<fims-layout-card-over title="{{ 'Identification card' | translate }}" [navigateBackTo]="['../../../../../../']">
+  <fims-layout-card-over-header-menu layout="row" layout-align="end center">
+    <a mat-icon-button title="{{'Edit identification card' | translate}}" [routerLink]="['edit']" *hasPermission="{ id: 'customer_identifications', accessLevel: 'CHANGE' }">
+      <mat-icon>mode_edit</mat-icon>
+    </a>
+    <button mat-icon-button (click)="deleteIdentificationCard()" title="{{'Delete this identification card' | translate}}" *hasPermission="{ id: 'customer_identifications', accessLevel: 'DELETE' }"><mat-icon>delete</mat-icon></button>
+  </fims-layout-card-over-header-menu>
+  <div class="mat-content inset" flex>
+    <mat-list>
+      <mat-list-item>
+        <h3 matLine translate>Number</h3>
+        <p matLine>{{identificationCard?.number}}</p>
+      </mat-list-item>
+      <mat-list-item>
+        <h3 matLine translate>Type</h3>
+        <p matLine>{{identificationCard?.type}}</p>
+      </mat-list-item>
+      <mat-list-item>
+        <h3 matLine translate>Issuer</h3>
+        <p matLine>{{identificationCard?.issuer}}</p>
+      </mat-list-item>
+      <mat-list-item>
+        <h3 matLine translate>Expiration date</h3>
+        <p matLine>{{identificationCard?.expirationDate | displayFimsDate}}</p>
+      </mat-list-item>
+    </mat-list>
+    <fims-identification-card-scan-list
+      [scans]="scans$ | async"
+      (onView)="viewScan($event)"
+      (onUpload)="uploadScan($event)"
+      (onDelete)="deleteScan($event)">
+    </fims-identification-card-scan-list>
+  </div>
+</fims-layout-card-over>
+<fims-fab-button title="{{'Add identification card scan' | translate}}" icon="add_a_photo" [link]="['addScan']" [permission]="{ id: 'customer_identifications', accessLevel: 'CHANGE'}"></fims-fab-button>
diff --git a/src/app/customers/detail/identityCard/identity-card.detail.component.ts b/src/app/customers/detail/identityCard/identity-card.detail.component.ts
new file mode 100644
index 0000000..52d1bc9
--- /dev/null
+++ b/src/app/customers/detail/identityCard/identity-card.detail.component.ts
@@ -0,0 +1,150 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, OnDestroy, OnInit} from '@angular/core';
+import {Subscription} from 'rxjs/Subscription';
+import * as fromCustomers from '../../store/index';
+import {CustomersStore} from '../../store/index';
+import {Customer} from '../../../services/customer/domain/customer.model';
+import {DELETE} from '../../store/identityCards/identity-cards.actions';
+import {CREATE, DELETE as DELETE_SCAN, LoadAllAction} from '../../store/identityCards/scans/scans.actions';
+import {ActivatedRoute} from '@angular/router';
+import {IdentificationCard} from '../../../services/customer/domain/identification-card.model';
+import {Observable} from 'rxjs/Observable';
+import {TranslateService} from '@ngx-translate/core';
+import {TdDialogService} from '@covalent/core';
+import {IdentificationCardScan} from '../../../services/customer/domain/identification-card-scan.model';
+import {UploadIdentificationCardScanEvent} from './scans/scan.list.component';
+import {ImageComponent} from '../../../common/image/image.component';
+import {CustomerService} from '../../../services/customer/customer.service';
+
+@Component({
+  templateUrl: './identity-card.detail.component.html'
+})
+export class CustomerIdentityCardDetailComponent implements OnInit, OnDestroy {
+
+  private actionSubscription: Subscription;
+
+  private customer: Customer;
+
+  identificationCard: IdentificationCard;
+
+  scans$: Observable<IdentificationCardScan[]>;
+
+  constructor(private route: ActivatedRoute, private customersStore: CustomersStore, private translate: TranslateService,
+              private dialogService: TdDialogService, private customerService: CustomerService) {}
+
+  ngOnInit(): void {
+    this.scans$ = this.customersStore.select(fromCustomers.getAllIdentificationCardScanEntities);
+
+    this.actionSubscription = Observable.combineLatest(
+      this.customersStore.select(fromCustomers.getSelectedIdentificationCard)
+        .filter(identificationCard => !!identificationCard),
+      this.customersStore.select(fromCustomers.getSelectedCustomer),
+      (identificationCard, customer) => ({
+        identificationCard,
+        customer
+      }))
+      .do(result => this.customer = result.customer)
+      .do(result => this.identificationCard = result.identificationCard)
+      .map(result => new LoadAllAction({
+        customerIdentifier: result.customer.identifier,
+        identificationCardNumber: result.identificationCard.number
+      }))
+      .subscribe(this.customersStore);
+  }
+
+  ngOnDestroy(): void {
+    this.actionSubscription.unsubscribe();
+  }
+
+  confirmDeletion(): Observable<boolean> {
+    const message = 'Do you want to delete this identification card?';
+    const title = 'Confirm deletion';
+    const button = 'DELETE IDENTIFICATION CARD';
+
+    return this.translate.get([title, message, button])
+      .flatMap(result =>
+        this.dialogService.openConfirm({
+          message: result[message],
+          title: result[title],
+          acceptButton: result[button]
+        }).afterClosed()
+      );
+  }
+
+  deleteIdentificationCard(): void {
+    this.confirmDeletion()
+      .filter(accept => accept)
+      .subscribe(() => {
+        this.customersStore.dispatch({ type: DELETE, payload: {
+          customerId: this.customer.identifier,
+          identificationCard: this.identificationCard,
+          activatedRoute: this.route
+        }});
+      });
+  }
+
+  viewScan(identifier: string): void {
+    this.customerService.getIdentificationCardScanImage(this.customer.identifier, this.identificationCard.number, identifier)
+      .subscribe(blob => {
+        this.dialogService.open(ImageComponent, {
+          data: blob
+        });
+      });
+  }
+
+  uploadScan(event: UploadIdentificationCardScanEvent): void {
+    this.customersStore.dispatch({
+      type: CREATE,
+      payload: {
+        customerIdentifier: this.customer.identifier,
+        identificationCardNumber: this.identificationCard.number,
+        scan: event.scan,
+        file: event.file
+      }
+    });
+  }
+
+  confirmScanDeletion(): Observable<boolean> {
+    const message = 'Do you want to delete this scan?';
+    const title = 'Confirm deletion';
+    const button = 'DELETE SCAN';
+
+    return this.translate.get([title, message, button])
+      .flatMap(result =>
+        this.dialogService.openConfirm({
+          message: result[message],
+          title: result[title],
+          acceptButton: result[button]
+        }).afterClosed()
+      );
+  }
+
+  deleteScan(scan: IdentificationCardScan): void {
+    this.confirmScanDeletion()
+      .filter(accept => accept)
+      .subscribe(() => {
+        this.customersStore.dispatch({ type: DELETE_SCAN, payload: {
+          customerIdentifier: this.customer.identifier,
+          identificationCardNumber: this.identificationCard.number,
+          scan
+        }});
+      });
+  }
+}
diff --git a/src/app/customers/detail/identityCard/identity-card.index.component.html b/src/app/customers/detail/identityCard/identity-card.index.component.html
new file mode 100644
index 0000000..ca721b3
--- /dev/null
+++ b/src/app/customers/detail/identityCard/identity-card.index.component.html
@@ -0,0 +1,18 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<router-outlet></router-outlet>
diff --git a/src/app/customers/detail/identityCard/identity-card.index.component.ts b/src/app/customers/detail/identityCard/identity-card.index.component.ts
new file mode 100644
index 0000000..7380648
--- /dev/null
+++ b/src/app/customers/detail/identityCard/identity-card.index.component.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 {Component, OnDestroy, OnInit} from '@angular/core';
+import {Subscription} from 'rxjs/Subscription';
+import {ActivatedRoute} from '@angular/router';
+import {CustomersStore} from '../../store/index';
+import {SelectAction} from '../../store/identityCards/identity-cards.actions';
+
+@Component({
+  templateUrl: './identity-card.index.component.html'
+})
+export class CustomerIdentityCardIndexComponent implements OnInit, OnDestroy {
+
+  private actionsSubscription: Subscription;
+
+  constructor(private route: ActivatedRoute, private customersStore: CustomersStore) {}
+
+  ngOnInit(): void {
+    this.actionsSubscription = this.route.params
+      .map(params => new SelectAction(params['number']))
+      .subscribe(this.customersStore);
+  }
+
+  ngOnDestroy(): void {
+    this.actionsSubscription.unsubscribe();
+  }
+}
diff --git a/src/app/customers/detail/identityCard/identity-card.list.component.html b/src/app/customers/detail/identityCard/identity-card.list.component.html
new file mode 100644
index 0000000..ad263cf
--- /dev/null
+++ b/src/app/customers/detail/identityCard/identity-card.list.component.html
@@ -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.
+-->
+
+<fims-layout-card-over title="{{'Manage identification cards' | translate}}" [navigateBackTo]="['../../../../']">
+  <fims-data-table
+    (onActionCellClick)="rowSelect($event)"
+    [columns]="columns"
+    [data]="identityCardData$ | async"
+    [sortable]="false"
+    [pageable]="false">
+  </fims-data-table>
+</fims-layout-card-over>
+<fims-fab-button title="{{'Create new identication card' | translate}}" icon="add" [link]="['create']" [permission]="{ id: 'customer_identifications', accessLevel: 'CHANGE'}"></fims-fab-button>
diff --git a/src/app/customers/detail/identityCard/identity-card.list.component.ts b/src/app/customers/detail/identityCard/identity-card.list.component.ts
new file mode 100644
index 0000000..69f7f26
--- /dev/null
+++ b/src/app/customers/detail/identityCard/identity-card.list.component.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 {Component, OnDestroy, OnInit} from '@angular/core';
+import {Subscription} from 'rxjs/Subscription';
+import * as fromCustomers from '../../store/index';
+import {CustomersStore} from '../../store/index';
+import {Observable} from 'rxjs/Observable';
+import {TableData} from '../../../common/data-table/data-table.component';
+import {LOAD_ALL} from '../../store/identityCards/identity-cards.actions';
+import {IdentificationCard} from '../../../services/customer/domain/identification-card.model';
+import {ActivatedRoute, Router} from '@angular/router';
+
+@Component({
+  templateUrl: './identity-card.list.component.html'
+})
+export class CustomerIdentityCardListComponent implements OnInit, OnDestroy {
+
+  private customerSubscription: Subscription;
+
+  identityCardData$: Observable<TableData>;
+
+  columns: any[] = [
+    { name: 'number', label: 'Number' },
+    { name: 'type', label: 'Type' },
+    { name: 'issuer', label: 'Issuer' }
+  ];
+
+  constructor(private router: Router, private route: ActivatedRoute, private store: CustomersStore) {}
+
+  ngOnInit(): void {
+    this.identityCardData$ = this.store.select(fromCustomers.getAllCustomerIdentificationCardEntities)
+      .map(entities => ({
+        data: entities,
+        totalElements: entities.length,
+        totalPages: 1
+      }));
+
+    this.customerSubscription = this.store.select(fromCustomers.getSelectedCustomer)
+      .subscribe(customer => {
+        this.store.dispatch({ type: LOAD_ALL, payload: customer.identifier});
+      });
+  }
+
+  ngOnDestroy(): void {
+    this.customerSubscription.unsubscribe();
+  }
+
+  rowSelect(identificationCard: IdentificationCard): void {
+    this.router.navigate(['detail', identificationCard.number], { relativeTo: this.route });
+  }
+
+}
diff --git a/src/app/customers/detail/identityCard/identity-card.module.ts b/src/app/customers/detail/identityCard/identity-card.module.ts
new file mode 100644
index 0000000..82e7937
--- /dev/null
+++ b/src/app/customers/detail/identityCard/identity-card.module.ts
@@ -0,0 +1,87 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 {FimsSharedModule} from '../../../common/common.module';
+import {IdentityCardRoutes} from './identity-card.routing';
+import {RouterModule} from '@angular/router';
+import {NgModule} from '@angular/core';
+import {IdentityCardExistsGuard} from './identity-card-exists.guard';
+import {CustomerIdentityCardListComponent} from './identity-card.list.component';
+import {CreateCustomerIdentificationCardFormComponent} from './form/create.form.component';
+import {CustomerIdentityCardIndexComponent} from './identity-card.index.component';
+import {CustomerIdentityCardDetailComponent} from './identity-card.detail.component';
+import {EditCustomerIdentificationCardFormComponent} from './form/edit.form.component';
+import {CustomerIdentificationCardNotificationEffects} from '../../store/identityCards/effects/notification.effects';
+import {EffectsModule} from '@ngrx/effects';
+import {CustomerIdentificationCardRouteEffects} from '../../store/identityCards/effects/route.effects';
+import {CustomerIdentificationCardApiEffects} from '../../store/identityCards/effects/service.effects';
+import {TranslateModule} from '@ngx-translate/core';
+import {CommonModule} from '@angular/common';
+import {FormsModule, ReactiveFormsModule} from '@angular/forms';
+import {MatButtonModule, MatDatepickerModule, MatIconModule, MatInputModule, MatListModule, MatToolbarModule} from '@angular/material';
+import {CovalentFileModule, CovalentStepsModule} from '@covalent/core';
+import {IdentityCardFormComponent} from './form/identity-card-form.component';
+import {CustomerIdentityCardScanListComponent} from './scans/scan.list.component';
+import {CustomerIdentificationCardScanApiEffects} from '../../store/identityCards/scans/effects/service.effects';
+import {CustomerIdentificationCardScanNotificationEffects} from '../../store/identityCards/scans/effects/notification.effects';
+import {CreateIdentificationCardScanComponent} from './scans/form/create.form.component';
+import {IdentificationCardScanComponent} from './scans/form/scan.form.component';
+import {CustomerIdentificationCardScanRouteEffects} from '../../store/identityCards/scans/effects/route.effects';
+
+@NgModule({
+  imports: [
+    RouterModule.forChild(IdentityCardRoutes),
+    FimsSharedModule,
+    TranslateModule,
+    CommonModule,
+    FormsModule,
+    ReactiveFormsModule,
+    MatIconModule,
+    MatListModule,
+    MatToolbarModule,
+    MatInputModule,
+    MatButtonModule,
+    MatDatepickerModule,
+    CovalentStepsModule,
+    CovalentFileModule,
+
+    EffectsModule.run(CustomerIdentificationCardApiEffects),
+    EffectsModule.run(CustomerIdentificationCardRouteEffects),
+    EffectsModule.run(CustomerIdentificationCardNotificationEffects),
+
+    EffectsModule.run(CustomerIdentificationCardScanApiEffects),
+    EffectsModule.run(CustomerIdentificationCardScanRouteEffects),
+    EffectsModule.run(CustomerIdentificationCardScanNotificationEffects)
+  ],
+  declarations: [
+    CustomerIdentityCardListComponent,
+    CreateCustomerIdentificationCardFormComponent,
+    CustomerIdentityCardIndexComponent,
+    CustomerIdentityCardDetailComponent,
+    EditCustomerIdentificationCardFormComponent,
+    IdentityCardFormComponent,
+    CustomerIdentityCardScanListComponent,
+    CreateIdentificationCardScanComponent,
+    IdentificationCardScanComponent
+  ],
+  providers: [
+    IdentityCardExistsGuard
+  ]
+})
+export class IdentityCardModule {}
+
diff --git a/src/app/customers/detail/identityCard/identity-card.routing.ts b/src/app/customers/detail/identityCard/identity-card.routing.ts
new file mode 100644
index 0000000..13f7267
--- /dev/null
+++ b/src/app/customers/detail/identityCard/identity-card.routing.ts
@@ -0,0 +1,78 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 {Routes} from '@angular/router';
+import {CustomerIdentityCardListComponent} from './identity-card.list.component';
+import {CreateCustomerIdentificationCardFormComponent} from './form/create.form.component';
+import {CustomerIdentityCardIndexComponent} from './identity-card.index.component';
+import {IdentityCardExistsGuard} from './identity-card-exists.guard';
+import {CustomerIdentityCardDetailComponent} from './identity-card.detail.component';
+import {EditCustomerIdentificationCardFormComponent} from './form/edit.form.component';
+import {CreateIdentificationCardScanComponent} from './scans/form/create.form.component';
+
+export const IdentityCardRoutes: Routes = [
+  {
+    path: '',
+    component: CustomerIdentityCardListComponent,
+    data: {
+      title: 'Manage identification cards',
+      hasPermission: { id: 'customer_identifications', accessLevel: 'READ' }
+    }
+  },
+  {
+    path: 'create',
+    component: CreateCustomerIdentificationCardFormComponent,
+    data: {
+      title: 'Create identification card',
+      hasPermission: { id: 'customer_identifications', accessLevel: 'CHANGE' }
+    },
+  },
+  {
+    path: 'detail/:number',
+    component: CustomerIdentityCardIndexComponent,
+    canActivate: [ IdentityCardExistsGuard ],
+    children: [
+      {
+        path: '',
+        component: CustomerIdentityCardDetailComponent,
+        data: {
+          title: 'Identification Card',
+          hasPermission: { id: 'customer_identifications', accessLevel: 'READ' }
+        }
+      },
+      {
+        path: 'edit',
+        component: EditCustomerIdentificationCardFormComponent,
+        data: {
+          title: 'Edit identification card',
+          hasPermission: {id: 'customer_identifications', accessLevel: 'CHANGE'}
+        },
+      },
+      {
+        path: 'addScan',
+        component: CreateIdentificationCardScanComponent,
+        data: {
+          title: 'Add identification card scan',
+          hasPermission: {id: 'customer_identifications', accessLevel: 'CHANGE'}
+        }
+      }
+    ]
+  }
+];
+
+
diff --git a/src/app/customers/detail/identityCard/scans/form/create.form.component.html b/src/app/customers/detail/identityCard/scans/form/create.form.component.html
new file mode 100644
index 0000000..cc47284
--- /dev/null
+++ b/src/app/customers/detail/identityCard/scans/form/create.form.component.html
@@ -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.
+-->
+
+<fims-layout-card-over title="{{'Upload new identification card scan' | translate}}">
+  <fims-identity-card-scan-form #form
+                           [error]="error$ | async"
+                           (onSave)="onSave($event)"
+                           (onCancel)="onCancel()">
+  </fims-identity-card-scan-form>
+</fims-layout-card-over>
diff --git a/src/app/customers/detail/identityCard/scans/form/create.form.component.ts b/src/app/customers/detail/identityCard/scans/form/create.form.component.ts
new file mode 100644
index 0000000..d49151e
--- /dev/null
+++ b/src/app/customers/detail/identityCard/scans/form/create.form.component.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 {Component, OnDestroy, OnInit, ViewChild} from '@angular/core';
+import {IdentificationCardScan} from '../../../../../services/customer/domain/identification-card-scan.model';
+import * as fromCustomers from '../../../../store/index';
+import {CustomersStore} from '../../../../store/index';
+import {IdentificationCardScanComponent, IdentityCardScanFormData} from './scan.form.component';
+import {CREATE, RESET_FORM} from '../../../../store/identityCards/scans/scans.actions';
+import {ActivatedRoute, Router} from '@angular/router';
+import {Observable} from 'rxjs/Observable';
+import {Error} from '../../../../../services/domain/error.model';
+import {Subscription} from 'rxjs/Subscription';
+import {IdentificationCard} from '../../../../../services/customer/domain/identification-card.model';
+import {Customer} from '../../../../../services/customer/domain/customer.model';
+
+@Component({
+  templateUrl: './create.form.component.html'
+})
+export class CreateIdentificationCardScanComponent implements OnInit, OnDestroy {
+
+  private customerSubscription: Subscription;
+
+  private identificationCardSubscription: Subscription;
+
+  customer: Customer;
+
+  identificationCard: IdentificationCard;
+
+  error$: Observable<Error>;
+
+  @ViewChild('form') formComponent: IdentificationCardScanComponent;
+
+  constructor(private router: Router, private route: ActivatedRoute, private customersStore: CustomersStore) {}
+
+  ngOnInit(): void {
+    this.error$ = this.customersStore.select(fromCustomers.getCustomerIdentificationCardScanFormError);
+
+    this.customerSubscription = this.customersStore.select(fromCustomers.getSelectedCustomer)
+      .subscribe(customer => this.customer = customer);
+
+    this.identificationCardSubscription = this.customersStore.select(fromCustomers.getSelectedIdentificationCard)
+      .subscribe(identificationCard => this.identificationCard = identificationCard);
+  }
+
+  ngOnDestroy(): void {
+    this.customerSubscription.unsubscribe();
+    this.identificationCardSubscription.unsubscribe();
+
+    this.customersStore.dispatch({ type: RESET_FORM });
+  }
+
+  onSave(formData: IdentityCardScanFormData): void {
+    const scan: IdentificationCardScan = {
+      identifier: formData.identifier,
+      description: formData.description
+    };
+
+    this.customersStore.dispatch({
+      type: CREATE,
+      payload: {
+        customerIdentifier: this.customer.identifier,
+        identificationCardNumber: this.identificationCard.number,
+        scan,
+        file: formData.file,
+        activatedRoute: this.route
+      }
+    });
+  }
+
+  onCancel(): void {
+    this.router.navigate(['../'], { relativeTo: this.route });
+  }
+
+}
diff --git a/src/app/customers/detail/identityCard/scans/form/scan.form.component.html b/src/app/customers/detail/identityCard/scans/form/scan.form.component.html
new file mode 100644
index 0000000..e5aaa84
--- /dev/null
+++ b/src/app/customers/detail/identityCard/scans/form/scan.form.component.html
@@ -0,0 +1,59 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<td-steps mode="'vertical'">
+  <td-step #detailsStep label="{{'Upload new identification card scan' | translate}}"
+           [state]="form.valid ? 'complete' : form.pristine ? 'none' : 'required'">
+    <form [formGroup]="form" layout="column">
+      <fims-id-input [form]="form" controlName="identifier" [readonly]="editMode"></fims-id-input>
+      <fims-text-input [form]="form" controlName="description" placeholder="{{'Description' | translate}}"></fims-text-input>
+      <div layout="row">
+        <mat-form-field tdFileDrop
+                            [disabled]="true"
+                            flex layout-margin>
+          <input matInput
+                 placeholder="{{'Selected file' | translate }}"
+                 [value]="form.get('file').value ? form.get('file').value.name : ''"
+                 [disabled]="true"
+                 readonly/>
+          <mat-hint class="tc-red-500" *ngIf="form.get('file').hasError('maxFileSize')">
+            {{ 'Max file size' | translate:{ value: form.get('file').getError('maxFileSize')['value']} }}
+          </mat-hint>
+        </mat-form-field>
+        <button mat-icon-button *ngIf="form.get('file').value" (click)="fileInput.clear()"
+                (keyup.enter)="fileInput.clear()">
+          <mat-icon>cancel</mat-icon>
+        </button>
+        <td-file-input class="push-left-sm push-right-sm" #fileInput formControlName="file" accept=".jpg,.png">
+          <mat-icon>folder</mat-icon>
+          <span class="text-upper" translate>Browse...</span>
+        </td-file-input>
+      </div>
+    </form>
+  </td-step>
+  <td-step label="{{'Final step' | translate}}" [state]="'complete'">
+    <ng-template td-step-summary>
+      <fims-form-final-action
+        [resourceName]="'IDENTIFICATION CARD SCAN'"
+        [editMode]="editMode"
+        [disabled]="!form.valid"
+        (onCancel)="cancel()"
+        (onSave)="save()">
+      </fims-form-final-action>
+    </ng-template>
+  </td-step>
+</td-steps>
diff --git a/src/app/customers/detail/identityCard/scans/form/scan.form.component.ts b/src/app/customers/detail/identityCard/scans/form/scan.form.component.ts
new file mode 100644
index 0000000..ddb6b38
--- /dev/null
+++ b/src/app/customers/detail/identityCard/scans/form/scan.form.component.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 {Component, EventEmitter, Input, OnInit, Output, ViewChild} from '@angular/core';
+import {FormBuilder, FormGroup, Validators} from '@angular/forms';
+import {Error} from '../../../../../services/domain/error.model';
+import {TdStepComponent} from '@covalent/core';
+import {FimsValidators} from '../../../../../common/validator/validators';
+
+export interface IdentityCardScanFormData {
+  identifier: string;
+  description: string;
+  file: File;
+}
+
+@Component({
+  selector: 'fims-identity-card-scan-form',
+  templateUrl: './scan.form.component.html'
+})
+export class IdentificationCardScanComponent implements OnInit {
+
+  @ViewChild('detailsStep') detailsStep: TdStepComponent;
+
+  form: FormGroup;
+
+  @Input() editMode: boolean;
+
+  @Input() set error(error: Error) {
+    if (!error) {
+      return;
+    }
+
+    this.form.get('identifier').setErrors({
+      'unique': true
+    });
+  }
+
+  @Output() onSave = new EventEmitter<IdentityCardScanFormData>();
+
+  @Output() onCancel = new EventEmitter<void>();
+
+  constructor(private formBuilder: FormBuilder) {}
+
+  ngOnInit() {
+    this.form = this.formBuilder.group({
+      identifier: ['', [Validators.required, Validators.minLength(3), Validators.maxLength(32), FimsValidators.urlSafe]],
+      description: ['', [Validators.required, Validators.maxLength(1024)]],
+      file: ['', [Validators.required, FimsValidators.maxFileSize(512)]]
+    });
+
+    this.detailsStep.open();
+  }
+
+  save(): void {
+    this.onSave.emit(this.form.getRawValue());
+  }
+
+  cancel(): void {
+    this.onCancel.emit();
+  }
+}
diff --git a/src/app/customers/detail/identityCard/scans/scan.list.component.html b/src/app/customers/detail/identityCard/scans/scan.list.component.html
new file mode 100644
index 0000000..8125d1c
--- /dev/null
+++ b/src/app/customers/detail/identityCard/scans/scan.list.component.html
@@ -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.
+-->
+
+<mat-list>
+  <h3 mat-subheader translate *ngIf="scans && scans.length > 0" translate>Scans uploaded</h3>
+  <mat-list-item *ngFor="let scan of scans">
+    <mat-icon mat-list-icon>photo_camera</mat-icon>
+    <h4 matLine>{{scan.description}}</h4>
+    <button mat-button color="warn" (click)="deleteScan(scan)" *hasPermission="{ id: 'customer_identifications', accessLevel: 'DELETE' }" translate>
+      Delete
+    </button>
+    <button mat-raised-button (click)="viewScan(scan.identifier)" translate>
+      View
+    </button>
+  </mat-list-item>
+</mat-list>
diff --git a/src/app/customers/detail/identityCard/scans/scan.list.component.ts b/src/app/customers/detail/identityCard/scans/scan.list.component.ts
new file mode 100644
index 0000000..aaaaff4
--- /dev/null
+++ b/src/app/customers/detail/identityCard/scans/scan.list.component.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 {Component, EventEmitter, Input, Output} from '@angular/core';
+import {IdentificationCardScan} from '../../../../services/customer/domain/identification-card-scan.model';
+import {FormGroup} from '@angular/forms';
+
+export interface UploadIdentificationCardScanEvent {
+  scan: IdentificationCardScan;
+  file: File;
+}
+
+@Component({
+  selector: 'fims-identification-card-scan-list',
+  templateUrl: './scan.list.component.html'
+})
+export class CustomerIdentityCardScanListComponent {
+
+  @Input() scans: IdentificationCardScan[];
+
+  @Output() onView: EventEmitter<string> = new EventEmitter();
+
+  @Output() onDelete: EventEmitter<IdentificationCardScan> = new EventEmitter();
+
+  formGroup: FormGroup;
+
+  constructor() {}
+
+  viewScan(identifier: string): void {
+    this.onView.emit(identifier);
+  }
+
+  deleteScan(scan: IdentificationCardScan): void {
+    this.onDelete.emit(scan);
+  }
+
+}
diff --git a/src/app/customers/detail/payroll/form/create.form.component.html b/src/app/customers/detail/payroll/form/create.form.component.html
new file mode 100644
index 0000000..f9e9759
--- /dev/null
+++ b/src/app/customers/detail/payroll/form/create.form.component.html
@@ -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.
+-->
+
+<fims-layout-card-over title="Change payroll allocations" [navigateBackTo]="['../']">
+  <fims-customer-payroll-form
+    [productInstances]="productInstances$ | async"
+    [distribution]="distribution$ | async"
+    (onSave)="onSave($event)"
+    (onCancel)="onCancel()">
+  </fims-customer-payroll-form>
+</fims-layout-card-over>
diff --git a/src/app/customers/detail/payroll/form/create.form.component.ts b/src/app/customers/detail/payroll/form/create.form.component.ts
new file mode 100644
index 0000000..9e4bbc3
--- /dev/null
+++ b/src/app/customers/detail/payroll/form/create.form.component.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 {Component} from '@angular/core';
+import {PayrollConfiguration} from '../../../../services/payroll/domain/payroll-configuration.model';
+import {Observable} from 'rxjs/Observable';
+import * as fromCustomers from '../../../store/index';
+import {CustomersStore} from '../../../store/index';
+import {ActivatedRoute, Router} from '@angular/router';
+import {UPDATE} from '../../../store/payroll/payroll.actions';
+import {ProductInstance} from '../../../../services/depositAccount/domain/instance/product-instance.model';
+import {DepositAccountService} from '../../../../services/depositAccount/deposit-account.service';
+
+@Component({
+  templateUrl: './create.form.component.html'
+})
+export class CreateCustomerPayrollFormComponent {
+
+  private customerId: string;
+
+  distribution$: Observable<PayrollConfiguration>;
+
+  productInstances$: Observable<ProductInstance[]>;
+
+  constructor(private store: CustomersStore, private router: Router, private route: ActivatedRoute,
+              private depositService: DepositAccountService) {
+    this.distribution$ = store.select(fromCustomers.getPayrollDistribution);
+
+    this.productInstances$ = this.store.select(fromCustomers.getSelectedCustomer)
+      .do(customer => this.customerId = customer.identifier)
+      .switchMap(customer => this.depositService.fetchProductInstances(customer.identifier))
+      .map(instances => instances.filter(instance => instance.state === 'ACTIVE'));
+  }
+
+  onSave(distribution: PayrollConfiguration): void {
+    this.store.dispatch({
+      type: UPDATE,
+      payload: {
+        customerId: this.customerId,
+        distribution,
+        activatedRoute: this.route
+      }
+    });
+  }
+
+  onCancel(): void {
+    this.router.navigate(['../'], { relativeTo: this.route });
+  }
+}
diff --git a/src/app/customers/detail/payroll/form/form.component.html b/src/app/customers/detail/payroll/form/form.component.html
new file mode 100644
index 0000000..95137b0
--- /dev/null
+++ b/src/app/customers/detail/payroll/form/form.component.html
@@ -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.
+-->
+
+<td-steps mode="'vertical'">
+  <td-step #detailsStep label="{{'Update payroll allocations' | translate}}" [state]="form.valid ? 'complete' : form.pristine ? 'none' : 'required'">
+    <form [formGroup]="form">
+      <mat-form-field layout-margin>
+        <mat-select formControlName="mainAccountNumber" placeholder="{{ 'Select main account' | translate }}">
+          <mat-option *ngFor="let instance of productInstances" [value]="instance.accountIdentifier">
+            {{instance.accountIdentifier}}({{instance.productIdentifier}})
+          </mat-option>
+        </mat-select>
+      </mat-form-field>
+      <div layout-gt-xs="column" layout-margin formArrayName="payrollAllocations">
+        <h4 translate>Allocations</h4>
+        <div *ngFor="let allocation of allocations; let i=index" [formGroupName]="i">
+          <mat-form-field layout-margin>
+            <mat-select formControlName="accountNumber" placeholder="{{ 'Select account' | translate }}">
+              <mat-option *ngFor="let instance of productInstances" [value]="instance.accountIdentifier">
+                {{instance.accountIdentifier}}({{instance.productIdentifier}})
+              </mat-option>
+            </mat-select>
+          </mat-form-field>
+          <fims-number-input placeholder="Amount" [form]="allocation" controlName="amount"></fims-number-input>
+          <mat-checkbox formControlName="proportional" translate>Proportional?</mat-checkbox>
+          <button mat-button (click)="removeAllocation(i)">{{'Remove' | translate}}</button>
+        </div>
+        <p *ngIf="form.hasError('accountUnique')" class="tc-red-600" translate>
+          Allocation accounts can't use main account or overlap with other allocation accounts.
+        </p>
+        <button mat-button (click)="addAllocation()">{{'Add allocation' | translate}}</button>
+      </div>
+    </form>
+    <ng-template td-step-actions>
+      <fims-form-final-action
+        [resourceName]="'ALLOCATIONS'"
+        [editMode]="true"
+        [disabled]="!form.valid"
+        (onCancel)="cancel()"
+        (onSave)="save()">
+      </fims-form-final-action>
+    </ng-template>
+  </td-step>
+</td-steps>
diff --git a/src/app/customers/detail/payroll/form/form.component.ts b/src/app/customers/detail/payroll/form/form.component.ts
new file mode 100644
index 0000000..653e56e
--- /dev/null
+++ b/src/app/customers/detail/payroll/form/form.component.ts
@@ -0,0 +1,112 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, OnChanges, OnInit, Output, SimpleChanges, ViewChild} from '@angular/core';
+import {PayrollConfiguration} from '../../../../services/payroll/domain/payroll-configuration.model';
+import {AbstractControl, FormArray, FormBuilder, FormGroup, Validators} from '@angular/forms';
+import {TdStepComponent} from '@covalent/core';
+import {FimsValidators} from '../../../../common/validator/validators';
+import {ProductInstance} from '../../../../services/depositAccount/domain/instance/product-instance.model';
+import {PayrollAllocation} from '../../../../services/payroll/domain/payroll-allocation.model';
+import {accountUnique} from './validator/account-unique.validator';
+
+@Component({
+  selector: 'fims-customer-payroll-form',
+  templateUrl: './form.component.html'
+})
+export class CustomerPayrollFormComponent implements OnInit, OnChanges {
+
+  form: FormGroup;
+
+  @ViewChild('detailsStep') detailsStep: TdStepComponent;
+
+  @Input('productInstances') productInstances: ProductInstance[];
+
+  @Input('distribution') distribution: PayrollConfiguration;
+
+  @Output('onSave') onSave = new EventEmitter<PayrollConfiguration>();
+
+  @Output('onCancel') onCancel = new EventEmitter<void>();
+
+  constructor(private formBuilder: FormBuilder) {
+    this.form = this.formBuilder.group({
+      mainAccountNumber: ['', [Validators.required]],
+      payrollAllocations: this.initAllocations([])
+    }, { validator: accountUnique });
+  }
+
+  ngOnInit(): void {
+    this.detailsStep.open();
+  }
+
+  ngOnChanges(changes: SimpleChanges): void {
+    if (changes.distribution) {
+      this.form.reset({
+        mainAccountNumber: this.distribution.mainAccountNumber
+      });
+
+      this.distribution.payrollAllocations.forEach(allocation => this.addAllocation(allocation));
+    }
+  }
+
+  save(): void {
+    const distribution = Object.assign({}, this.distribution, {
+      mainAccountNumber: this.form.get('mainAccountNumber').value,
+      payrollAllocations: this.form.get('payrollAllocations').value
+    });
+
+    this.onSave.emit(distribution);
+  }
+
+  cancel(): void {
+    this.onCancel.emit();
+  }
+
+  private initAllocations(allocations: PayrollAllocation[]): FormArray {
+    const formControls: FormGroup[] = [];
+    allocations.forEach(allocation => formControls.push(this.initAllocation(allocation)));
+    return this.formBuilder.array(formControls);
+  }
+
+  private initAllocation(allocation?: PayrollAllocation): FormGroup {
+    return this.formBuilder.group({
+      accountNumber: [allocation ? allocation.accountNumber : '', [Validators.required]],
+      amount: [allocation ? allocation.amount : '', [
+        Validators.required,
+        FimsValidators.minValue(0.001),
+        FimsValidators.maxValue(9999999999.99999)]
+      ],
+      proportional: [allocation ? allocation.proportional : false]
+    });
+  }
+
+  addAllocation(allocation?: PayrollAllocation): void {
+    const allocations: FormArray = this.form.get('payrollAllocations') as FormArray;
+    allocations.push(this.initAllocation(allocation));
+  }
+
+  removeAllocation(index: number): void {
+    const allocations: FormArray = this.form.get('payrollAllocations') as FormArray;
+    allocations.removeAt(index);
+  }
+
+  get allocations(): AbstractControl[] {
+    const allocations: FormArray = this.form.get('payrollAllocations') as FormArray;
+    return allocations.controls;
+  }
+}
diff --git a/src/app/customers/detail/payroll/form/validator/account-unique.validator.spec.ts b/src/app/customers/detail/payroll/form/validator/account-unique.validator.spec.ts
new file mode 100644
index 0000000..427589a
--- /dev/null
+++ b/src/app/customers/detail/payroll/form/validator/account-unique.validator.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 {accountUnique} from './account-unique.validator';
+import {FormArray, FormControl, FormGroup} from '@angular/forms';
+
+describe('accountUnique validator', () => {
+
+  function setup(mainAccountNumber: string, accountNumbers: string[]): FormGroup {
+    const payrollAllocations = new FormArray([]);
+    accountNumbers.forEach(number => payrollAllocations.push(
+      new FormGroup({
+        accountNumber: new FormControl(number),
+        amount: new FormControl(),
+        proportional: new FormControl()
+      })));
+
+    const group = new FormGroup({
+      mainAccountNumber: new FormControl(mainAccountNumber),
+      payrollAllocations
+    });
+
+    return group;
+  }
+
+  it('should not return error when no account overlap', () => {
+    const group = setup('testa', ['testb', 'testc']);
+
+    const result = accountUnique(group);
+
+    expect(result).toBeNull();
+  });
+
+  it('should return error when allocation account overlap with main account', () => {
+    const group = setup('testa', ['testa', 'testc']);
+
+    const result = accountUnique(group);
+
+    expect(result).toEqual({
+      accountUnique: true
+    });
+  });
+
+  it('should return error when allocation account overlap with other allocation account', () => {
+    const group = setup('testa', ['testb', 'testb']);
+
+    const result = accountUnique(group);
+
+    expect(result).toEqual({
+      accountUnique: true
+    });
+  });
+});
diff --git a/src/app/customers/detail/payroll/form/validator/account-unique.validator.ts b/src/app/customers/detail/payroll/form/validator/account-unique.validator.ts
new file mode 100644
index 0000000..9385ec3
--- /dev/null
+++ b/src/app/customers/detail/payroll/form/validator/account-unique.validator.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 {FormGroup, ValidationErrors} from '@angular/forms';
+import {PayrollAllocation} from '../../../../../services/payroll/domain/payroll-allocation.model';
+import {isEmptyInputValue} from '../../../../../common/validator/validators';
+
+export function accountUnique(group: FormGroup): ValidationErrors | null {
+  const mainAccountNumber: string = group.controls.mainAccountNumber.value;
+  const payrollAllocations: PayrollAllocation[] = group.controls.payrollAllocations.value;
+
+  if (isEmptyInputValue(mainAccountNumber)) {
+    return;
+  }
+
+  const numbers = payrollAllocations
+    .filter(allocation => allocation.accountNumber.length > 0)
+    .map(allocation => allocation.accountNumber);
+
+  const set = new Set();
+
+  numbers.forEach(number => set.add(number));
+
+  if (numbers.indexOf(mainAccountNumber) > -1 || set.size !== numbers.length) {
+    return {
+      accountUnique: true
+    };
+  }
+
+  return null;
+}
diff --git a/src/app/customers/detail/payroll/payroll-exists.guard.ts b/src/app/customers/detail/payroll/payroll-exists.guard.ts
new file mode 100644
index 0000000..f127dc2
--- /dev/null
+++ b/src/app/customers/detail/payroll/payroll-exists.guard.ts
@@ -0,0 +1,69 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, CanActivate, RouterStateSnapshot} from '@angular/router';
+import {Injectable} from '@angular/core';
+import * as fromCustomers from '../../store/index';
+import {CustomersStore} from '../../store/index';
+import {Observable} from 'rxjs/Observable';
+import {of} from 'rxjs/observable/of';
+import {ExistsGuardService} from '../../../common/guards/exists-guard';
+import {LoadAction} from '../../store/payroll/payroll.actions';
+import {PayrollService} from '../../../services/payroll/payroll.service';
+
+@Injectable()
+export class PayrollExistsGuard implements CanActivate {
+
+  constructor(private store: CustomersStore,
+              private payrollService: PayrollService,
+              private existsGuardService: ExistsGuardService) {}
+
+  hasPayrollInStore(): Observable<boolean> {
+    const timestamp$ = this.store.select(fromCustomers.getPayrollDistributionLoadedAt);
+    return this.existsGuardService.isWithinExpiry(timestamp$);
+  }
+
+  loadPayrollFromApi(id: string): Observable<boolean> {
+    const getPayroll$ = this.payrollService.findPayrollConfiguration(id, true)
+      .catch(() => {
+        return Observable.of({
+          mainAccountNumber: '',
+          payrollAllocations: []
+        });
+      })
+      .map(distribution => new LoadAction(distribution))
+      .do((action: LoadAction) => this.store.dispatch(action))
+      .map(distribution => !!distribution);
+
+    return this.existsGuardService.routeTo404OnError(getPayroll$);
+  }
+
+  hasPayroll(id: string): Observable<boolean> {
+    return this.hasPayrollInStore()
+      .switchMap(inStore => {
+        if (inStore) {
+          return of(inStore);
+        }
+        return this.loadPayrollFromApi(id);
+      });
+  }
+
+  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
+    return this.hasPayroll(route.parent.params['id']);
+  }
+}
diff --git a/src/app/customers/detail/payroll/payroll.detail.component.html b/src/app/customers/detail/payroll/payroll.detail.component.html
new file mode 100644
index 0000000..bb3ba37
--- /dev/null
+++ b/src/app/customers/detail/payroll/payroll.detail.component.html
@@ -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.
+-->
+
+<fims-layout-card-over title="{{'Payroll allocations' | translate }}" [navigateBackTo]="['../../../../']">
+  <div class="mat-content inset" flex *ngIf="(distribution$ | async) as distribution">
+    <div layout="row">
+      <mat-list>
+        <mat-list-item>
+          <h3 matLine translate>Account number</h3>
+          <p matLine>{{distribution.mainAccountNumber}}</p>
+        </mat-list-item>
+        <mat-list-item>
+          <h3 matLine translate>Created by</h3>
+          <p matLine>{{distribution.createdBy}} - {{distribution.createdOn | date:'medium'}}</p>
+        </mat-list-item>
+        <mat-list-item>
+          <h3 matLine translate>Last modified by</h3>
+          <p matLine>{{distribution.lastModifiedBy}} - {{distribution.lastModifiedOn | date:'medium'}}</p>
+        </mat-list-item>
+      </mat-list>
+    </div>
+    <fims-data-table flex [columns]="columns" [data]="allocationData" [actionColumn]="false">
+    </fims-data-table>
+  </div>
+</fims-layout-card-over>
+<fims-fab-button title="{{'Edit payroll distribution' | translate}}" icon="mode_edit" [link]="['edit']"
+                 [permission]="{ id: 'customer_customers', accessLevel: 'CHANGE'}">
+</fims-fab-button>
diff --git a/src/app/customers/detail/payroll/payroll.detail.component.ts b/src/app/customers/detail/payroll/payroll.detail.component.ts
new file mode 100644
index 0000000..c2e1c91
--- /dev/null
+++ b/src/app/customers/detail/payroll/payroll.detail.component.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 {Component} from '@angular/core';
+import {PayrollConfiguration} from '../../../services/payroll/domain/payroll-configuration.model';
+import {Observable} from 'rxjs/Observable';
+import * as fromCustomers from '../../store/index';
+import {CustomersStore} from '../../store/index';
+import {TableData} from '../../../common/data-table/data-table.component';
+
+@Component({
+  templateUrl: './payroll.detail.component.html'
+})
+export class CustomerPayrollDetailComponent {
+
+  distribution$: Observable<PayrollConfiguration>;
+
+  allocationData: TableData;
+
+  columns: any[] = [
+    { name: 'accountNumber', label: 'Account Number' },
+    { name: 'amount', label: 'Amount' },
+    { name: 'proportional', label: 'Proportional?' }
+  ];
+
+  constructor(private store: CustomersStore) {
+    this.distribution$ = store.select(fromCustomers.getPayrollDistribution)
+      .filter(distribution => !!distribution)
+      .do(distribution => this.allocationData = {
+        data: distribution.payrollAllocations,
+        totalElements: distribution.payrollAllocations.length,
+        totalPages: 1
+      });
+  }
+}
diff --git a/src/app/customers/detail/portrait/portrait.component.html b/src/app/customers/detail/portrait/portrait.component.html
new file mode 100644
index 0000000..bacdab5
--- /dev/null
+++ b/src/app/customers/detail/portrait/portrait.component.html
@@ -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.
+-->
+
+<fims-layout-card-over title="{{'Upload portrait' | translate}}" [navigateBackTo]="['../']">
+  <mat-card-content>
+    <div layout="column" layout-align="center center">
+      <fims-portrait [blob]="portrait" [size]="'large'"></fims-portrait>
+    </div>
+    <div layout="column" layout-align="center center" *hasPermission="{ id: 'customer_portrait', accessLevel: 'CHANGE' }">
+      <span translate>Selected file:</span> {{ fileSelectMsg | translate }}
+      <span *ngIf="fileSelectMsg" translate>
+        Press the upload button below to upload the portrait.
+      </span>
+      <div layout="row" layout-margin>
+        <td-file-upload #singleFileUpload (select)="selectEvent($event)" (upload)="uploadEvent($event)"
+                        accept=".jpg,.png">
+          <mat-icon>file_upload</mat-icon>
+          <span translate>Click here to upload</span><span>{{ singleFileUpload.files?.name }}</span>
+          <ng-template td-file-input-label>
+            <mat-icon>attach_file</mat-icon>
+            <span translate>Choose a file to upload(max size 512 KB)</span>
+          </ng-template>
+        </td-file-upload>
+      </div>
+      <span layout-margin *ngIf="invalidSize" class="tc-red-500" translate>
+        Portrait can't exceed size of 512KB.
+      </span>
+      <div layout="row" layout-margin *hasPermission="{ id: 'customer_portrait', accessLevel: 'DELETE' }">
+        <button mat-raised-button color="warn" (click)="deletePortrait()">{{'DELETE PORTRAIT' | translate}}
+        </button>
+      </div>
+
+    </div>
+  </mat-card-content>
+</fims-layout-card-over>
diff --git a/src/app/customers/detail/portrait/portrait.component.ts b/src/app/customers/detail/portrait/portrait.component.ts
new file mode 100644
index 0000000..d54e245
--- /dev/null
+++ b/src/app/customers/detail/portrait/portrait.component.ts
@@ -0,0 +1,112 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, OnDestroy, OnInit} from '@angular/core';
+import {CustomerService} from '../../../services/customer/customer.service';
+import {Subscription} from 'rxjs/Subscription';
+import {Customer} from '../../../services/customer/domain/customer.model';
+import {CustomersStore} from '../../store/index';
+import * as fromCustomers from '../../store';
+import {ActivatedRoute, Router} from '@angular/router';
+import {NotificationService, NotificationType} from '../../../services/notification/notification.service';
+import {TdDialogService} from '@covalent/core';
+import {Observable} from 'rxjs/Observable';
+import {TranslateService} from '@ngx-translate/core';
+
+@Component({
+  templateUrl: './portrait.component.html'
+})
+export class CustomerPortraitComponent implements OnInit, OnDestroy {
+
+  private customerSubscription: Subscription;
+
+  private customer: Customer;
+
+  fileSelectMsg = 'No file selected yet.';
+
+  invalidSize = false;
+
+  portrait: Blob;
+
+  constructor(private router: Router, private route: ActivatedRoute, private customerService: CustomerService,
+              private store: CustomersStore, private notificationService: NotificationService,
+              private dialogService: TdDialogService, private translate: TranslateService) {
+  }
+
+  ngOnInit(): void {
+    this.customerSubscription = this.store.select(fromCustomers.getSelectedCustomer)
+      .do(customer => this.customer = customer)
+      .flatMap(customer => this.customerService.getPortrait(customer.identifier))
+      .subscribe(portrait => this.portrait = portrait);
+  }
+
+  ngOnDestroy(): void {
+    this.customerSubscription.unsubscribe();
+  }
+
+  selectEvent(file: File): void {
+    this.invalidSize = file.size > 524288;
+    this.fileSelectMsg = file.name;
+  };
+
+  uploadEvent(file: File): void {
+    if (this.invalidSize) {
+      return;
+    }
+
+    this.customerService.uploadPortrait(this.customer.identifier, file).subscribe(() => {
+      this.notificationService.send({
+        type: NotificationType.MESSAGE,
+        message: 'Portrait is going to be uploaded'
+      });
+      this.navigateAway();
+    });
+  };
+
+  confirmDeletion(): Observable<boolean> {
+    const message = 'Do you want to delete the portrait?';
+    const title = 'Confirm deletion';
+    const button = 'DELETE PORTRAIT';
+
+    return this.translate.get([title, message, button])
+      .flatMap(result =>
+        this.dialogService.openConfirm({
+          message: result[message],
+          title: result[title],
+          acceptButton: result[button]
+        }).afterClosed()
+      );
+  }
+
+  deletePortrait(): void {
+    this.confirmDeletion()
+      .filter(accept => accept)
+      .flatMap(() => this.customerService.deletePortrait(this.customer.identifier))
+      .subscribe(() => {
+        this.notificationService.send({
+          type: NotificationType.MESSAGE,
+          message: 'Portrait is going to be deleted'
+        });
+        this.navigateAway();
+      });
+  }
+
+  navigateAway(): void {
+    this.router.navigate(['../'], {relativeTo: this.route});
+  }
+}
diff --git a/src/app/customers/detail/status/customer-task.component.html b/src/app/customers/detail/status/customer-task.component.html
new file mode 100644
index 0000000..4db5b15
--- /dev/null
+++ b/src/app/customers/detail/status/customer-task.component.html
@@ -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.
+-->
+
+<mat-list>
+  <mat-list-item>
+    <mat-icon matListAvatar>playlist_add_check</mat-icon>
+    <h3 matLine>{{task.name}}</h3>
+    <h4 matLine>{{task.description}}</h4>
+    <p matLine>
+      <span translate>Mandatory:</span>{{task.mandatory}}
+    </p>
+    <p matLine>
+      <span translate>Type:</span>{{formatType(task.type)}}
+    </p>
+    <mat-checkbox labelPosition="after" title="{{'Execute task' | translate}}"
+                 (click)="selectTask($event)"></mat-checkbox>
+  </mat-list-item>
+</mat-list>
diff --git a/src/app/customers/detail/status/customer-task.component.ts b/src/app/customers/detail/status/customer-task.component.ts
new file mode 100644
index 0000000..ef123e3
--- /dev/null
+++ b/src/app/customers/detail/status/customer-task.component.ts
@@ -0,0 +1,53 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 {TaskDefinition, TaskDefinitionType} from '../../../services/customer/domain/task-definition.model';
+import {MatCheckboxChange} from '@angular/material';
+import {defaultTypeOptions} from '../../tasks/domain/type-options.model';
+
+export interface SelectTaskEvent {
+  taskIdentifier: string;
+  checked: boolean;
+}
+
+@Component({
+  selector: 'fims-customer-task',
+  templateUrl: './customer-task.component.html'
+})
+export class CustomerTaskComponent {
+
+  @Input() task: TaskDefinition;
+
+  @Input() disabled: boolean;
+
+  @Output() onSelectTask = new EventEmitter<SelectTaskEvent>();
+
+  constructor() {}
+
+  selectTask(change: MatCheckboxChange): void {
+    this.onSelectTask.emit({
+      taskIdentifier: this.task.identifier,
+      checked: change.checked
+    });
+  }
+
+  formatType(type: TaskDefinitionType): string {
+    return defaultTypeOptions.find(option => option.type === type).label;
+  }
+}
diff --git a/src/app/customers/detail/status/status.component.html b/src/app/customers/detail/status/status.component.html
new file mode 100644
index 0000000..0b3a013
--- /dev/null
+++ b/src/app/customers/detail/status/status.component.html
@@ -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.
+-->
+
+<fims-layout-card-over title="{{'Tasks' | translate}}" [navigateBackTo]="['../']">
+  <div layout="row" layout-align="start start" layout-margin>
+    <div layout="column" flex="100">
+      <div layout="row" layout-align="start start" *ngFor="let step of processSteps$ | async">
+        <div layout="column" flex="100">
+          <h5 translate [translateParams]="{ value: step.command.action }">Please verify the following tasks before you
+            can action this member </h5>
+          <fims-customer-task *ngFor="let task of step.taskDefinitions"
+            [task]="task"
+            (onSelectTask)="executeTask($event)">
+          </fims-customer-task>
+          <div layout="row" layout-align="start center">
+            <mat-form-field layout-margin flex>
+              <textarea matInput placeholder="{{'Enter your comment here...' | translate}}"
+                        [(ngModel)]="step.command.comment"></textarea>
+            </mat-form-field>
+            <button mat-raised-button style="margin-left: 10px;" color="accent" (click)="executeCommand(step.command)">
+              {{step.command.action}}
+            </button>
+          </div>
+        </div>
+      </div>
+    </div>
+  </div>
+</fims-layout-card-over>
diff --git a/src/app/customers/detail/status/status.component.ts b/src/app/customers/detail/status/status.component.ts
new file mode 100644
index 0000000..20388f9
--- /dev/null
+++ b/src/app/customers/detail/status/status.component.ts
@@ -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.
+ */
+import {Component, OnDestroy, OnInit} from '@angular/core';
+import {Customer} from '../../../services/customer/domain/customer.model';
+import {Command} from '../../../services/customer/domain/command.model';
+import {ActivatedRoute} from '@angular/router';
+import * as fromCustomers from '../../store';
+import {Subscription} from 'rxjs/Subscription';
+import {EXECUTE_COMMAND, EXECUTE_TASK, LOAD_ALL} from '../../store/customerTasks/customer-task.actions';
+import {CustomersStore} from '../../store/index';
+import {Observable} from 'rxjs/Observable';
+import {ProcessStep} from '../../../services/customer/domain/process-step.model';
+import {SelectTaskEvent} from './customer-task.component';
+
+
+@Component({
+  templateUrl: './status.component.html'
+})
+export class CustomerStatusComponent implements OnInit, OnDestroy {
+
+  private customerSubscription: Subscription;
+
+  customer: Customer;
+
+  processSteps$: Observable<ProcessStep[]>;
+
+  constructor(private route: ActivatedRoute, private store: CustomersStore) {}
+
+  ngOnInit(): void {
+    this.processSteps$ = this.store.select(fromCustomers.getCustomerTaskProcessSteps);
+
+    this.customerSubscription = this.store.select(fromCustomers.getSelectedCustomer)
+      .do(customer => this.store.dispatch({ type: LOAD_ALL, payload: customer.identifier }))
+      .subscribe(customer => this.customer = customer);
+  }
+
+  ngOnDestroy(): void {
+    this.customerSubscription.unsubscribe();
+  }
+
+  executeTask(event: SelectTaskEvent): void {
+    this.store.dispatch({ type: EXECUTE_TASK, payload: {
+      customerId: this.customer.identifier,
+      taskId: event.taskIdentifier
+    } });
+  }
+
+  executeCommand(command: Command): void {
+    this.store.dispatch({ type: EXECUTE_COMMAND, payload: {
+      customerId: this.customer.identifier,
+      command,
+      activatedRoute: this.route
+    } });
+  }
+
+
+
+}
diff --git a/src/app/customers/form/contact/contact.component.html b/src/app/customers/form/contact/contact.component.html
new file mode 100644
index 0000000..bbfc699
--- /dev/null
+++ b/src/app/customers/form/contact/contact.component.html
@@ -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.
+-->
+
+<form [formGroup]="form" layout="column">
+  <fims-text-input [form]="form" controlName="email" placeholder="{{'Email(optional)' | translate}}" type="email"></fims-text-input>
+  <fims-text-input [form]="form" controlName="phone" placeholder="{{'Phone(optional)' | translate}}" type="tel"></fims-text-input>
+  <fims-text-input [form]="form" controlName="mobile" placeholder="{{'Mobile(optional)' | translate}}" type="tel"></fims-text-input>
+</form>
diff --git a/src/app/customers/form/contact/contact.component.spec.ts b/src/app/customers/form/contact/contact.component.spec.ts
new file mode 100644
index 0000000..2fef748
--- /dev/null
+++ b/src/app/customers/form/contact/contact.component.spec.ts
@@ -0,0 +1,88 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, ViewChild} from '@angular/core';
+import {ContactDetail} from '../../../services/domain/contact/contact-detail.model';
+import {CustomerContactFormComponent} from './contact.component';
+import {NoopAnimationsModule} from '@angular/platform-browser/animations';
+import {ReactiveFormsModule} from '@angular/forms';
+import {ComponentFixture, TestBed} from '@angular/core/testing';
+import {setValueByCssSelector} from '../../../common/testing/input-fields';
+import {TranslateModule} from '@ngx-translate/core';
+import {FimsSharedModule} from '../../../common/common.module';
+import {MatInputModule} from '@angular/material';
+
+const contactDetails: ContactDetail[] = [
+  { group: 'BUSINESS', type: 'EMAIL', value: 'test@test.de', preferenceLevel: 1 },
+  { group: 'BUSINESS', type: 'PHONE', value: '1234', preferenceLevel: 1 },
+  { group: 'BUSINESS', type: 'MOBILE', value: '5678', preferenceLevel: 1 }
+];
+
+describe('Test contact form', () => {
+
+  let fixture: ComponentFixture<TestComponent>;
+
+  let testComponent: TestComponent;
+
+  beforeEach(() => {
+    TestBed.configureTestingModule({
+      declarations: [
+        TestComponent,
+        CustomerContactFormComponent
+      ],
+      imports: [
+        TranslateModule.forRoot(),
+        FimsSharedModule,
+        ReactiveFormsModule,
+        MatInputModule,
+        NoopAnimationsModule
+      ]
+    });
+
+    fixture = TestBed.createComponent(TestComponent);
+    testComponent = fixture.componentInstance;
+  });
+
+  it('component should collect only contact fields with values', () => {
+    fixture.detectChanges();
+
+    setValueByCssSelector(fixture, '#email', '');
+    setValueByCssSelector(fixture, '#phone', '5678');
+
+    fixture.detectChanges();
+
+    const expectedResult: ContactDetail[] = [
+      { group: 'BUSINESS', type: 'MOBILE', value: '5678', preferenceLevel: 1 },
+      { group: 'BUSINESS', type: 'PHONE', value: '5678', preferenceLevel: 1 }
+    ];
+
+    expect(testComponent.formComponent.formData).toEqual(expectedResult);
+  });
+
+});
+
+@Component({
+  template: '<fims-customer-contact-form #form [formData]="formData"></fims-customer-contact-form>'
+})
+class TestComponent {
+
+  @ViewChild('form') formComponent: CustomerContactFormComponent;
+
+  formData: ContactDetail[] = contactDetails;
+
+}
diff --git a/src/app/customers/form/contact/contact.component.ts b/src/app/customers/form/contact/contact.component.ts
new file mode 100644
index 0000000..8ce2f17
--- /dev/null
+++ b/src/app/customers/form/contact/contact.component.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 {Component, Input} from '@angular/core';
+import {FormComponent} from '../../../common/forms/form.component';
+import {AbstractControl, FormBuilder, Validators} from '@angular/forms';
+import {BUSINESS, ContactDetail, ContactDetailType, EMAIL, MOBILE, PHONE} from '../../../services/domain/contact/contact-detail.model';
+import {getContactDetailValueByType} from '../../contact.helper';
+import {FimsValidators} from '../../../common/validator/validators';
+
+@Component({
+  selector: 'fims-customer-contact-form',
+  templateUrl: './contact.component.html'
+})
+export class CustomerContactFormComponent extends FormComponent<ContactDetail[]> {
+
+  @Input() set formData(contactDetails: ContactDetail[]) {
+    if (!contactDetails) {
+      throw new Error('contact details must be defined');
+    }
+
+    let phone = '';
+    let mobile = '';
+    let email = '';
+
+    const businessContacts: ContactDetail[] = contactDetails.filter(contactDetail => contactDetail.group === BUSINESS);
+
+    if (businessContacts.length) {
+      phone = getContactDetailValueByType(businessContacts, PHONE);
+      mobile = getContactDetailValueByType(businessContacts, MOBILE);
+      email = getContactDetailValueByType(businessContacts, EMAIL);
+    }
+
+    this.form = this.formBuilder.group({
+      email: [email, [Validators.maxLength(32), FimsValidators.email]],
+      phone: [phone, Validators.maxLength(32)],
+      mobile: [mobile, Validators.maxLength(32)]
+    });
+  };
+
+  constructor(private formBuilder: FormBuilder) {
+    super();
+  }
+
+  get formData(): ContactDetail[] {
+    const contactDetails: ContactDetail[] = [];
+
+    this.pushIfValue(contactDetails, this.form.get('email'), 'EMAIL');
+    this.pushIfValue(contactDetails, this.form.get('mobile'), 'MOBILE');
+    this.pushIfValue(contactDetails, this.form.get('phone'), 'PHONE');
+
+    return contactDetails;
+  }
+
+  private pushIfValue(contactDetails: ContactDetail[], control: AbstractControl, type: ContactDetailType): void {
+    if (control.value && control.value.length > 0) {
+      contactDetails.push({
+        group: 'BUSINESS',
+        type: type,
+        value: control.value,
+        preferenceLevel: 1
+      });
+    }
+  }
+
+}
diff --git a/src/app/customers/form/create/create.form.component.html b/src/app/customers/form/create/create.form.component.html
new file mode 100644
index 0000000..1aadbca
--- /dev/null
+++ b/src/app/customers/form/create/create.form.component.html
@@ -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.
+-->
+
+<fims-layout-card-over title="{{'Create new member ' | translate}}">
+  <fims-customer-form-component #form
+                                  (onSave)="onSave($event)"
+                                  (onCancel)="onCancel()"
+                                  [catalog]="catalog$ | async"
+                                  [customer]="customer">
+  </fims-customer-form-component>
+</fims-layout-card-over>
diff --git a/src/app/customers/form/create/create.form.component.ts b/src/app/customers/form/create/create.form.component.ts
new file mode 100644
index 0000000..721c179
--- /dev/null
+++ b/src/app/customers/form/create/create.form.component.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 {Component, OnDestroy, OnInit, ViewChild} from '@angular/core';
+import {ActivatedRoute, Router} from '@angular/router';
+import {Customer} from '../../../services/customer/domain/customer.model';
+import {CustomerFormComponent} from '../form.component';
+import * as fromCustomers from '../../store';
+import {Error} from '../../../services/domain/error.model';
+import {Subscription} from 'rxjs/Subscription';
+import {CustomersStore} from '../../store/index';
+import {CREATE, RESET_FORM} from '../../store/customer.actions';
+import {Catalog} from '../../../services/catalog/domain/catalog.model';
+import {Observable} from 'rxjs/Observable';
+
+@Component({
+  templateUrl: './create.form.component.html'
+})
+export class CreateCustomerFormComponent implements OnInit, OnDestroy {
+
+  private formStateSubscription: Subscription;
+
+  @ViewChild('form') formComponent: CustomerFormComponent;
+
+  customer: Customer = {
+    identifier: '',
+    type: 'PERSON',
+    givenName: '',
+    surname: '',
+    address: {
+      street: '',
+      city: '',
+      countryCode: '',
+      country: ''
+    },
+    member: true,
+    dateOfBirth: undefined,
+    contactDetails: [],
+    customValues: []
+  };
+
+  catalog$: Observable<Catalog>;
+
+  constructor(private router: Router, private route: ActivatedRoute, private store: CustomersStore) {
+    this.catalog$ = store.select(fromCustomers.getCustomerCatalog);
+  }
+
+  ngOnInit() {
+    this.formStateSubscription = this.store.select(fromCustomers.getCustomerFormError)
+      .filter((error: Error) => !!error)
+      .subscribe((error: Error) => this.formComponent.showIdentifierValidationError());
+  }
+
+  ngOnDestroy(): void {
+    this.formStateSubscription.unsubscribe();
+
+    this.store.dispatch({ type: RESET_FORM });
+  }
+
+  onSave(customer: Customer) {
+    this.store.dispatch({ type: CREATE, payload: {
+      customer,
+      activatedRoute: this.route
+    } });
+  }
+
+  onCancel() {
+    this.navigateAway();
+  }
+
+  navigateAway(): void {
+    this.router.navigate(['../'], { relativeTo: this.route });
+  }
+}
diff --git a/src/app/customers/form/customFields/custom-fields.component.html b/src/app/customers/form/customFields/custom-fields.component.html
new file mode 100644
index 0000000..4bca7fa
--- /dev/null
+++ b/src/app/customers/form/customFields/custom-fields.component.html
@@ -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.
+-->
+
+<form [formGroup]="form">
+    <h4>{{catalog?.name}}</h4>
+    <div *ngFor="let field of catalog?.fields" [ngSwitch]="field.dataType" layout="row">
+      <fims-text-input *ngSwitchCase="'TEXT'" [form]="form" [controlName]="field.identifier" [placeholder]="field.label" [title]="field.hint"></fims-text-input>
+      <fims-text-input *ngSwitchCase="'NUMBER'" type="number" [form]="form" [controlName]="field.identifier" [placeholder]="field.label" [title]="field.hint"></fims-text-input>
+      <fims-date-input *ngSwitchCase="'DATE'" [form]="form" [controlName]="field.identifier" [placeholder]="field.label" [title]="field.hint"></fims-date-input>
+      <div class="pad-bottom-xs" *ngSwitchCase="'SINGLE_SELECTION'">
+        <div class="mat-body-1">{{field.label}}</div>
+        <mat-radio-group [formControlName]="field.identifier">
+          <mat-radio-button [value]="" layout-margin *ngIf="!field.mandatory" translate>
+            None
+          </mat-radio-button>
+          <mat-radio-button *ngFor="let option of field.options" [value]="option.value" title="{{field.hint}}" layout-margin>
+            {{option.label}}
+          </mat-radio-button>
+        </mat-radio-group>
+      </div>
+      <div class="pad-bottom-xs" *ngSwitchCase="'MULTI_SELECTION'">
+        <div class="mat-body-1">{{field.label}}</div>
+        <td-chips [items]="field.options"
+                  [formControlName]="field.identifier"
+                  placeholder="{{field.hint}}">
+          <ng-template td-chip let-chip="chip">
+            <div class="tc-grey-100 bgc-teal-700" td-chip-avatar>
+              {{chip.label.substring(0, 1).toUpperCase()}}
+            </div> {{chip.label}}
+          </ng-template>
+          <ng-template td-autocomplete-option let-option="option">
+            {{option.label}}
+          </ng-template>
+        </td-chips>
+      </div>
+    </div>
+</form>
diff --git a/src/app/customers/form/customFields/custom-fields.component.ts b/src/app/customers/form/customFields/custom-fields.component.ts
new file mode 100644
index 0000000..8318c08
--- /dev/null
+++ b/src/app/customers/form/customFields/custom-fields.component.ts
@@ -0,0 +1,200 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, Input, OnChanges, SimpleChanges} from '@angular/core';
+import {Catalog} from '../../../services/catalog/domain/catalog.model';
+import {FormBuilder, FormControl, FormGroup, ValidatorFn, Validators} from '@angular/forms';
+import {Field} from '../../../services/catalog/domain/field.model';
+import {Value} from '../../../services/catalog/domain/value.model';
+import {FormComponent} from '../../../common/forms/form.component';
+import {FimsValidators} from '../../../common/validator/validators';
+import {addCurrentTime} from '../../../services/domain/date.converter';
+import {Option} from '../../../services/catalog/domain/option.model';
+
+@Component({
+  selector: 'fims-custom-fields-component',
+  templateUrl: './custom-fields.component.html'
+})
+export class CustomerCustomFieldsComponent extends FormComponent<Value[]> implements OnChanges {
+
+  private _formData: Value[];
+
+  @Input('catalog') catalog: Catalog;
+
+  @Input('formData') set formData(formData: Value[]) {
+    this._formData = formData;
+  };
+
+  constructor(private formBuilder: FormBuilder) {
+    super();
+  }
+
+  ngOnChanges(changes: SimpleChanges): void {
+    if (changes.catalog || changes.formData) {
+      this.form = this.buildFormGroup();
+    }
+  }
+
+  private findValue(fieldIdentifier: string): Value {
+    return this._formData.find((value: Value) =>
+      value.fieldIdentifier === fieldIdentifier
+    );
+  }
+
+  private findField(fieldIdentifier: string): Field {
+    return this.catalog.fields.find((field: Field) => field.identifier === fieldIdentifier);
+  }
+
+  private buildFormGroup(): FormGroup {
+    const group: FormGroup = this.formBuilder.group({});
+
+    if (!this._formData || !this.catalog) {
+      return group;
+    }
+
+    for (const field of this.catalog.fields) {
+      const value = this.findValue(field.identifier);
+
+      const valueString: string = value && value.value ? value.value : '';
+
+      const formControl: FormControl = new FormControl({value: valueString, disabled: false});
+
+      const validators: ValidatorFn[] = [];
+
+      switch (field.dataType) {
+        case 'TEXT': {
+          validators.push(...this.buildTextValidators(field));
+          break;
+        }
+
+        case 'NUMBER': {
+          formControl.setValue(valueString.length ? Number(valueString) : undefined);
+          validators.push(...this.buildNumberValidators(field));
+          break;
+        }
+
+        case 'DATE': {
+          formControl.setValue(valueString.length ? valueString.substring(0, 10) : '');
+          break;
+        }
+
+        case 'SINGLE_SELECTION': {
+          formControl.setValue(valueString.length ? Number(valueString) : undefined);
+          break;
+        }
+
+        case 'MULTI_SELECTION': {
+          const optionValues = valueString.length ? valueString.split(',').map(optionValue => Number(optionValue)) : [];
+          const foundOptions = field.options
+            .filter((option: Option) => optionValues.indexOf(option.value) > -1);
+          formControl.setValue(foundOptions);
+          break;
+        }
+
+        default:
+          break;
+      }
+
+      if (field.mandatory) {
+        validators.push(Validators.required);
+      }
+
+      formControl.setValidators(validators);
+
+      group.addControl(field.identifier, formControl);
+    }
+
+    return group;
+  }
+
+  get formData(): Value[] {
+    const fields: any = this.form.getRawValue();
+
+    const values: Value[] = [];
+
+    for (const fieldIdentifier in fields) {
+      if (fields.hasOwnProperty(fieldIdentifier)) {
+        let value = fields[fieldIdentifier];
+
+        const field: Field = this.findField(fieldIdentifier);
+
+        if (value == null || value.length === 0) {
+          continue;
+        }
+
+        switch (field.dataType) {
+          case 'NUMBER': {
+            value = value.toString();
+            break;
+          }
+
+          case 'DATE': {
+            const date = new Date(value);
+            value = addCurrentTime(date).toISOString();
+            break;
+          }
+
+          case 'SINGLE_SELECTION': {
+            value = value.toString();
+            break;
+          }
+
+          case 'MULTI_SELECTION': {
+            value = value.map(fieldValue => fieldValue.value).join(',');
+            break;
+          }
+        }
+
+        values.push({
+          catalogIdentifier: this.catalog.identifier,
+          fieldIdentifier,
+          value
+        });
+      }
+    }
+    return values;
+  }
+
+  private buildTextValidators(field: Field): ValidatorFn[] {
+    const validators: ValidatorFn[] = [];
+
+    if (field.length != null) {
+      validators.push(Validators.maxLength(field.length));
+    }
+
+    return validators;
+  }
+
+  private buildNumberValidators(field: Field): ValidatorFn[] {
+    const validators: ValidatorFn[] = [];
+
+    if (field.minValue != null) {
+      validators.push(FimsValidators.minValue(field.minValue));
+    }
+
+    if (field.maxValue != null) {
+      validators.push(FimsValidators.maxValue(field.maxValue));
+    }
+
+    if (field.precision != null) {
+      validators.push(FimsValidators.maxScale(field.precision));
+    }
+
+    return validators;
+  }
+}
diff --git a/src/app/customers/form/detail/detail.component.html b/src/app/customers/form/detail/detail.component.html
new file mode 100644
index 0000000..e5a57e3
--- /dev/null
+++ b/src/app/customers/form/detail/detail.component.html
@@ -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.
+-->
+
+<form [formGroup]="form" layout="column">
+  <fims-id-input [form]="form" placeholder="Account" controlName="identifier" [readonly]="editMode"></fims-id-input>
+  <fims-text-input [form]="form" controlName="firstName" placeholder="{{'First name' | translate}}"></fims-text-input>
+  <fims-text-input [form]="form" controlName="middleName" placeholder="{{'Middle name(optional)' | translate}}"></fims-text-input>
+  <fims-text-input [form]="form" controlName="lastName" placeholder="{{'Last name' | translate}}"></fims-text-input>
+  <fims-date-input [form]="form" controlName="dayOfBirth" placeholder="{{'Day of birth' | translate}}"></fims-date-input>
+  <mat-checkbox formControlName="member" layout-margin translate>Is member?</mat-checkbox>
+</form>
diff --git a/src/app/customers/form/detail/detail.component.ts b/src/app/customers/form/detail/detail.component.ts
new file mode 100644
index 0000000..cfe7323
--- /dev/null
+++ b/src/app/customers/form/detail/detail.component.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 {Component, Input} from '@angular/core';
+import {FormComponent} from '../../../common/forms/form.component';
+import {FormBuilder, Validators} from '@angular/forms';
+import {FimsValidators} from '../../../common/validator/validators';
+
+export interface CustomerDetailFormData {
+  identifier: string;
+  firstName: string;
+  middleName: string;
+  lastName: string;
+  dateOfBirth: {
+    day?: number;
+    month?: number;
+    year?: number;
+  };
+  member: boolean;
+}
+
+@Component({
+  selector: 'fims-customer-detail-form',
+  templateUrl: './detail.component.html'
+})
+export class CustomerDetailFormComponent extends FormComponent<CustomerDetailFormData> {
+
+  @Input() set formData(formData: CustomerDetailFormData) {
+    const dateOfBirth = formData.dateOfBirth;
+
+    this.form = this.formBuilder.group({
+      identifier: [formData.identifier, [Validators.required, Validators.minLength(3), Validators.maxLength(32), FimsValidators.urlSafe]],
+      firstName: [formData.firstName, [Validators.required, Validators.maxLength(256)]],
+      middleName: [formData.middleName, Validators.maxLength(256)],
+      lastName: [formData.lastName, [Validators.required, Validators.maxLength(256)]],
+      dayOfBirth: [dateOfBirth ? this.formatDate(dateOfBirth.year, dateOfBirth.month, dateOfBirth.day) : undefined,
+        [Validators.required, FimsValidators.beforeToday]],
+      member: [formData.member],
+    });
+  };
+
+  @Input() editMode: boolean;
+
+  private formatDate(year: number, month: number, day: number): string {
+    return `${year}-${this.addZero(month)}-${this.addZero(day)}`;
+  }
+
+  private addZero(value: number): string {
+    return ('0' + value).slice(-2);
+  }
+
+  constructor(private formBuilder: FormBuilder) {
+    super();
+  }
+
+  get formData(): CustomerDetailFormData{
+    const birthDate: string = this.form.get('dayOfBirth').value;
+
+    const chunks: string[] = birthDate ? birthDate.split('-') : [];
+
+    return {
+      identifier: this.form.get('identifier').value,
+      firstName: this.form.get('firstName').value,
+      middleName: this.form.get('middleName').value,
+      lastName: this.form.get('lastName').value,
+      dateOfBirth: {
+        year: chunks.length ? Number(chunks[0]) : undefined,
+        month: chunks.length ? Number(chunks[1]) : undefined,
+        day: chunks.length ? Number(chunks[2]) : undefined,
+      },
+      member: this.form.get('member').value
+    };
+  }
+
+}
diff --git a/src/app/customers/form/edit/edit.form.component.html b/src/app/customers/form/edit/edit.form.component.html
new file mode 100644
index 0000000..8bdff89
--- /dev/null
+++ b/src/app/customers/form/edit/edit.form.component.html
@@ -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.
+-->
+
+<fims-layout-card-over title="{{'Edit member ' | translate}}">
+  <fims-customer-form-component #form
+                                  (onSave)="onSave($event)"
+                                  (onCancel)="onCancel()"
+                                  [catalog]="catalog$ | async"
+                                  [customer]="customer$ | async"
+                                  [editMode]="true">
+  </fims-customer-form-component>
+</fims-layout-card-over>
diff --git a/src/app/customers/form/edit/edit.form.component.ts b/src/app/customers/form/edit/edit.form.component.ts
new file mode 100644
index 0000000..55894c9
--- /dev/null
+++ b/src/app/customers/form/edit/edit.form.component.ts
@@ -0,0 +1,56 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 {ActivatedRoute, Router} from '@angular/router';
+import {Customer} from '../../../services/customer/domain/customer.model';
+import * as fromCustomers from '../../store';
+import {CustomersStore} from '../../store/index';
+import {UPDATE} from '../../store/customer.actions';
+import {Catalog} from '../../../services/catalog/domain/catalog.model';
+import {Observable} from 'rxjs/Observable';
+
+@Component({
+  templateUrl: './edit.form.component.html'
+})
+export class EditCustomerFormComponent {
+
+  customer$: Observable<Customer>;
+
+  catalog$: Observable<Catalog>;
+
+  constructor(private router: Router, private route: ActivatedRoute, private store: CustomersStore) {
+    this.catalog$ = store.select(fromCustomers.getCustomerCatalog);
+    this.customer$ = store.select(fromCustomers.getSelectedCustomer);
+  }
+
+  onSave(customer: Customer) {
+    this.store.dispatch({ type: UPDATE, payload: {
+      customer,
+      activatedRoute: this.route
+    } });
+  }
+
+  onCancel() {
+    this.navigateAway();
+  }
+
+  navigateAway(): void {
+    this.router.navigate(['../'], { relativeTo: this.route });
+  }
+}
diff --git a/src/app/customers/form/employees/employees.component.html b/src/app/customers/form/employees/employees.component.html
new file mode 100644
index 0000000..1f6046b
--- /dev/null
+++ b/src/app/customers/form/employees/employees.component.html
@@ -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.
+-->
+
+<fims-select-list flex
+                    [data]="employees"
+                    id="identifier"
+                    displayName="surname"
+                    listIcon="group"
+                    [preSelection]="preSelection"
+                    (onSearch)="search($event)"
+                    (onSelectionChange)="selectionChange($event)"
+                    title="Assigned Employee"
+                    noResultsMessage="No employee was found."
+                    noSelectionMessage="No employee assigned to member , yet."
+></fims-select-list>
diff --git a/src/app/customers/form/employees/employees.component.ts b/src/app/customers/form/employees/employees.component.ts
new file mode 100644
index 0000000..e6c70f2
--- /dev/null
+++ b/src/app/customers/form/employees/employees.component.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 {Component, EventEmitter, Input, OnInit, Output} from '@angular/core';
+import {Observable} from 'rxjs/Observable';
+import {Employee} from '../../../services/office/domain/employee.model';
+import {FetchRequest} from '../../../services/domain/paging/fetch-request.model';
+import * as fromRoot from '../../../store';
+import {SEARCH} from '../../../store/employee/employee.actions';
+import {Store} from '@ngrx/store';
+
+@Component({
+  selector: 'fims-customer-employees-form',
+  templateUrl: './employees.component.html'
+})
+export class CustomerEmployeesComponent implements OnInit {
+
+  employees: Observable<Employee[]>;
+
+  @Input() preSelection: string[];
+
+  @Output() onSelectionChange = new EventEmitter<string[]>();
+
+  constructor(private store: Store<fromRoot.State>) {}
+
+  ngOnInit(): void {
+    this.employees = this.store.select(fromRoot.getEmployeeSearchResults)
+      .map(employeePage => employeePage.employees);
+  }
+
+  search(searchTerm) {
+    const fetchRequest: FetchRequest = {
+      searchTerm
+    };
+    this.store.dispatch({ type: SEARCH, payload: fetchRequest });
+  }
+
+  selectionChange(selections: string[]): void {
+    this.onSelectionChange.emit(selections);
+  }
+
+}
diff --git a/src/app/customers/form/form.component.html b/src/app/customers/form/form.component.html
new file mode 100644
index 0000000..0bcfdc5
--- /dev/null
+++ b/src/app/customers/form/form.component.html
@@ -0,0 +1,87 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<td-steps mode="'vertical'">
+  <td-step #detailsStep label="{{'Member details' | translate}}" [state]="detailForm.valid ? 'complete' : detailForm.pristine ? 'none' : 'required'">
+
+    <fims-customer-detail-form #detailForm [formData]="detailFormData" [editMode]="editMode"></fims-customer-detail-form>
+
+    <ng-template td-step-actions>
+      <fims-form-continue-action (onContinue)="addressStep.open()"></fims-form-continue-action>
+    </ng-template>
+  </td-step>
+
+  <td-step #addressStep label="{{'Member Address' | translate}}" [state]="addressForm.valid ? 'complete' : addressForm.pristine ? 'none' : 'required'">
+
+    <fims-address-form #addressForm [formData]="addressFormData"></fims-address-form>
+
+    <ng-template td-step-actions>
+      <fims-form-continue-action (onContinue)="contactStep.open()"></fims-form-continue-action>
+    </ng-template>
+  </td-step>
+
+  <td-step #contactStep label="{{'Member contact(optional)' | translate}}" [state]="contactForm.pristine ? 'none' : contactForm.valid ? 'completed' : 'required'">
+
+    <fims-customer-contact-form #contactForm [formData]="contactFormData"></fims-customer-contact-form>
+
+    <ng-template td-step-actions>
+      <fims-form-continue-action (onContinue)="officeStep.open()"></fims-form-continue-action>
+    </ng-template>
+  </td-step>
+
+  <td-step #officeStep label="{{'Assign member to office(optional)' | translate}}"
+           [state]="selectedOffices.length ? 'complete' : 'none'">
+
+    <fims-customer-offices-form [preSelection]="selectedOffices" (onSelectionChange)="selectOffice($event)"></fims-customer-offices-form>
+
+    <ng-template td-step-actions>
+      <fims-form-continue-action (onContinue)="employeeStep.open()"></fims-form-continue-action>
+    </ng-template>
+    <ng-template td-step-summary *ngIf="selectedOffices && selectedOffices.length > 0" [translate]="'Member is assigned to office:'" [translateParams]="{value: selectedOffices[0]}">
+    </ng-template>
+  </td-step>
+
+  <td-step #employeeStep label="{{'Assign member to employee(optional)' | translate}}"
+           [state]="selectedEmployees.length ? 'complete' : 'none'">
+
+    <fims-customer-employees-form [preSelection]="selectedEmployees" (onSelectionChange)="selectEmployee($event)"></fims-customer-employees-form>
+
+    <ng-template td-step-actions>
+      <fims-form-continue-action (onContinue)="customFieldsStep.open()"></fims-form-continue-action>
+    </ng-template>
+  </td-step>
+
+  <td-step #customFieldsStep label="{{'Custom fields' | translate}}" [state]="customFieldsForm.pristine ? 'none' : customFieldsForm.valid ? 'completed' : 'required'">
+    <fims-custom-fields-component
+      #customFieldsForm
+      [catalog]="catalog"
+      [formData]="customFieldsFormData">
+    </fims-custom-fields-component>
+  </td-step>
+
+  <td-step label="{{'Final step' | translate}}" [state]="'complete'">
+    <ng-template td-step-summary>
+      <fims-form-final-action
+        [resourceName]="'MEMBER'"
+        [editMode]="editMode"
+        [disabled]="!isValid"
+        (onCancel)="cancel()"
+        (onSave)="save()">
+      </fims-form-final-action>
+    </ng-template>
+  </td-step>
+</td-steps>
diff --git a/src/app/customers/form/form.component.spec.ts b/src/app/customers/form/form.component.spec.ts
new file mode 100644
index 0000000..6151e30
--- /dev/null
+++ b/src/app/customers/form/form.component.spec.ts
@@ -0,0 +1,202 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, TestBed} from '@angular/core/testing';
+import {CustomerDetailFormComponent} from './detail/detail.component';
+import {CustomerFormComponent} from './form.component';
+import {CustomerContactFormComponent} from './contact/contact.component';
+import {CustomerCustomFieldsComponent} from './customFields/custom-fields.component';
+import {ReactiveFormsModule} from '@angular/forms';
+import {CovalentChipsModule, CovalentStepsModule} from '@covalent/core';
+import {Component, EventEmitter, ViewChild} from '@angular/core';
+import {Customer} from '../../services/customer/domain/customer.model';
+import {TranslateModule} from '@ngx-translate/core';
+import {CustomerEmployeesComponent} from './employees/employees.component';
+import {CustomerOfficesComponent} from './offices/offices.component';
+import {Observable} from 'rxjs/Observable';
+import {Store} from '@ngrx/store';
+import {CustomersStore} from '../store/index';
+import {NoopAnimationsModule} from '@angular/platform-browser/animations';
+import {CountryService} from '../../services/country/country.service';
+import {Country} from '../../services/country/model/country.model';
+import {FimsSharedModule} from '../../common/common.module';
+import {MatAutocompleteModule, MatCheckboxModule, MatIconModule, MatInputModule, MatRadioModule} from '@angular/material';
+
+const customerTemplate: Customer = {
+  identifier: 'test',
+  currentState: 'ACTIVE',
+  type: 'PERSON',
+  givenName: 'test',
+  middleName: 'test',
+  surname: 'test',
+  address: {
+    street: 'test',
+    city: 'test',
+    countryCode: 'te',
+    country: 'test',
+    region: 'test',
+    postalCode: 'test'
+  },
+  dateOfBirth: {
+    year: 1982,
+    month: 6,
+    day: 24
+  },
+  member: true,
+  identificationCard: {
+    issuer: 'test',
+    expirationDate: {
+      year: 1982,
+      month: 6,
+      day: 24
+    },
+    type: 'passport',
+    number: '12312'
+  },
+  contactDetails: [{
+    type: 'EMAIL',
+    group: 'BUSINESS',
+    value: 'test@test.de',
+    preferenceLevel: 0
+  }],
+  customValues: []
+};
+
+const country: Country = {
+  displayName: '',
+  name: customerTemplate.address.country,
+  alpha2Code: customerTemplate.address.countryCode,
+  translations: {}
+};
+
+describe('Test customer form', () => {
+
+  let fixture: ComponentFixture<TestComponent>;
+
+  let testComponent: TestComponent;
+
+  beforeEach(() => {
+    TestBed.configureTestingModule({
+      declarations: [
+        TestComponent,
+        CustomerFormComponent,
+        CustomerDetailFormComponent,
+        CustomerContactFormComponent,
+        CustomerCustomFieldsComponent,
+        CustomerEmployeesComponent,
+        CustomerOfficesComponent
+      ],
+      imports: [
+        TranslateModule.forRoot(),
+        FimsSharedModule,
+        ReactiveFormsModule,
+        MatInputModule,
+        MatIconModule,
+        MatRadioModule,
+        MatAutocompleteModule,
+        MatCheckboxModule,
+        CovalentStepsModule,
+        CovalentChipsModule,
+        NoopAnimationsModule
+      ],
+      providers: [
+        {
+          // Used by address component
+          provide: CountryService, useClass: class {
+            fetchByCountryCode = jasmine.createSpy('fetchByCountryCode').and.returnValue(country);
+            fetchCountries = jasmine.createSpy('fetchCountries').and.returnValue([country]);
+          }
+        },
+        {
+          provide: CustomersStore, useClass: class {
+            dispatch = jasmine.createSpy('dispatch');
+            select = jasmine.createSpy('select').and.returnValue(Observable.empty());
+          }
+        },
+        {
+          provide: Store, useClass: class {
+            dispatch = jasmine.createSpy('dispatch');
+            select = jasmine.createSpy('select').and.returnValue(Observable.empty());
+          }
+        }
+      ]
+    });
+
+    fixture = TestBed.createComponent(TestComponent);
+    testComponent = fixture.componentInstance;
+  });
+
+  it('should test if the form save the original values', () => {
+    fixture.detectChanges();
+
+    testComponent.saveEmitter.subscribe((customer: Customer) => {
+      expect(customerTemplate.identifier).toEqual(customer.identifier);
+      expect(customerTemplate.currentState).toEqual(customer.currentState);
+      expect(customerTemplate.type).toEqual(customer.type);
+      expect(customerTemplate.givenName).toEqual(customer.givenName);
+      expect(customerTemplate.middleName).toEqual(customer.middleName);
+      expect(customerTemplate.surname).toEqual(customer.surname);
+
+      expect(customerTemplate.accountBeneficiary).toEqual(customer.accountBeneficiary);
+      expect(customerTemplate.referenceCustomer).toEqual(customer.referenceCustomer);
+      expect(customerTemplate.assignedOffice).toEqual(customer.assignedOffice);
+      expect(customerTemplate.assignedEmployee).toEqual(customer.assignedEmployee);
+
+      expect(customerTemplate.address.city).toEqual(customer.address.city);
+      expect(customerTemplate.address.country).toEqual(customer.address.country);
+      expect(customerTemplate.address.countryCode).toEqual(customer.address.countryCode);
+      expect(customerTemplate.address.postalCode).toEqual(customer.address.postalCode);
+      expect(customerTemplate.address.region).toEqual(customer.address.region);
+      expect(customerTemplate.address.street).toEqual(customer.address.street);
+
+      expect(customerTemplate.dateOfBirth.day).toEqual(customer.dateOfBirth.day);
+      expect(customerTemplate.dateOfBirth.month).toEqual(customer.dateOfBirth.month);
+      expect(customerTemplate.dateOfBirth.year).toEqual(customer.dateOfBirth.year);
+
+      expect(customer.contactDetails.length).toEqual(1);
+    });
+
+    testComponent.triggerSave();
+  });
+
+});
+
+@Component({
+  template: `
+    <fims-customer-form-component #form (onSave)="onSave($event)" (onCancel)="onCancel($event)" [customer]="customer">
+    </fims-customer-form-component>
+  `
+})
+class TestComponent {
+
+  saveEmitter = new EventEmitter<Customer>();
+
+  @ViewChild('form') formComponent: CustomerFormComponent;
+
+  customer: Customer = customerTemplate;
+
+  triggerSave(): void {
+    this.formComponent.save();
+  }
+
+  onSave(customer: Customer): void {
+    this.saveEmitter.emit(customer);
+  }
+
+  onCancel(): void {}
+}
diff --git a/src/app/customers/form/form.component.ts b/src/app/customers/form/form.component.ts
new file mode 100644
index 0000000..7b4de63
--- /dev/null
+++ b/src/app/customers/form/form.component.ts
@@ -0,0 +1,144 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, OnInit, Output, ViewChild} from '@angular/core';
+import {TdStepComponent} from '@covalent/core';
+import {Customer} from '../../services/customer/domain/customer.model';
+import {CustomerDetailFormComponent, CustomerDetailFormData} from './detail/detail.component';
+import {AddressFormComponent} from '../../common/address/address.component';
+import {Address} from '../../services/domain/address/address.model';
+import {CustomerContactFormComponent} from './contact/contact.component';
+import {ContactDetail} from '../../services/domain/contact/contact-detail.model';
+import {Value} from '../../services/catalog/domain/value.model';
+import {CustomerCustomFieldsComponent} from './customFields/custom-fields.component';
+import {Catalog} from '../../services/catalog/domain/catalog.model';
+
+@Component({
+  selector: 'fims-customer-form-component',
+  templateUrl: './form.component.html'
+})
+export class CustomerFormComponent implements OnInit {
+
+  private _customer: Customer;
+
+  @Input('customer') set customer(customer: Customer) {
+    this._customer = customer;
+
+    this.detailFormData = {
+      identifier: customer.identifier,
+      firstName: customer.givenName,
+      middleName: customer.middleName,
+      lastName: customer.surname,
+      dateOfBirth: customer.dateOfBirth,
+      member: customer.member
+    };
+
+    this.addressFormData = customer.address;
+
+    this.contactFormData = customer.contactDetails;
+
+    this.selectedOffices = customer.assignedOffice ? [customer.assignedOffice] : [];
+
+    this.selectedEmployees = customer.assignedEmployee ? [customer.assignedEmployee] : [];
+
+    this.customFieldsFormData = customer.customValues;
+  };
+
+  @Input('catalog') catalog: Catalog;
+
+  @Input('editMode') editMode: boolean;
+
+  @Output('onSave') onSave = new EventEmitter<Customer>();
+
+  @Output('onCancel') onCancel = new EventEmitter<void>();
+
+  @ViewChild('detailsStep') step: TdStepComponent;
+
+  @ViewChild('detailForm') detailForm: CustomerDetailFormComponent;
+  detailFormData: CustomerDetailFormData;
+
+  @ViewChild('contactForm') contactForm: CustomerContactFormComponent;
+  contactFormData: ContactDetail[];
+
+  @ViewChild('addressForm') addressForm: AddressFormComponent;
+  addressFormData: Address;
+
+  selectedOffices: string[] = [];
+
+  selectedEmployees: string[] = [];
+
+  @ViewChild('customFieldsForm') customFieldsForm: CustomerCustomFieldsComponent;
+  customFieldsFormData: Value[];
+
+  ngOnInit() {
+    this.openDetailStep();
+  }
+
+  openDetailStep(): void {
+    this.step.open();
+  }
+
+  showIdentifierValidationError(): void {
+    this.detailForm.setError('identifier', 'unique', true);
+    this.openDetailStep();
+  }
+
+  selectOffice(selections: string[]): void {
+    this.selectedOffices = selections;
+  }
+
+  selectEmployee(selections: string[]): void {
+    this.selectedEmployees = selections;
+  }
+
+  get isValid(): boolean {
+    return (this.detailForm.valid && this.addressForm.valid)
+      && this.contactForm.validWhenOptional
+      && this.customFieldsForm.valid;
+  }
+
+  get customer(): Customer {
+    return this._customer;
+  }
+
+  save() {
+    const detailFormData = this.detailForm.formData;
+
+    const customer: Customer = {
+      identifier: detailFormData.identifier,
+      currentState: this.customer.currentState,
+      givenName: detailFormData.firstName,
+      surname: detailFormData.lastName,
+      middleName: detailFormData.middleName,
+      type: 'PERSON',
+      address: this.addressForm.formData,
+      contactDetails: this.contactForm.formData,
+      dateOfBirth: detailFormData.dateOfBirth,
+      member: detailFormData.member,
+      assignedOffice: this.selectedOffices && this.selectedOffices.length > 0 ? this.selectedOffices[0] : undefined,
+      assignedEmployee: this.selectedEmployees && this.selectedEmployees.length > 0 ? this.selectedEmployees[0] : undefined,
+      customValues: this.customFieldsForm.formData
+    };
+    this.onSave.emit(customer);
+  }
+
+  cancel() {
+    this.onCancel.emit();
+  }
+
+}
diff --git a/src/app/customers/form/offices/offices.component.html b/src/app/customers/form/offices/offices.component.html
new file mode 100644
index 0000000..8cdc509
--- /dev/null
+++ b/src/app/customers/form/offices/offices.component.html
@@ -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.
+-->
+
+<fims-select-list flex
+                    [data]="offices"
+                    id="identifier"
+                    displayName="name"
+                    listIcon="store"
+                    [preSelection]="preSelection"
+                    (onSearch)="search($event)"
+                    (onSelectionChange)="select($event)"
+                    title="Assigned Office"
+                    noResultsMessage="No office was found."
+                    noSelectionMessage="No office assigned to member , yet."
+></fims-select-list>
diff --git a/src/app/customers/form/offices/offices.component.ts b/src/app/customers/form/offices/offices.component.ts
new file mode 100644
index 0000000..82a3061
--- /dev/null
+++ b/src/app/customers/form/offices/offices.component.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 {Component, EventEmitter, Input, OnInit, Output} from '@angular/core';
+import {Observable} from 'rxjs/Observable';
+import {Office} from '../../../services/office/domain/office.model';
+import {FetchRequest} from '../../../services/domain/paging/fetch-request.model';
+import {Store} from '@ngrx/store';
+import * as fromRoot from '../../../store';
+import {SEARCH} from '../../../store/office/office.actions';
+
+@Component({
+  selector: 'fims-customer-offices-form',
+  templateUrl: './offices.component.html'
+})
+export class CustomerOfficesComponent implements OnInit {
+
+  offices: Observable<Office[]>;
+
+  @Input() preSelection: string;
+
+  @Output() onSelectionChange = new EventEmitter<string[]>();
+
+  constructor(private store: Store<fromRoot.State>) {}
+
+  ngOnInit(): void {
+    this.offices = this.store.select(fromRoot.getOfficeSearchResults)
+      .map(officePage => officePage.offices);
+  }
+
+  search(searchTerm) {
+    const fetchRequest: FetchRequest = {
+      searchTerm
+    };
+
+    this.store.dispatch({ type: SEARCH, payload: fetchRequest });
+  }
+
+  select(selections: string[]): void {
+    this.onSelectionChange.emit(selections);
+  }
+
+}
diff --git a/src/app/customers/store/catalogs/catalog.actions.ts b/src/app/customers/store/catalogs/catalog.actions.ts
new file mode 100644
index 0000000..d6d05b8
--- /dev/null
+++ b/src/app/customers/store/catalogs/catalog.actions.ts
@@ -0,0 +1,152 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 {Action} from '@ngrx/store';
+import {Catalog} from '../../../services/catalog/domain/catalog.model';
+import {RoutePayload} from '../../../common/store/route-payload';
+import {Field} from '../../../services/catalog/domain/field.model';
+
+export const LOAD = type('[Customer Catalog] Load');
+
+export const CREATE = type('[Customer Catalog] Create');
+export const CREATE_SUCCESS = type('[Customer Catalog] Create Success');
+export const CREATE_FAIL = type('[Customer Catalog] Create Fail');
+
+export const DELETE = type('[Customer Catalog] Delete');
+export const DELETE_SUCCESS = type('[Customer Catalog] Delete Success');
+export const DELETE_FAIL = type('[Customer Catalog] Delete Fail');
+
+export const SELECT_FIELD = type('[Customer Catalog] Select Field');
+
+export const UPDATE_FIELD = type('[Customer Catalog] Update Field');
+export const UPDATE_FIELD_SUCCESS = type('[Customer Catalog] Update Field Success');
+export const UPDATE_FIELD_FAIL = type('[Customer Catalog] Update Field Fail');
+
+export const DELETE_FIELD = type('[Customer Catalog] Delete Field');
+export const DELETE_FIELD_SUCCESS = type('[Customer Catalog] Delete Field Success');
+export const DELETE_FIELD_FAIL = type('[Customer Catalog] Delete Field Fail');
+
+export interface CatalogRoutePayload extends RoutePayload {
+  catalog: Catalog;
+}
+
+export interface FieldRoutePayload extends RoutePayload {
+  catalogIdentifier: string;
+  field: Field;
+}
+
+export class LoadAction implements Action {
+  readonly type = LOAD;
+
+  constructor(public payload: Catalog) { }
+}
+
+export class CreateCatalogAction implements Action {
+  readonly type = CREATE;
+
+  constructor(public payload: CatalogRoutePayload) { }
+}
+
+export class CreateCatalogSuccessAction implements Action {
+  readonly type = CREATE_SUCCESS;
+
+  constructor(public payload: CatalogRoutePayload) { }
+}
+
+export class CreateCatalogFailAction implements Action {
+  readonly type = CREATE_FAIL;
+
+  constructor(public payload: Error) { }
+}
+
+export class DeleteCatalogAction implements Action {
+  readonly type = DELETE;
+
+  constructor(public payload: CatalogRoutePayload) { }
+}
+
+export class DeleteCatalogSuccessAction implements Action {
+  readonly type = DELETE_SUCCESS;
+
+  constructor(public payload: CatalogRoutePayload) { }
+}
+
+export class DeleteCatalogFailAction implements Action {
+  readonly type = DELETE_FAIL;
+
+  constructor(public payload: Error) { }
+}
+
+export class SelectFieldAction implements Action {
+  readonly type = SELECT_FIELD;
+
+  constructor(public payload: string) { }
+}
+
+export class UpdateFieldAction implements Action {
+  readonly type = UPDATE_FIELD;
+
+  constructor(public payload: FieldRoutePayload) { }
+}
+
+export class UpdateFieldSuccessAction implements Action {
+  readonly type = UPDATE_FIELD_SUCCESS;
+
+  constructor(public payload: FieldRoutePayload) { }
+}
+
+export class UpdateFieldFailAction implements Action {
+  readonly type = UPDATE_FIELD_FAIL;
+
+  constructor(public payload: Error) { }
+}
+
+export class DeleteFieldAction implements Action {
+  readonly type = DELETE_FIELD;
+
+  constructor(public payload: FieldRoutePayload) { }
+}
+
+export class DeleteFieldSuccessAction implements Action {
+  readonly type = DELETE_FIELD_SUCCESS;
+
+  constructor(public payload: FieldRoutePayload) { }
+}
+
+export class DeleteFieldFailAction implements Action {
+  readonly type = DELETE_FIELD_FAIL;
+
+  constructor(public payload: Error) { }
+}
+
+export type Actions
+  = LoadAction
+  | CreateCatalogAction
+  | CreateCatalogSuccessAction
+  | CreateCatalogFailAction
+  | DeleteCatalogAction
+  | DeleteCatalogSuccessAction
+  | DeleteCatalogFailAction
+  | SelectFieldAction
+  | UpdateFieldAction
+  | UpdateFieldSuccessAction
+  | UpdateFieldFailAction
+  | DeleteFieldAction
+  | DeleteFieldSuccessAction
+  | DeleteFieldFailAction;
diff --git a/src/app/customers/store/catalogs/catalog.reducer.ts b/src/app/customers/store/catalogs/catalog.reducer.ts
new file mode 100644
index 0000000..e1ddd20
--- /dev/null
+++ b/src/app/customers/store/catalogs/catalog.reducer.ts
@@ -0,0 +1,111 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 catalogActions from './catalog.actions';
+import {Catalog} from '../../../services/catalog/domain/catalog.model';
+import {Field} from '../../../services/catalog/domain/field.model';
+import {createSelector} from 'reselect';
+
+export interface State {
+  catalog: Catalog;
+  loadedAt: number;
+  selectedFieldIdentifier: string;
+}
+
+const initialState: State = {
+  catalog: null,
+  loadedAt: null,
+  selectedFieldIdentifier: null
+};
+
+export function reducer(state: State = initialState, action: catalogActions.Actions): State {
+
+  switch (action.type) {
+
+    case catalogActions.LOAD: {
+      const catalog: Catalog = action.payload;
+
+      return Object.assign({}, state, {
+        catalog,
+        loadedAt: Date.now()
+      });
+    }
+
+    case catalogActions.CREATE_SUCCESS: {
+      const catalog: Catalog = action.payload.catalog;
+
+      return Object.assign({}, state, {
+        catalog,
+        loadedAt: state.loadedAt
+      });
+    }
+
+    case catalogActions.DELETE_SUCCESS: {
+      return initialState;
+    }
+
+    case catalogActions.SELECT_FIELD: {
+      return Object.assign({}, state, {
+        selectedFieldIdentifier: action.payload
+      });
+    }
+
+    case catalogActions.UPDATE_FIELD_SUCCESS: {
+      const payload = action.payload;
+      const updatedField: Field = payload.field;
+
+      const catalog = Object.assign({}, state.catalog, {
+        fields: state.catalog.fields.map(field =>
+          field.identifier === updatedField.identifier ? updatedField : field
+        )
+      });
+
+      return Object.assign({}, state, {
+        catalog,
+        loadedAt: state.loadedAt
+      });
+    }
+
+    case catalogActions.DELETE_FIELD_SUCCESS: {
+      const payload = action.payload;
+      const deletedField: Field = payload.field;
+
+      const catalog = Object.assign({}, state.catalog, {
+        fields: state.catalog.fields.filter(field =>
+          field.identifier !== deletedField.identifier
+        )
+      });
+
+      return Object.assign({}, state, {
+        catalog,
+        loadedAt: state.loadedAt
+      });
+    }
+
+    default: {
+      return state;
+    }
+  }
+}
+
+export const getCustomerCatalog = (state: State) => state.catalog;
+export const getCustomerCatalogLoadedAt = (state: State) => state.loadedAt;
+export const getSelectedFieldId = (state: State) => state.selectedFieldIdentifier;
+export const getSelectedField = createSelector(getCustomerCatalog, getSelectedFieldId, (catalog, selectedId) => {
+  return catalog.fields.find(field => field.identifier === selectedId);
+});
diff --git a/src/app/customers/store/catalogs/effects/notification.effects.ts b/src/app/customers/store/catalogs/effects/notification.effects.ts
new file mode 100644
index 0000000..2483668
--- /dev/null
+++ b/src/app/customers/store/catalogs/effects/notification.effects.ts
@@ -0,0 +1,91 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 {NotificationService, NotificationType} from '../../../../services/notification/notification.service';
+import {Action} from '@ngrx/store';
+import * as catalogActions from '../catalog.actions';
+import {DeleteCatalogFailAction, DeleteFieldFailAction, UpdateFieldFailAction} from '../catalog.actions';
+
+@Injectable()
+export class CatalogNotificationEffects {
+
+  @Effect({ dispatch: false })
+  createCatalogSuccess$: Observable<Action> = this.actions$
+    .ofType(catalogActions.CREATE_SUCCESS)
+    .do(() => this.notificationService.send({
+      type: NotificationType.MESSAGE,
+      message: 'Catalog is going to be saved'
+    }));
+
+  @Effect({ dispatch: false })
+  deleteCatalogSuccess$: Observable<Action> = this.actions$
+    .ofType(catalogActions.DELETE_SUCCESS)
+    .do(() => this.notificationService.send({
+      type: NotificationType.MESSAGE,
+      message: 'Catalog is going to be deleted'
+    }));
+
+  @Effect({ dispatch: false })
+  deleteCatalogFail$: Observable<Action> = this.actions$
+    .ofType(catalogActions.DELETE_FAIL)
+    .do((action: DeleteCatalogFailAction) => this.notificationService.send({
+      type: NotificationType.ALERT,
+      title: 'Catalog can\'t be deleted',
+      message: action.payload.message
+    }));
+
+  @Effect({ dispatch: false })
+  updateFieldSuccess$: Observable<Action> = this.actions$
+    .ofType(catalogActions.UPDATE_FIELD_SUCCESS)
+    .do(() => this.notificationService.send({
+      type: NotificationType.MESSAGE,
+      message: 'Field is going to be updated'
+    }));
+
+  @Effect({ dispatch: false })
+  updateFieldFail$: Observable<Action> = this.actions$
+    .ofType(catalogActions.UPDATE_FIELD_FAIL)
+    .do((action: UpdateFieldFailAction) => this.notificationService.send({
+      type: NotificationType.ALERT,
+      title: 'Field can\'t be updated',
+      message: action.payload.message
+    }));
+
+  @Effect({ dispatch: false })
+  deleteFieldSuccess$: Observable<Action> = this.actions$
+    .ofType(catalogActions.DELETE_FIELD_SUCCESS)
+    .do(() => this.notificationService.send({
+      type: NotificationType.MESSAGE,
+      message: 'Field is going to be deleted'
+    }));
+
+  @Effect({ dispatch: false })
+  deleteFieldFail$: Observable<Action> = this.actions$
+    .ofType(catalogActions.DELETE_FIELD_FAIL)
+    .do((action: DeleteFieldFailAction) => this.notificationService.send({
+      type: NotificationType.ALERT,
+      title: 'Field can\'t be deleted',
+      message: action.payload.message
+    }));
+
+  constructor(private actions$: Actions, private notificationService: NotificationService) {}
+
+}
diff --git a/src/app/customers/store/catalogs/effects/route.effects.ts b/src/app/customers/store/catalogs/effects/route.effects.ts
new file mode 100644
index 0000000..efcf223
--- /dev/null
+++ b/src/app/customers/store/catalogs/effects/route.effects.ts
@@ -0,0 +1,55 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 * as catalogActions from '../catalog.actions';
+import {Action} from '@ngrx/store';
+import {Router} from '@angular/router';
+
+@Injectable()
+export class CatalogRouteEffects {
+
+  @Effect({ dispatch: false })
+  createCatalogSuccess: Observable<Action> = this.actions$
+    .ofType(catalogActions.CREATE_SUCCESS)
+    .map(action => action.payload)
+    .do(payload => this.router.navigate(['../'], { relativeTo: payload.activatedRoute }));
+
+  @Effect({ dispatch: false })
+  deleteCatalogSuccess: Observable<Action> = this.actions$
+    .ofType(catalogActions.DELETE_SUCCESS)
+    .map(action => action.payload)
+    .do(payload => this.router.navigate(['../../../'], { relativeTo: payload.activatedRoute }));
+
+  @Effect({ dispatch: false })
+  updateFieldSuccess: Observable<Action> = this.actions$
+    .ofType(catalogActions.UPDATE_FIELD_SUCCESS)
+    .map(action => action.payload)
+    .do(payload => this.router.navigate(['../'], { relativeTo: payload.activatedRoute }));
+
+  @Effect({ dispatch: false })
+  deleteFieldSuccess: Observable<Action> = this.actions$
+    .ofType(catalogActions.DELETE_FIELD_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/customers/store/catalogs/effects/service.effects.ts b/src/app/customers/store/catalogs/effects/service.effects.ts
new file mode 100644
index 0000000..c834b27
--- /dev/null
+++ b/src/app/customers/store/catalogs/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 catalogActions from '../catalog.actions';
+import {CatalogService} from '../../../../services/catalog/catalog.service';
+
+@Injectable()
+export class CatalogApiEffects {
+
+  @Effect()
+  createCatalog$: Observable<Action> = this.actions$
+    .ofType(catalogActions.CREATE)
+    .map((action: catalogActions.CreateCatalogAction) => action.payload)
+    .mergeMap(payload =>
+      this.catalogService.createCatalog(payload.catalog)
+        .map(() => new catalogActions.CreateCatalogSuccessAction(payload))
+        .catch((error) => of(new catalogActions.CreateCatalogFailAction(error)))
+    );
+
+  @Effect()
+  deleteCatalog$: Observable<Action> = this.actions$
+    .ofType(catalogActions.DELETE)
+    .map((action: catalogActions.DeleteCatalogAction) => action.payload)
+    .mergeMap(payload =>
+      this.catalogService.deleteCatalog(payload.catalog)
+        .map(() => new catalogActions.DeleteCatalogSuccessAction(payload))
+        .catch((error) => of(new catalogActions.DeleteCatalogFailAction(error)))
+    );
+
+  @Effect()
+  updateField$: Observable<Action> = this.actions$
+    .ofType(catalogActions.UPDATE_FIELD)
+    .map((action: catalogActions.UpdateFieldAction) => action.payload)
+    .mergeMap(payload =>
+      this.catalogService.updateField(payload.catalogIdentifier, payload.field)
+        .map(() => new catalogActions.UpdateFieldSuccessAction(payload))
+        .catch((error) => of(new catalogActions.UpdateFieldFailAction(error)))
+    );
+
+  @Effect()
+  deleteField$: Observable<Action> = this.actions$
+    .ofType(catalogActions.DELETE_FIELD)
+    .map((action: catalogActions.DeleteFieldAction) => action.payload)
+    .mergeMap(payload =>
+      this.catalogService.deleteField(payload.catalogIdentifier, payload.field)
+        .map(() => new catalogActions.DeleteFieldSuccessAction(payload))
+        .catch((error) => of(new catalogActions.DeleteFieldFailAction(error)))
+    );
+
+  constructor(private actions$: Actions, private catalogService: CatalogService) { }
+
+}
diff --git a/src/app/customers/store/commands/commands.actions.ts b/src/app/customers/store/commands/commands.actions.ts
new file mode 100644
index 0000000..d185afa
--- /dev/null
+++ b/src/app/customers/store/commands/commands.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 {type} from '../../../store/util';
+import {Action} from '@ngrx/store';
+import {Command} from '../../../services/customer/domain/command.model';
+
+export const LOAD_ALL = type('[Customer Command] Load All');
+export const LOAD_ALL_COMPLETE = type('[Customer Command] Load All Complete');
+
+export class LoadAllAction implements Action {
+  readonly type = LOAD_ALL;
+
+  constructor(public payload: string) { }
+}
+
+export class LoadAllCompleteAction implements Action {
+  readonly type = LOAD_ALL_COMPLETE;
+
+  constructor(public payload: Command[]) { }
+}
+
+export type Actions
+  = LoadAllAction
+  | LoadAllCompleteAction;
diff --git a/src/app/customers/store/commands/commands.reducer.ts b/src/app/customers/store/commands/commands.reducer.ts
new file mode 100644
index 0000000..01ca674
--- /dev/null
+++ b/src/app/customers/store/commands/commands.reducer.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 * as command from './commands.actions';
+import {Command} from '../../../services/customer/domain/command.model';
+
+export interface State {
+  commands: Command[];
+}
+
+export const initialState: State = {
+  commands: []
+};
+
+export function reducer(state = initialState, action: command.Actions): State {
+
+  switch (action.type) {
+
+    case command.LOAD_ALL: {
+      return initialState;
+    }
+
+    case command.LOAD_ALL_COMPLETE: {
+      const commands = action.payload;
+
+      return {
+        commands: commands
+      };
+    }
+
+    default: {
+      return state;
+    }
+  }
+}
+
+export const getCommands = (state: State) => state.commands;
diff --git a/src/app/customers/store/commands/effects/service.effects.ts b/src/app/customers/store/commands/effects/service.effects.ts
new file mode 100644
index 0000000..c9017ad
--- /dev/null
+++ b/src/app/customers/store/commands/effects/service.effects.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 {Actions, Effect, toPayload} from '@ngrx/effects';
+import {Observable} from 'rxjs/Observable';
+import {Action} from '@ngrx/store';
+import {of} from 'rxjs/observable/of';
+import * as commandActions from '../commands.actions';
+import {CustomerService} from '../../../../services/customer/customer.service';
+
+@Injectable()
+export class CustomerCommandApiEffects {
+
+  @Effect()
+  loadCommands$: Observable<Action> = this.actions$
+    .ofType(commandActions.LOAD_ALL)
+    .map(toPayload)
+    .mergeMap(customerId =>
+      this.customerService.listCustomerCommand(customerId)
+        .map(commands => new commandActions.LoadAllCompleteAction(commands))
+        .catch((error) => of(new commandActions.LoadAllCompleteAction([])))
+    );
+
+  constructor(private actions$: Actions, private customerService: CustomerService) { }
+
+}
diff --git a/src/app/customers/store/customer.actions.ts b/src/app/customers/store/customer.actions.ts
new file mode 100644
index 0000000..87cc4c9
--- /dev/null
+++ b/src/app/customers/store/customer.actions.ts
@@ -0,0 +1,111 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 {Error} from '../../services/domain/error.model';
+import {Customer} from '../../services/customer/domain/customer.model';
+import {RoutePayload} from '../../common/store/route-payload';
+import {
+  CreateResourceSuccessPayload,
+  LoadResourcePayload,
+  SelectResourcePayload,
+  UpdateResourceSuccessPayload
+} from '../../common/store/resource.reducer';
+
+export const LOAD = type('[Customer] Load');
+export const SELECT = type('[Customer] Select');
+
+export const CREATE = type('[Customer] Create');
+export const CREATE_SUCCESS = type('[Customer] Create Success');
+export const CREATE_FAIL = type('[Customer] Create Fail');
+
+export const UPDATE = type('[Customer] Update');
+export const UPDATE_SUCCESS = type('[Customer] Update Success');
+export const UPDATE_FAIL = type('[Customer] Update Fail');
+
+export const RESET_FORM = type('[Customer] Reset Form');
+
+export interface CustomerRoutePayload extends RoutePayload {
+  customer: Customer;
+}
+
+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 CreateCustomerAction implements Action {
+  readonly type = CREATE;
+
+  constructor(public payload: CustomerRoutePayload) { }
+}
+
+export class CreateCustomerSuccessAction implements Action {
+  readonly type = CREATE_SUCCESS;
+
+  constructor(public payload: CreateResourceSuccessPayload) { }
+}
+
+export class CreateCustomerFailAction implements Action {
+  readonly type = CREATE_FAIL;
+
+  constructor(public payload: Error) { }
+}
+
+export class UpdateCustomerAction implements Action {
+  readonly type = UPDATE;
+
+  constructor(public payload: CustomerRoutePayload) { }
+}
+
+export class UpdateCustomerSuccessAction implements Action {
+  readonly type = UPDATE_SUCCESS;
+
+  constructor(public payload: UpdateResourceSuccessPayload) { }
+}
+
+export class UpdateCustomerFailAction implements Action {
+  readonly type = UPDATE_FAIL;
+
+  constructor(public payload: Error) { }
+}
+
+export class ResetCustomerFormAction implements Action {
+  readonly type = RESET_FORM;
+
+  constructor() {}
+}
+
+export type Actions
+  = LoadAction
+  | SelectAction
+  | CreateCustomerAction
+  | CreateCustomerSuccessAction
+  | CreateCustomerFailAction
+  | UpdateCustomerAction
+  | UpdateCustomerSuccessAction
+  | UpdateCustomerFailAction
+  | ResetCustomerFormAction;
diff --git a/src/app/customers/store/customerTasks/customer-task.actions.ts b/src/app/customers/store/customerTasks/customer-task.actions.ts
new file mode 100644
index 0000000..b621609
--- /dev/null
+++ b/src/app/customers/store/customerTasks/customer-task.actions.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 {Action} from '@ngrx/store';
+import {type} from '../../../store/util';
+import {Error} from '../../../services/domain/error.model';
+import {Command} from '../../../services/customer/domain/command.model';
+import {RoutePayload} from '../../../common/store/route-payload';
+import {ProcessStep} from '../../../services/customer/domain/process-step.model';
+
+export const LOAD_ALL = type('[Customer Task] Load All Process Steps');
+export const LOAD_ALL_COMPLETE = type('[Customer Task] Load All Process Steps Complete');
+
+export const EXECUTE_TASK = type('[Customer Task] Execute');
+export const EXECUTE_TASK_SUCCESS = type('[Customer Task] Success');
+export const EXECUTE_TASK_FAIL = type('[Customer Task] Fail');
+
+export const EXECUTE_COMMAND = type('[Customer Command] Execute');
+export const EXECUTE_COMMAND_SUCCESS = type('[Customer Command] Success');
+export const EXECUTE_COMMAND_FAIL = type('[Customer Command] Fail');
+
+export interface ExecuteTaskPayload extends RoutePayload {
+  customerId: string;
+  taskId: string;
+}
+
+export interface ExecuteCommandPayload {
+  customerId: string;
+  command: Command;
+}
+
+export class LoadAllAction implements Action {
+  readonly type = LOAD_ALL;
+
+  constructor(public payload: string) { }
+}
+
+export class LoadAllCompleteAction implements Action {
+  readonly type = LOAD_ALL_COMPLETE;
+
+  constructor(public payload: ProcessStep[]) { }
+}
+
+export class ExecuteTaskAction implements Action {
+  readonly type = EXECUTE_TASK;
+
+  constructor(public payload: ExecuteTaskPayload) { }
+}
+
+export class ExecuteTaskSuccessAction implements Action {
+  readonly type = EXECUTE_TASK_SUCCESS;
+
+  constructor(public payload: ExecuteTaskPayload) { }
+}
+
+export class ExecuteTaskFailAction implements Action {
+  readonly type = EXECUTE_TASK_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: Error) { }
+}
+
+export type Actions
+  = LoadAllAction
+  | LoadAllCompleteAction
+  | ExecuteTaskAction
+  | ExecuteTaskSuccessAction
+  | ExecuteTaskFailAction
+  | ExecuteCommandAction
+  | ExecuteCommandSuccessAction
+  | ExecuteCommandFailAction;
diff --git a/src/app/customers/store/customerTasks/customer-tasks.reducer.ts b/src/app/customers/store/customerTasks/customer-tasks.reducer.ts
new file mode 100644
index 0000000..ccddda5
--- /dev/null
+++ b/src/app/customers/store/customerTasks/customer-tasks.reducer.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 * as task from './customer-task.actions';
+import {ProcessStep} from '../../../services/customer/domain/process-step.model';
+
+export interface State {
+  processSteps: ProcessStep[];
+}
+
+const initialState: State = {
+  processSteps: []
+};
+
+export function reducer(state = initialState, action: task.Actions): State {
+
+  switch (action.type) {
+
+    case task.LOAD_ALL: {
+      return initialState;
+    }
+
+    case task.LOAD_ALL_COMPLETE: {
+      const processSteps = action.payload;
+
+      return {
+        processSteps
+      };
+    }
+
+    default: {
+      return state;
+    }
+  }
+}
+
+export const getProcessSteps = (state: State) => state.processSteps;
diff --git a/src/app/customers/store/customerTasks/domain/status-command.model.ts b/src/app/customers/store/customerTasks/domain/status-command.model.ts
new file mode 100644
index 0000000..a9b7035
--- /dev/null
+++ b/src/app/customers/store/customerTasks/domain/status-command.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 {CustomerState} from '../../../../services/customer/domain/customer-state.model';
+import {TaskDefinition} from '../../../../services/customer/domain/task-definition.model';
+import {CommandAction} from '../../../../services/customer/domain/command.model';
+
+export interface StatusCommand {
+  action: CommandAction;
+  comment?: string;
+  tasks: TaskDefinition[];
+  preStates: CustomerState[];
+}
diff --git a/src/app/customers/store/customerTasks/effects/notification.effects.ts b/src/app/customers/store/customerTasks/effects/notification.effects.ts
new file mode 100644
index 0000000..9a78cf4
--- /dev/null
+++ b/src/app/customers/store/customerTasks/effects/notification.effects.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 {Injectable} from '@angular/core';
+import {Actions, Effect} from '@ngrx/effects';
+import {Observable} from 'rxjs/Observable';
+import {Action} from '@ngrx/store';
+import * as taskActions from '../customer-task.actions';
+import {NotificationService, NotificationType} from '../../../../services/notification/notification.service';
+
+@Injectable()
+export class CustomerTasksNotificationEffects {
+
+  @Effect({ dispatch: false })
+  executeCustomerTaskSuccess$: Observable<Action> = this.actions$
+    .ofType(taskActions.EXECUTE_TASK_SUCCESS)
+    .do(() => this.notificationService.send({
+      type: NotificationType.MESSAGE,
+      message: 'Task is going to be executed'
+    }));
+
+  @Effect({ dispatch: false })
+  executeCustomerTaskFail$: Observable<Action> = this.actions$
+    .ofType(taskActions.EXECUTE_TASK_FAIL)
+    .do(() => this.notificationService.send({
+      type: NotificationType.ALERT,
+      message: 'Sorry, there was a problem executing your task'
+    }));
+
+  @Effect({ dispatch: false })
+  executeCustomerCommandSuccess$: Observable<Action> = this.actions$
+    .ofType(taskActions.EXECUTE_COMMAND_SUCCESS)
+    .do(() => this.notificationService.send({
+      type: NotificationType.MESSAGE,
+      message: 'Command is going to be executed'
+    }));
+
+  @Effect({ dispatch: false })
+  executeCustomerCommandFail$: Observable<Action> = this.actions$
+    .ofType(taskActions.EXECUTE_COMMAND_FAIL)
+    .do(() => this.notificationService.send({
+      type: NotificationType.ALERT,
+      message: 'Sorry, there was a problem executing your command'
+    }));
+
+  constructor(private actions$: Actions, private notificationService: NotificationService) {}
+
+}
+
diff --git a/src/app/customers/store/customerTasks/effects/route.effects.ts b/src/app/customers/store/customerTasks/effects/route.effects.ts
new file mode 100644
index 0000000..4e6e2aa
--- /dev/null
+++ b/src/app/customers/store/customerTasks/effects/route.effects.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 {Injectable} from '@angular/core';
+import {Actions, Effect} from '@ngrx/effects';
+import {Observable} from 'rxjs/Observable';
+import {Action} from '@ngrx/store';
+import * as taskActions from '../customer-task.actions';
+import {Router} from '@angular/router';
+
+@Injectable()
+export class CustomerTasksRouteEffects {
+
+  @Effect({ dispatch: false })
+  executeCustomerTaskSuccess$: Observable<Action> = this.actions$
+    .ofType(taskActions.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/customers/store/customerTasks/effects/service.effects.ts b/src/app/customers/store/customerTasks/effects/service.effects.ts
new file mode 100644
index 0000000..ff2551c
--- /dev/null
+++ b/src/app/customers/store/customerTasks/effects/service.effects.ts
@@ -0,0 +1,66 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 taskActions from '../customer-task.actions';
+import {CustomerService} from '../../../../services/customer/customer.service';
+
+@Injectable()
+export class CustomerTasksApiEffects {
+
+  @Effect()
+  loadAll$: Observable<Action> = this.actions$
+    .ofType(taskActions.LOAD_ALL)
+    .debounceTime(300)
+    .map((action: taskActions.LoadAllAction) => action.payload)
+    .switchMap(id => {
+      const nextSearch$ = this.actions$.ofType(taskActions.LOAD_ALL).skip(1);
+
+      return this.customerService.fetchProcessSteps(id)
+        .takeUntil(nextSearch$)
+        .map(processSteps => new taskActions.LoadAllCompleteAction(processSteps))
+        .catch(() => of(new taskActions.LoadAllCompleteAction([])));
+    });
+
+  @Effect()
+  executeTask: Observable<Action> = this.actions$
+    .ofType(taskActions.EXECUTE_TASK)
+    .map((action: taskActions.ExecuteTaskAction) => action.payload)
+    .mergeMap(payload =>
+      this.customerService.markTaskAsExecuted(payload.customerId, payload.taskId)
+        .map(() => new taskActions.ExecuteTaskSuccessAction(payload))
+        .catch((error) => of(new taskActions.ExecuteTaskFailAction(error)))
+    );
+
+  @Effect()
+  executeCommand: Observable<Action> = this.actions$
+    .ofType(taskActions.EXECUTE_COMMAND)
+    .map((action: taskActions.ExecuteCommandAction) => action.payload)
+    .mergeMap(payload =>
+      this.customerService.executeCustomerCommand(payload.customerId, payload.command)
+        .map(() => new taskActions.ExecuteCommandSuccessAction(payload))
+        .catch((error) => of(new taskActions.ExecuteCommandFailAction(error)))
+    );
+
+  constructor(private actions$: Actions, private customerService: CustomerService) { }
+
+}
diff --git a/src/app/customers/store/customers.reducer.ts b/src/app/customers/store/customers.reducer.ts
new file mode 100644
index 0000000..6ab0d6a
--- /dev/null
+++ b/src/app/customers/store/customers.reducer.ts
@@ -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.
+ */
+import * as customer from './customer.actions';
+import * as customerTasks from './customerTasks/customer-task.actions';
+import {Command} from '../../services/customer/domain/command.model';
+import {CustomerState} from '../../services/customer/domain/customer-state.model';
+import {ResourceState} from '../../common/store/resource.reducer';
+
+export const initialState: ResourceState = {
+  ids: [],
+  entities: {},
+  loadedAt: {},
+  selectedId: null,
+};
+
+export function reducer(state = initialState, action: customer.Actions | customerTasks.Actions): ResourceState {
+
+  switch (action.type) {
+
+    case customerTasks.EXECUTE_COMMAND_SUCCESS: {
+      const payload = action.payload;
+
+      const customerId = payload.customerId;
+      const command: Command = payload.command;
+
+      const customer = state.entities[customerId];
+
+      let customerState: CustomerState = null;
+
+      if (command.action === 'ACTIVATE') {
+        customerState = 'ACTIVE';
+      }else if (command.action === 'LOCK') {
+        customerState = 'LOCKED';
+      }else if (command.action === 'UNLOCK') {
+        customerState = 'ACTIVE';
+      }else if (command.action === 'CLOSE') {
+        customerState = 'CLOSED';
+      }else if (command.action === 'REOPEN') {
+        customerState = 'ACTIVE';
+      }
+
+      customer.currentState = customerState;
+
+      return {
+        ids: [ ...state.ids ],
+        entities: Object.assign({}, state.entities, {
+          [customer.identifier]: customer
+        }),
+        loadedAt: state.loadedAt,
+        selectedId: state.selectedId
+      };
+    }
+
+    default: {
+      return state;
+    }
+  }
+}
diff --git a/src/app/customers/store/effects/notification.effects.ts b/src/app/customers/store/effects/notification.effects.ts
new file mode 100644
index 0000000..f545265
--- /dev/null
+++ b/src/app/customers/store/effects/notification.effects.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 {Injectable} from '@angular/core';
+import {Actions, Effect} from '@ngrx/effects';
+import {Observable} from 'rxjs/Observable';
+import {Action} from '@ngrx/store';
+import * as customerActions from '../customer.actions';
+import {NotificationService, NotificationType} from '../../../services/notification/notification.service';
+
+@Injectable()
+export class CustomerNotificationEffects {
+
+  @Effect({ dispatch: false })
+  createCustomerSuccess$: Observable<Action> = this.actions$
+    .ofType(customerActions.CREATE_SUCCESS, customerActions.UPDATE_SUCCESS)
+    .do(() => this.notificationService.send({
+      type: NotificationType.MESSAGE,
+      message: 'Member is going to be saved'
+    }));
+
+  constructor(private actions$: Actions, private notificationService: NotificationService) {}
+
+}
+
diff --git a/src/app/customers/store/effects/route.effects.ts b/src/app/customers/store/effects/route.effects.ts
new file mode 100644
index 0000000..2d4e816
--- /dev/null
+++ b/src/app/customers/store/effects/route.effects.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 {Injectable} from '@angular/core';
+import {Actions, Effect} from '@ngrx/effects';
+import {Observable} from 'rxjs/Observable';
+import {Action} from '@ngrx/store';
+import * as customerActions from '../customer.actions';
+import {Router} from '@angular/router';
+
+@Injectable()
+export class CustomerRouteEffects {
+
+  @Effect({ dispatch: false })
+  createCustomerSuccess$: Observable<Action> = this.actions$
+    .ofType(customerActions.CREATE_SUCCESS, customerActions.UPDATE_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/customers/store/effects/service.effects.ts b/src/app/customers/store/effects/service.effects.ts
new file mode 100644
index 0000000..4f8be9f
--- /dev/null
+++ b/src/app/customers/store/effects/service.effects.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 {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 '../../../services/customer/customer.service';
+
+@Injectable()
+export class CustomerApiEffects {
+
+  @Effect()
+  createCustomer$: Observable<Action> = this.actions$
+    .ofType(customerActions.CREATE)
+    .map((action: customerActions.CreateCustomerAction) => action.payload)
+    .mergeMap(payload =>
+      this.customerService.createCustomer(payload.customer)
+        .map(() => new customerActions.CreateCustomerSuccessAction({
+          resource: payload.customer,
+          activatedRoute: payload.activatedRoute
+        }))
+        .catch((error) => of(new customerActions.CreateCustomerFailAction(error)))
+    );
+
+  @Effect()
+  updateCustomer$: Observable<Action> = this.actions$
+    .ofType(customerActions.UPDATE)
+    .map((action: customerActions.UpdateCustomerAction) => action.payload)
+    .mergeMap(payload =>
+      this.customerService.updateCustomer(payload.customer)
+        .map(() => new customerActions.UpdateCustomerSuccessAction({
+          resource: payload.customer,
+          activatedRoute: payload.activatedRoute
+        }))
+        .catch((error) => of(new customerActions.UpdateCustomerFailAction(error)))
+    );
+
+  constructor(private actions$: Actions, private customerService: CustomerService) { }
+
+}
diff --git a/src/app/customers/store/identityCards/effects/notification.effects.ts b/src/app/customers/store/identityCards/effects/notification.effects.ts
new file mode 100644
index 0000000..b7e6d96
--- /dev/null
+++ b/src/app/customers/store/identityCards/effects/notification.effects.ts
@@ -0,0 +1,47 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 {NotificationService, NotificationType} from '../../../../services/notification/notification.service';
+import {Action} from '@ngrx/store';
+import {Observable} from 'rxjs/Observable';
+import {Actions, Effect} from '@ngrx/effects';
+import {Injectable} from '@angular/core';
+import * as identificationCardActions from '../identity-cards.actions';
+
+@Injectable()
+export class CustomerIdentificationCardNotificationEffects {
+
+  @Effect({ dispatch: false })
+  createIdentificationCardSuccess$: Observable<Action> = this.actions$
+    .ofType(identificationCardActions.CREATE_SUCCESS, identificationCardActions.UPDATE_SUCCESS)
+    .do(() => this.notificationService.send({
+      type: NotificationType.MESSAGE,
+      message: 'Identification card is going to be saved'
+    }));
+
+  @Effect({ dispatch: false })
+  deleteIdentificationCardSuccess$: Observable<Action> = this.actions$
+    .ofType(identificationCardActions.DELETE_SUCCESS)
+    .do(() => this.notificationService.send({
+      type: NotificationType.MESSAGE,
+      message: 'Identification card is going to be deleted'
+    }));
+
+  constructor(private actions$: Actions, private notificationService: NotificationService) {}
+
+}
diff --git a/src/app/customers/store/identityCards/effects/route.effects.ts b/src/app/customers/store/identityCards/effects/route.effects.ts
new file mode 100644
index 0000000..c5ada1d
--- /dev/null
+++ b/src/app/customers/store/identityCards/effects/route.effects.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 {Observable} from 'rxjs/Observable';
+import {Actions, Effect} from '@ngrx/effects';
+import {Router} from '@angular/router';
+import {Injectable} from '@angular/core';
+import * as identificationCardActions from '../identity-cards.actions';
+
+@Injectable()
+export class CustomerIdentificationCardRouteEffects {
+  @Effect({ dispatch: false })
+  createIdentificationCardSuccess$: Observable<Action> = this.actions$
+    .ofType(identificationCardActions.CREATE_SUCCESS, identificationCardActions.UPDATE_SUCCESS)
+    .map(action => action.payload)
+    .do(payload => this.router.navigate(['../'], { relativeTo: payload.activatedRoute }));
+
+  @Effect({ dispatch: false })
+  deleteIdentificationCardSuccess$: Observable<Action> = this.actions$
+    .ofType(identificationCardActions.DELETE_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/customers/store/identityCards/effects/service.effects.ts b/src/app/customers/store/identityCards/effects/service.effects.ts
new file mode 100644
index 0000000..60fa27c
--- /dev/null
+++ b/src/app/customers/store/identityCards/effects/service.effects.ts
@@ -0,0 +1,85 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 {of} from 'rxjs/observable/of';
+import {Action} from '@ngrx/store';
+import {Observable} from 'rxjs/Observable';
+import {Actions, Effect} from '@ngrx/effects';
+import {CustomerService} from '../../../../services/customer/customer.service';
+import {Injectable} from '@angular/core';
+import * as identificationCards from '../identity-cards.actions';
+
+@Injectable()
+export class CustomerIdentificationCardApiEffects {
+
+  @Effect()
+  loadAll$: Observable<Action> = this.actions$
+    .ofType(identificationCards.LOAD_ALL)
+    .debounceTime(300)
+    .map((action: identificationCards.LoadAllAction) => action.payload)
+    .switchMap(id => {
+      const nextSearch$ = this.actions$.ofType(identificationCards.LOAD_ALL).skip(1);
+
+      return this.customerService.fetchIdentificationCards(id)
+        .takeUntil(nextSearch$)
+        .map(identifications => new identificationCards.LoadAllCompleteAction(identifications))
+        .catch(() => of(new identificationCards.LoadAllCompleteAction([])));
+    });
+
+  @Effect()
+  createIdentificationCard$: Observable<Action> = this.actions$
+    .ofType(identificationCards.CREATE)
+    .map((action: identificationCards.CreateIdentityCardAction) => action.payload)
+    .mergeMap(payload =>
+      this.customerService.createIdentificationCard(payload.customerId, payload.identificationCard)
+        .map(() => new identificationCards.CreateIdentityCardSuccessAction({
+          resource: payload.identificationCard,
+          activatedRoute: payload.activatedRoute
+        }))
+        .catch((error) => of(new identificationCards.CreateIdentityCardFailAction(error)))
+    );
+
+  @Effect()
+  updateIdentificationCard$: Observable<Action> = this.actions$
+    .ofType(identificationCards.UPDATE)
+    .map((action: identificationCards.UpdateIdentityCardAction) => action.payload)
+    .mergeMap(payload =>
+      this.customerService.updateIdentificationCard(payload.customerId, payload.identificationCard)
+        .map(() => new identificationCards.UpdateIdentityCardSuccessAction({
+          resource: payload.identificationCard,
+          activatedRoute: payload.activatedRoute
+        }))
+        .catch((error) => of(new identificationCards.UpdateIdentityCardFailAction(error)))
+    );
+
+  @Effect()
+  deleteIdentificationCard$: Observable<Action> = this.actions$
+    .ofType(identificationCards.DELETE)
+    .map((action: identificationCards.DeleteIdentityCardAction) => action.payload)
+    .mergeMap(payload =>
+      this.customerService.deleteIdentificationCard(payload.customerId, payload.identificationCard.number)
+        .map(() => new identificationCards.DeleteIdentityCardSuccessAction({
+          resource: payload.identificationCard,
+          activatedRoute: payload.activatedRoute
+        }))
+        .catch((error) => of(new identificationCards.DeleteIdentityCardFailAction(error)))
+    );
+
+  constructor(private actions$: Actions, private customerService: CustomerService) {}
+
+}
diff --git a/src/app/customers/store/identityCards/identity-cards.actions.ts b/src/app/customers/store/identityCards/identity-cards.actions.ts
new file mode 100644
index 0000000..91f84db
--- /dev/null
+++ b/src/app/customers/store/identityCards/identity-cards.actions.ts
@@ -0,0 +1,155 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 {RoutePayload} from '../../../common/store/route-payload';
+import {IdentificationCard} from '../../../services/customer/domain/identification-card.model';
+import {Action} from '@ngrx/store';
+import {
+  CreateResourceSuccessPayload,
+  DeleteResourceSuccessPayload,
+  LoadResourcePayload,
+  SelectResourcePayload,
+  UpdateResourceSuccessPayload
+} from '../../../common/store/resource.reducer';
+import {Error} from '../../../services/domain/error.model';
+
+export const LOAD_ALL = type('[Customer Identity Card] Load All');
+export const LOAD_ALL_COMPLETE = type('[Customer Identity Card] Load All Complete');
+
+export const LOAD = type('[Customer Identity Card] Load');
+export const SELECT = type('[Customer Identity Card] Select');
+
+export const CREATE = type('[Customer Identity Card] Create');
+export const CREATE_SUCCESS = type('[Customer Identity Card] Create Success');
+export const CREATE_FAIL = type('[Customer Identity Card] Create Fail');
+
+export const UPDATE = type('[Customer Identity Card] Update');
+export const UPDATE_SUCCESS = type('[Customer Identity Card] Update Success');
+export const UPDATE_FAIL = type('[Customer Identity Card] Update Fail');
+
+export const DELETE = type('[Customer Identity Card] Delete');
+export const DELETE_SUCCESS = type('[Customer Identity Card] Delete Success');
+export const DELETE_FAIL = type('[Customer Identity Card] Delete Fail');
+
+export const RESET_FORM = type('[Customer Identity Card] Reset Form');
+
+export interface IdentityCardPayload extends RoutePayload {
+  customerId: string;
+  identificationCard: IdentificationCard;
+}
+
+export class LoadAllAction implements Action {
+  readonly type = LOAD_ALL;
+
+  constructor(public payload: string) { }
+}
+
+export class LoadAllCompleteAction implements Action {
+  readonly type = LOAD_ALL_COMPLETE;
+
+  constructor(public payload: IdentificationCard[]) { }
+}
+
+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 CreateIdentityCardAction implements Action {
+  readonly type = CREATE;
+
+  constructor(public payload: IdentityCardPayload) { }
+}
+
+export class CreateIdentityCardSuccessAction implements Action {
+  readonly type = CREATE_SUCCESS;
+
+  constructor(public payload: CreateResourceSuccessPayload) { }
+}
+
+export class CreateIdentityCardFailAction implements Action {
+  readonly type = CREATE_FAIL;
+
+  constructor(public payload: Error) { }
+}
+
+export class UpdateIdentityCardAction implements Action {
+  readonly type = UPDATE;
+
+  constructor(public payload: IdentityCardPayload) { }
+}
+
+export class UpdateIdentityCardSuccessAction implements Action {
+  readonly type = UPDATE_SUCCESS;
+
+  constructor(public payload: UpdateResourceSuccessPayload) { }
+}
+
+export class UpdateIdentityCardFailAction implements Action {
+  readonly type = UPDATE_FAIL;
+
+  constructor(public payload: Error) { }
+}
+
+export class DeleteIdentityCardAction implements Action {
+  readonly type = DELETE;
+
+  constructor(public payload: IdentityCardPayload) { }
+}
+
+export class DeleteIdentityCardSuccessAction implements Action {
+  readonly type = DELETE_SUCCESS;
+
+  constructor(public payload: DeleteResourceSuccessPayload) { }
+}
+
+export class DeleteIdentityCardFailAction implements Action {
+  readonly type = DELETE_FAIL;
+
+  constructor(public payload: Error) { }
+}
+
+export class ResetIdentityCardForm implements Action {
+  readonly type = RESET_FORM;
+
+  constructor() {}
+}
+
+export type Actions
+  = LoadAllAction
+  | LoadAllCompleteAction
+  | LoadAction
+  | SelectAction
+  | CreateIdentityCardAction
+  | CreateIdentityCardSuccessAction
+  | CreateIdentityCardFailAction
+  | UpdateIdentityCardAction
+  | UpdateIdentityCardSuccessAction
+  | UpdateIdentityCardFailAction
+  | DeleteIdentityCardAction
+  | DeleteIdentityCardSuccessAction
+  | DeleteIdentityCardFailAction
+  | ResetIdentityCardForm;
diff --git a/src/app/customers/store/identityCards/identity-cards.reducer.ts b/src/app/customers/store/identityCards/identity-cards.reducer.ts
new file mode 100644
index 0000000..87f33e8
--- /dev/null
+++ b/src/app/customers/store/identityCards/identity-cards.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 * as identityCards from './identity-cards.actions';
+import {ResourceState} from '../../../common/store/resource.reducer';
+import {IdentificationCard} from '../../../services/customer/domain/identification-card.model';
+import {idsToHashWithCurrentTimestamp, resourcesToHash} from '../../../common/store/reducer.helper';
+
+export const initialState: ResourceState = {
+  ids: [],
+  entities: {},
+  loadedAt: {},
+  selectedId: null,
+};
+
+export function reducer(state = initialState, action: identityCards.Actions): ResourceState {
+
+  switch (action.type) {
+
+    case identityCards.LOAD_ALL: {
+      return initialState;
+    }
+
+    case identityCards.LOAD_ALL_COMPLETE: {
+      const identificationCards: IdentificationCard[] = action.payload;
+
+      const ids = identificationCards.map(identificationCard => identificationCard.number);
+
+      const entities = resourcesToHash(identificationCards, 'number');
+
+      const loadedAt = idsToHashWithCurrentTimestamp(ids);
+
+      return {
+        ids: [ ...ids ],
+        entities: entities,
+        loadedAt: loadedAt,
+        selectedId: state.selectedId
+      };
+    }
+
+    default: {
+      return state;
+    }
+  }
+}
diff --git a/src/app/customers/store/identityCards/scans/effects/notification.effects.ts b/src/app/customers/store/identityCards/scans/effects/notification.effects.ts
new file mode 100644
index 0000000..324ec6f
--- /dev/null
+++ b/src/app/customers/store/identityCards/scans/effects/notification.effects.ts
@@ -0,0 +1,47 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 {Actions, Effect} from '@ngrx/effects';
+import {Injectable} from '@angular/core';
+import {NotificationService, NotificationType} from '../../../../../services/notification/notification.service';
+import {Observable} from 'rxjs/Observable';
+import {Action} from '@ngrx/store';
+import * as identificationCardScanActions from '../scans.actions';
+
+@Injectable()
+export class CustomerIdentificationCardScanNotificationEffects {
+
+  @Effect({ dispatch: false })
+  createIdentificationCardScanSuccess$: Observable<Action> = this.actions$
+    .ofType(identificationCardScanActions.CREATE_SUCCESS)
+    .do(() => this.notificationService.send({
+      type: NotificationType.MESSAGE,
+      message: 'Scan is going to be uploaded'
+    }));
+
+  @Effect({ dispatch: false })
+  deleteIdentificationCardScanSuccess$: Observable<Action> = this.actions$
+    .ofType(identificationCardScanActions.DELETE_SUCCESS)
+    .do(() => this.notificationService.send({
+      type: NotificationType.MESSAGE,
+      message: 'Scan is going to be deleted'
+    }));
+
+  constructor(private actions$: Actions, private notificationService: NotificationService) {}
+
+}
diff --git a/src/app/customers/store/identityCards/scans/effects/route.effects.ts b/src/app/customers/store/identityCards/scans/effects/route.effects.ts
new file mode 100644
index 0000000..40a8549
--- /dev/null
+++ b/src/app/customers/store/identityCards/scans/effects/route.effects.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 {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 identificationCardScanActions from '../scans.actions';
+
+@Injectable()
+export class CustomerIdentificationCardScanRouteEffects {
+
+  @Effect({ dispatch: false })
+  createIdentificationCardScanSuccess$: Observable<Action> = this.actions$
+    .ofType(identificationCardScanActions.CREATE_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/customers/store/identityCards/scans/effects/service.effects.ts b/src/app/customers/store/identityCards/scans/effects/service.effects.ts
new file mode 100644
index 0000000..5079f47
--- /dev/null
+++ b/src/app/customers/store/identityCards/scans/effects/service.effects.ts
@@ -0,0 +1,73 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 {of} from 'rxjs/observable/of';
+import {Injectable} from '@angular/core';
+import {CustomerService} from '../../../../../services/customer/customer.service';
+import {Actions, Effect} from '@ngrx/effects';
+import {Observable} from 'rxjs/Observable';
+import {Action} from '@ngrx/store';
+import * as identificationCardScans from '../scans.actions';
+
+@Injectable()
+export class CustomerIdentificationCardScanApiEffects {
+
+  @Effect()
+  loadAll$: Observable<Action> = this.actions$
+    .ofType(identificationCardScans.LOAD_ALL)
+    .debounceTime(300)
+    .map((action: identificationCardScans.LoadAllAction) => action.payload)
+    .switchMap(payload => {
+      const nextSearch$ = this.actions$.ofType(identificationCardScans.LOAD_ALL).skip(1);
+
+      return this.customerService.fetchIdentificationCardScans(payload.customerIdentifier, payload.identificationCardNumber)
+        .takeUntil(nextSearch$)
+        .map(scans => new identificationCardScans.LoadAllCompleteAction(scans))
+        .catch(() => of(new identificationCardScans.LoadAllCompleteAction([])));
+    });
+
+  @Effect()
+  createIdentificationCardScan$: Observable<Action> = this.actions$
+    .ofType(identificationCardScans.CREATE)
+    .map((action: identificationCardScans.CreateIdentityCardScanAction) => action.payload)
+    .mergeMap(payload =>
+      this.customerService.uploadIdentificationCardScan(payload.customerIdentifier, payload.identificationCardNumber,
+        payload.scan, payload.file)
+        .map(() => new identificationCardScans.CreateIdentityCardScanSuccessAction({
+          resource: payload.scan,
+          activatedRoute: payload.activatedRoute
+        }))
+        .catch((error) => of(new identificationCardScans.CreateIdentityCardScanFailAction(error)))
+    );
+
+  @Effect()
+  deleteIdentificationCardScan$: Observable<Action> = this.actions$
+    .ofType(identificationCardScans.DELETE)
+    .map((action: identificationCardScans.DeleteIdentityCardScanAction) => action.payload)
+    .mergeMap(payload =>
+      this.customerService.deleteIdentificationCardScan(payload.customerIdentifier, payload.identificationCardNumber,
+        payload.scan.identifier)
+        .map(() => new identificationCardScans.DeleteIdentityCardScanSuccessAction({
+          resource: payload.scan,
+          activatedRoute: undefined
+        }))
+        .catch((error) => of(new identificationCardScans.DeleteIdentityCardScanFailAction(error)))
+    );
+
+  constructor(private actions$: Actions, private customerService: CustomerService) {}
+}
diff --git a/src/app/customers/store/identityCards/scans/scans.actions.ts b/src/app/customers/store/identityCards/scans/scans.actions.ts
new file mode 100644
index 0000000..6b1dcb5
--- /dev/null
+++ b/src/app/customers/store/identityCards/scans/scans.actions.ts
@@ -0,0 +1,119 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 {IdentificationCardScan} from '../../../../services/customer/domain/identification-card-scan.model';
+import {Action} from '@ngrx/store';
+import {CreateResourceSuccessPayload, DeleteResourceSuccessPayload} from '../../../../common/store/resource.reducer';
+import {RoutePayload} from '../../../../common/store/route-payload';
+
+export const LOAD_ALL = type('[Customer Identity Card Scan] Load All');
+export const LOAD_ALL_COMPLETE = type('[Customer Identity Card Scan] Load All Complete');
+
+export const CREATE = type('[Customer Identity Card Scan] Create');
+export const CREATE_SUCCESS = type('[Customer Identity Card Scan] Create Success');
+export const CREATE_FAIL = type('[Customer Identity Card Scan] Create Fail');
+
+export const DELETE = type('[Customer Identity Card Scan] Delete');
+export const DELETE_SUCCESS = type('[Customer Identity Card Scan] Delete Success');
+export const DELETE_FAIL = type('[Customer Identity Card Scan] Delete Fail');
+
+export const RESET_FORM = type('[Customer Identity Card Scan] Reset Form');
+
+export interface LoadAllPayload {
+  customerIdentifier: string;
+  identificationCardNumber: string;
+}
+
+export interface IdentityCardScanPayload extends RoutePayload {
+  customerIdentifier: string;
+  identificationCardNumber: string;
+  scan: IdentificationCardScan;
+  file: File;
+}
+
+export interface DeleteIdentityCardScanPayload {
+  customerIdentifier: string;
+  identificationCardNumber: string;
+  scan: IdentificationCardScan;
+}
+
+export class LoadAllAction implements Action {
+  readonly type = LOAD_ALL;
+
+  constructor(public payload: LoadAllPayload) { }
+}
+
+export class LoadAllCompleteAction implements Action {
+  readonly type = LOAD_ALL_COMPLETE;
+
+  constructor(public payload: IdentificationCardScan[]) { }
+}
+
+export class CreateIdentityCardScanAction implements Action {
+  readonly type = CREATE;
+
+  constructor(public payload: IdentityCardScanPayload) { }
+}
+
+export class CreateIdentityCardScanSuccessAction implements Action {
+  readonly type = CREATE_SUCCESS;
+
+  constructor(public payload: CreateResourceSuccessPayload) { }
+}
+
+export class CreateIdentityCardScanFailAction implements Action {
+  readonly type = CREATE_FAIL;
+
+  constructor(public payload: Error) { }
+}
+
+export class DeleteIdentityCardScanAction implements Action {
+  readonly type = DELETE;
+
+  constructor(public payload: DeleteIdentityCardScanPayload) { }
+}
+
+export class DeleteIdentityCardScanSuccessAction implements Action {
+  readonly type = DELETE_SUCCESS;
+
+  constructor(public payload: DeleteResourceSuccessPayload) { }
+}
+
+export class DeleteIdentityCardScanFailAction implements Action {
+  readonly type = DELETE_FAIL;
+
+  constructor(public payload: Error) { }
+}
+
+export class ResetIdentityCardScanForm implements Action {
+  readonly type = RESET_FORM;
+
+  constructor() {}
+}
+
+export type Actions
+  = LoadAllAction
+  | LoadAllCompleteAction
+  | CreateIdentityCardScanAction
+  | CreateIdentityCardScanSuccessAction
+  | CreateIdentityCardScanFailAction
+  | DeleteIdentityCardScanAction
+  | DeleteIdentityCardScanSuccessAction
+  | DeleteIdentityCardScanFailAction
+  | ResetIdentityCardScanForm;
diff --git a/src/app/customers/store/identityCards/scans/scans.reducer.ts b/src/app/customers/store/identityCards/scans/scans.reducer.ts
new file mode 100644
index 0000000..30ae17e
--- /dev/null
+++ b/src/app/customers/store/identityCards/scans/scans.reducer.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 {ResourceState} from '../../../../common/store/resource.reducer';
+import {IdentificationCardScan} from '../../../../services/customer/domain/identification-card-scan.model';
+import {idsToHashWithCurrentTimestamp, resourcesToHash} from '../../../../common/store/reducer.helper';
+import * as identityCardScans from './scans.actions';
+
+export const initialState: ResourceState = {
+  ids: [],
+  entities: {},
+  loadedAt: {},
+  selectedId: null,
+};
+
+export function reducer(state = initialState, action: identityCardScans.Actions): ResourceState {
+
+  switch (action.type) {
+
+    case identityCardScans.LOAD_ALL: {
+      return initialState;
+    }
+
+    case identityCardScans.LOAD_ALL_COMPLETE: {
+      const cardScans: IdentificationCardScan[] = action.payload;
+      const ids = cardScans.map(scan => scan.identifier);
+      const entities = resourcesToHash(cardScans);
+      const loadedAt = idsToHashWithCurrentTimestamp(ids);
+
+      return {
+        ids: [ ...ids ],
+        entities,
+        loadedAt,
+        selectedId: state.selectedId
+      };
+    }
+
+    default: {
+      return state;
+    }
+  }
+}
diff --git a/src/app/customers/store/index.ts b/src/app/customers/store/index.ts
new file mode 100644
index 0000000..8662cb3
--- /dev/null
+++ b/src/app/customers/store/index.ts
@@ -0,0 +1,153 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 fromCustomers from './customers.reducer';
+import * as fromCustomerTasks from './customerTasks/customer-tasks.reducer';
+import * as fromCustomerIdentificationCards from './identityCards/identity-cards.reducer';
+import * as fromCatalogs from './catalogs/catalog.reducer';
+import * as fromCommands from './commands/commands.reducer';
+import * as fromScans from './identityCards/scans/scans.reducer';
+import * as fromTasks from './tasks/tasks.reducer';
+import * as fromPayrollDistribution from './payroll/payroll.reducer';
+
+import {ActionReducer, Store} from '@ngrx/store';
+import {createReducer} from '../../store/index';
+import {createSelector} from 'reselect';
+import {
+  createResourceReducer,
+  getResourceAll,
+  getResourceLoadedAt,
+  getResourceSelected,
+  ResourceState
+} from '../../common/store/resource.reducer';
+import {createFormReducer, FormState, getFormError} from '../../common/store/form.reducer';
+
+export interface State extends fromRoot.State {
+  customers: ResourceState;
+  customerForm: FormState;
+  tasks: ResourceState;
+  taskForm: FormState;
+  customerTasks: fromCustomerTasks.State;
+  customerCatalog: fromCatalogs.State;
+  customerCommands: fromCommands.State;
+  customerIdentificationCards: ResourceState;
+  customerIdentificationCardForm: FormState;
+  customerIdentificationCardScans: ResourceState;
+  customerIdentificationCardScanForm: FormState;
+  customerPayrollDistribution: fromPayrollDistribution.State;
+}
+
+const reducers = {
+  customers: createResourceReducer('Customer', fromCustomers.reducer),
+  customerForm: createFormReducer('Customer'),
+  tasks: createResourceReducer('Task', fromTasks.reducer),
+  taskForm: createFormReducer('Task'),
+  customerTasks: fromCustomerTasks.reducer,
+  customerCatalog: fromCatalogs.reducer,
+  customerCommands: fromCommands.reducer,
+  customerIdentificationCards: createResourceReducer('Customer Identity Card', fromCustomerIdentificationCards.reducer, 'number'),
+  customerIdentificationCardForm: createFormReducer('Customer Identity Card'),
+  customerIdentificationCardScans: createResourceReducer('Customer Identity Card Scan', fromScans.reducer),
+  customerIdentificationCardScanForm: createFormReducer('Customer Identity Card Scan'),
+  customerPayrollDistribution: fromPayrollDistribution.reducer
+};
+
+export class CustomersStore extends Store<State> {}
+
+export const customerModuleReducer: ActionReducer<State> = createReducer(reducers);
+
+export function customerStoreFactory(appStore: Store<fromRoot.State>) {
+  appStore.replaceReducer(customerModuleReducer);
+  return appStore;
+}
+
+export const getCustomersState = (state: State) => state.customers;
+
+export const getCustomerFormState = (state: State) => state.customerForm;
+export const getCustomerFormError = createSelector(getCustomerFormState, getFormError);
+
+export const getCustomerLoadedAt = createSelector(getCustomersState, getResourceLoadedAt);
+export const getSelectedCustomer = createSelector(getCustomersState, getResourceSelected);
+
+/**
+ * Task Selectors
+ */
+export const getTasksState = (state: State) => state.tasks;
+
+export const getAllTaskEntities = createSelector(getTasksState, getResourceAll);
+
+export const getTaskLoadedAt = createSelector(getTasksState, getResourceLoadedAt);
+export const getSelectedTask = createSelector(getTasksState, getResourceSelected);
+
+/**
+ * Customer Task Selectors
+ */
+export const getCustomerTaskCommandsState = (state: State) => state.customerTasks;
+
+export const getCustomerTaskProcessSteps = createSelector(getCustomerTaskCommandsState, fromCustomerTasks.getProcessSteps);
+
+
+/**
+ * Customer Command Selectors
+ */
+
+export const getCustomerCommandsState = (state: State) => state.customerCommands;
+
+export const getAllCustomerCommands = createSelector(getCustomerCommandsState, fromCommands.getCommands);
+
+/**
+ * Customer Identification Card Selectors
+ */
+export const getCustomerIdentificationCardsState = (state: State) => state.customerIdentificationCards;
+
+export const getAllCustomerIdentificationCardEntities = createSelector(getCustomerIdentificationCardsState, getResourceAll);
+
+export const getCustomerIdentificationCardFormState = (state: State) => state.customerIdentificationCardForm;
+export const getCustomerIdentificationCardFormError = createSelector(getCustomerIdentificationCardFormState, getFormError);
+
+export const getIdentificationCardLoadedAt = createSelector(getCustomerIdentificationCardsState, getResourceLoadedAt);
+export const getSelectedIdentificationCard = createSelector(getCustomerIdentificationCardsState, getResourceSelected);
+
+/**
+ * Customer Identification Card Scan Selectors
+ */
+export const getIdentificationCardScansState = (state: State) => state.customerIdentificationCardScans;
+
+export const getAllIdentificationCardScanEntities = createSelector(getIdentificationCardScansState, getResourceAll);
+
+export const getCustomerIdentificationCardScanFormState = (state: State) => state.customerIdentificationCardScanForm;
+export const getCustomerIdentificationCardScanFormError = createSelector(getCustomerIdentificationCardScanFormState, getFormError);
+
+/**
+ * Customer Payroll Distribution Selectors
+ */
+export const getPayrollDistributionState = (state: State) => state.customerPayrollDistribution;
+
+export const getPayrollDistribution = createSelector(getPayrollDistributionState, fromPayrollDistribution.getPayrollDistribution);
+export const getPayrollDistributionLoadedAt = createSelector(getPayrollDistributionState,
+  fromPayrollDistribution.getPayrollDistributionLoadedAt);
+
+/**
+ * Customer Catalog Selectors
+ */
+export const getCustomerCatalogState = (state: State) => state.customerCatalog;
+
+export const getCustomerCatalog = createSelector(getCustomerCatalogState, fromCatalogs.getCustomerCatalog);
+export const getCustomerCatalogLoadedAt = createSelector(getCustomerCatalogState, fromCatalogs.getCustomerCatalogLoadedAt);
+export const getSelectedField = createSelector(getCustomerCatalogState, fromCatalogs.getSelectedField);
diff --git a/src/app/customers/store/payroll/effects/notification.effects.ts b/src/app/customers/store/payroll/effects/notification.effects.ts
new file mode 100644
index 0000000..281a210
--- /dev/null
+++ b/src/app/customers/store/payroll/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 {NotificationService, NotificationType} from '../../../../services/notification/notification.service';
+import {Observable} from 'rxjs/Observable';
+import {Action} from '@ngrx/store';
+import * as payrollActions from '../payroll.actions';
+
+@Injectable()
+export class CustomerPayrollNotificationEffects {
+
+  @Effect({ dispatch: false })
+  updatePayrollSuccess$: Observable<Action> = this.actions$
+    .ofType(payrollActions.UPDATE_SUCCESS)
+    .do(() => this.notificationService.send({
+      type: NotificationType.MESSAGE,
+      message: 'Payroll is going to be saved'
+    }));
+
+  constructor(private actions$: Actions, private notificationService: NotificationService) {}
+
+}
diff --git a/src/app/customers/store/payroll/effects/route.effects.ts b/src/app/customers/store/payroll/effects/route.effects.ts
new file mode 100644
index 0000000..fda200f
--- /dev/null
+++ b/src/app/customers/store/payroll/effects/route.effects.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 {Injectable} from '@angular/core';
+import {Actions, Effect} from '@ngrx/effects';
+import {Router} from '@angular/router';
+import {Observable} from 'rxjs/Observable';
+import * as payrollActions from '../payroll.actions';
+import {Action} from '@ngrx/store';
+
+@Injectable()
+export class CustomerPayrollRouteEffects {
+  @Effect({ dispatch: false })
+  updatePayrollSuccess$: Observable<Action> = this.actions$
+    .ofType(payrollActions.UPDATE_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/customers/store/payroll/effects/service.effects.ts b/src/app/customers/store/payroll/effects/service.effects.ts
new file mode 100644
index 0000000..4466b8a
--- /dev/null
+++ b/src/app/customers/store/payroll/effects/service.effects.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 {Actions, Effect} from '@ngrx/effects';
+import {Observable} from 'rxjs/Observable';
+import * as payrollActions from '../payroll.actions';
+import {Action} from '@ngrx/store';
+import {of} from 'rxjs/observable/of';
+import {PayrollService} from '../../../../services/payroll/payroll.service';
+
+@Injectable()
+export class CustomerPayrollApiEffects {
+
+  @Effect()
+  updatePayroll$: Observable<Action> = this.actions$
+    .ofType(payrollActions.UPDATE)
+    .map((action: payrollActions.UpdatePayrollDistributionAction) => action.payload)
+    .mergeMap(payload =>
+      this.payrollService.setPayrollConfiguration(payload.customerId, payload.distribution)
+        .map(() => new payrollActions.UpdatePayrollDistributionSuccessAction(payload))
+        .catch((error) => of(new payrollActions.UpdatePayrollDistributionFailAction(error)))
+    );
+
+  constructor(private actions$: Actions, private payrollService: PayrollService) { }
+
+}
diff --git a/src/app/customers/store/payroll/payroll.actions.ts b/src/app/customers/store/payroll/payroll.actions.ts
new file mode 100644
index 0000000..662646e
--- /dev/null
+++ b/src/app/customers/store/payroll/payroll.actions.ts
@@ -0,0 +1,63 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 {Action} from '@ngrx/store';
+import {RoutePayload} from '../../../common/store/route-payload';
+import {PayrollConfiguration} from '../../../services/payroll/domain/payroll-configuration.model';
+
+export const LOAD = type('[Customer Payroll] Load');
+
+export const UPDATE = type('[Customer Payroll] Update');
+export const UPDATE_SUCCESS = type('[Customer Payroll] Update Success');
+export const UPDATE_FAIL = type('[Customer Payroll] Update Fail');
+
+export interface PayrollDistributionRoutePayload extends RoutePayload {
+  customerId: string;
+  distribution: PayrollConfiguration;
+}
+
+export class LoadAction implements Action {
+  readonly type = LOAD;
+
+  constructor(public payload: PayrollConfiguration) { }
+}
+
+export class UpdatePayrollDistributionAction implements Action {
+  readonly type = UPDATE;
+
+  constructor(public payload: PayrollDistributionRoutePayload) { }
+}
+
+export class UpdatePayrollDistributionSuccessAction implements Action {
+  readonly type = UPDATE_SUCCESS;
+
+  constructor(public payload: PayrollDistributionRoutePayload) { }
+}
+
+export class UpdatePayrollDistributionFailAction implements Action {
+  readonly type = UPDATE_FAIL;
+
+  constructor(public payload: Error) { }
+}
+
+export type Actions
+  = LoadAction
+  | UpdatePayrollDistributionAction
+  | UpdatePayrollDistributionSuccessAction
+  | UpdatePayrollDistributionFailAction;
diff --git a/src/app/customers/store/payroll/payroll.reducer.ts b/src/app/customers/store/payroll/payroll.reducer.ts
new file mode 100644
index 0000000..619ec31
--- /dev/null
+++ b/src/app/customers/store/payroll/payroll.reducer.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 {PayrollConfiguration} from '../../../services/payroll/domain/payroll-configuration.model';
+import * as payrollActions from './payroll.actions';
+import {PayrollDistributionRoutePayload} from './payroll.actions';
+
+export interface State {
+  distribution: PayrollConfiguration;
+  loadedAt: number;
+}
+
+const initialState: State = {
+  distribution: null,
+  loadedAt: null
+};
+
+export function reducer(state: State = initialState, action: payrollActions.Actions): State {
+
+  switch (action.type) {
+
+    case payrollActions.LOAD: {
+      const distribution: PayrollConfiguration = action.payload;
+
+      return {
+        distribution,
+        loadedAt: Date.now()
+      };
+    }
+
+    case payrollActions.UPDATE_SUCCESS: {
+      const payload: PayrollDistributionRoutePayload = action.payload;
+
+      return {
+        distribution: payload.distribution,
+        loadedAt: state.loadedAt
+      };
+    }
+
+    default: {
+      return state;
+    }
+  }
+}
+
+export const getPayrollDistribution = (state: State) => state.distribution;
+export const getPayrollDistributionLoadedAt = (state: State) => state.loadedAt;
diff --git a/src/app/customers/store/tasks/effects/notification.effects.ts b/src/app/customers/store/tasks/effects/notification.effects.ts
new file mode 100644
index 0000000..a049100
--- /dev/null
+++ b/src/app/customers/store/tasks/effects/notification.effects.ts
@@ -0,0 +1,47 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 {NotificationService, NotificationType} from '../../../../services/notification/notification.service';
+import {Observable} from 'rxjs/Observable';
+import {Actions, Effect} from '@ngrx/effects';
+import {Injectable} from '@angular/core';
+import {Action} from '@ngrx/store';
+import * as taskActions from '../task.actions';
+
+@Injectable()
+export class TasksNotificationEffects {
+
+  @Effect({dispatch: false})
+  createTaskSuccess$: Observable<Action> = this.actions$
+    .ofType(taskActions.CREATE_SUCCESS)
+    .do(() => this.notificationService.send({
+      type: NotificationType.MESSAGE,
+      message: 'Task is going to be saved'
+    }));
+
+  @Effect({dispatch: false})
+  updateTaskSuccess$: Observable<Action> = this.actions$
+    .ofType(taskActions.UPDATE_SUCCESS)
+    .do(() => this.notificationService.send({
+      type: NotificationType.MESSAGE,
+      message: 'Task is going to be updated'
+    }));
+
+  constructor(private actions$: Actions, private notificationService: NotificationService) {}
+
+}
diff --git a/src/app/customers/store/tasks/effects/route.effects.ts b/src/app/customers/store/tasks/effects/route.effects.ts
new file mode 100644
index 0000000..0a510b3
--- /dev/null
+++ b/src/app/customers/store/tasks/effects/route.effects.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 {Observable} from 'rxjs/Observable';
+import {Actions, Effect} from '@ngrx/effects';
+import {Action} from '@ngrx/store';
+import {Router} from '@angular/router';
+import {Injectable} from '@angular/core';
+import * as taskActions from '../task.actions';
+
+@Injectable()
+export class TasksRouteEffects {
+
+  @Effect({ dispatch: false })
+  createCustomerTaskSuccess$: Observable<Action> = this.actions$
+    .ofType(taskActions.CREATE_SUCCESS, taskActions.UPDATE_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/customers/store/tasks/effects/service.effects.ts b/src/app/customers/store/tasks/effects/service.effects.ts
new file mode 100644
index 0000000..0319df4
--- /dev/null
+++ b/src/app/customers/store/tasks/effects/service.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 {CustomerService} from '../../../../services/customer/customer.service';
+import * as taskActions from '../task.actions';
+import {Observable} from 'rxjs/Observable';
+import {Action} from '@ngrx/store';
+import {of} from 'rxjs/observable/of';
+
+@Injectable()
+export class TasksApiEffects {
+
+  @Effect()
+  loadAll$: Observable<Action> = this.actions$
+    .ofType(taskActions.LOAD_ALL)
+    .debounceTime(300)
+    .map((action: taskActions.LoadAllAction) => action.payload)
+    .switchMap(() => {
+      const nextSearch$ = this.actions$.ofType(taskActions.LOAD_ALL).skip(1);
+
+      return this.customerService.fetchTasks()
+        .takeUntil(nextSearch$)
+        .map(taskPage => new taskActions.LoadAllCompleteAction(taskPage))
+        .catch(() => of(new taskActions.LoadAllCompleteAction([])));
+    });
+
+  @Effect()
+  createTask$: Observable<Action> = this.actions$
+    .ofType(taskActions.CREATE)
+    .map((action: taskActions.CreateTaskAction) => action.payload)
+    .mergeMap(payload =>
+      this.customerService.createTask(payload.task)
+        .map(() => new taskActions.CreateTaskSuccessAction({
+          resource: payload.task,
+          activatedRoute: payload.activatedRoute
+        }))
+        .catch((error) => of(new taskActions.CreateTaskFailAction(error)))
+    );
+
+  @Effect()
+  updateTask$: Observable<Action> = this.actions$
+    .ofType(taskActions.UPDATE)
+    .map((action: taskActions.CreateTaskAction) => action.payload)
+    .mergeMap(payload =>
+      this.customerService.updateTask(payload.task)
+        .map(() => new taskActions.UpdateTaskSuccessAction({
+          resource: payload.task,
+          activatedRoute: payload.activatedRoute
+        }))
+        .catch((error) => of(new taskActions.UpdateTaskFailAction(error)))
+    );
+
+  constructor(private actions$: Actions, private customerService: CustomerService) {}
+}
diff --git a/src/app/customers/store/tasks/task.actions.ts b/src/app/customers/store/tasks/task.actions.ts
new file mode 100644
index 0000000..5c11a1c
--- /dev/null
+++ b/src/app/customers/store/tasks/task.actions.ts
@@ -0,0 +1,152 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 {Action} from '@ngrx/store';
+import {TaskDefinition} from '../../../services/customer/domain/task-definition.model';
+import {
+  CreateResourceSuccessPayload,
+  LoadResourcePayload,
+  SelectResourcePayload,
+  UpdateResourceSuccessPayload
+} from '../../../common/store/resource.reducer';
+import {RoutePayload} from '../../../common/store/route-payload';
+
+export const LOAD_ALL = type('[Task] Load All');
+export const LOAD_ALL_COMPLETE = type('[Task] Load All Complete');
+
+export const LOAD = type('[Task] Load');
+export const SELECT = type('[Task] Select');
+
+export const CREATE = type('[Task] Create');
+export const CREATE_SUCCESS = type('[Task] Create Success');
+export const CREATE_FAIL = type('[Task] Create Fail');
+
+export const UPDATE = type('[Task] Update');
+export const UPDATE_SUCCESS = type('[Task] Update Success');
+export const UPDATE_FAIL = type('[Task] Update Fail');
+
+export const DELETE = type('[Task] Delete');
+export const DELETE_SUCCESS = type('[Task] Delete Success');
+export const DELETE_FAIL = type('[Task] Delete Fail');
+
+export const RESET_FORM = type('[Task] Reset Form');
+
+export interface TaskRoutePayload extends RoutePayload {
+  task: TaskDefinition;
+}
+
+export class LoadAllAction implements Action {
+  readonly type = LOAD_ALL;
+
+  constructor(public payload: string) { }
+}
+
+export class LoadAllCompleteAction implements Action {
+  readonly type = LOAD_ALL_COMPLETE;
+
+  constructor(public payload: TaskDefinition[]) { }
+}
+
+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 CreateTaskAction implements Action {
+  readonly type = CREATE;
+
+  constructor(public payload: TaskRoutePayload) { }
+}
+
+export class CreateTaskSuccessAction implements Action {
+  readonly type = CREATE_SUCCESS;
+
+  constructor(public payload: CreateResourceSuccessPayload) { }
+}
+
+export class CreateTaskFailAction implements Action {
+  readonly type = CREATE_FAIL;
+
+  constructor(public payload: Error) { }
+}
+
+export class UpdateTaskAction implements Action {
+  readonly type = UPDATE;
+
+  constructor(public payload: TaskRoutePayload) { }
+}
+
+export class UpdateTaskSuccessAction implements Action {
+  readonly type = UPDATE_SUCCESS;
+
+  constructor(public payload: UpdateResourceSuccessPayload) { }
+}
+
+export class UpdateTaskFailAction implements Action {
+  readonly type = UPDATE_FAIL;
+
+  constructor(public payload: Error) { }
+}
+
+export class DeleteTaskAction implements Action {
+  readonly type = DELETE;
+
+  constructor(public payload: TaskRoutePayload) { }
+}
+
+export class DeleteTaskSuccessAction implements Action {
+  readonly type = DELETE_SUCCESS;
+
+  constructor(public payload: UpdateResourceSuccessPayload) { }
+}
+
+export class DeleteTaskFailAction implements Action {
+  readonly type = DELETE_FAIL;
+
+  constructor(public payload: Error) { }
+}
+
+export class ResetTaskFormAction implements Action {
+  readonly type = RESET_FORM;
+
+  constructor() {}
+}
+
+export type Actions
+  = LoadAllAction
+  | LoadAllCompleteAction
+  | LoadAction
+  | SelectAction
+  | CreateTaskAction
+  | CreateTaskSuccessAction
+  | CreateTaskFailAction
+  | UpdateTaskAction
+  | UpdateTaskSuccessAction
+  | UpdateTaskFailAction
+  | DeleteTaskAction
+  | DeleteTaskSuccessAction
+  | DeleteTaskFailAction
+  | ResetTaskFormAction;
diff --git a/src/app/customers/store/tasks/tasks.reducer.ts b/src/app/customers/store/tasks/tasks.reducer.ts
new file mode 100644
index 0000000..6c02922
--- /dev/null
+++ b/src/app/customers/store/tasks/tasks.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 {ResourceState} from '../../../common/store/resource.reducer';
+import {TaskDefinition} from '../../../services/customer/domain/task-definition.model';
+import {idsToHashWithCurrentTimestamp, resourcesToHash} from '../../../common/store/reducer.helper';
+import * as task from './task.actions';
+
+export const initialState: ResourceState = {
+  ids: [],
+  entities: {},
+  loadedAt: {},
+  selectedId: null,
+};
+
+export function reducer(state = initialState, action: task.Actions): ResourceState {
+
+  switch (action.type) {
+
+    case task.LOAD_ALL: {
+      return initialState;
+    }
+
+    case task.LOAD_ALL_COMPLETE: {
+      const taskDefinitions: TaskDefinition[] = action.payload;
+
+      const ids = taskDefinitions.map(task => task.identifier);
+
+      const entities = resourcesToHash(taskDefinitions);
+
+      const loadedAt = idsToHashWithCurrentTimestamp(ids);
+
+      return {
+        ids: [ ...ids ],
+        entities: entities,
+        loadedAt: loadedAt,
+        selectedId: state.selectedId
+      };
+    }
+
+    default: {
+      return state;
+    }
+  }
+}
diff --git a/src/app/customers/tasks/domain/command-options.model.ts b/src/app/customers/tasks/domain/command-options.model.ts
new file mode 100644
index 0000000..020bb46
--- /dev/null
+++ b/src/app/customers/tasks/domain/command-options.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 {TaskDefinitionCommand} from '../../../services/customer/domain/task-definition.model';
+
+interface CommandOption {
+  command: TaskDefinitionCommand;
+  label: string;
+}
+
+export const defaultCommandOptions: CommandOption[] = [
+  { command: 'ACTIVATE', label: 'is activated' },
+  { command: 'UNLOCK', label: 'is unlocked' },
+  { command: 'REOPEN', label: 'is reopened' }
+];
diff --git a/src/app/customers/tasks/domain/type-options.model.ts b/src/app/customers/tasks/domain/type-options.model.ts
new file mode 100644
index 0000000..8702333
--- /dev/null
+++ b/src/app/customers/tasks/domain/type-options.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 {TaskDefinitionType} from '../../../services/customer/domain/task-definition.model';
+
+interface TypeOption {
+  type: TaskDefinitionType;
+  label: string;
+}
+
+export const defaultTypeOptions: TypeOption[] = [
+  { type: 'ID_CARD', label: 'Identification card' },
+  { type: 'FOUR_EYES', label: 'Four eyes' },
+  { type: 'CUSTOM', label: 'Custom' }
+];
diff --git a/src/app/customers/tasks/form/create.form.component.html b/src/app/customers/tasks/form/create.form.component.html
new file mode 100644
index 0000000..bfa84f5
--- /dev/null
+++ b/src/app/customers/tasks/form/create.form.component.html
@@ -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.
+-->
+
+<fims-layout-card-over title="{{'Create new task definition' | translate}}">
+  <fims-task-form-component
+    #form
+    (onSave)="onSave($event)"
+    (onCancel)="onCancel()"
+    [task]="task">
+  </fims-task-form-component>
+</fims-layout-card-over>
diff --git a/src/app/customers/tasks/form/create.form.component.ts b/src/app/customers/tasks/form/create.form.component.ts
new file mode 100644
index 0000000..a935d41
--- /dev/null
+++ b/src/app/customers/tasks/form/create.form.component.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 {Component} from '@angular/core';
+import {TaskDefinition} from '../../../services/customer/domain/task-definition.model';
+import {ActivatedRoute, Router} from '@angular/router';
+import {CustomersStore} from '../../store/index';
+import {CREATE} from '../../store/tasks/task.actions';
+
+@Component({
+  templateUrl: './create.form.component.html'
+})
+export class TaskCreateFormComponent {
+
+  task: TaskDefinition = {
+    identifier: '',
+    name: '',
+    description: '',
+    type: 'ID_CARD',
+    commands: [
+      'ACTIVATE'
+    ],
+    mandatory: false,
+    predefined: false
+  };
+
+  constructor(private router: Router, private route: ActivatedRoute, private store: CustomersStore) {}
+
+  onSave(task: TaskDefinition): void {
+    this.store.dispatch({ type: CREATE, payload: {
+      task,
+      activatedRoute: this.route
+    }});
+  }
+
+  onCancel(): void {
+    this.navigateAway();
+  }
+
+  navigateAway(): void {
+    this.router.navigate(['../'], { relativeTo: this.route });
+  }
+}
diff --git a/src/app/customers/tasks/form/edit.form.component.html b/src/app/customers/tasks/form/edit.form.component.html
new file mode 100644
index 0000000..bee6675
--- /dev/null
+++ b/src/app/customers/tasks/form/edit.form.component.html
@@ -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.
+-->
+
+<fims-layout-card-over title="{{'Edit task definition' | translate}}">
+  <fims-task-form-component
+    #form
+    [editMode]="true"
+    (onSave)="onSave($event)"
+    (onCancel)="onCancel()"
+    [task]="task$ | async">
+  </fims-task-form-component>
+</fims-layout-card-over>
diff --git a/src/app/customers/tasks/form/edit.form.component.ts b/src/app/customers/tasks/form/edit.form.component.ts
new file mode 100644
index 0000000..f73c2db
--- /dev/null
+++ b/src/app/customers/tasks/form/edit.form.component.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 {Component} from '@angular/core';
+import {TaskDefinition} from '../../../services/customer/domain/task-definition.model';
+import {Observable} from 'rxjs/Observable';
+import {ActivatedRoute, Router} from '@angular/router';
+import * as fromCustomer from '../../store/index';
+import {CustomersStore} from '../../store/index';
+import {UPDATE} from '../../store/tasks/task.actions';
+
+@Component({
+  templateUrl: './edit.form.component.html'
+})
+export class TaskEditFormComponent {
+
+  task$: Observable<TaskDefinition>;
+
+  constructor(private router: Router, private route: ActivatedRoute, private store: CustomersStore) {
+    this.task$ = this.store.select(fromCustomer.getSelectedTask);
+  }
+
+  onSave(task: TaskDefinition): void {
+    this.store.dispatch({ type: UPDATE, payload: {
+      task,
+      activatedRoute: this.route
+    }});
+  }
+
+  onCancel(): void {
+    this.navigateAway();
+  }
+
+  navigateAway(): void {
+    this.router.navigate(['../'], { relativeTo: this.route });
+  }
+}
diff --git a/src/app/customers/tasks/form/form.component.html b/src/app/customers/tasks/form/form.component.html
new file mode 100644
index 0000000..3e133fa
--- /dev/null
+++ b/src/app/customers/tasks/form/form.component.html
@@ -0,0 +1,65 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<td-steps mode="'vertical'">
+  <td-step #detailsStep label="{{'Task details' | translate}}" [state]="form.valid ? 'complete' : form.pristine ? 'none' : 'required'">
+    <form [formGroup]="form" layout="column">
+      <fims-id-input flex [form]="form" controlName="identifier" [readonly]="editMode"></fims-id-input>
+      <fims-text-input [form]="form" controlName="name" placeholder="{{'Name' | translate}}"></fims-text-input>
+      <mat-form-field layout-margin flex>
+        <textarea matInput placeholder="{{'Description' | translate}}" formControlName="description"></textarea>
+        <mat-error *ngIf="form.get('description').hasError('required')" translate>
+          Required
+        </mat-error>
+      </mat-form-field>
+      <mat-radio-group formControlName="type">
+        <mat-radio-button *ngFor="let type of typeOptions" [value]="type.type" layout-margin>
+          {{type.label}}
+        </mat-radio-button>
+      </mat-radio-group>
+      <mat-checkbox formControlName="mandatory" layout-margin translate>
+        Mandatory
+      </mat-checkbox>
+      <mat-checkbox formControlName="predefined" layout-margin translate>
+        Automatically assign this task when member is created
+      </mat-checkbox>
+      <div layout-gt-xs="column" layout-margin formArrayName="commands">
+        <h4 translate>Task needs to be executed, before member </h4>
+        <div *ngFor="let test of commands; let i=index" layout="row" [formGroupName]="i">
+          <mat-form-field>
+            <mat-select formControlName="command">
+              <mat-option *ngFor="let commandOption of commandOptions" [value]="commandOption.command">
+                {{commandOption.label}}
+              </mat-option>
+            </mat-select>
+          </mat-form-field>
+          <button mat-button (click)="removeCommand(i)" [disabled]="i === 0">{{'Remove' | translate}}</button>
+        </div>
+        <button mat-button (click)="addCommand()">{{'Add trigger' | translate}}</button>
+      </div>
+    </form>
+    <ng-template td-step-actions>
+      <fims-form-final-action
+        [resourceName]="'TASK'"
+        [editMode]="editMode"
+        [disabled]="!form.valid"
+        (onCancel)="cancel()"
+        (onSave)="save()">
+      </fims-form-final-action>
+    </ng-template>
+  </td-step>
+</td-steps>
diff --git a/src/app/customers/tasks/form/form.component.ts b/src/app/customers/tasks/form/form.component.ts
new file mode 100644
index 0000000..4d5d051
--- /dev/null
+++ b/src/app/customers/tasks/form/form.component.ts
@@ -0,0 +1,128 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, OnChanges, OnInit, Output, SimpleChanges, ViewChild} from '@angular/core';
+import {TaskDefinition, TaskDefinitionCommand} from '../../../services/customer/domain/task-definition.model';
+import {AbstractControl, FormArray, FormBuilder, FormGroup, Validators} from '@angular/forms';
+import {FimsValidators} from '../../../common/validator/validators';
+import {TdStepComponent} from '@covalent/core';
+import {defaultTypeOptions} from '../domain/type-options.model';
+import {defaultCommandOptions} from '../domain/command-options.model';
+
+@Component({
+  selector: 'fims-task-form-component',
+  templateUrl: './form.component.html'
+})
+export class TaskFormComponent implements OnInit, OnChanges {
+
+  form: FormGroup;
+
+  typeOptions = defaultTypeOptions;
+
+  commandOptions = defaultCommandOptions;
+
+  @ViewChild('detailsStep') detailsStep: TdStepComponent;
+
+  @Input('task') task: TaskDefinition;
+
+  @Input('editMode') editMode: boolean;
+
+  @Output('onSave') onSave = new EventEmitter<TaskDefinition>();
+
+  @Output('onCancel') onCancel = new EventEmitter<void>();
+
+  constructor(private formBuilder: FormBuilder) {
+    this.form = formBuilder.group({
+      identifier: ['', [Validators.required, Validators.minLength(3), Validators.maxLength(32), FimsValidators.urlSafe]],
+      type: ['', [Validators.required]],
+      description: ['', [Validators.required, Validators.maxLength(4096)]],
+      name: ['', [Validators.required, Validators.maxLength(256)]],
+      mandatory: ['', [Validators.required]],
+      commands: this.initCommands([]),
+      predefined: ['', [Validators.required]]
+    });
+  }
+
+  ngOnInit(): void {
+    this.detailsStep.open();
+  }
+
+  ngOnChanges(changes: SimpleChanges): void {
+    if (changes.task) {
+      this.form.reset({
+        identifier: this.task.identifier,
+        type: this.task.type,
+        description: this.task.description,
+        name: this.task.name,
+        mandatory: this.task.mandatory,
+        predefined: this.task.predefined
+      });
+
+      this.task.commands.forEach(command => this.addCommand(command));
+    }
+  }
+
+  save() {
+    const formCommands = this.form.get('commands').value;
+    const commands: TaskDefinitionCommand[] = formCommands.map(command => command.command);
+
+    const task: TaskDefinition = {
+      identifier: this.form.get('identifier').value,
+      type: this.form.get('type').value,
+      description: this.form.get('description').value,
+      name: this.form.get('name').value,
+      mandatory: this.form.get('mandatory').value,
+      commands,
+      predefined: this.form.get('predefined').value
+    };
+
+    this.onSave.emit(task);
+  }
+
+  cancel() {
+    this.onCancel.emit();
+  }
+
+  private initCommands(commands: TaskDefinitionCommand[]): FormArray {
+    const formControls: FormGroup[] = [];
+    commands.forEach(value => formControls.push(this.initCommand(value)));
+    return this.formBuilder.array(formControls);
+  }
+
+  private initCommand(value?: TaskDefinitionCommand): FormGroup {
+    return this.formBuilder.group({
+      command: [value ? value : '', Validators.required]
+    });
+  }
+
+  addCommand(value?: TaskDefinitionCommand): void {
+    const commands: FormArray = this.form.get('commands') as FormArray;
+    commands.push(this.initCommand(value));
+  }
+
+  removeCommand(index: number): void {
+    const commands: FormArray = this.form.get('commands') as FormArray;
+    commands.removeAt(index);
+  }
+
+  get commands(): AbstractControl[] {
+    const commands: FormArray = this.form.get('commands') as FormArray;
+    return commands.controls;
+  }
+
+}
diff --git a/src/app/customers/tasks/task-exists.guard.ts b/src/app/customers/tasks/task-exists.guard.ts
new file mode 100644
index 0000000..ab3dc61
--- /dev/null
+++ b/src/app/customers/tasks/task-exists.guard.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 {ActivatedRouteSnapshot, CanActivate, RouterStateSnapshot} from '@angular/router';
+import {Injectable} from '@angular/core';
+import * as fromCustomers from '../store';
+import {Observable} from 'rxjs/Observable';
+import {of} from 'rxjs/observable/of';
+import {CustomersStore} from '../store/index';
+import {CustomerService} from '../../services/customer/customer.service';
+import {ExistsGuardService} from '../../common/guards/exists-guard';
+import {LoadAction} from '../store/tasks/task.actions';
+
+@Injectable()
+export class TaskExistsGuard implements CanActivate {
+
+  constructor(private store: CustomersStore,
+              private customerService: CustomerService,
+              private existsGuardService: ExistsGuardService) {
+  }
+
+  hasTaskInStore(id: string): Observable<boolean> {
+    const timestamp$: Observable<number> = this.store.select(fromCustomers.getTaskLoadedAt)
+      .map(loadedAt => loadedAt[id]);
+
+    return this.existsGuardService.isWithinExpiry(timestamp$);
+  }
+
+  hasTaskInApi(id: string): Observable<boolean> {
+    const getTask$: Observable<any> = this.customerService.getTask(id)
+      .map(taskEntity => new LoadAction({
+        resource: taskEntity
+      }))
+      .do((action: LoadAction) => this.store.dispatch(action))
+      .map(task => !!task);
+
+    return this.existsGuardService.routeTo404OnError(getTask$);
+  }
+
+  hasTask(id: string): Observable<boolean> {
+    return this.hasTaskInStore(id)
+      .switchMap(inStore => {
+        if (inStore) {
+          return of(inStore);
+        }
+        return this.hasTaskInApi(id);
+      });
+  }
+
+  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
+    return this.hasTask(route.params['id']);
+  }
+}
diff --git a/src/app/customers/tasks/task.detail.component.html b/src/app/customers/tasks/task.detail.component.html
new file mode 100644
index 0000000..f03fd20
--- /dev/null
+++ b/src/app/customers/tasks/task.detail.component.html
@@ -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.
+-->
+
+<fims-layout-card-over [title]="task.name" [subTitle]="task.description" *ngIf="task$ | async as task" [navigateBackTo]="['../../../']">
+  <div class="mat-content inset" flex>
+    <div layout="row">
+      <mat-list>
+        <mat-list-item>
+          <h3 matLine translate>Type</h3>
+          <p matLine>{{formatType(task.type)}}</p>
+        </mat-list-item>
+        <mat-list-item>
+          <h3 matLine translate>Mandatory</h3>
+          <p matLine>{{task.mandatory}}</p>
+        </mat-list-item>
+        <mat-list-item>
+          <h3 matLine translate>Automatically assign this task when member is created</h3>
+          <p matLine>{{task.predefined}}</p>
+        </mat-list-item>
+        <mat-list-item>
+          <h3 matLine translate>Trigger</h3>
+          <p matLine>{{formatCommands(task.commands)}}</p>
+        </mat-list-item>
+      </mat-list>
+    </div>
+  </div>
+</fims-layout-card-over>
+<fims-fab-button title="{{'Edit task' | translate}}" icon="mode_edit" [link]="['edit']" [permission]="{ id: 'customer_tasks', accessLevel: 'CHANGE'}"></fims-fab-button>
diff --git a/src/app/customers/tasks/task.detail.component.ts b/src/app/customers/tasks/task.detail.component.ts
new file mode 100644
index 0000000..1135206
--- /dev/null
+++ b/src/app/customers/tasks/task.detail.component.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 {Component} from '@angular/core';
+import {TaskDefinition, TaskDefinitionCommand, TaskDefinitionType} from '../../services/customer/domain/task-definition.model';
+import {Observable} from 'rxjs/Observable';
+import * as fromCustomers from '../store/index';
+import {CustomersStore} from '../store/index';
+import {defaultCommandOptions} from './domain/command-options.model';
+import {defaultTypeOptions} from './domain/type-options.model';
+
+@Component({
+  templateUrl: './task.detail.component.html'
+})
+export class TaskDetailComponent {
+
+  task$: Observable<TaskDefinition>;
+
+  constructor(private store: CustomersStore) {
+    this.task$ = store.select(fromCustomers.getSelectedTask);
+  }
+
+  formatType(type: TaskDefinitionType): string {
+    return defaultTypeOptions.find(option => option.type === type).label;
+  }
+
+  formatCommands(commands: TaskDefinitionCommand[]): string {
+    const options = commands.map(command =>
+      defaultCommandOptions.find(option => option.command === command).label
+    );
+
+    return options.join(', ');
+  }
+}
diff --git a/src/app/customers/tasks/task.index.component.html b/src/app/customers/tasks/task.index.component.html
new file mode 100644
index 0000000..ca721b3
--- /dev/null
+++ b/src/app/customers/tasks/task.index.component.html
@@ -0,0 +1,18 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<router-outlet></router-outlet>
diff --git a/src/app/customers/tasks/task.index.component.ts b/src/app/customers/tasks/task.index.component.ts
new file mode 100644
index 0000000..1655ada
--- /dev/null
+++ b/src/app/customers/tasks/task.index.component.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 {Component, OnDestroy, OnInit} from '@angular/core';
+import {SelectAction} from '../store/tasks/task.actions';
+import {CustomersStore} from '../store/index';
+import {ActivatedRoute} from '@angular/router';
+import {Subscription} from 'rxjs/Subscription';
+
+@Component({
+  templateUrl: './task.index.component.html'
+})
+export class TaskIndexComponent implements OnInit, OnDestroy {
+
+  private actionsSubscription: Subscription;
+
+  constructor(private route: ActivatedRoute, private customersStore: CustomersStore) {}
+
+  ngOnInit(): void {
+    this.actionsSubscription = this.route.params
+      .map(params => new SelectAction(params['id']))
+      .subscribe(this.customersStore);
+  }
+
+  ngOnDestroy(): void {
+    this.actionsSubscription.unsubscribe();
+  }
+}
diff --git a/src/app/customers/tasks/task.list.component.html b/src/app/customers/tasks/task.list.component.html
new file mode 100644
index 0000000..11ec167
--- /dev/null
+++ b/src/app/customers/tasks/task.list.component.html
@@ -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.
+-->
+
+<fims-layout-card-over title="{{'Manage tasks' | translate}}" [navigateBackTo]="['../']">
+  <fims-data-table
+    (onActionCellClick)="rowSelect($event)"
+    [columns]="columns"
+    [data]="tasksData$ | async"
+    [sortable]="false"
+    [pageable]="false">
+  </fims-data-table>
+</fims-layout-card-over>
+<fims-fab-button title="{{'Create new task' | translate}}" icon="add" [link]="['create']" [permission]="{ id: 'customer_tasks', accessLevel: 'CHANGE'}"></fims-fab-button>
diff --git a/src/app/customers/tasks/task.list.component.ts b/src/app/customers/tasks/task.list.component.ts
new file mode 100644
index 0000000..6d0d16f
--- /dev/null
+++ b/src/app/customers/tasks/task.list.component.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 {ActivatedRoute, Router} from '@angular/router';
+import * as fromCustomers from '../store';
+import {Component} from '@angular/core';
+import {CustomersStore} from '../store/index';
+import {TaskDefinition, TaskDefinitionType} from '../../services/customer/domain/task-definition.model';
+import {Observable} from 'rxjs/Observable';
+import {LOAD_ALL} from '../store/tasks/task.actions';
+import {TableData} from '../../common/data-table/data-table.component';
+import {defaultTypeOptions} from './domain/type-options.model';
+
+@Component({
+  templateUrl: './task.list.component.html'
+})
+export class TaskListComponent {
+
+  tasksData$: Observable<TableData>;
+
+  columns: any[] = [
+    { name: 'identifier', label: 'Id' },
+    { name: 'name', label: 'Name' },
+    { name: 'mandatory', label: 'Mandatory?' },
+    { name: 'predefined', label: 'Auto assign?' },
+    { name: 'type', label: 'Type',
+      format: (value: TaskDefinitionType) =>
+        defaultTypeOptions.find(option => option.type === value).label
+    }
+  ];
+
+  constructor(private router: Router, private route: ActivatedRoute, private store: CustomersStore) {
+    this.tasksData$ = this.store.select(fromCustomers.getAllTaskEntities)
+      .map(tasks => ({
+        data: tasks,
+        totalElements: tasks.length,
+        totalPages: 1
+      }));
+
+    this.fetchTasks();
+  }
+
+  fetchTasks(): void {
+    this.store.dispatch({
+      type: LOAD_ALL
+    });
+  }
+
+  rowSelect(task: TaskDefinition): void {
+    this.router.navigate(['detail', task.identifier], { relativeTo: this.route });
+  }
+}
diff --git a/src/app/dashboard/dashboard.component.html b/src/app/dashboard/dashboard.component.html
deleted file mode 100644
index 3a5c3c2..0000000
--- a/src/app/dashboard/dashboard.component.html
+++ /dev/null
@@ -1,37 +0,0 @@
-<mat-card class="clients">
-  <span>Clients</span>
-  <mat-list>
-    <mat-list-item>
-      <a [routerLink]="['/navbar/manage_members']">View Members</a> &nbsp;|
-      &nbsp;<span>View Groups</span>
-    </mat-list-item>
-  </mat-list>
-</mat-card>
-<mat-card class="accounting">
-  <span>Accounting</span>
-  <mat-list>
-    <mat-list-item>
-      <a [routerLink]="['/navbar/GL']">View General Ledger</a>|
-      &nbsp;<a [routerLink]="['/navbar/chart_of_accounts']">View chart of Account</a>
-    </mat-list-item>
-  </mat-list>
-</mat-card>
-<mat-card class="organisation">
-  <span>Organisation</span>
-  <mat-list>
-    <mat-list-item>
-      <a [routerLink]="['/navbar/view_offices']">View Offices</a>&nbsp;|
-      &nbsp;<a [routerLink]="['/navbar/manage_employees']">View Employees</a>
-    </mat-list-item>
-  </mat-list>
-</mat-card>
-<mat-card class="products">
-  <span>Products</span>
-  <mat-list>
-    <mat-list-item>
-      <span>View Loan Products</span>|
-      &nbsp;<span>View Savings Product</span>
-    </mat-list-item>
-  </mat-list>
-</mat-card>
-
diff --git a/src/app/dashboard/dashboard.component.scss b/src/app/dashboard/dashboard.component.scss
deleted file mode 100644
index dcfd98d..0000000
--- a/src/app/dashboard/dashboard.component.scss
+++ /dev/null
@@ -1,23 +0,0 @@
-.clients,.accounting{
-    display: flex;
-    display: inline-block;
-    vertical-align: top;
-    margin-left: 5%;
-    margin-bottom: 5%;
-    width: 35%;
-    height:120px;
-
-}
-.organisation,.products{
-    display: flex;
-    display: inline-block;
-    vertical-align:top;
-    margin-left: 5%;
-    width: 35%;
-    height:120px;
-}
-
-mat-list-item{
-    padding-top:50px;
-    
-}
\ No newline at end of file
diff --git a/src/app/dashboard/dashboard.component.spec.ts b/src/app/dashboard/dashboard.component.spec.ts
deleted file mode 100644
index 9c996c3..0000000
--- a/src/app/dashboard/dashboard.component.spec.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-import { async, ComponentFixture, TestBed } from '@angular/core/testing';
-
-import { DashboardComponent } from './dashboard.component';
-
-describe('DashboardComponent', () => {
-  let component: DashboardComponent;
-  let fixture: ComponentFixture<DashboardComponent>;
-
-  beforeEach(async(() => {
-    TestBed.configureTestingModule({
-      declarations: [ DashboardComponent ]
-    })
-    .compileComponents();
-  }));
-
-  beforeEach(() => {
-    fixture = TestBed.createComponent(DashboardComponent);
-    component = fixture.componentInstance;
-    fixture.detectChanges();
-  });
-
-  it('should create', () => {
-    expect(component).toBeTruthy();
-  });
-});
diff --git a/src/app/dashboard/dashboard.component.ts b/src/app/dashboard/dashboard.component.ts
deleted file mode 100644
index 0824577..0000000
--- a/src/app/dashboard/dashboard.component.ts
+++ /dev/null
@@ -1,15 +0,0 @@
-import { Component, OnInit } from '@angular/core';
-
-@Component({
-  selector: 'app-dashboard',
-  templateUrl: './dashboard.component.html',
-  styleUrls: ['./dashboard.component.scss']
-})
-export class DashboardComponent implements OnInit {
-
-  constructor() { }
-
-  ngOnInit() {
-  }
-
-}
diff --git a/src/app/depositAccount/deposit-account.component.html b/src/app/depositAccount/deposit-account.component.html
new file mode 100644
index 0000000..42ce16d
--- /dev/null
+++ b/src/app/depositAccount/deposit-account.component.html
@@ -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.
+-->
+
+<fims-layout-card-over title="{{'Manage deposit products' | translate}}">
+  <fims-data-table flex (onFetch)="fetchProducts($event)" (onActionCellClick)="rowSelect($event)" [columns]="columns" [data]="productData | async"></fims-data-table>
+</fims-layout-card-over>
+<fims-fab-button title="{{'Create new product' | translate}}" icon="add" [link]="['create']" [permission]="{ id: 'deposit_definitions', accessLevel: 'CHANGE'}"></fims-fab-button>
diff --git a/src/app/depositAccount/deposit-account.component.ts b/src/app/depositAccount/deposit-account.component.ts
new file mode 100644
index 0000000..4c5d500
--- /dev/null
+++ b/src/app/depositAccount/deposit-account.component.ts
@@ -0,0 +1,63 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, OnInit} from '@angular/core';
+import {ActivatedRoute, Router} from '@angular/router';
+import {TableData} from '../common/data-table/data-table.component';
+import {FetchRequest} from '../services/domain/paging/fetch-request.model';
+import * as fromDepositAccounts from './store';
+import {Observable} from 'rxjs/Observable';
+import {SEARCH} from './store/product.actions';
+import {DepositAccountStore} from './store/index';
+import {ProductDefinition} from '../services/depositAccount/domain/definition/product-definition.model';
+
+@Component({
+  templateUrl: './deposit-account.component.html'
+})
+export class DepositProductComponent implements OnInit {
+
+  productData: Observable<TableData>;
+
+  columns: any[] = [
+    { name: 'identifier', label: 'Id' },
+    { name: 'name', label: 'Name' },
+    { name: 'type', label: 'Type' },
+    { name: 'active', label: 'Enabled'},
+    { name: 'interest', label: 'Interest'}
+  ];
+
+  constructor(private router: Router, private route: ActivatedRoute, private store: DepositAccountStore) {}
+
+  ngOnInit(): void {
+    this.productData = this.store.select(fromDepositAccounts.getProductSearchResults)
+      .map(productPage => ({
+        data: productPage.products,
+        totalElements: productPage.totalElements,
+        totalPages: productPage.totalPages
+      }));
+    this.fetchProducts();
+  }
+
+  fetchProducts(fetchRequest?: FetchRequest): void {
+    this.store.dispatch({ type: SEARCH });
+  }
+
+  rowSelect(productDefinition: ProductDefinition): void {
+    this.router.navigate(['detail', productDefinition.identifier], { relativeTo: this.route });
+  }
+}
diff --git a/src/app/depositAccount/deposit-account.module.ts b/src/app/depositAccount/deposit-account.module.ts
new file mode 100644
index 0000000..f10d04b
--- /dev/null
+++ b/src/app/depositAccount/deposit-account.module.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 {Store} from '@ngrx/store';
+import {DepositAccountStore, depositAccountStoreFactory} from './store/index';
+import {DepositProductDefinitionNotificationEffects} from './store/effects/notification.effects';
+import {DepositProductDefinitionRouteEffects} from './store/effects/route.effects';
+import {DepositProductDefinitionApiEffects} from './store/effects/service.effects';
+import {EffectsModule} from '@ngrx/effects';
+import {CovalentMessageModule, CovalentStepsModule} from '@covalent/core';
+import {
+  MatButtonModule,
+  MatCardModule,
+  MatCheckboxModule,
+  MatIconModule,
+  MatInputModule,
+  MatListModule,
+  MatRadioModule,
+  MatSelectModule,
+  MatSlideToggleModule,
+  MatToolbarModule
+} from '@angular/material';
+import {CommonModule} from '@angular/common';
+import {TranslateModule} from '@ngx-translate/core';
+import {FimsSharedModule} from '../common/common.module';
+import {NgModule} from '@angular/core';
+import {RouterModule} from '@angular/router';
+import {FormsModule, ReactiveFormsModule} from '@angular/forms';
+import {DepositAccountRoutes} from './deposit-account.routes';
+import {ProductDefinitionExistsGuard} from './product-definition-exists.guard';
+import {DepositProductComponent} from './deposit-account.component';
+import {DepositProductCreateComponent} from './form/create.component';
+import {DepositProductFormComponent} from './form/form.component';
+import {DepositProductChargesFormComponent} from './form/charges/charges.component';
+import {DepositProductDetailComponent} from './detail/deposit-product.detail.component';
+import {DepositProductIndexComponent} from './detail/deposit-product.index.component';
+import {DepositProductEditComponent} from './form/edit.component';
+import {DepositProductDividendsComponent} from './detail/dividends/dividends.component';
+import {DepositProductDividendApiEffects} from './store/dividends/effects/service.effects';
+import {DepositProductDividendNotificationEffects} from './store/dividends/effects/notification.effects';
+import {DepositProductDividendRouteEffects} from './store/dividends/effects/route.effects';
+import {DividendFormComponent} from './detail/dividends/form/form.component';
+import {CreateDividendFormComponent} from './detail/dividends/form/create.component';
+
+@NgModule({
+  imports: [
+    RouterModule.forChild(DepositAccountRoutes),
+    FimsSharedModule,
+    TranslateModule,
+    CommonModule,
+    FormsModule,
+    ReactiveFormsModule,
+    MatCardModule,
+    MatIconModule,
+    MatListModule,
+    MatToolbarModule,
+    MatInputModule,
+    MatButtonModule,
+    MatSelectModule,
+    MatRadioModule,
+    MatCheckboxModule,
+    MatSlideToggleModule,
+
+    CovalentStepsModule,
+    CovalentMessageModule,
+
+    EffectsModule.run(DepositProductDefinitionApiEffects),
+    EffectsModule.run(DepositProductDefinitionRouteEffects),
+    EffectsModule.run(DepositProductDefinitionNotificationEffects),
+
+    EffectsModule.run(DepositProductDividendApiEffects),
+    EffectsModule.run(DepositProductDividendRouteEffects),
+    EffectsModule.run(DepositProductDividendNotificationEffects)
+  ],
+  declarations: [
+    DepositProductComponent,
+    DepositProductCreateComponent,
+    DepositProductEditComponent,
+    DepositProductFormComponent,
+    DepositProductChargesFormComponent,
+    DepositProductIndexComponent,
+    DepositProductDetailComponent,
+    DepositProductDividendsComponent,
+    CreateDividendFormComponent,
+    DividendFormComponent
+  ],
+  providers: [
+    ProductDefinitionExistsGuard,
+    { provide: DepositAccountStore, useFactory: depositAccountStoreFactory, deps: [Store]}
+  ]
+})
+export class DepositAccountModule {}
diff --git a/src/app/depositAccount/deposit-account.routes.ts b/src/app/depositAccount/deposit-account.routes.ts
new file mode 100644
index 0000000..0f8e7f3
--- /dev/null
+++ b/src/app/depositAccount/deposit-account.routes.ts
@@ -0,0 +1,66 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 {Routes} from '@angular/router';
+import {DepositProductComponent} from './deposit-account.component';
+import {DepositProductCreateComponent} from './form/create.component';
+import {DepositProductDetailComponent} from './detail/deposit-product.detail.component';
+import {ProductDefinitionExistsGuard} from './product-definition-exists.guard';
+import {DepositProductIndexComponent} from './detail/deposit-product.index.component';
+import {DepositProductEditComponent} from './form/edit.component';
+import {DepositProductDividendsComponent} from './detail/dividends/dividends.component';
+import {CreateDividendFormComponent} from './detail/dividends/form/create.component';
+
+export const DepositAccountRoutes: Routes = [
+  {
+    path: '',
+    component: DepositProductComponent,
+    data: { hasPermission: { id: 'deposit_definitions', accessLevel: 'READ' } },
+  },
+  {
+    path: 'create',
+    component: DepositProductCreateComponent,
+    data: { hasPermission: { id: 'deposit_definitions', accessLevel: 'CHANGE' } },
+  },
+  {
+    path: 'detail/:id',
+    component: DepositProductIndexComponent,
+    canActivate: [ProductDefinitionExistsGuard],
+    data: { hasPermission: { id: 'deposit_definitions', accessLevel: 'READ' } },
+    children: [
+      {
+        path: '',
+        component: DepositProductDetailComponent
+      },
+      {
+        path: 'dividends',
+        component: DepositProductDividendsComponent
+      },
+      {
+        path: 'dividends/distribute',
+        component: CreateDividendFormComponent,
+        data: { hasPermission: { id: 'deposit_definitions', accessLevel: 'CHANGE' } },
+      },
+      {
+        path: 'edit',
+        component: DepositProductEditComponent,
+        data: { hasPermission: { id: 'deposit_definitions', accessLevel: 'CHANGE' } },
+      }
+    ]
+  }
+];
diff --git a/src/app/depositAccount/detail/deposit-product.detail.component.html b/src/app/depositAccount/detail/deposit-product.detail.component.html
new file mode 100644
index 0000000..b9d2585
--- /dev/null
+++ b/src/app/depositAccount/detail/deposit-product.detail.component.html
@@ -0,0 +1,93 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+
+<fims-layout-card-over [title]="definition.name" [subTitle]="definition.description" [navigateBackTo]="['../../../']">
+  <fims-layout-card-over-header-menu>
+    <button mat-icon-button (click)="deleteProduct()" title="{{'Delete this product' | translate}}" *hasPermission="{ id: 'deposit_definitions', accessLevel: 'DELETE'}"><mat-icon>delete</mat-icon></button>
+  </fims-layout-card-over-header-menu>
+  <td-message *ngIf="!definition.active" label="{{'Product not enabled' | translate }}"
+              sublabel="{{'To assign this product to a member it needs to be enabled first' | translate }}"
+              color="warn" icon="error">
+    <button td-message-actions mat-button (click)="enableProduct()" *hasPermission="{ id: 'deposit_definitions', accessLevel: 'CHANGE'}" translate>ENABLE PRODUCT</button>
+  </td-message>
+  <td-message *ngIf="definition.active" label="{{'Product enabled' | translate }}"
+              sublabel="{{'This product can be assigned to a member ' | translate }}" color="accent" icon="check">
+    <button td-message-actions mat-button (click)="disableProduct()" *hasPermission="{ id: 'deposit_definitions', accessLevel: 'CHANGE'}" translate>DISABLE PRODUCT</button>
+  </td-message>
+  <fims-two-column-layout>
+    <mat-nav-list left *ngIf="canDistributeDividends$ | async">
+      <h3 mat-subheader translate>Management</h3>
+      <a mat-list-item [routerLink]="['./dividends']">
+        <mat-icon matListAvatar>zoom_out_map</mat-icon>
+        <h3 matLine translate>Dividends</h3>
+        <p matLine translate>View and distribute dividends</p>
+      </a>
+    </mat-nav-list>
+    <ng-container right>
+      <div layout="row">
+        <mat-list dense>
+          <mat-list-item>
+            <h3 matLine translate>Type</h3>
+            <p matLine>{{definition.type}}</p>
+          </mat-list-item>
+          <mat-list-item *ngIf="hasTerm(definition)">
+            <h3 matLine translate>Term</h3>
+            <p matLine>{{definition.term?.period + ' ' + definition.term?.timeUnit}}</p>
+          </mat-list-item>
+          <mat-list-item>
+            <h3 matLine translate>Interest payable</h3>
+            <p matLine>{{definition.term?.interestPayable}}</p>
+          </mat-list-item>
+          <mat-list-item>
+            <h3 matLine translate>Minimum balance</h3>
+            <p matLine>{{definition.minimumBalance | number:numberFormat}}</p>
+          </mat-list-item>
+          <mat-list-item>
+            <h3 matLine translate>Interest</h3>
+            <p matLine>{{definition.interest | number:numberFormat}}</p>
+          </mat-list-item>
+          <mat-list-item>
+            <h3 matLine translate>Currency</h3>
+            <p matLine>{{definition.currency.code}}</p>
+          </mat-list-item>
+          <mat-list-item>
+            <h3 matLine translate>Cash account</h3>
+            <p matLine>{{definition.cashAccountIdentifier}}</p>
+          </mat-list-item>
+          <mat-list-item>
+            <h3 matLine translate>Expense account</h3>
+            <p matLine>{{definition.expenseAccountIdentifier}}</p>
+          </mat-list-item>
+          <mat-list-item *ngIf="definition.accrueAccountIdentifier">
+            <h3 matLine translate>Accrue account</h3>
+            <p matLine>{{definition.accrueAccountIdentifier}}</p>
+          </mat-list-item>
+          <mat-list-item>
+            <h3 matLine translate>Equity ledger</h3>
+            <p matLine>{{definition.equityLedgerIdentifier}}</p>
+          </mat-list-item>
+        </mat-list>
+      </div>
+      <div layout="column">
+        <h4>Fees</h4>
+        <fims-data-table flex [data]="charges" [columns]="columns" [actionColumn]="false"></fims-data-table>
+      </div>
+    </ng-container>
+  </fims-two-column-layout>
+</fims-layout-card-over>
+<fims-fab-button title="{{'Edit product' | translate}}" icon="mode_edit" [link]="['edit']" [permission]="{ id: 'deposit_definitions', accessLevel: 'CHANGE'}"></fims-fab-button>
diff --git a/src/app/depositAccount/detail/deposit-product.detail.component.ts b/src/app/depositAccount/detail/deposit-product.detail.component.ts
new file mode 100644
index 0000000..0c514b2
--- /dev/null
+++ b/src/app/depositAccount/detail/deposit-product.detail.component.ts
@@ -0,0 +1,138 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, OnDestroy, OnInit} from '@angular/core';
+import {ProductDefinition} from '../../services/depositAccount/domain/definition/product-definition.model';
+import {Subscription} from 'rxjs/Subscription';
+import {ActivatedRoute, Router} from '@angular/router';
+import {DepositAccountStore} from '../store/index';
+import * as fromDepositAccounts from './../store';
+import * as fromRoot from '../../store';
+import {TableData} from '../../common/data-table/data-table.component';
+import {TdDialogService} from '@covalent/core';
+import {Observable} from 'rxjs/Observable';
+import {DELETE, EXECUTE_COMMAND} from '../store/product.actions';
+import {FimsPermission} from '../../services/security/authz/fims-permission.model';
+
+@Component({
+  templateUrl: './deposit-product.detail.component.html'
+})
+export class DepositProductDetailComponent implements OnInit, OnDestroy {
+
+  private productSubscription: Subscription;
+
+  numberFormat = '1.2-2';
+
+  definition: ProductDefinition;
+
+  canDistributeDividends$: Observable<boolean>;
+
+  charges: TableData;
+
+  columns: any[] = [
+    { name: 'name', label: 'Name' },
+    { name: 'description', label: 'Description' },
+    { name: 'actionIdentifier', label: 'Applied on' },
+    { name: 'proportional', label: 'Proportional?' },
+    { name: 'incomeAccountIdentifier', label: 'Income account' },
+    { name: 'amount', label: 'Amount', numeric: true, format: value => value ? value.toFixed(2) : undefined }
+  ];
+
+  constructor(private router: Router, private route: ActivatedRoute, private store: DepositAccountStore,
+              private dialogService: TdDialogService) {}
+
+  ngOnInit(): void {
+    const selectedProduct$ = this.store.select(fromDepositAccounts.getSelectedProduct)
+      .filter(product => !!product);
+
+    this.productSubscription = selectedProduct$
+      .subscribe(product => {
+        this.definition = product;
+        this.charges = {
+          data: product.charges,
+          totalElements: product.charges.length,
+          totalPages: 1
+        };
+      });
+
+    this.canDistributeDividends$ = Observable.combineLatest(
+      selectedProduct$,
+      this.store.select(fromRoot.getPermissions),
+      (product, permissions) => ({
+        isShareProduct: product.type === 'SHARE',
+        hasPermission: this.hasChangePermission(permissions)
+      })
+    ).map(result => result.isShareProduct && result.hasPermission);
+  }
+
+  ngOnDestroy(): void {
+    this.productSubscription.unsubscribe();
+  }
+
+  goToTasks(): void {
+    this.router.navigate(['tasks'], { relativeTo: this.route });
+  }
+
+  confirmDeletion(): Observable<boolean> {
+    return this.dialogService.openConfirm({
+      message: 'Do you want to delete this product?',
+      title: 'Confirm deletion',
+      acceptButton: 'DELETE PRODUCT',
+    }).afterClosed();
+  }
+
+  deleteProduct(): void {
+    this.confirmDeletion()
+      .filter(accept => accept)
+      .subscribe(() => this.store.dispatch({
+        type: DELETE, payload: {
+          productDefinition: this.definition,
+          activatedRoute: this.route
+        }
+      }));
+  }
+
+  hasTerm(defininition: ProductDefinition): boolean {
+    return !!defininition.term.timeUnit || !!defininition.term.period;
+  }
+
+  private hasChangePermission(permissions: FimsPermission[]): boolean {
+    return permissions.filter(permission =>
+      permission.id === 'deposit_definitions' &&
+      permission.accessLevel === 'CHANGE'
+    ).length > 0;
+  }
+
+  enableProduct(): void {
+    this.store.dispatch({ type: EXECUTE_COMMAND, payload: {
+      definitionId: this.definition.identifier,
+      command: {
+        action: 'ACTIVATE'
+      }
+    }});
+  }
+
+  disableProduct(): void {
+    this.store.dispatch({ type: EXECUTE_COMMAND, payload: {
+      definitionId: this.definition.identifier,
+      command: {
+        action: 'DEACTIVATE'
+      }
+    }});
+  }
+}
diff --git a/src/app/depositAccount/detail/deposit-product.index.component.html b/src/app/depositAccount/detail/deposit-product.index.component.html
new file mode 100644
index 0000000..ca721b3
--- /dev/null
+++ b/src/app/depositAccount/detail/deposit-product.index.component.html
@@ -0,0 +1,18 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<router-outlet></router-outlet>
diff --git a/src/app/depositAccount/detail/deposit-product.index.component.ts b/src/app/depositAccount/detail/deposit-product.index.component.ts
new file mode 100644
index 0000000..ed558ee
--- /dev/null
+++ b/src/app/depositAccount/detail/deposit-product.index.component.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 {Component, OnDestroy, OnInit} from '@angular/core';
+import {DepositAccountStore} from '../store/index';
+import {Subscription} from 'rxjs/Subscription';
+import {ActivatedRoute} from '@angular/router';
+import {SelectAction} from '../store/product.actions';
+
+@Component({
+  templateUrl: './deposit-product.index.component.html'
+})
+export class DepositProductIndexComponent implements OnInit, OnDestroy {
+
+  private actionsSubscription: Subscription;
+
+  constructor(private route: ActivatedRoute, private store: DepositAccountStore) {}
+
+  ngOnInit(): void {
+    this.actionsSubscription = this.route.params
+      .map(params => new SelectAction(params['id']))
+      .subscribe(this.store);
+  }
+
+  ngOnDestroy(): void {
+    this.actionsSubscription.unsubscribe();
+  }
+}
diff --git a/src/app/depositAccount/detail/dividends/dividends.component.html b/src/app/depositAccount/detail/dividends/dividends.component.html
new file mode 100644
index 0000000..58ef036
--- /dev/null
+++ b/src/app/depositAccount/detail/dividends/dividends.component.html
@@ -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.
+-->
+
+<fims-layout-card-over [title]="'Dividend distributions' | translate" [navigateBackTo]="['../']">
+  <fims-data-table flex [columns]="columns" [data]="dividendData$ | async" [actionColumn]="false"></fims-data-table>
+</fims-layout-card-over>
+<fims-fab-button title="{{'Distribute dividends' | translate}}" icon="zoom_out_map" [link]="['distribute']" [permission]="{ id: 'deposit_definitions', accessLevel: 'CHANGE'}"></fims-fab-button>
diff --git a/src/app/depositAccount/detail/dividends/dividends.component.ts b/src/app/depositAccount/detail/dividends/dividends.component.ts
new file mode 100644
index 0000000..7907943
--- /dev/null
+++ b/src/app/depositAccount/detail/dividends/dividends.component.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 {Component, OnDestroy, OnInit} from '@angular/core';
+import * as fromDepositAccounts from '../../store/index';
+import {DepositAccountStore} from '../../store/index';
+import {Observable} from 'rxjs/Observable';
+import {TableData} from '../../../common/data-table/data-table.component';
+import {LOAD_ALL} from '../../store/dividends/dividend.actions';
+import {Subscription} from 'rxjs/Subscription';
+import {DisplayFimsDate} from '../../../common/date/fims-date.pipe';
+
+@Component({
+  providers: [DisplayFimsDate],
+  templateUrl: './dividends.component.html'
+})
+export class DepositProductDividendsComponent implements OnInit, OnDestroy {
+
+  private productSubscription: Subscription;
+
+  private productIdentifer: string;
+
+  dividendData$: Observable<TableData>;
+
+  columns: any[] = [
+    { name: 'dueDate', label: 'Due date', format: value => this.displayFimsDate.transform(value) },
+    { name: 'dividendRate', label: 'Dividend rate' }
+  ];
+
+  constructor(private store: DepositAccountStore, private displayFimsDate: DisplayFimsDate) {}
+
+  ngOnInit() {
+    this.productSubscription = this.store.select(fromDepositAccounts.getSelectedProduct)
+      .filter(product => !!product)
+      .subscribe(product => this.productIdentifer = product.identifier);
+
+    this.dividendData$ = this.store.select(fromDepositAccounts.getDividends)
+      .map(dividends => ({
+        data: dividends,
+        totalElements: dividends.length,
+        totalPages: 1
+      }));
+
+    this.store.dispatch({
+      type: LOAD_ALL,
+      payload: this.productIdentifer
+    });
+  }
+
+  ngOnDestroy(): void {
+    this.productSubscription.unsubscribe();
+  }
+}
diff --git a/src/app/depositAccount/detail/dividends/form/create.component.html b/src/app/depositAccount/detail/dividends/form/create.component.html
new file mode 100644
index 0000000..0715c27
--- /dev/null
+++ b/src/app/depositAccount/detail/dividends/form/create.component.html
@@ -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.
+-->
+
+<fims-layout-card-over>
+  <fims-deposit-product-dividend-form
+    [productDefinitionId]="productDefinitionId$ | async"
+    (onSave)="save($event)"
+    (onCancel)="cancel()">
+  </fims-deposit-product-dividend-form>
+</fims-layout-card-over>
diff --git a/src/app/depositAccount/detail/dividends/form/create.component.ts b/src/app/depositAccount/detail/dividends/form/create.component.ts
new file mode 100644
index 0000000..72a7bc8
--- /dev/null
+++ b/src/app/depositAccount/detail/dividends/form/create.component.ts
@@ -0,0 +1,59 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, OnInit} from '@angular/core';
+import {DepositAccountStore} from '../../../store/index';
+import {CREATE} from '../../../store/dividends/dividend.actions';
+import * as fromDepositAccounts from './../../../store';
+import {ActivatedRoute, Router} from '@angular/router';
+import {Observable} from 'rxjs/Observable';
+import {DistributeDividendFormData} from './form.component';
+
+@Component({
+  templateUrl: './create.component.html'
+})
+export class CreateDividendFormComponent implements OnInit {
+
+  productDefinitionId$: Observable<string>;
+
+  constructor(private router: Router, private route: ActivatedRoute, private store: DepositAccountStore) {}
+
+  ngOnInit() {
+    this.productDefinitionId$ = this.store.select(fromDepositAccounts.getSelectedProduct)
+      .filter(product => !!product)
+      .map(product => product.identifier);
+  }
+
+  save(payload: DistributeDividendFormData): void {
+    this.store.dispatch({
+      type: CREATE,
+      payload: {
+        productDefinitionId: payload.productDefinitionId,
+        dividendDistribution: {
+          dueDate: payload.dueDate,
+          dividendRate: payload.dividendRate
+        },
+        activatedRoute: this.route
+      }
+    });
+  }
+
+  cancel(): void {
+    this.router.navigate(['../'], { relativeTo: this.route });
+  }
+}
diff --git a/src/app/depositAccount/detail/dividends/form/form.component.html b/src/app/depositAccount/detail/dividends/form/form.component.html
new file mode 100644
index 0000000..9810f92
--- /dev/null
+++ b/src/app/depositAccount/detail/dividends/form/form.component.html
@@ -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.
+-->
+
+<td-steps mode="'vertical'">
+  <td-step #detailsStep label="{{'Distribute dividend' | translate}}" [state]="form.valid ? 'complete' : form.pristine ? 'none' : 'required'" [active]="true">
+    <form [formGroup]="form" layout="column">
+      <fims-date-input [form]="form" controlName="dueDate" placeholder="{{'Due date' | translate}}"></fims-date-input>
+      <fims-number-input [form]="form" controlName="dividendRate" placeholder="{{'Dividend rate' | translate}}"></fims-number-input>
+    </form>
+  </td-step>
+
+  <td-step label="{{'Final step' | translate}}" [state]="'complete'">
+    <ng-template td-step-summary>
+      <button mat-raised-button color="primary" (click)="save()" [disabled]="form.invalid">{{'DISTRIBUTE DIVIDEND'}}</button>
+      <span flex></span>
+      <button mat-button (click)="cancel()">{{'CANCEL' | translate}}</button>
+    </ng-template>
+  </td-step>
+</td-steps>
diff --git a/src/app/depositAccount/detail/dividends/form/form.component.ts b/src/app/depositAccount/detail/dividends/form/form.component.ts
new file mode 100644
index 0000000..da2653f
--- /dev/null
+++ b/src/app/depositAccount/detail/dividends/form/form.component.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 {Component, EventEmitter, Input, OnInit, Output} from '@angular/core';
+import {FormBuilder, FormGroup, Validators} from '@angular/forms';
+import {FimsValidators} from '../../../../common/validator/validators';
+import {todayAsISOString, toFimsDate} from '../../../../services/domain/date.converter';
+
+export interface DistributeDividendFormData {
+  productDefinitionId: string;
+  dueDate: {
+    year?: number;
+    month?: number;
+    day?: number;
+  };
+  dividendRate: string;
+}
+
+@Component({
+  selector: 'fims-deposit-product-dividend-form',
+  templateUrl: './form.component.html'
+})
+export class DividendFormComponent implements OnInit {
+
+  @Input() productDefinitionId: string;
+
+  form: FormGroup;
+
+  @Output() onSave = new EventEmitter<DistributeDividendFormData>();
+
+  @Output() onCancel = new EventEmitter<void>();
+
+  constructor(private formBuilder: FormBuilder) {}
+
+  ngOnInit(): void {
+    this.form = this.formBuilder.group({
+      dueDate: [todayAsISOString(), [Validators.required]],
+      dividendRate: ['', [ Validators.required, FimsValidators.minValue(0)] ]
+    });
+  }
+
+  save(): void {
+    this.onSave.emit({
+      productDefinitionId: this.productDefinitionId,
+      dueDate: toFimsDate(this.form.get('dueDate').value),
+      dividendRate: this.form.get('dividendRate').value
+    });
+  }
+
+  cancel(): void {
+    this.onCancel.emit();
+  }
+}
diff --git a/src/app/depositAccount/domain/interest-payable-option-list.model.ts b/src/app/depositAccount/domain/interest-payable-option-list.model.ts
new file mode 100644
index 0000000..4719307
--- /dev/null
+++ b/src/app/depositAccount/domain/interest-payable-option-list.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 {InterestPayable} from '../../services/depositAccount/domain/interest-payable.model';
+
+export interface InterestPayableOption {
+  label: string;
+  type: InterestPayable;
+}
+
+export const interestPayableOptionList: InterestPayableOption[] = [
+  { type: 'MATURITY', label: 'Maturity'},
+  { type: 'ANNUALLY', label: 'Annually'},
+  { type: 'QUARTERLY', label: 'Quarterly'},
+  { type: 'MONTHLY', label: 'Monthly'}
+];
diff --git a/src/app/depositAccount/domain/time-unit-option-list.model.ts b/src/app/depositAccount/domain/time-unit-option-list.model.ts
new file mode 100644
index 0000000..b61d898
--- /dev/null
+++ b/src/app/depositAccount/domain/time-unit-option-list.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 {TimeUnit} from '../../services/depositAccount/domain/time-unit.model';
+
+export interface TimeUnitOption {
+  label: string;
+  type: TimeUnit;
+}
+
+export const timeUnitOptionList: TimeUnitOption[] = [
+  { type: 'MONTH', label: 'Month'},
+  { type: 'YEAR', label: 'Year'},
+];
diff --git a/src/app/depositAccount/domain/type-option-list.model.ts b/src/app/depositAccount/domain/type-option-list.model.ts
new file mode 100644
index 0000000..6d9a4a8
--- /dev/null
+++ b/src/app/depositAccount/domain/type-option-list.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 {Type} from '../../services/depositAccount/domain/type.model';
+
+export interface TypeOption {
+  label: string;
+  type: Type;
+}
+
+export const typeOptionList: TypeOption[] = [
+  { type: 'CHECKING', label: 'Checking'},
+  { type: 'SAVINGS', label: 'Savings'},
+  { type: 'SHARE', label: 'Share'}
+];
diff --git a/src/app/depositAccount/form/charges/charges.component.html b/src/app/depositAccount/form/charges/charges.component.html
new file mode 100644
index 0000000..f2daa6b
--- /dev/null
+++ b/src/app/depositAccount/form/charges/charges.component.html
@@ -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.
+-->
+
+<form [formGroup]="form">
+  <div layout-gt-xs="column" layout-margin formArrayName="charges">
+    <mat-card *ngFor="let charge of charges; let i=index" [formGroupName]="i">
+      <mat-card-content layout="column">
+        <fims-text-input [form]="charge" controlName="name" placeholder="{{'Fee name' | translate}}"></fims-text-input>
+        <mat-form-field layout-margin flex>
+          <textarea matInput placeholder="{{'Description' | translate}}" formControlName="description"></textarea>
+          <mat-error *ngIf="charge.get('description').hasError('maxlength')">
+            {{ 'Only characters allowed.' | translate:{ value: charge.get('description').getError('maxlength')['requiredLength']} }}
+          </mat-error>
+        </mat-form-field>
+        <div layout="row">
+          <fims-number-input [form]="getFormGroup(i)" controlName="amount" placeholder="{{'Amount' | translate}}"></fims-number-input>
+          <mat-form-field layout-margin>
+            <mat-select formControlName="actionIdentifier" placeholder="{{'Applied on' | translate}}">
+              <mat-option *ngFor="let basis of actions" [value]="basis.identifier">
+                {{basis.name | translate}}
+              </mat-option>
+            </mat-select>
+          </mat-form-field>
+          <mat-checkbox layout-margin formControlName="proportional" translate>Proportional?</mat-checkbox>
+        </div>
+        <fims-account-select title="{{'Income account' | translate}}" formControlName="incomeAccountIdentifier" [type]="'REVENUE'">
+          <ng-container *ngIf="!charge.get('incomeAccountIdentifier').pristine && charge.get('incomeAccountIdentifier').hasError('required')" translate>
+            Required
+          </ng-container>
+          <ng-container *ngIf="charge.get('incomeAccountIdentifier').hasError('invalidAccount')" translate>
+            Invalid account
+          </ng-container>
+        </fims-account-select>
+      </mat-card-content>
+      <mat-card-actions>
+        <button mat-button (click)="removeCharge(i)">{{'REMOVE FEE' | translate}}</button>
+      </mat-card-actions>
+    </mat-card>
+
+    <div layout="row">
+      <button flex mat-button mat-raised-button (click)="addCharge()">{{'ADD FEE' | translate}}</button>
+    </div>
+  </div>
+</form>
diff --git a/src/app/depositAccount/form/charges/charges.component.ts b/src/app/depositAccount/form/charges/charges.component.ts
new file mode 100644
index 0000000..bc3a6e6
--- /dev/null
+++ b/src/app/depositAccount/form/charges/charges.component.ts
@@ -0,0 +1,93 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, Input} from '@angular/core';
+import {AbstractControl, FormArray, FormBuilder, FormGroup, Validators} from '@angular/forms';
+import {Charge} from '../../../services/depositAccount/domain/definition/charge.model';
+import {FormComponent} from '../../../common/forms/form.component';
+import {Action} from '../../../services/depositAccount/domain/definition/action.model';
+import {accountExists} from '../../../common/validator/account-exists.validator';
+import {AccountingService} from '../../../services/accounting/accounting.service';
+import {FimsValidators} from '../../../common/validator/validators';
+
+@Component({
+  selector: 'fims-deposit-product-charges-form',
+  templateUrl: './charges.component.html'
+})
+export class DepositProductChargesFormComponent extends FormComponent<Charge[]> {
+
+  @Input('actions') actions: Action[];
+
+  @Input('formData') set formData(charges: Charge[]) {
+    charges = charges || [];
+    this.form = this.formBuilder.group({
+      charges: this.initCharges(charges),
+    });
+  }
+
+  constructor(private formBuilder: FormBuilder, private accountingService: AccountingService) {
+    super();
+  }
+
+  get formData(): Charge[] {
+    const charges = this.form.get('charges').value;
+
+    return charges.map(charge => Object.assign({}, charge, {
+      amount: parseFloat(charge.amount)
+    }));
+  }
+
+  private initCharges(charges: Charge[]): FormArray {
+    const formControls: FormGroup[] = [];
+    charges.forEach(charge => formControls.push(this.initCharge(charge)));
+    return this.formBuilder.array(formControls);
+  }
+
+  private initCharge(charge?: Charge): FormGroup {
+    const amount = charge ? charge.amount : 0;
+
+    return this.formBuilder.group({
+      actionIdentifier: [charge ? charge.actionIdentifier : '', Validators.required],
+      incomeAccountIdentifier: [charge ? charge.incomeAccountIdentifier : '', [Validators.required], accountExists(this.accountingService)],
+      name: [charge ? charge.name : '', [Validators.required, Validators.maxLength(256)]],
+      description: [charge ? charge.description : '', Validators.maxLength(2048)],
+      proportional: [charge ? charge.proportional : false ],
+      amount: [amount.toFixed(2), [ FimsValidators.minValue(0)] ]
+    });
+  }
+
+  addCharge(): void {
+    const charges: FormArray = this.form.get('charges') as FormArray;
+    charges.push(this.initCharge());
+  }
+
+  removeCharge(index: number): void {
+    const charges: FormArray = this.form.get('charges') as FormArray;
+    charges.removeAt(index);
+  }
+
+  get charges(): AbstractControl[] {
+    const charges: FormArray = this.form.get('charges') as FormArray;
+    return charges.controls;
+  }
+
+  getFormGroup(index: number): FormGroup {
+    const charges = this.form.get('charges') as FormArray;
+    return charges.at(index) as FormGroup;
+  }
+}
diff --git a/src/app/depositAccount/form/create.component.html b/src/app/depositAccount/form/create.component.html
new file mode 100644
index 0000000..64f7e85
--- /dev/null
+++ b/src/app/depositAccount/form/create.component.html
@@ -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.
+-->
+
+<fims-layout-card-over title="{{'Create new deposit product' | translate}}">
+  <fims-deposit-product-form #form
+                               (onSave)="onSave($event)"
+                               (onCancel)="onCancel()"
+                               [definition]="definition"
+                               [currencies]="currencies | async"
+                               [actions]="actions | async">
+  </fims-deposit-product-form>
+</fims-layout-card-over>
diff --git a/src/app/depositAccount/form/create.component.ts b/src/app/depositAccount/form/create.component.ts
new file mode 100644
index 0000000..8bcf444
--- /dev/null
+++ b/src/app/depositAccount/form/create.component.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 {Component, OnDestroy, OnInit, ViewChild} from '@angular/core';
+import {ActivatedRoute, Router} from '@angular/router';
+import {DepositAccountStore} from '../store/index';
+import {CREATE, RESET_FORM} from '../store/product.actions';
+import {Subscription} from 'rxjs/Subscription';
+import * as fromDepositAccount from '../store';
+import {Error} from '../../services/domain/error.model';
+import {ProductDefinition} from '../../services/depositAccount/domain/definition/product-definition.model';
+import {DepositProductFormComponent} from './form.component';
+import {CurrencyService} from '../../services/currency/currency.service';
+import {DepositAccountService} from '../../services/depositAccount/deposit-account.service';
+import {Currency} from '../../services/currency/domain/currency.model';
+import {Action} from '../../services/depositAccount/domain/definition/action.model';
+import {Observable} from 'rxjs/Observable';
+
+@Component({
+  templateUrl: './create.component.html'
+})
+export class DepositProductCreateComponent implements OnInit, OnDestroy {
+
+  private formStateSubscription: Subscription;
+
+  @ViewChild('form') formComponent: DepositProductFormComponent;
+
+  definition: ProductDefinition = {
+    type: 'CHECKING',
+    identifier: '',
+    name: '',
+    interest: 0,
+    charges: [],
+    currency: {
+      code: 'USD',
+      name: '',
+      scale: 2,
+      sign: ''
+    },
+    flexible: false,
+    minimumBalance: 0,
+    cashAccountIdentifier: '',
+    expenseAccountIdentifier: '',
+    term: {
+      interestPayable: 'ANNUALLY'
+    }
+  };
+
+  currencies: Observable<Currency[]>;
+
+  actions: Observable<Action[]>;
+
+  constructor(private router: Router, private route: ActivatedRoute, private depositStore: DepositAccountStore,
+              private depositService: DepositAccountService, private currencyService: CurrencyService) {}
+
+  ngOnInit(): void {
+    this.currencies = this.currencyService.fetchCurrencies();
+    this.actions = this.depositService.fetchActions();
+
+    this.formStateSubscription = this.depositStore.select(fromDepositAccount.getProductFormError)
+      .filter((error: Error) => !!error)
+      .subscribe((error: Error) => {
+        const detailForm = this.formComponent.formGroup;
+        const errors = detailForm.get('identifier').errors || {};
+        errors['unique'] = true;
+        detailForm.get('identifier').setErrors(errors);
+        this.formComponent.step.open();
+      });
+  }
+
+  ngOnDestroy(): void {
+    this.formStateSubscription.unsubscribe();
+
+    this.depositStore.dispatch({ type: RESET_FORM });
+  }
+
+  onSave(productDefinition: ProductDefinition): void {
+    this.depositStore.dispatch({ type: CREATE, payload: {
+      productDefinition,
+      activatedRoute: this.route
+    }});
+  }
+
+  onCancel() {
+    this.navigateAway();
+  }
+
+  navigateAway(): void {
+    this.router.navigate(['../'], { relativeTo: this.route });
+  }
+
+}
diff --git a/src/app/depositAccount/form/edit.component.html b/src/app/depositAccount/form/edit.component.html
new file mode 100644
index 0000000..d93938b
--- /dev/null
+++ b/src/app/depositAccount/form/edit.component.html
@@ -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.
+-->
+
+<fims-layout-card-over title="{{'Edit deposit product' | translate}}">
+  <fims-deposit-product-form #form
+                             (onSave)="onSave($event)"
+                             (onCancel)="onCancel()"
+                             [definition]="definition$ | async"
+                             [currencies]="currencies | async"
+                             [actions]="actions | async"
+                             [editMode]="true">
+  </fims-deposit-product-form>
+</fims-layout-card-over>
diff --git a/src/app/depositAccount/form/edit.component.ts b/src/app/depositAccount/form/edit.component.ts
new file mode 100644
index 0000000..745d771
--- /dev/null
+++ b/src/app/depositAccount/form/edit.component.ts
@@ -0,0 +1,73 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, OnDestroy, OnInit, ViewChild} from '@angular/core';
+import {ActivatedRoute, Router} from '@angular/router';
+import {DepositAccountStore} from '../store/index';
+import {RESET_FORM, UPDATE} from '../store/product.actions';
+import * as fromDepositAccount from '../store';
+import {ProductDefinition} from '../../services/depositAccount/domain/definition/product-definition.model';
+import {DepositProductFormComponent} from './form.component';
+import {CurrencyService} from '../../services/currency/currency.service';
+import {DepositAccountService} from '../../services/depositAccount/deposit-account.service';
+import {Currency} from '../../services/currency/domain/currency.model';
+import {Action} from '../../services/depositAccount/domain/definition/action.model';
+import {Observable} from 'rxjs/Observable';
+
+@Component({
+  templateUrl: './edit.component.html'
+})
+export class DepositProductEditComponent implements OnInit, OnDestroy {
+
+  @ViewChild('form') formComponent: DepositProductFormComponent;
+
+  definition$: Observable<ProductDefinition>;
+
+  currencies: Observable<Currency[]>;
+
+  actions: Observable<Action[]>;
+
+  constructor(private router: Router, private route: ActivatedRoute, private depositStore: DepositAccountStore,
+              private depositService: DepositAccountService, private currencyService: CurrencyService) {}
+
+  ngOnInit(): void {
+    this.currencies = this.currencyService.fetchCurrencies();
+    this.actions = this.depositService.fetchActions();
+    this.definition$ = this.depositStore.select(fromDepositAccount.getSelectedProduct);
+  }
+
+  ngOnDestroy(): void {
+    this.depositStore.dispatch({ type: RESET_FORM });
+  }
+
+  onSave(productDefinition: ProductDefinition): void {
+    this.depositStore.dispatch({ type: UPDATE, payload: {
+      productDefinition,
+      activatedRoute: this.route
+    }});
+  }
+
+  onCancel() {
+    this.navigateAway();
+  }
+
+  navigateAway(): void {
+    this.router.navigate(['../'], { relativeTo: this.route });
+  }
+
+}
diff --git a/src/app/depositAccount/form/form.component.html b/src/app/depositAccount/form/form.component.html
new file mode 100644
index 0000000..46c5bd6
--- /dev/null
+++ b/src/app/depositAccount/form/form.component.html
@@ -0,0 +1,134 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<td-steps mode="'vertical'">
+  <td-step #detailsStep label="{{'Product details' | translate}}" [state]="formGroup.valid ? 'complete' : formGroup.pristine ? 'none' : 'required'">
+    <form [formGroup]="formGroup" layout="column">
+      <div layout="row">
+        <mat-radio-group formControlName="type">
+          <mat-radio-button *ngFor="let basis of typeOptions" [value]="basis.type" layout-margin>
+            {{ basis.label | translate }}
+          </mat-radio-button>
+        </mat-radio-group>
+      </div>
+      <div layout="row">
+        <fims-id-input placeholder="Short name" [form]="formGroup" controlName="identifier" [readonly]="editMode"></fims-id-input>
+      </div>
+      <div layout="row">
+        <fims-text-input [form]="formGroup" controlName="name" placeholder="{{'Name' | translate}}"></fims-text-input>
+      </div>
+      <div layout="row">
+        <mat-form-field layout-margin flex>
+          <textarea matInput placeholder="{{'Description(optional)' | translate}}" formControlName="description"></textarea>
+          <mat-error *ngIf="formGroup.get('description').hasError('maxlength')">
+            {{ 'Only characters allowed.' | translate:{ value: formGroup.get('description').getError('maxlength')['requiredLength']} }}
+          </mat-error>
+        </mat-form-field>
+      </div>
+      <div layout="row">
+        <mat-form-field layout-margin>
+          <mat-select formControlName="currencyCode" placeholder="{{ 'Currency' | translate }}">
+            <mat-option *ngFor="let currency of currencies" [value]="currency.code">
+              {{currency.code}}
+            </mat-option>
+          </mat-select>
+        </mat-form-field>
+      </div>
+      <div layout="row">
+        <fims-number-input [form]="formGroup" controlName="minimumBalance" placeholder="{{'Minimum balance' | translate}}"></fims-number-input>
+      </div>
+      <div layout="column">
+        <span translate>Interest payable:</span>
+        <mat-radio-group formControlName="termInterestPayable">
+          <mat-radio-button *ngFor="let basis of interestPayableOptions" [value]="basis.type" layout-margin>
+            {{ basis.label | translate }}
+          </mat-radio-button>
+        </mat-radio-group>
+      </div>
+      <div layout="row" layout-margin>
+        <mat-checkbox formControlName="flexible" [disabled]="editMode" translate>Flexible interest during the term?</mat-checkbox>
+      </div>
+      <div layout="row">
+        <fims-number-input [form]="formGroup" controlName="interest" placeholder="{{'Interest' | translate}}"></fims-number-input>
+      </div>
+      <div layout="row">
+        <mat-slide-toggle formControlName="fixedTermEnabled" layout-margin translate>
+          Fixed term?
+        </mat-slide-toggle>
+      </div>
+      <div layout="row">
+        <fims-text-input type="number" [form]="formGroup" controlName="termPeriod" placeholder="{{'Term period' | translate}}"></fims-text-input>
+        <mat-radio-group formControlName="termTimeUnit">
+          <mat-radio-button *ngFor="let basis of timeUnitOptions" [value]="basis.type" layout-margin>
+            {{ basis.label | translate }}
+          </mat-radio-button>
+        </mat-radio-group>
+      </div>
+      <fims-account-select title="{{'Cash account(Asset accounts only)' | translate}}" formControlName="cashAccountIdentifier" [type]="'ASSET'">
+        <ng-container *ngIf="!formGroup.get('cashAccountIdentifier').pristine && formGroup.get('cashAccountIdentifier').hasError('required')" translate>
+          Required
+        </ng-container>
+        <ng-container *ngIf="formGroup.get('cashAccountIdentifier').hasError('invalidAccount')" translate>
+          Invalid account
+        </ng-container>
+      </fims-account-select>
+      <fims-account-select title="{{'Expense account(Expense accounts only)' | translate}}" formControlName="expenseAccountIdentifier" [type]="'EXPENSE'">
+        <ng-container *ngIf="!formGroup.get('expenseAccountIdentifier').pristine && formGroup.get('expenseAccountIdentifier').hasError('required')" translate>
+          Required
+        </ng-container>
+        <ng-container *ngIf="formGroup.get('expenseAccountIdentifier').hasError('invalidAccount')" translate>
+          Invalid account
+        </ng-container>
+      </fims-account-select>
+      <fims-account-select title="{{'Accrue account(Liability accounts only)' | translate}}" formControlName="accrueAccountIdentifier" [type]="'LIABILITY'" *ngIf="formGroup.get('accrueAccountIdentifier').status !== 'DISABLED'">
+        <ng-container *ngIf="!formGroup.get('accrueAccountIdentifier').pristine && formGroup.get('accrueAccountIdentifier').hasError('required')" translate>
+          Required
+        </ng-container>
+        <ng-container *ngIf="formGroup.get('accrueAccountIdentifier').hasError('invalidAccount')" translate>
+          Invalid account
+        </ng-container>
+      </fims-account-select>
+      <fims-ledger-select title="{{'Equity ledger(Equity ledgers only)' | translate}}" formControlName="equityLedgerIdentifier" [type]="'EQUITY'">
+        <ng-container *ngIf="!formGroup.get('equityLedgerIdentifier').pristine && formGroup.get('equityLedgerIdentifier').hasError('required')" translate>
+          Required
+        </ng-container>
+        <ng-container *ngIf="formGroup.get('equityLedgerIdentifier').hasError('invalidLedger')" translate>
+          Invalid ledger
+        </ng-container>
+      </fims-ledger-select>
+    </form>
+    <ng-template td-step-actions>
+      <fims-form-continue-action (onContinue)="chargesStep.open()"></fims-form-continue-action>
+    </ng-template>
+  </td-step>
+
+  <td-step #chargesStep label="{{'Fees' | translate}}" [state]="chargesForm.valid ? 'complete' : chargesForm.pristine ? 'none' : 'required'">
+    <fims-deposit-product-charges-form #chargesForm [actions]="actions" [formData]="charges"></fims-deposit-product-charges-form>
+  </td-step>
+
+  <td-step label="{{'Final step' | translate}}" [state]="'complete'">
+    <ng-template td-step-summary>
+      <fims-form-final-action
+        [resourceName]="'PRODUCT'"
+        [editMode]="editMode"
+        [disabled]="!isValid"
+        (onCancel)="cancel()"
+        (onSave)="save()">
+      </fims-form-final-action>
+    </ng-template>
+  </td-step>
+</td-steps>
diff --git a/src/app/depositAccount/form/form.component.ts b/src/app/depositAccount/form/form.component.ts
new file mode 100644
index 0000000..71cdba3
--- /dev/null
+++ b/src/app/depositAccount/form/form.component.ts
@@ -0,0 +1,222 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, OnChanges, OnDestroy, OnInit, Output, SimpleChanges, ViewChild} from '@angular/core';
+import {ProductDefinition} from '../../services/depositAccount/domain/definition/product-definition.model';
+import {TdStepComponent} from '@covalent/core';
+import {AsyncValidatorFn, FormBuilder, FormControl, FormGroup, ValidatorFn, Validators} from '@angular/forms';
+import {FimsValidators} from '../../common/validator/validators';
+import {interestPayableOptionList} from '../domain/interest-payable-option-list.model';
+import {timeUnitOptionList} from '../domain/time-unit-option-list.model';
+import {DepositProductChargesFormComponent} from './charges/charges.component';
+import {Charge} from '../../services/depositAccount/domain/definition/charge.model';
+import {Currency} from '../../services/currency/domain/currency.model';
+import {Action} from '../../services/depositAccount/domain/definition/action.model';
+import {typeOptionList} from '../domain/type-option-list.model';
+import {accountExists} from '../../common/validator/account-exists.validator';
+import {AccountingService} from '../../services/accounting/accounting.service';
+import {ledgerExists} from '../../common/validator/ledger-exists.validator';
+import {Subscription} from 'rxjs/Subscription';
+import {Type} from '../../services/depositAccount/domain/type.model';
+
+@Component({
+  selector: 'fims-deposit-product-form',
+  templateUrl: './form.component.html'
+})
+export class DepositProductFormComponent implements OnInit, OnDestroy, OnChanges {
+
+  private termChangeSubscription: Subscription;
+
+  private typeChangeSubscription: Subscription;
+
+  interestPayableOptions = interestPayableOptionList;
+
+  timeUnitOptions = timeUnitOptionList;
+
+  typeOptions = typeOptionList;
+
+  formGroup: FormGroup;
+
+  @ViewChild('detailsStep') step: TdStepComponent;
+
+  @ViewChild('chargesForm') chargesForm: DepositProductChargesFormComponent;
+  charges: Charge[];
+
+  @Input('editMode') editMode: boolean;
+
+  @Input('definition') definition: ProductDefinition;
+
+  @Input('currencies') currencies: Currency[];
+
+  @Input('actions') actions: Action[];
+
+  @Output('onSave') onSave = new EventEmitter<ProductDefinition>();
+
+  @Output('onCancel') onCancel = new EventEmitter<void>();
+
+  constructor(private formBuilder: FormBuilder, private accountingService: AccountingService) {
+    this.formGroup = this.formBuilder.group({
+      identifier: ['', [Validators.required, Validators.minLength(3), Validators.maxLength(32), FimsValidators.urlSafe]],
+      type: ['', [Validators.required]],
+      name: ['', [Validators.required, Validators.maxLength(256)]],
+      description: ['', Validators.maxLength(2048)],
+      currencyCode: ['', [Validators.required]],
+      minimumBalance: ['', [Validators.required]],
+      fixedTermEnabled: [false],
+      interest: ['', [Validators.required, FimsValidators.minValue(0)]],
+      flexible: ['', [Validators.required]],
+      termPeriod: [''],
+      termTimeUnit: [''],
+      termInterestPayable: ['', [Validators.required]],
+      cashAccountIdentifier: ['', [Validators.required], accountExists(this.accountingService)],
+      expenseAccountIdentifier: ['', [Validators.required], accountExists(this.accountingService)],
+      equityLedgerIdentifier: ['', [Validators.required], ledgerExists(this.accountingService)],
+      accrueAccountIdentifier: ['', [Validators.required], accountExists(this.accountingService)]
+    });
+
+    this.termChangeSubscription = this.formGroup.get('fixedTermEnabled').valueChanges
+      .startWith(null)
+      .subscribe(enabled => this.toggleFixedTerm(enabled));
+
+    this.typeChangeSubscription = this.formGroup.get('type').valueChanges
+      .startWith(null)
+      .subscribe(type => this.toggleType(type));
+  }
+
+  ngOnInit(): void {
+    this.step.open();
+  }
+
+  ngOnDestroy(): void {
+    this.termChangeSubscription.unsubscribe();
+    this.typeChangeSubscription.unsubscribe();
+  }
+
+  ngOnChanges(changes: SimpleChanges): void {
+    this.charges = this.definition.charges;
+
+    const interestDisabled = this.editMode && !this.definition.flexible;
+
+    const fixedTermEnabled: boolean = this.hasPeriodOrTimeUnit(this.definition);
+
+    this.formGroup.reset({
+      identifier: this.definition.identifier,
+      type: this.definition.type,
+      name: this.definition.name,
+      description: this.definition.description,
+      currencyCode: this.definition.currency.code,
+      minimumBalance: this.definition.minimumBalance.toFixed(2),
+      fixedTermEnabled: fixedTermEnabled,
+      interest: { value: this.definition.interest.toFixed(2), disabled: interestDisabled },
+      flexible: { value: this.definition.flexible, disabled: this.editMode },
+      termPeriod: this.definition.term.period,
+      termTimeUnit: this.definition.term.timeUnit,
+      termInterestPayable: this.definition.term.interestPayable,
+      cashAccountIdentifier: this.definition.cashAccountIdentifier,
+      expenseAccountIdentifier: this.definition.expenseAccountIdentifier,
+      accrueAccountIdentifier: this.definition.accrueAccountIdentifier,
+      equityLedgerIdentifier: this.definition.equityLedgerIdentifier
+    });
+  }
+
+  toggleFixedTerm(enabled: boolean): void {
+    const termPeriodControl: FormControl = this.formGroup.get('termPeriod') as FormControl;
+    const termTimeUnitControl: FormControl = this.formGroup.get('termTimeUnit') as FormControl;
+
+    if (enabled) {
+      this.enable(termPeriodControl, [Validators.required, FimsValidators.minValue(1), FimsValidators.maxScale(0)]);
+      this.enable(termTimeUnitControl, [Validators.required]);
+    } else {
+      this.disable(termPeriodControl);
+      this.disable(termTimeUnitControl);
+    }
+  }
+
+  toggleType(type: Type): void {
+    const enableAccrueAccount = type !== 'SHARE';
+
+    const accrueAccountControl: FormControl = this.formGroup.get('accrueAccountIdentifier') as FormControl;
+
+    if (enableAccrueAccount) {
+      this.enable(accrueAccountControl, [Validators.required], accountExists(this.accountingService));
+    } else {
+      this.disable(accrueAccountControl);
+    }
+  }
+
+  private enable(formControl: FormControl, validators: ValidatorFn[], asyncValidator?: AsyncValidatorFn): void {
+    formControl.enable();
+    formControl.setValidators(validators);
+    formControl.setAsyncValidators(asyncValidator);
+    formControl.updateValueAndValidity();
+  }
+
+  private disable(formControl: FormControl): void {
+    formControl.disable();
+    formControl.clearValidators();
+    formControl.updateValueAndValidity();
+  }
+
+  hasPeriodOrTimeUnit(product: ProductDefinition): boolean {
+    return !!product.term.timeUnit || !!product.term.period;
+  }
+
+  save(): void {
+    const foundCurrency = this.currencies.find(currency => currency.code === this.formGroup.get('currencyCode').value);
+
+    const isShare = this.formGroup.get('type').value === 'SHARE';
+
+    const fixedTerm: boolean = this.formGroup.get('fixedTermEnabled').value === true;
+
+    const definition: ProductDefinition = {
+      identifier: this.formGroup.get('identifier').value,
+      type: this.formGroup.get('type').value,
+      name: this.formGroup.get('name').value,
+      description: this.formGroup.get('description').value,
+      minimumBalance: parseFloat(this.formGroup.get('minimumBalance').value),
+      interest: parseFloat(this.formGroup.get('interest').value),
+      flexible: this.formGroup.get('flexible').value,
+      term: {
+        period: fixedTerm ? this.formGroup.get('termPeriod').value : undefined,
+        timeUnit: fixedTerm ? this.formGroup.get('termTimeUnit').value : undefined,
+        interestPayable: this.formGroup.get('termInterestPayable').value
+      },
+      currency: {
+        code: foundCurrency.code,
+        name: foundCurrency.name,
+        sign: foundCurrency.sign,
+        scale: foundCurrency.digits
+      },
+      charges: this.chargesForm.formData,
+      expenseAccountIdentifier: this.formGroup.get('expenseAccountIdentifier').value,
+      equityLedgerIdentifier: this.formGroup.get('equityLedgerIdentifier').value,
+      cashAccountIdentifier: this.formGroup.get('cashAccountIdentifier').value,
+      accrueAccountIdentifier: !isShare ? this.formGroup.get('accrueAccountIdentifier').value : undefined
+    };
+
+    this.onSave.emit(definition);
+  }
+
+  cancel(): void {
+    this.onCancel.emit();
+  }
+
+  get isValid(): boolean {
+    return this.formGroup.valid && this.chargesForm.valid;
+  }
+}
diff --git a/src/app/depositAccount/product-definition-exists.guard.ts b/src/app/depositAccount/product-definition-exists.guard.ts
new file mode 100644
index 0000000..6f184de
--- /dev/null
+++ b/src/app/depositAccount/product-definition-exists.guard.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 {of} from 'rxjs/observable/of';
+import {Observable} from 'rxjs/Observable';
+import {LoadAction} from './store/product.actions';
+import {ActivatedRouteSnapshot, CanActivate, RouterStateSnapshot} from '@angular/router';
+import {Injectable} from '@angular/core';
+import * as fromProducts from './store/index';
+import {DepositAccountStore} from './store/index';
+import {DepositAccountService} from '../services/depositAccount/deposit-account.service';
+import {ExistsGuardService} from '../common/guards/exists-guard';
+
+@Injectable()
+export class ProductDefinitionExistsGuard implements CanActivate {
+
+  constructor(private store: DepositAccountStore,
+              private accountService: DepositAccountService,
+              private existsGuardService: ExistsGuardService) {}
+
+  hasProductInStore(id: string): Observable<boolean> {
+    const timestamp$ = this.store.select(fromProducts.getProductsLoadedAt)
+      .map(loadedAt => loadedAt[id]);
+
+    return this.existsGuardService.isWithinExpiry(timestamp$);
+  }
+
+  hasProductInApi(id: string): Observable<boolean> {
+    const getProduct = this.accountService.findProductDefinition(id)
+      .map(productEntity => new LoadAction({
+        resource: productEntity
+      }))
+      .do((action: LoadAction) => this.store.dispatch(action))
+      .map(product => !!product);
+
+    return this.existsGuardService.routeTo404OnError(getProduct);
+  }
+
+  hasProduct(id: string): Observable<boolean> {
+    return this.hasProductInStore(id)
+      .switchMap(inStore => {
+        if (inStore) {
+          return of(inStore);
+        }
+
+        return this.hasProductInApi(id);
+      });
+  }
+
+  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
+    return this.hasProduct(route.params['id']);
+  }
+}
diff --git a/src/app/depositAccount/store/dividends/dividend.actions.ts b/src/app/depositAccount/store/dividends/dividend.actions.ts
new file mode 100644
index 0000000..10cec0b
--- /dev/null
+++ b/src/app/depositAccount/store/dividends/dividend.actions.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 {type} from '../../../store/util';
+import {RoutePayload} from '../../../common/store/route-payload';
+import {Action} from '@ngrx/store';
+import {DividendDistribution} from '../../../services/depositAccount/domain/definition/dividend-distribution.model';
+
+export const LOAD_ALL = type('[Deposit Product Definition Dividend] Load All');
+export const LOAD_ALL_COMPLETE = type('[Deposit Product Definition Dividend] Load All Complete');
+
+export const CREATE = type('[Deposit Product Definition Dividend] Create');
+export const CREATE_SUCCESS = type('[Deposit Product Definition Dividend] Create Success');
+export const CREATE_FAIL = type('[Deposit Product Definition Dividend] Create Fail');
+
+export interface DividendPayload extends RoutePayload {
+  productDefinitionId: string;
+  dividendDistribution: DividendDistribution;
+}
+
+export class LoadAllAction implements Action {
+  readonly type = LOAD_ALL;
+
+  constructor(public payload: string) { }
+}
+
+export class LoadAllCompleteAction implements Action {
+  readonly type = LOAD_ALL_COMPLETE;
+
+  constructor(public payload: DividendDistribution[]) { }
+}
+
+export class CreateDividendDistributionAction implements Action {
+  readonly type = CREATE;
+
+  constructor(public payload: DividendPayload) { }
+}
+
+export class CreateDividendDistributionSuccessAction implements Action {
+  readonly type = CREATE_SUCCESS;
+
+  constructor(public payload: DividendPayload) { }
+}
+
+export class CreateDividendDistributionFailAction implements Action {
+  readonly type = CREATE_FAIL;
+
+  constructor(public payload: Error) { }
+}
+
+export type Actions
+  = LoadAllAction
+  | LoadAllCompleteAction
+  | CreateDividendDistributionAction
+  | CreateDividendDistributionSuccessAction
+  | CreateDividendDistributionFailAction;
diff --git a/src/app/depositAccount/store/dividends/dividends.reducer.ts b/src/app/depositAccount/store/dividends/dividends.reducer.ts
new file mode 100644
index 0000000..c84996e
--- /dev/null
+++ b/src/app/depositAccount/store/dividends/dividends.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 {DividendDistribution} from '../../../services/depositAccount/domain/definition/dividend-distribution.model';
+import * as dividend from './dividend.actions';
+
+export interface State {
+  entities: DividendDistribution[];
+}
+
+export const initialState: State = {
+  entities: []
+};
+
+export function reducer(state = initialState, action: dividend.Actions): State {
+
+  switch (action.type) {
+
+    case dividend.LOAD_ALL: {
+      return initialState;
+    }
+
+    case dividend.LOAD_ALL_COMPLETE: {
+      const entities: DividendDistribution[] = action.payload;
+
+      return {
+        entities
+      };
+    }
+
+    case dividend.CREATE_SUCCESS: {
+      const entity: DividendDistribution = action.payload.dividendDistribution;
+
+      return {
+        entities: [...state.entities, entity]
+      };
+    }
+
+    default: {
+      return state;
+    }
+  }
+}
+
+export const getDividends = (state: State) => state.entities;
+
diff --git a/src/app/depositAccount/store/dividends/effects/notification.effects.ts b/src/app/depositAccount/store/dividends/effects/notification.effects.ts
new file mode 100644
index 0000000..a303e4d
--- /dev/null
+++ b/src/app/depositAccount/store/dividends/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 {NotificationService, NotificationType} from '../../../../services/notification/notification.service';
+import {Action} from '@ngrx/store';
+import {Observable} from 'rxjs/Observable';
+import {Actions, Effect} from '@ngrx/effects';
+import {Injectable} from '@angular/core';
+import * as dividendActions from '../dividend.actions';
+
+@Injectable()
+export class DepositProductDividendNotificationEffects {
+
+  @Effect({dispatch: false})
+  createDividendDistributionSuccess$: Observable<Action> = this.actions$
+    .ofType(dividendActions.CREATE_SUCCESS)
+    .do(() => this.notificationService.send({
+      type: NotificationType.MESSAGE,
+      message: 'Dividend is going to be distributed'
+    }));
+
+  constructor(private actions$: Actions, private notificationService: NotificationService) {}
+
+}
diff --git a/src/app/depositAccount/store/dividends/effects/route.effects.ts b/src/app/depositAccount/store/dividends/effects/route.effects.ts
new file mode 100644
index 0000000..2e24c90
--- /dev/null
+++ b/src/app/depositAccount/store/dividends/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 * as dividendActions from '../dividend.actions';
+import {Action} from '@ngrx/store';
+import {Observable} from 'rxjs/Observable';
+import {Actions, Effect} from '@ngrx/effects';
+import {Router} from '@angular/router';
+import {Injectable} from '@angular/core';
+
+@Injectable()
+export class DepositProductDividendRouteEffects {
+
+  @Effect({dispatch: false})
+  createDividendDistributionSuccess$: Observable<Action> = this.actions$
+    .ofType(dividendActions.CREATE_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/depositAccount/store/dividends/effects/service.effects.ts b/src/app/depositAccount/store/dividends/effects/service.effects.ts
new file mode 100644
index 0000000..9b817cb
--- /dev/null
+++ b/src/app/depositAccount/store/dividends/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 {DepositAccountService} from '../../../../services/depositAccount/deposit-account.service';
+import {Actions, Effect} from '@ngrx/effects';
+import {Injectable} from '@angular/core';
+import {Observable} from 'rxjs/Observable';
+import {Action} from '@ngrx/store';
+import {of} from 'rxjs/observable/of';
+import * as dividendActions from '../dividend.actions';
+
+@Injectable()
+export class DepositProductDividendApiEffects {
+
+  @Effect()
+  loadAll$: Observable<Action> = this.actions$
+    .ofType(dividendActions.LOAD_ALL)
+    .switchMap((action) => {
+      return this.depositService.fetchDividendDistributions(action.payload)
+        .map(dividendDistributions => new dividendActions.LoadAllCompleteAction(dividendDistributions))
+        .catch(() => of(new dividendActions.LoadAllCompleteAction([])));
+    });
+
+  @Effect()
+  createDividendDistribution$: Observable<Action> = this.actions$
+    .ofType(dividendActions.CREATE)
+    .map((action: dividendActions.CreateDividendDistributionAction) => action.payload)
+    .mergeMap(payload =>
+      this.depositService.distributeDividend(payload.productDefinitionId, payload.dividendDistribution)
+        .map(() => new dividendActions.CreateDividendDistributionSuccessAction(payload))
+        .catch((error) => of(new dividendActions.CreateDividendDistributionFailAction(error)))
+    );
+
+  constructor(private actions$: Actions, private depositService: DepositAccountService) {
+  }
+
+}
diff --git a/src/app/depositAccount/store/effects/notification.effects.ts b/src/app/depositAccount/store/effects/notification.effects.ts
new file mode 100644
index 0000000..ed5fb83
--- /dev/null
+++ b/src/app/depositAccount/store/effects/notification.effects.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 {Injectable} from '@angular/core';
+import {Actions, Effect} from '@ngrx/effects';
+import {NotificationService, NotificationType} from '../../../services/notification/notification.service';
+import {Observable} from 'rxjs/Observable';
+import {Action} from '@ngrx/store';
+import * as definitionActions from '../product.actions';
+
+@Injectable()
+export class DepositProductDefinitionNotificationEffects {
+
+  @Effect({dispatch: false})
+  createProductDefinitionSuccess$: Observable<Action> = this.actions$
+    .ofType(definitionActions.CREATE_SUCCESS, definitionActions.UPDATE_SUCCESS)
+    .do(() => this.notificationService.send({
+      type: NotificationType.MESSAGE,
+      message: 'Product is going to be saved'
+    }));
+
+  @Effect({dispatch: false})
+  deleteProductDefinitionSuccess$: Observable<Action> = this.actions$
+    .ofType(definitionActions.DELETE_SUCCESS)
+    .do(() => this.notificationService.send({
+      type: NotificationType.MESSAGE,
+      message: 'Product is going to be deleted'
+    }));
+
+  @Effect({dispatch: false})
+  deleteProductDefinitionFail$: Observable<Action> = this.actions$
+    .ofType(definitionActions.DELETE_FAIL)
+    .do(() => this.notificationService.send({
+      type: NotificationType.ALERT,
+      message: 'Product is already assigned to a member.'
+    }));
+
+  @Effect({dispatch: false})
+  executeCommandSuccess$: Observable<Action> = this.actions$
+    .ofType(definitionActions.EXECUTE_COMMAND_SUCCESS)
+    .do(() => this.notificationService.send({
+      type: NotificationType.MESSAGE,
+      message: 'Product is going to be updated'
+    }));
+
+  constructor(private actions$: Actions, private notificationService: NotificationService) {}
+}
diff --git a/src/app/depositAccount/store/effects/route.effects.ts b/src/app/depositAccount/store/effects/route.effects.ts
new file mode 100644
index 0000000..ea79dc0
--- /dev/null
+++ b/src/app/depositAccount/store/effects/route.effects.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 {Action} from '@ngrx/store';
+import {Observable} from 'rxjs/Observable';
+import {Injectable} from '@angular/core';
+import {Actions, Effect} from '@ngrx/effects';
+import {Router} from '@angular/router';
+import * as definitionActions from '../product.actions';
+
+@Injectable()
+export class DepositProductDefinitionRouteEffects {
+
+  @Effect({ dispatch: false })
+  createProductDefinitionSuccess$: Observable<Action> = this.actions$
+    .ofType(definitionActions.CREATE_SUCCESS, definitionActions.UPDATE_SUCCESS)
+    .map(action => action.payload)
+    .do(payload => this.router.navigate(['../'], { relativeTo: payload.activatedRoute }));
+
+  @Effect({ dispatch: false })
+  deleteProductDefinitionSuccess$: Observable<Action> = this.actions$
+    .ofType(definitionActions.DELETE_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/depositAccount/store/effects/service.effects.ts b/src/app/depositAccount/store/effects/service.effects.ts
new file mode 100644
index 0000000..f169348
--- /dev/null
+++ b/src/app/depositAccount/store/effects/service.effects.ts
@@ -0,0 +1,98 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 definitionActions from '../product.actions';
+import {Action} from '@ngrx/store';
+import {Observable} from 'rxjs/Observable';
+import {Actions, Effect} from '@ngrx/effects';
+import {Injectable} from '@angular/core';
+import {DepositAccountService} from '../../../services/depositAccount/deposit-account.service';
+import {of} from 'rxjs/observable/of';
+import {emptySearchResult} from '../../../common/store/search.reducer';
+
+@Injectable()
+export class DepositProductDefinitionApiEffects {
+
+  @Effect()
+  search$: Observable<Action> = this.actions$
+    .ofType(definitionActions.SEARCH)
+    .debounceTime(300)
+    .switchMap(() => {
+      const nextSearch$ = this.actions$.ofType(definitionActions.SEARCH).skip(1);
+
+      return this.depositService.fetchProductDefinitions()
+        .takeUntil(nextSearch$)
+        .map(products => new definitionActions.SearchCompleteAction({
+          elements: products,
+          totalElements: products.length,
+          totalPages: 1
+        }))
+        .catch(() => of(new definitionActions.SearchCompleteAction(emptySearchResult())));
+    });
+
+  @Effect()
+  createProduct$: Observable<Action> = this.actions$
+    .ofType(definitionActions.CREATE)
+    .map((action: definitionActions.CreateProductDefinitionAction) => action.payload)
+    .mergeMap(payload =>
+      this.depositService.createProductDefinition(payload.productDefinition)
+        .map(() => new definitionActions.CreateProductDefinitionSuccessAction({
+          resource: payload.productDefinition,
+          activatedRoute: payload.activatedRoute
+        }))
+        .catch((error) => of(new definitionActions.CreateProductDefinitionFailAction(error)))
+    );
+
+  @Effect()
+  updateProduct$: Observable<Action> = this.actions$
+    .ofType(definitionActions.UPDATE)
+    .map((action: definitionActions.UpdateProductDefinitionAction) => action.payload)
+    .mergeMap(payload =>
+      this.depositService.updateProductDefinition(payload.productDefinition)
+        .map(() => new definitionActions.UpdateProductDefinitionSuccessAction({
+          resource: payload.productDefinition,
+          activatedRoute: payload.activatedRoute
+        }))
+        .catch((error) => of(new definitionActions.UpdateProductDefinitionFailAction(error)))
+    );
+
+  @Effect()
+  deleteProduct$: Observable<Action> = this.actions$
+    .ofType(definitionActions.DELETE)
+    .map((action: definitionActions.DeleteProductDefinitionAction) => action.payload)
+    .mergeMap(payload =>
+      this.depositService.deleteProductDefinition(payload.productDefinition.identifier)
+        .map(() => new definitionActions.DeleteProductDefinitionSuccessAction({
+          resource: payload.productDefinition,
+          activatedRoute: payload.activatedRoute
+        }))
+        .catch((error) => of(new definitionActions.DeleteProductDefinitionFailAction(error)))
+    );
+
+  @Effect()
+  executeCommand$: Observable<Action> = this.actions$
+    .ofType(definitionActions.EXECUTE_COMMAND)
+    .map((action: definitionActions.ExecuteCommandAction) => action.payload)
+    .mergeMap(payload =>
+      this.depositService.processCommand(payload.definitionId, payload.command)
+        .map(() => new definitionActions.ExecuteCommandSuccessAction(payload))
+        .catch((error) => of(new definitionActions.ExecuteCommandFailAction(error)))
+    );
+
+  constructor(private actions$: Actions, private depositService: DepositAccountService) { }
+}
diff --git a/src/app/depositAccount/store/index.ts b/src/app/depositAccount/store/index.ts
new file mode 100644
index 0000000..9d1b0f1
--- /dev/null
+++ b/src/app/depositAccount/store/index.ts
@@ -0,0 +1,88 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 {ActionReducer, Store} from '@ngrx/store';
+import {createReducer} from '../../store/index';
+import {createFormReducer, FormState, getFormError} from '../../common/store/form.reducer';
+import {createResourceReducer, getResourceLoadedAt, getResourceSelected, ResourceState} from '../../common/store/resource.reducer';
+
+import * as fromRoot from '../../store';
+import * as fromProducts from './products.reducer';
+import * as fromDividends from './dividends/dividends.reducer';
+import {createSelector} from 'reselect';
+import {
+  createSearchReducer,
+  getSearchEntities,
+  getSearchTotalElements,
+  getSearchTotalPages,
+  SearchState
+} from '../../common/store/search.reducer';
+
+export interface State extends fromRoot.State {
+  depositProducts: ResourceState;
+  depositProductForm: FormState;
+  depositProductSearch: SearchState;
+  depositProductDividends: fromDividends.State;
+}
+
+const reducers = {
+  depositProducts: createResourceReducer('Deposit Product Definition', fromProducts.reducer),
+  depositProductForm: createFormReducer('Deposit Product Definition'),
+  depositProductSearch: createSearchReducer('Deposit Product Definition'),
+  depositProductDividends: fromDividends.reducer
+};
+
+export const depositAccountModuleReducer: ActionReducer<State> = createReducer(reducers);
+
+export class DepositAccountStore extends Store<State> {}
+
+export function depositAccountStoreFactory(appStore: Store<fromRoot.State>) {
+  appStore.replaceReducer(depositAccountModuleReducer);
+  return appStore;
+}
+
+export const getProductsState = (state: State) => state.depositProducts;
+
+export const getProductFormState = (state: State) => state.depositProductForm;
+export const getProductFormError = createSelector(getProductFormState, getFormError);
+
+export const getProductsLoadedAt = createSelector(getProductsState, getResourceLoadedAt);
+export const getSelectedProduct = createSelector(getProductsState, getResourceSelected);
+
+/**
+ * Product search selector
+ */
+export const getProductSearchState = (state: State) => state.depositProductSearch;
+
+export const getSearchProducts = createSelector(getProductSearchState, getSearchEntities);
+export const getProductSearchTotalElements = createSelector(getProductSearchState, getSearchTotalElements);
+export const getProductSearchTotalPages = createSelector(getProductSearchState, getSearchTotalPages);
+
+export const getProductSearchResults = createSelector(getSearchProducts, getProductSearchTotalPages, getProductSearchTotalElements,
+  (products, totalPages, totalElements) => {
+  return {
+    products: products,
+    totalPages: totalPages,
+    totalElements: totalElements
+  };
+});
+
+
+export const getProductDividendsState = (state: State) => state.depositProductDividends;
+
+export const getDividends = createSelector(getProductDividendsState, fromDividends.getDividends);
diff --git a/src/app/depositAccount/store/product.actions.ts b/src/app/depositAccount/store/product.actions.ts
new file mode 100644
index 0000000..0f19505
--- /dev/null
+++ b/src/app/depositAccount/store/product.actions.ts
@@ -0,0 +1,178 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 {RoutePayload} from '../../common/store/route-payload';
+import {ProductDefinition} from '../../services/depositAccount/domain/definition/product-definition.model';
+import {Action} from '@ngrx/store';
+import {SearchResult} from '../../common/store/search.reducer';
+import {
+  CreateResourceSuccessPayload,
+  DeleteResourceSuccessPayload,
+  LoadResourcePayload,
+  SelectResourcePayload,
+  UpdateResourceSuccessPayload
+} from '../../common/store/resource.reducer';
+import {ProductDefinitionCommand} from '../../services/depositAccount/domain/definition/product-definition-command.model';
+
+export const SEARCH = type('[Deposit Product Definition] Search');
+export const SEARCH_COMPLETE = type('[Deposit Product Definition] Search Complete');
+
+export const LOAD = type('[Deposit Product Definition] Load');
+export const SELECT = type('[Deposit Product Definition] Select');
+
+export const CREATE = type('[Deposit Product Definition] Create');
+export const CREATE_SUCCESS = type('[Deposit Product Definition] Create Success');
+export const CREATE_FAIL = type('[Deposit Product Definition] Create Fail');
+
+export const UPDATE = type('[Deposit Product Definition] Update');
+export const UPDATE_SUCCESS = type('[Deposit Product Definition] Update Success');
+export const UPDATE_FAIL = type('[Deposit Product Definition] Update Fail');
+
+export const DELETE = type('[Deposit Product Definition] Delete');
+export const DELETE_SUCCESS = type('[Deposit Product Definition] Delete Success');
+export const DELETE_FAIL = type('[Deposit Product Definition] Delete Fail');
+
+export const RESET_FORM = type('[Deposit Product Definition] Reset Form');
+
+export const EXECUTE_COMMAND = type('[Deposit Product Definition] Execute Command');
+export const EXECUTE_COMMAND_SUCCESS = type('[Deposit Product Definition] Execute Command Success');
+export const EXECUTE_COMMAND_FAIL = type('[Deposit Product Definition] Execute Command Fail');
+
+export interface ProductDefinitionRoutePayload extends RoutePayload {
+  productDefinition: ProductDefinition;
+}
+
+export interface ExecuteCommandPayload extends RoutePayload {
+  definitionId: string;
+  command: ProductDefinitionCommand;
+}
+
+export class SearchAction implements Action {
+  readonly type = SEARCH;
+
+  constructor() { }
+}
+
+export class SearchCompleteAction implements Action {
+  readonly type = SEARCH_COMPLETE;
+
+  constructor(public payload: SearchResult) { }
+}
+
+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 CreateProductDefinitionAction implements Action {
+  readonly type = CREATE;
+
+  constructor(public payload: ProductDefinitionRoutePayload) { }
+}
+
+export class CreateProductDefinitionSuccessAction implements Action {
+  readonly type = CREATE_SUCCESS;
+
+  constructor(public payload: CreateResourceSuccessPayload) { }
+}
+
+export class CreateProductDefinitionFailAction implements Action {
+  readonly type = CREATE_FAIL;
+
+  constructor(public payload: Error) { }
+}
+
+export class UpdateProductDefinitionAction implements Action {
+  readonly type = UPDATE;
+
+  constructor(public payload: ProductDefinitionRoutePayload) { }
+}
+
+export class UpdateProductDefinitionSuccessAction implements Action {
+  readonly type = UPDATE_SUCCESS;
+
+  constructor(public payload: UpdateResourceSuccessPayload) { }
+}
+
+export class UpdateProductDefinitionFailAction implements Action {
+  readonly type = UPDATE_FAIL;
+
+  constructor(public payload: Error) { }
+}
+
+export class DeleteProductDefinitionAction implements Action {
+  readonly type = DELETE;
+
+  constructor(public payload: ProductDefinitionRoutePayload) { }
+}
+
+export class DeleteProductDefinitionSuccessAction implements Action {
+  readonly type = DELETE_SUCCESS;
+
+  constructor(public payload: DeleteResourceSuccessPayload) { }
+}
+
+export class DeleteProductDefinitionFailAction implements Action {
+  readonly type = DELETE_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: Error) { }
+}
+
+export type Actions
+  = SearchAction
+  | SearchCompleteAction
+  | LoadAction
+  | SelectAction
+  | CreateProductDefinitionAction
+  | CreateProductDefinitionSuccessAction
+  | CreateProductDefinitionFailAction
+  | UpdateProductDefinitionAction
+  | UpdateProductDefinitionSuccessAction
+  | UpdateProductDefinitionFailAction
+  | DeleteProductDefinitionAction
+  | DeleteProductDefinitionSuccessAction
+  | DeleteProductDefinitionFailAction
+  | ExecuteCommandAction
+  | ExecuteCommandSuccessAction
+  | ExecuteCommandFailAction;
diff --git a/src/app/depositAccount/store/products.reducer.ts b/src/app/depositAccount/store/products.reducer.ts
new file mode 100644
index 0000000..0421c7d
--- /dev/null
+++ b/src/app/depositAccount/store/products.reducer.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 * as productActions from '../store/product.actions';
+import {ResourceState} from '../../common/store/resource.reducer';
+import {ProductDefinitionCommand} from '../../services/depositAccount/domain/definition/product-definition-command.model';
+
+export const initialState: ResourceState = {
+  ids: [],
+  entities: {},
+  loadedAt: {},
+  selectedId: null,
+};
+
+export function reducer(state = initialState, action: productActions.Actions): ResourceState {
+
+  switch (action.type) {
+
+    case productActions.EXECUTE_COMMAND_SUCCESS: {
+      const payload = action.payload;
+
+      const definitionId = payload.definitionId;
+      const command: ProductDefinitionCommand = payload.command;
+
+      const definition = state.entities[definitionId];
+
+      let active = false;
+
+      if (command.action === 'ACTIVATE') {
+        active = true;
+      }
+
+      definition.active = active;
+
+      return {
+        ids: [ ...state.ids ],
+        entities: Object.assign({}, state.entities, {
+          [definition.identifier]: definition
+        }),
+        loadedAt: state.loadedAt,
+        selectedId: state.selectedId
+      };
+    }
+
+    default: {
+      return state;
+    }
+  }
+}
diff --git a/src/app/employee/add-employee/add-employee.component.html b/src/app/employee/add-employee/add-employee.component.html
deleted file mode 100644
index 4689635..0000000
--- a/src/app/employee/add-employee/add-employee.component.html
+++ /dev/null
@@ -1,63 +0,0 @@
-<div class="main-div mat-elevation-z2">
-  <h3 class="heading">Add Employee</h3>
-  <mat-divider></mat-divider>
-  <span>EMPLOYEE DETAILS</span>
-<form class="fineract-form">
-  <mat-form-field>
-    <input matInput placeholder="username">
-  </mat-form-field>
-<br/>
-  <mat-form-field>
-    <input matInput placeholder="Firstname">
-  </mat-form-field>
-<br/>
-  <mat-form-field>
-    <input matInput placeholder="Middlename">
-  </mat-form-field>
-  <br/>
-  <mat-form-field>
-    <input matInput placeholder="Lastname">
-  </mat-form-field>
-  <br/>
-  <mat-form-field>
-    <mat-select placeholder="Role">
-      <mat-option *ngFor="let office of offices" [value]="office.value">
-        {{ office.viewValue }}
-      </mat-option>
-    </mat-select>
-  </mat-form-field>
-  <br/>
-  <mat-form-field>
-    <mat-select placeholder="Office(optional)">
-      <mat-option *ngFor="let office of offices" [value]="office.value">
-        {{ office.viewValue }}
-      </mat-option>
-    </mat-select>
-  </mat-form-field>
-  <br/>
-  <mat-form-field>
-    <input matInput placeholder="Enter your password" [type]="hide ? 'password' : 'text'">
-   
-  </mat-form-field>
-
-  </form>
-  
-<span>EMPLOYEE CONTACT(OPTIONAL)</span>
-<form class="fineract-form">
-<mat-form-field>
-  <input matInput placeholder="Email">
-</mat-form-field>
-<br/>
-<mat-form-field>
-  <input matInput placeholder="Phone">
-</mat-form-field>
-<br/>
-<mat-form-field>
-  <input matInput placeholder="Mobile">
-</mat-form-field>
-</form>
-  <div>
-  <button mat-raised-button class="button1" color="primary">Submit</button>
-  <button mat-raised-button class="button2" color="warn">Cancel</button>
-  </div>
-</div>
\ No newline at end of file
diff --git a/src/app/employee/add-employee/add-employee.component.scss b/src/app/employee/add-employee/add-employee.component.scss
deleted file mode 100644
index 7109ed8..0000000
--- a/src/app/employee/add-employee/add-employee.component.scss
+++ /dev/null
@@ -1,13 +0,0 @@
-.fineract-form {
-    min-width: 150px;
-    max-width: 500px;
-    width: 100%;
-  }
-  .button1{
-      margin-left: 50%;
-      
-      }
-  .button1, .button2{
-      width:20px;
-      display: inline-block;
-  }
\ No newline at end of file
diff --git a/src/app/employee/add-employee/add-employee.component.spec.ts b/src/app/employee/add-employee/add-employee.component.spec.ts
deleted file mode 100644
index 87be9fe..0000000
--- a/src/app/employee/add-employee/add-employee.component.spec.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-import { async, ComponentFixture, TestBed } from '@angular/core/testing';
-
-import { AddEmployeeComponent } from './add-employee.component';
-
-describe('AddEmployeeComponent', () => {
-  let component: AddEmployeeComponent;
-  let fixture: ComponentFixture<AddEmployeeComponent>;
-
-  beforeEach(async(() => {
-    TestBed.configureTestingModule({
-      declarations: [ AddEmployeeComponent ]
-    })
-    .compileComponents();
-  }));
-
-  beforeEach(() => {
-    fixture = TestBed.createComponent(AddEmployeeComponent);
-    component = fixture.componentInstance;
-    fixture.detectChanges();
-  });
-
-  it('should create', () => {
-    expect(component).toBeTruthy();
-  });
-});
diff --git a/src/app/employee/add-employee/add-employee.component.ts b/src/app/employee/add-employee/add-employee.component.ts
deleted file mode 100644
index 526ce35..0000000
--- a/src/app/employee/add-employee/add-employee.component.ts
+++ /dev/null
@@ -1,27 +0,0 @@
-import { Component, OnInit } from '@angular/core';
-
-@Component({
-  selector: 'app-add-employee',
-  templateUrl: './add-employee.component.html',
-  styleUrls: ['./add-employee.component.scss']
-})
-export class AddEmployeeComponent implements OnInit {
-
-  hide = true;
-
-  offices = [
-    {value: 'office-1', viewValue: 'Head office'},
-   
-  ];
-
-  employees = [
-    {value: '', viewValue: ''},
-   
-  ];
-
-  constructor() { }
-
-  ngOnInit() {
-  }
-
-}
diff --git a/src/app/employee/manage-employee/manage-employee.component.html b/src/app/employee/manage-employee/manage-employee.component.html
deleted file mode 100644
index c6b2e94..0000000
--- a/src/app/employee/manage-employee/manage-employee.component.html
+++ /dev/null
@@ -1,42 +0,0 @@
-<div class="main-div mat-elevation-z2">
-  <h3 class="heading">Manage Employees</h3>
-  <mat-divider></mat-divider>
-  <div class="fineract-button">
-  <a mat-raised-button  color="primary" [routerLink]="['/navbar/add_employee']">
-    <mat-icon>add</mat-icon>Add Employee</a>
-    </div>
-  <mat-form-field>
-    <input matInput (keyup)="applyFilter($event.target.value)" placeholder="Filter by firstName/lastName">
-  </mat-form-field>
-  
-<br>
-<mat-divider></mat-divider>
-
-<mat-table #table [dataSource]="dataSource">
-
-  <!-- Position Column -->
-  <ng-container matColumnDef="id">
-    <mat-header-cell *matHeaderCellDef>Code</mat-header-cell>
-    <mat-cell *matCellDef="let element">
-      <a routerLink="{{element.id}}">{{element.id}}</a>
-    </mat-cell>
-  </ng-container>
-
-  <!-- Name Column -->
-  <ng-container matColumnDef="firstName">
-    <mat-header-cell *matHeaderCellDef>First Name</mat-header-cell>
-    <mat-cell *matCellDef="let element"> {{element.firstName}} </mat-cell>
-  </ng-container>
-
-  <ng-container matColumnDef="lastName">
-      <mat-header-cell *matHeaderCellDef>Last Name</mat-header-cell>
-      <mat-cell *matCellDef="let element"> {{element.lastName}} </mat-cell>
-    </ng-container>
-
-  <mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
-  <mat-row *matRowDef="let row; columns: displayedColumns;"></mat-row>
-</mat-table>
-  
-  
-</div>
-  
diff --git a/src/app/employee/manage-employee/manage-employee.component.scss b/src/app/employee/manage-employee/manage-employee.component.scss
deleted file mode 100644
index 87b71f6..0000000
--- a/src/app/employee/manage-employee/manage-employee.component.scss
+++ /dev/null
@@ -1,16 +0,0 @@
-.my-div{
-    margin-left: 2%;
-    margin-right:2%;
-    border-radius: 5px 5px 5px 5px;
-    background-color: #e6e6ff;
-    min-height: 100%;
-   
-  }
-  table {
-      width: 100%;
-    }
-    
-    .mat-form-field {
-      font-size: 14px;
-      width: 100%;
-    }
\ No newline at end of file
diff --git a/src/app/employee/manage-employee/manage-employee.component.spec.ts b/src/app/employee/manage-employee/manage-employee.component.spec.ts
deleted file mode 100644
index 8ebc029..0000000
--- a/src/app/employee/manage-employee/manage-employee.component.spec.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-import { async, ComponentFixture, TestBed } from '@angular/core/testing';
-
-import { ManageEmployeeComponent } from './manage-employee.component';
-
-describe('ManageEmployeeComponent', () => {
-  let component: ManageEmployeeComponent;
-  let fixture: ComponentFixture<ManageEmployeeComponent>;
-
-  beforeEach(async(() => {
-    TestBed.configureTestingModule({
-      declarations: [ ManageEmployeeComponent ]
-    })
-    .compileComponents();
-  }));
-
-  beforeEach(() => {
-    fixture = TestBed.createComponent(ManageEmployeeComponent);
-    component = fixture.componentInstance;
-    fixture.detectChanges();
-  });
-
-  it('should create', () => {
-    expect(component).toBeTruthy();
-  });
-});
diff --git a/src/app/employee/manage-employee/manage-employee.component.ts b/src/app/employee/manage-employee/manage-employee.component.ts
deleted file mode 100644
index ea62ebe..0000000
--- a/src/app/employee/manage-employee/manage-employee.component.ts
+++ /dev/null
@@ -1,35 +0,0 @@
-import { Component, OnInit } from '@angular/core';
-import {MatTableDataSource} from '@angular/material';
-
-
-@Component({
-  selector: 'app-manage-employee',
-  templateUrl: './manage-employee.component.html',
-  styleUrls: ['./manage-employee.component.scss']
-})
-export class ManageEmployeeComponent implements OnInit {
-  displayedColumns = ['id','firstName','lastName'];
-  dataSource = new MatTableDataSource(ELEMENT_DATA);
-
-  applyFilter(filterValue: string) {
-    filterValue = filterValue.trim(); // Remove whitespace
-    filterValue = filterValue.toLowerCase(); // MatTableDataSource defaults to lowercase matches
-    this.dataSource.filter = filterValue;
-  }
-
-  constructor() { }
-
-  ngOnInit() {
-  }
-
-}
-export interface Element {
-  id: number;
-  firstName: string;
-  lastName: string;
-  
-}
-
-const ELEMENT_DATA: Element[] = [
-  
-];
diff --git a/src/app/employees/detail/employee.detail.component.html b/src/app/employees/detail/employee.detail.component.html
new file mode 100644
index 0000000..f4d6824
--- /dev/null
+++ b/src/app/employees/detail/employee.detail.component.html
@@ -0,0 +1,47 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<fims-layout-card-over title="{{employee.givenName}} {{employee.surname}}" [navigateBackTo]="'/employees'">
+  <fims-layout-card-over-header-menu layout="row" layout-align="end center">
+    <td-search-box placeholder="{{'Search' | translate}}" (search)="searchEmployee($event)" [alwaysVisible]="false"></td-search-box>
+    <button mat-icon-button (click)="deleteEmployee()" title="{{'Delete this employee' | translate}}" *hasPermission="{ id: 'office_employees', accessLevel: 'DELETE' }"><mat-icon>delete</mat-icon></button>
+  </fims-layout-card-over-header-menu>
+  <mat-list>
+    <h3 mat-subheader translate>Assigned office</h3>
+    <mat-list-item>
+      <mat-icon matListAvatar>store</mat-icon>
+      <h3 matLine *ngIf="employee.assignedOffice">{{employee.assignedOffice}}</h3>
+      <h3 matLine *ngIf="!employee.assignedOffice" translate>No office assigned</h3>
+    </mat-list-item>
+    <h3 mat-subheader translate>Assigned role</h3>
+    <mat-list-item>
+      <mat-icon matListAvatar>lock</mat-icon>
+      <h3 matLine>{{user.role}}</h3>
+    </mat-list-item>
+    <h3 mat-subheader translate>Contact Information</h3>
+    <mat-list-item [ngSwitch]="detail.type" *ngFor="let detail of employee.contactDetails">
+      <mat-icon *ngSwitchCase="'EMAIL'" matListAvatar>email</mat-icon>
+      <mat-icon *ngSwitchCase="'PHONE'" matListAvatar>phone</mat-icon>
+      <mat-icon *ngSwitchCase="'MOBILE'" matListAvatar>smartphone</mat-icon>
+      <h3 matLine>{{detail.value}}</h3>
+    </mat-list-item>
+    <mat-list-item *ngIf="!employee.contactDetails.length">
+      <h3 matLine translate>No contact details available</h3>
+    </mat-list-item>
+  </mat-list>
+</fims-layout-card-over>
+<fims-fab-button title="{{'Edit member ' | translate}}" icon="mode_edit" [link]="['edit']" [permission]="{ id: 'office_employees', accessLevel: 'CHANGE'}"></fims-fab-button>
diff --git a/src/app/employees/detail/employee.detail.component.ts b/src/app/employees/detail/employee.detail.component.ts
new file mode 100644
index 0000000..052020a
--- /dev/null
+++ b/src/app/employees/detail/employee.detail.component.ts
@@ -0,0 +1,96 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, Router} from '@angular/router';
+import {Component, OnDestroy, OnInit} from '@angular/core';
+import {Employee} from '../../services/office/domain/employee.model';
+import {TdDialogService} from '@covalent/core';
+import {Observable} from 'rxjs/Observable';
+import {Subscription} from 'rxjs/Subscription';
+import {User} from '../../services/identity/domain/user.model';
+import * as fromEmployee from '../store';
+import {DELETE, SelectAction} from '../store/employee.actions';
+import {EmployeesStore} from '../store/index';
+
+@Component({
+  selector: 'fims-employee-detail',
+  templateUrl: './employee.detail.component.html'
+})
+export class EmployeeDetailComponent implements OnInit, OnDestroy {
+
+  private actionsSubscription: Subscription;
+
+  private employeeSubscription: Subscription;
+
+  employee: Employee;
+
+  user: User;
+
+  constructor(private route: ActivatedRoute, private router: Router, private dialogService: TdDialogService,
+              private store: EmployeesStore) {}
+
+  ngOnInit(): void {
+    this.actionsSubscription = this.route.params
+      .map(params => new SelectAction(params['id']))
+      .subscribe(this.store);
+
+    this.employeeSubscription = this.store.select(fromEmployee.getSelectedEmployee)
+      .filter(employee => !!employee)
+      .subscribe(employee => this.employee = employee);
+
+    // TODO load user via store
+    this.route.data.subscribe(( data: { user: User }) => {
+      this.user = data.user;
+    });
+  }
+
+  ngOnDestroy(): void {
+    this.actionsSubscription.unsubscribe();
+    this.employeeSubscription.unsubscribe();
+  }
+
+  searchEmployee(term): void {
+    if (!term) {
+      return;
+    }
+    this.goToOverviewPage(term);
+  }
+
+  confirmDeletion(): Observable<boolean> {
+    return this.dialogService.openConfirm({
+      message: 'Do you want to delete employee "' + this.employee.givenName + ' ' + this.employee.surname + '"?',
+      title: 'Confirm deletion',
+      acceptButton: 'DELETE EMPLOYEE',
+    }).afterClosed();
+  }
+
+  deleteEmployee(): void {
+    this.confirmDeletion()
+      .filter(accept => accept)
+      .subscribe(() => {
+        this.store.dispatch({ type: DELETE, payload: {
+          employee: this.employee,
+          activatedRoute: this.route
+        } });
+      });
+  }
+
+  goToOverviewPage(term?: string): void {
+    this.router.navigate(['../../'], { queryParams: { term: term }, relativeTo: this.route });
+  }
+}
diff --git a/src/app/employees/employee-exists.guard.ts b/src/app/employees/employee-exists.guard.ts
new file mode 100644
index 0000000..98e2b94
--- /dev/null
+++ b/src/app/employees/employee-exists.guard.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 {ActivatedRouteSnapshot, CanActivate, RouterStateSnapshot} from '@angular/router';
+import {Injectable} from '@angular/core';
+import {OfficeService} from '../services/office/office.service';
+import * as fromEmployees from './store';
+import {Observable} from 'rxjs/Observable';
+import {LoadAction} from './store/employee.actions';
+import {of} from 'rxjs/observable/of';
+import {EmployeesStore} from './store/index';
+import {ExistsGuardService} from '../common/guards/exists-guard';
+
+@Injectable()
+export class EmployeeExistsGuard implements CanActivate {
+
+  constructor(private store: EmployeesStore,
+              private officeService: OfficeService,
+              private existsGuardService: ExistsGuardService) {}
+
+  hasEmployeeInStore(id: string): Observable<boolean> {
+    const timestamp$ = this.store.select(fromEmployees.getEmployeesLoadedAt)
+      .map(loadedAt => loadedAt[id]);
+
+    return this.existsGuardService.isWithinExpiry(timestamp$);
+  }
+
+  hasEmployeeInApi(id: string): Observable<boolean> {
+    const getEmployee$ = this.officeService.getEmployee(id)
+      .map(employeeEntity => new LoadAction({
+        resource: employeeEntity
+      }))
+      .do((action: LoadAction) => this.store.dispatch(action))
+      .map(employee => !!employee);
+
+    return this.existsGuardService.routeTo404OnError(getEmployee$);
+  }
+
+  hasEmployee(id: string): Observable<boolean> {
+    return this.hasEmployeeInStore(id)
+      .switchMap(inStore => {
+        if (inStore) {
+          return of(inStore);
+        }
+
+        return this.hasEmployeeInApi(id);
+      });
+  }
+
+  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
+    return this.hasEmployee(route.params['id']);
+  }
+}
diff --git a/src/app/employees/employee.component.html b/src/app/employees/employee.component.html
new file mode 100644
index 0000000..5c563f5
--- /dev/null
+++ b/src/app/employees/employee.component.html
@@ -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.
+-->
+
+<fims-layout-card-over title="{{'Manage employees' | translate}}">
+  <fims-layout-card-over-header-menu>
+    <td-search-box #searchBox placeholder="{{'Search' | translate}}" (search)="search($event)" [alwaysVisible]="false"></td-search-box>
+  </fims-layout-card-over-header-menu>
+  <fims-data-table flex
+                   (onFetch)="fetchEmployees($event)"
+                   (onActionCellClick)="rowSelect($event)"
+                   [columns]="columns"
+                   [data]="employeeData$ | async"
+                   [loading]="loading$ | async"
+                   [sortable]="true"
+                   [pageable]="true">
+  </fims-data-table>
+</fims-layout-card-over>
+<fims-fab-button title="{{'Create new employee' | translate}}" icon="add" [link]="['create']" [permission]="{ id: 'office_employees', accessLevel: 'CHANGE'}"></fims-fab-button>
diff --git a/src/app/employees/employee.component.ts b/src/app/employees/employee.component.ts
new file mode 100644
index 0000000..e9c35bf
--- /dev/null
+++ b/src/app/employees/employee.component.ts
@@ -0,0 +1,85 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, OnInit} from '@angular/core';
+import {ActivatedRoute, Params, Router} from '@angular/router';
+import {Employee} from '../services/office/domain/employee.model';
+import {FetchRequest} from '../services/domain/paging/fetch-request.model';
+import {TableData} from '../common/data-table/data-table.component';
+import {Store} from '@ngrx/store';
+import * as fromRoot from '../store';
+import {Observable} from 'rxjs/Observable';
+import {SEARCH} from '../store/employee/employee.actions';
+
+@Component({
+  selector: 'fims-employee',
+  templateUrl: './employee.component.html'
+})
+export class EmployeeComponent implements OnInit {
+
+  employeeData$: Observable<TableData>;
+
+  loading$: Observable<boolean>;
+
+  columns: any[] = [
+    { name: 'identifier', label: 'Id' },
+    { name: 'givenName', label: 'First Name' },
+    { name: 'surname', label: 'Last Name' }
+  ];
+
+  searchTerm: string;
+
+  private lastFetchRequest: FetchRequest = {};
+
+  constructor(private router: Router, private route: ActivatedRoute, private store: Store<fromRoot.State>) {}
+
+  ngOnInit(): void {
+
+    this.employeeData$ = this.store.select(fromRoot.getEmployeeSearchResults)
+      .map(employeePage => ({
+        data: employeePage.employees,
+        totalElements: employeePage.totalElements,
+        totalPages: employeePage.totalPages
+      }));
+
+    this.loading$ = this.store.select(fromRoot.getEmployeeSearchLoading);
+
+    this.route.queryParams.subscribe((params: Params) => {
+      this.search(params['term']);
+    });
+  }
+
+  search(searchTerm: string): void {
+    this.searchTerm = searchTerm;
+    this.fetchEmployees();
+  }
+
+  rowSelect(row: Employee): void {
+    this.router.navigate(['detail', row.identifier], { relativeTo: this.route });
+  }
+
+  fetchEmployees(fetchRequest?: FetchRequest) {
+    if (fetchRequest) {
+      this.lastFetchRequest = fetchRequest;
+    }
+
+    this.lastFetchRequest.searchTerm = this.searchTerm;
+
+    this.store.dispatch({ type: SEARCH, payload: this.lastFetchRequest });
+  }
+}
diff --git a/src/app/employees/employee.module.ts b/src/app/employees/employee.module.ts
new file mode 100644
index 0000000..04d9a08
--- /dev/null
+++ b/src/app/employees/employee.module.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 {NgModule} from '@angular/core';
+import {RouterModule} from '@angular/router';
+import {EmployeeComponent} from './employee.component';
+import {EmployeeRoutes} from './employee.routing';
+import {EmployeeFormComponent} from './form/form.component';
+import {CreateEmployeeFormComponent} from './form/create/create.form.component';
+import {EmployeeDetailComponent} from './detail/employee.detail.component';
+import {EditEmployeeFormComponent} from './form/edit/edit.form.component';
+import {UserResolver} from './user.resolver';
+import {FimsSharedModule} from '../common/common.module';
+import {EmployeeExistsGuard} from './employee-exists.guard';
+import {Store} from '@ngrx/store';
+import {EmployeesStore, employeeStoreFactory} from './store/index';
+import {EmployeeNotificationEffects} from './store/effects/notification.effects';
+import {EffectsModule} from '@ngrx/effects';
+import {EmployeeApiEffects} from './store/effects/service.effects';
+import {EmployeeRouteEffects} from './store/effects/route.effects';
+import {
+  MatButtonModule,
+  MatIconModule,
+  MatInputModule,
+  MatListModule,
+  MatOptionModule,
+  MatSelectModule,
+  MatToolbarModule
+} from '@angular/material';
+import {CovalentSearchModule, CovalentStepsModule} from '@covalent/core';
+import {TranslateModule} from '@ngx-translate/core';
+import {CommonModule} from '@angular/common';
+import {ReactiveFormsModule} from '@angular/forms';
+
+@NgModule({
+  imports: [
+    RouterModule.forChild(EmployeeRoutes),
+    FimsSharedModule,
+    ReactiveFormsModule,
+    CommonModule,
+    TranslateModule,
+    MatIconModule,
+    MatListModule,
+    MatToolbarModule,
+    MatOptionModule,
+    MatInputModule,
+    MatButtonModule,
+    MatSelectModule,
+    CovalentSearchModule,
+    CovalentStepsModule,
+
+    EffectsModule.run(EmployeeApiEffects),
+    EffectsModule.run(EmployeeRouteEffects),
+    EffectsModule.run(EmployeeNotificationEffects)
+  ],
+  declarations: [
+    EmployeeComponent,
+    EmployeeFormComponent,
+    CreateEmployeeFormComponent,
+    EditEmployeeFormComponent,
+    EmployeeDetailComponent
+  ],
+  providers: [
+    UserResolver,
+    EmployeeExistsGuard,
+    { provide: EmployeesStore, useFactory: employeeStoreFactory, deps: [Store]}
+  ]
+})
+export class EmployeeModule {}
diff --git a/src/app/employees/employee.routing.ts b/src/app/employees/employee.routing.ts
new file mode 100644
index 0000000..9fd1f2d
--- /dev/null
+++ b/src/app/employees/employee.routing.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 {Routes} from '@angular/router';
+import {EmployeeComponent} from './employee.component';
+import {CreateEmployeeFormComponent} from './form/create/create.form.component';
+import {EmployeeDetailComponent} from './detail/employee.detail.component';
+import {EditEmployeeFormComponent} from './form/edit/edit.form.component';
+import {UserResolver} from './user.resolver';
+import {EmployeeExistsGuard} from './employee-exists.guard';
+
+export const EmployeeRoutes: Routes = [
+  {
+    path: '',
+    component: EmployeeComponent,
+    data: {title: 'Manage Employees', hasPermission: {id: 'office_employees', accessLevel: 'READ'}}
+  },
+  {
+    path: 'create',
+    component: CreateEmployeeFormComponent,
+    data: {title: 'Create Employee', hasPermission: {id: 'office_employees', accessLevel: 'CHANGE'}}
+  },
+  {
+    path: 'detail/:id/edit',
+    component: EditEmployeeFormComponent,
+    canActivate: [EmployeeExistsGuard],
+    resolve: {user: UserResolver},
+    data: {title: 'Edit Employee', hasPermission: {id: 'office_employees', accessLevel: 'CHANGE'}}
+  },
+  {
+    path: 'detail/:id',
+    component: EmployeeDetailComponent,
+    canActivate: [EmployeeExistsGuard],
+    resolve: {user: UserResolver},
+    data: {title: 'View Employee', hasPermission: {id: 'office_employees', accessLevel: 'READ'}}
+  }
+];
diff --git a/src/app/employees/form/create/create.form.component.html b/src/app/employees/form/create/create.form.component.html
new file mode 100644
index 0000000..a2fccd0
--- /dev/null
+++ b/src/app/employees/form/create/create.form.component.html
@@ -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.
+-->
+
+<fims-layout-card-over title="{{'Create new employee' | translate}}">
+  <fims-employee-form-component #form
+                                  (onSave)="onSave($event)"
+                                  (onCancel)="onCancel()"
+                                  [formData]="employeeFormData">
+  </fims-employee-form-component>
+</fims-layout-card-over>
diff --git a/src/app/employees/form/create/create.form.component.spec.ts b/src/app/employees/form/create/create.form.component.spec.ts
new file mode 100644
index 0000000..b27d416
--- /dev/null
+++ b/src/app/employees/form/create/create.form.component.spec.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 {async, ComponentFixture, inject, TestBed} from '@angular/core/testing';
+import {TranslateModule} from '@ngx-translate/core';
+import {ReactiveFormsModule} from '@angular/forms';
+import {CovalentStepsModule} from '@covalent/core';
+import {EmployeeFormComponent, EmployeeSaveEvent} from '../form.component';
+import {ActivatedRoute, Router} from '@angular/router';
+import {Observable} from 'rxjs/Observable';
+import {CreateEmployeeFormComponent} from './create.form.component';
+import {mapEmployee, mapUser} from '../form.mapper';
+import {EmployeesStore} from '../../store/index';
+import {CREATE} from '../../store/employee.actions';
+import {Store} from '@ngrx/store';
+import {NoopAnimationsModule} from '@angular/platform-browser/animations';
+import {MatCardModule, MatInputModule, MatOptionModule, MatSelectModule} from '@angular/material';
+import {CUSTOM_ELEMENTS_SCHEMA} from '@angular/core';
+import {FimsSharedModule} from '../../../common/common.module';
+
+const eventMock: EmployeeSaveEvent = {
+  detailForm: {
+    identifier: 'test',
+    firstName: 'test',
+    middleName: 'test',
+    lastName: 'test',
+    password: 'test',
+    role: 'test'
+  },
+  contactForm: {
+    email: 'test',
+    mobile: 'test',
+    phone: 'test'
+  },
+  officeForm: {
+    assignedOffice: 'test'
+  }
+};
+
+let router: Router;
+
+describe('Test employee form component', () => {
+
+  let fixture: ComponentFixture<CreateEmployeeFormComponent>;
+
+  let testComponent: CreateEmployeeFormComponent;
+
+  beforeEach(() => {
+    router = jasmine.createSpyObj('Router', ['navigate']);
+
+    TestBed.configureTestingModule({
+      declarations: [
+        EmployeeFormComponent,
+        CreateEmployeeFormComponent,
+      ],
+      imports: [
+        TranslateModule.forRoot(),
+        FimsSharedModule,
+        ReactiveFormsModule,
+        MatInputModule,
+        MatCardModule,
+        MatSelectModule,
+        MatOptionModule,
+        CovalentStepsModule,
+        NoopAnimationsModule
+      ],
+      providers: [
+        { provide: Router, useValue: router},
+        { provide: ActivatedRoute, useValue: {} },
+        {
+          provide: Store, useClass: class {
+          dispatch = jasmine.createSpy('dispatch');
+          select = jasmine.createSpy('select').and.returnValue(Observable.empty());
+        }},
+        {
+          provide: EmployeesStore, useClass: class {
+            dispatch = jasmine.createSpy('dispatch');
+            select = jasmine.createSpy('select').and.returnValue(Observable.empty());
+          }
+        }
+      ],
+      schemas: [CUSTOM_ELEMENTS_SCHEMA]
+    });
+
+    fixture = TestBed.createComponent(CreateEmployeeFormComponent);
+    testComponent = fixture.componentInstance;
+  });
+
+  it('should test if employee is created', async(inject([EmployeesStore], (store: EmployeesStore) => {
+    fixture.detectChanges();
+
+    testComponent.onSave(eventMock);
+
+    fixture.whenStable().then(() => {
+      const employee = mapEmployee(eventMock);
+      const user = mapUser(eventMock);
+
+      expect(store.dispatch).toHaveBeenCalledWith({ type: CREATE, payload: {
+        employee: employee,
+        user: user,
+        activatedRoute: {}
+      }});
+    });
+  })));
+
+  xit('should test if error is set on 409', async(() => {
+    fixture.detectChanges();
+
+    fixture.whenStable().then(() => {
+      expect(testComponent.formComponent.detailForm.get('identifier').errors).toBeDefined();
+      expect(testComponent.formComponent.detailForm.get('identifier').errors['unique']).toBeTruthy();
+    });
+  }));
+});
diff --git a/src/app/employees/form/create/create.form.component.ts b/src/app/employees/form/create/create.form.component.ts
new file mode 100644
index 0000000..22a39c4
--- /dev/null
+++ b/src/app/employees/form/create/create.form.component.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 {Component, OnDestroy, OnInit, ViewChild} from '@angular/core';
+import {ActivatedRoute, Router} from '@angular/router';
+import {EmployeeFormComponent, EmployeeFormData, EmployeeSaveEvent} from '../form.component';
+import {mapEmployee, mapUser} from '../form.mapper';
+import {Employee} from '../../../services/office/domain/employee.model';
+import {UserWithPassword} from '../../../services/identity/domain/user-with-password.model';
+import * as fromEmployees from '../../store';
+import {Subscription} from 'rxjs/Subscription';
+import {CREATE, RESET_FORM} from '../../store/employee.actions';
+import {Error} from '../../../services/domain/error.model';
+import {EmployeesStore} from '../../store/index';
+
+@Component({
+  templateUrl: './create.form.component.html'
+})
+export class CreateEmployeeFormComponent implements OnInit, OnDestroy {
+
+  private formStateSubscription: Subscription;
+
+  @ViewChild('form') formComponent: EmployeeFormComponent;
+
+  employeeFormData: EmployeeFormData = {
+    user: { identifier: '', role: ''},
+    employee: { identifier: '', givenName: '', surname: '', contactDetails: [] }
+  };
+
+  constructor(private router: Router, private route: ActivatedRoute, private store: EmployeesStore) {}
+
+  ngOnInit(): void {
+    this.formStateSubscription = this.store.select(fromEmployees.getEmployeeFormError)
+      .filter((error: Error) => !!error)
+      .subscribe((error: Error) => {
+        const detailForm = this.formComponent.detailForm;
+        const errors = detailForm.get('identifier').errors || {};
+        errors['unique'] = true;
+        detailForm.get('identifier').setErrors(errors);
+        this.formComponent.step.open();
+      });
+  }
+
+  ngOnDestroy(): void {
+    this.formStateSubscription.unsubscribe();
+
+    this.store.dispatch({ type: RESET_FORM });
+  }
+
+  onSave(event: EmployeeSaveEvent): void {
+    const employee: Employee = mapEmployee(event);
+    const user: UserWithPassword = mapUser(event);
+
+    this.store.dispatch({ type: CREATE, payload: {
+      employee: employee,
+      user: user,
+      activatedRoute: this.route
+    }});
+
+  }
+
+  onCancel(): void {
+    this.navigateAway();
+  }
+
+  navigateAway(): void {
+    this.router.navigate(['../'], { relativeTo: this.route });
+  }
+}
diff --git a/src/app/employees/form/edit/edit.form.component.html b/src/app/employees/form/edit/edit.form.component.html
new file mode 100644
index 0000000..efc62dc
--- /dev/null
+++ b/src/app/employees/form/edit/edit.form.component.html
@@ -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.
+-->
+
+<fims-layout-card-over title="{{'Edit employee' | translate}}">
+  <fims-employee-form-component #form
+                                  [editMode]="true"
+                                  (onSave)="onSave($event)"
+                                  (onCancel)="onCancel()"
+                                  [formData]="formData | async">
+  </fims-employee-form-component>
+</fims-layout-card-over>
diff --git a/src/app/employees/form/edit/edit.form.component.spec.ts b/src/app/employees/form/edit/edit.form.component.spec.ts
new file mode 100644
index 0000000..13ca983
--- /dev/null
+++ b/src/app/employees/form/edit/edit.form.component.spec.ts
@@ -0,0 +1,140 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 {async, ComponentFixture, inject, TestBed} from '@angular/core/testing';
+import {TranslateModule} from '@ngx-translate/core';
+import {ReactiveFormsModule} from '@angular/forms';
+import {CovalentStepsModule} from '@covalent/core';
+import {EditEmployeeFormComponent} from './edit.form.component';
+import {EmployeeFormComponent} from '../form.component';
+import {ActivatedRoute, Router} from '@angular/router';
+import {User} from '../../../services/identity/domain/user.model';
+import {Employee} from '../../../services/office/domain/employee.model';
+import {Observable} from 'rxjs/Observable';
+import {EmployeesStore} from '../../store/index';
+import {Store} from '@ngrx/store';
+import {UPDATE} from '../../store/employee.actions';
+import * as fromEmployees from '../../store';
+import {NoopAnimationsModule} from '@angular/platform-browser/animations';
+import {MatCardModule, MatInputModule, MatOptionModule, MatSelectModule} from '@angular/material';
+import {CUSTOM_ELEMENTS_SCHEMA} from '@angular/core';
+import {FimsSharedModule} from '../../../common/common.module';
+
+const userMock: User = {
+  identifier: 'test',
+  role: 'test'
+};
+
+const employeeMock: Employee = {
+  identifier: 'test',
+  assignedOffice: 'test',
+  givenName: 'test',
+  middleName: 'test',
+  surname: 'test',
+  contactDetails: [
+    {
+      group: 'BUSINESS',
+      type: 'EMAIL',
+      value: 'test',
+      preferenceLevel: 1
+    }
+  ]
+};
+
+const activatedRoute = {
+  data: Observable.of({
+    user: userMock
+  })
+};
+let router: Router;
+
+describe('Test employee form component', () => {
+
+  let fixture: ComponentFixture<EditEmployeeFormComponent>;
+
+  let testComponent: EditEmployeeFormComponent;
+
+  beforeEach(() => {
+    router = jasmine.createSpyObj('Router', ['navigate']);
+
+    TestBed.configureTestingModule({
+      declarations: [
+        EmployeeFormComponent,
+        EditEmployeeFormComponent,
+      ],
+      imports: [
+        TranslateModule.forRoot(),
+        FimsSharedModule,
+        ReactiveFormsModule,
+        MatInputModule,
+        MatCardModule,
+        MatSelectModule,
+        MatOptionModule,
+        CovalentStepsModule,
+        NoopAnimationsModule
+      ],
+      providers: [
+        { provide: Router, useValue: router},
+        { provide: ActivatedRoute, useValue: activatedRoute },
+        {
+          provide: Store, useClass: class {
+            dispatch = jasmine.createSpy('dispatch');
+            select = jasmine.createSpy('select').and.returnValue(Observable.empty());
+          }
+        },
+        {
+          provide: EmployeesStore, useClass: class {
+            dispatch = jasmine.createSpy('dispatch');
+            select = jasmine.createSpy('select').and.callFake(selector => {
+              if (selector === fromEmployees.getSelectedEmployee) {
+                return Observable.of(employeeMock);
+              }
+
+              return Observable.empty();
+            });
+          }
+        }
+      ],
+      schemas: [CUSTOM_ELEMENTS_SCHEMA]
+    });
+
+    fixture = TestBed.createComponent(EditEmployeeFormComponent);
+    testComponent = fixture.componentInstance;
+  });
+
+  it('should test if employee is updated', async(inject([EmployeesStore], (store: EmployeesStore) => {
+    fixture.detectChanges();
+
+    testComponent.formComponent.detailForm.get('password').setValue('newPassword');
+
+    fixture.detectChanges();
+
+    testComponent.formComponent.save();
+
+    fixture.whenStable().then(() => {
+      expect(store.dispatch).toHaveBeenCalledWith({ type: UPDATE, payload: {
+        employee: employeeMock,
+        contactDetails: employeeMock.contactDetails,
+        role: userMock.role,
+        password: 'newPassword',
+        activatedRoute: activatedRoute
+      }});
+    });
+
+  })));
+});
diff --git a/src/app/employees/form/edit/edit.form.component.ts b/src/app/employees/form/edit/edit.form.component.ts
new file mode 100644
index 0000000..dd3e1d7
--- /dev/null
+++ b/src/app/employees/form/edit/edit.form.component.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 {Component, OnInit, ViewChild} from '@angular/core';
+import {ActivatedRoute, Router} from '@angular/router';
+import {EmployeeFormComponent, EmployeeFormData, EmployeeSaveEvent} from '../form.component';
+import {mapContactDetails, mapEmployee} from '../form.mapper';
+import {Employee} from '../../../services/office/domain/employee.model';
+import {User} from '../../../services/identity/domain/user.model';
+import {UPDATE} from '../../store/employee.actions';
+import {Observable} from 'rxjs/Observable';
+import {EmployeesStore, getSelectedEmployee} from '../../store/index';
+
+@Component({
+  templateUrl: './edit.form.component.html'
+})
+export class EditEmployeeFormComponent implements OnInit {
+
+  @ViewChild('form') formComponent: EmployeeFormComponent;
+
+  formData: Observable<EmployeeFormData>;
+
+  employee: Employee;
+
+  user: User;
+
+  constructor(private router: Router, private route: ActivatedRoute, private store: EmployeesStore) {}
+
+  ngOnInit() {
+    this.formData = Observable.combineLatest(
+      this.store.select(getSelectedEmployee),
+      this.route.data,
+      (employee: Employee, data: { user: User }) => {
+        return {
+          user: data.user,
+          employee: employee
+        };
+      }
+    );
+  }
+
+  onSave(event: EmployeeSaveEvent) {
+    const employee: Employee = mapEmployee(event);
+
+    this.store.dispatch({ type: UPDATE, payload: {
+      employee: employee,
+      contactDetails: mapContactDetails(event.contactForm),
+      role: event.detailForm.role,
+      password: event.detailForm.password,
+      activatedRoute: this.route
+    }});
+  }
+
+  onCancel() {
+    this.navigateToOffice();
+  }
+
+  private navigateToOffice() {
+    this.router.navigate(['../'], { relativeTo: this.route });
+  }
+}
diff --git a/src/app/employees/form/form.component.html b/src/app/employees/form/form.component.html
new file mode 100644
index 0000000..dd618ee
--- /dev/null
+++ b/src/app/employees/form/form.component.html
@@ -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.
+-->
+
+<td-steps mode="'vertical'">
+  <td-step #detailsStep label="{{'Employee details' | translate}}" [state]="detailForm.valid ? 'complete' : detailForm.pristine ? 'none' : 'required'">
+    <form [formGroup]="detailForm" layout="column">
+      <fims-id-input [form]="detailForm" placeholder="Username" controlName="identifier" [readonly]="editMode"></fims-id-input>
+      <fims-text-input [form]="detailForm" controlName="firstName" placeholder="{{'First name' | translate}}"></fims-text-input>
+      <fims-text-input [form]="detailForm" controlName="middleName" placeholder="{{'Middle name(optional)' | translate}}"></fims-text-input>
+      <fims-text-input [form]="detailForm" controlName="lastName" placeholder="{{'Last name' | translate}}"></fims-text-input>
+      <mat-form-field layout-margin>
+        <mat-select formControlName="role" placeholder="{{ 'Role' | translate }}">
+          <mat-option *ngFor="let role of roles | async" [value]="role.identifier">
+            {{ role.identifier }}
+          </mat-option>
+        </mat-select>
+        <mat-error *ngIf="detailForm.get('role').hasError('required')" translate>Required</mat-error>
+      </mat-form-field>
+      <div layout="row">
+        <mat-form-field layout-margin flex>
+          <input matInput placeholder="{{'Password' | translate}}" type="password" formControlName="password" autocomplete="new-password" tdAutoTrim/>
+          <mat-error *ngIf="detailForm.get('password').hasError('required')" translate>
+            Required
+          </mat-error>
+          <mat-error *ngIf="detailForm.get('password').hasError('minlength')">
+            {{ 'Must have at least characters.' | translate:{ value: detailForm.get('password').getError('minlength')['requiredLength']} }}
+          </mat-error>
+        </mat-form-field>
+      </div>
+    </form>
+    <ng-template td-step-actions>
+      <fims-form-continue-action (onContinue)="officeStep.open()"></fims-form-continue-action>
+    </ng-template>
+  </td-step>
+
+  <td-step #officeStep label="{{'Assign employee to office(optional)' | translate}}"
+           [state]="officeForm.get('assignedOffice').value ? 'complete' : 'none'">
+
+    <fims-select-list #officeList flex
+                        [data]="offices"
+                        id="identifier"
+                        listIcon="store"
+                        [preSelection]="officeForm.get('assignedOffice').value ? [officeForm.get('assignedOffice').value] : []"
+                        (onSearch)="searchOffice($event)"
+                        (onSelectionChange)="assignOffice($event)"
+                        title="{{'Assigned Office' | translate}}"
+                        noResultsMessage="{{'No office was found.' | translate}}"
+                        noSelectionMessage="{{'No office assigned to employee, yet.' | translate}}">
+    </fims-select-list>
+    <ng-template td-step-actions>
+      <fims-form-continue-action (onContinue)="contactStep.open()"></fims-form-continue-action>
+    </ng-template>
+  </td-step>
+
+  <td-step #contactStep label="{{'Employee contact(optional)' | translate}}" [state]="contactForm.valid && !contactForm.pristine ? 'complete' : contactForm.pristine ? 'none' : 'required'">
+    <form [formGroup]="contactForm" layout="column">
+      <fims-text-input [form]="contactForm" controlName="email" placeholder="{{'Email(optional)' | translate}}" type="email"></fims-text-input>
+      <fims-text-input [form]="contactForm" controlName="phone" placeholder="{{'Phone(optional)' | translate}}" type="tel"></fims-text-input>
+      <fims-text-input [form]="contactForm" controlName="mobile" placeholder="{{'Mobile(optional)' | translate}}" type="tel"></fims-text-input>
+    </form>
+  </td-step>
+
+  <td-step label="{{'Final step' | translate}}" [state]="'complete'">
+    <ng-template td-step-summary>
+      <fims-form-final-action
+        [resourceName]="'EMPLOYEE'"
+        [editMode]="editMode"
+        [disabled]="formsInvalid()"
+        (onCancel)="cancel()"
+        (onSave)="save()">
+      </fims-form-final-action>
+    </ng-template>
+  </td-step>
+
+</td-steps>
diff --git a/src/app/employees/form/form.component.spec.ts b/src/app/employees/form/form.component.spec.ts
new file mode 100644
index 0000000..ef69575
--- /dev/null
+++ b/src/app/employees/form/form.component.spec.ts
@@ -0,0 +1,135 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, TestBed} from '@angular/core/testing';
+import {Component, EventEmitter, ViewChild} from '@angular/core';
+import {Employee} from '../../services/office/domain/employee.model';
+import {EmployeeFormComponent, EmployeeFormData, EmployeeSaveEvent} from './form.component';
+import {User} from '../../services/identity/domain/user.model';
+import {TranslateModule} from '@ngx-translate/core';
+import {ReactiveFormsModule} from '@angular/forms';
+import {CovalentStepsModule} from '@covalent/core';
+import {Observable} from 'rxjs/Observable';
+import {Store} from '@ngrx/store';
+import {NoopAnimationsModule} from '@angular/platform-browser/animations';
+import {FimsSharedModule} from '../../common/common.module';
+import {MatIconModule, MatInputModule, MatOptionModule, MatSelectModule} from '@angular/material';
+
+const employeeTemplate: Employee = {
+  identifier: 'test',
+  givenName: 'test',
+  middleName: 'test',
+  surname: 'test',
+  contactDetails: [{
+    type: 'EMAIL',
+    group: 'BUSINESS',
+    value: 'test@test.de',
+    preferenceLevel: 0
+  }],
+  assignedOffice: 'test'
+};
+
+const userTemplate: User = {
+  identifier: 'test',
+  role: 'test'
+};
+
+describe('Test employee form component', () => {
+
+  let fixture: ComponentFixture<TestComponent>;
+
+  let testComponent: TestComponent;
+
+  beforeEach(() => {
+    TestBed.configureTestingModule({
+      declarations: [
+        TestComponent,
+        EmployeeFormComponent
+      ],
+      imports: [
+        TranslateModule.forRoot(),
+        FimsSharedModule,
+        ReactiveFormsModule,
+        MatSelectModule,
+        MatOptionModule,
+        MatIconModule,
+        MatInputModule,
+        CovalentStepsModule,
+        NoopAnimationsModule
+      ],
+      providers: [
+        {
+          provide: Store, useClass: class {
+          dispatch = jasmine.createSpy('dispatch');
+          select = jasmine.createSpy('select').and.returnValue(Observable.empty());
+        }}
+      ]
+    });
+
+    fixture = TestBed.createComponent(TestComponent);
+    testComponent = fixture.componentInstance;
+  });
+
+  it('should test if the form save the original values', () => {
+    fixture.detectChanges();
+
+    testComponent.saveEmitter.subscribe((saveEvent: EmployeeSaveEvent) => {
+      expect(employeeTemplate.identifier).toEqual(saveEvent.detailForm.identifier);
+      expect(employeeTemplate.givenName).toEqual(saveEvent.detailForm.firstName);
+      expect(employeeTemplate.middleName).toEqual(saveEvent.detailForm.middleName);
+      expect(employeeTemplate.surname).toEqual(saveEvent.detailForm.lastName);
+      expect(saveEvent.detailForm.password).toEqual('');
+
+      expect(employeeTemplate.assignedOffice).toEqual(saveEvent.officeForm.assignedOffice);
+
+      expect(employeeTemplate.contactDetails.length).toEqual(1);
+      expect(employeeTemplate.contactDetails[0].value).toEqual(saveEvent.contactForm.email);
+
+      expect(userTemplate.role).toEqual(saveEvent.detailForm.role);
+    });
+
+    testComponent.triggerSave();
+
+  });
+});
+
+@Component({
+  template: `
+    <fims-employee-form-component #form (onSave)="onSave($event)" (onCancel)="onCancel($event)" [formData]="employeeFormData">
+    </fims-employee-form-component>`
+})
+class TestComponent {
+
+  saveEmitter = new EventEmitter<EmployeeSaveEvent>();
+
+  @ViewChild('form') formComponent: EmployeeFormComponent;
+
+  employeeFormData: EmployeeFormData = {
+    employee: employeeTemplate,
+    user: userTemplate
+  };
+
+  triggerSave(): void {
+    this.formComponent.save();
+  }
+
+  onSave(event: EmployeeSaveEvent): void {
+    this.saveEmitter.emit(event);
+  }
+
+}
diff --git a/src/app/employees/form/form.component.ts b/src/app/employees/form/form.component.ts
new file mode 100644
index 0000000..029fa9c
--- /dev/null
+++ b/src/app/employees/form/form.component.ts
@@ -0,0 +1,185 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, OnInit, Output, ViewChild} from '@angular/core';
+import {FormBuilder, FormGroup, ValidatorFn, Validators} from '@angular/forms';
+import {Observable} from 'rxjs/Observable';
+import {TdStepComponent} from '@covalent/core';
+import {Office} from '../../services/office/domain/office.model';
+import {Employee} from '../../services/office/domain/employee.model';
+import {BUSINESS, ContactDetail, EMAIL, MOBILE, PHONE} from '../../services/domain/contact/contact-detail.model';
+import {FetchRequest} from '../../services/domain/paging/fetch-request.model';
+import {Role} from '../../services/identity/domain/role.model';
+import {User} from '../../services/identity/domain/user.model';
+import {FimsValidators} from '../../common/validator/validators';
+import {Store} from '@ngrx/store';
+import * as fromRoot from '../../store';
+import {SEARCH as SEARCH_OFFICE} from '../../store/office/office.actions';
+import {SEARCH as SEARCH_ROLE} from '../../store/role/role.actions';
+
+export interface EmployeeFormData {
+  user: User;
+  employee: Employee;
+}
+
+export interface EmployeeSaveEvent {
+  detailForm: {
+    identifier: string;
+    firstName: string;
+    middleName: string;
+    lastName: string;
+    password: string;
+    role: string;
+  };
+  contactForm: {
+    email: string;
+    phone: string;
+    mobile: string;
+  };
+  officeForm: {
+    assignedOffice: string;
+  };
+}
+
+@Component({
+  selector: 'fims-employee-form-component',
+  templateUrl: './form.component.html'
+})
+export class EmployeeFormComponent implements OnInit {
+
+  offices: Observable<Office[]>;
+
+  roles: Observable<Role[]>;
+
+  detailForm: FormGroup;
+  contactForm: FormGroup;
+  officeForm: FormGroup;
+
+  @ViewChild('detailsStep') step: TdStepComponent;
+
+  @Input('editMode') editMode: boolean;
+
+  @Input('formData') set formData(formData: EmployeeFormData) {
+    this.prepareDetailForm(formData.employee, formData.user);
+    this.prepareOfficeForm(formData.employee);
+    this.prepareContactForm(formData.employee.contactDetails);
+  }
+
+  @Output('onSave') onSave = new EventEmitter<EmployeeSaveEvent>();
+  @Output('onCancel') onCancel = new EventEmitter<void>();
+
+  constructor(private formBuilder: FormBuilder, private store: Store<fromRoot.State>) {}
+
+  ngOnInit(): void {
+    this.offices = this.store.select(fromRoot.getOfficeSearchResults)
+      .map(officePage => officePage.offices);
+
+    this.roles = this.store.select(fromRoot.getRoleSearchResults)
+      .map(rolesPage => rolesPage.roles);
+
+    this.fetchRoles();
+
+    this.step.open();
+  }
+
+  prepareDetailForm(employee: Employee, user: User): void {
+    const passwordValidators: ValidatorFn[] = [Validators.minLength(8)];
+
+    if (!this.editMode) {
+      passwordValidators.push(Validators.required);
+    }
+
+    this.detailForm = this.formBuilder.group({
+      identifier: [employee.identifier, [Validators.required, Validators.minLength(3), Validators.maxLength(32), FimsValidators.urlSafe]],
+      firstName: [employee.givenName, [Validators.required, Validators.maxLength(256)]],
+      middleName: [employee.middleName, Validators.maxLength(256)],
+      lastName: [employee.surname, [Validators.required, Validators.maxLength(256)]],
+      password: ['', passwordValidators],
+      role: [user ? user.role : '', Validators.required]
+    });
+  }
+
+  private prepareOfficeForm(employee: Employee) {
+    this.officeForm = this.formBuilder.group({
+      assignedOffice: [employee.assignedOffice]
+    });
+  }
+
+  private prepareContactForm(contactDetails: ContactDetail[]): void {
+    let phone = '';
+    let mobile = '';
+    let email = '';
+
+    const businessContacts: ContactDetail[] = contactDetails.filter(contactDetail => contactDetail.group === BUSINESS);
+
+    if (businessContacts.length) {
+      phone = this.getFirstItemByType(businessContacts, PHONE);
+      mobile = this.getFirstItemByType(businessContacts, MOBILE);
+      email = this.getFirstItemByType(businessContacts, EMAIL);
+    }
+
+    this.contactForm = this.formBuilder.group({
+      email: [email, [Validators.maxLength(256), FimsValidators.email]],
+      phone: [phone, Validators.maxLength(256)],
+      mobile: [mobile, Validators.maxLength(256)]
+    });
+  }
+
+  getFirstItemByType(contactDetails: ContactDetail[], type: string): string {
+    const items = contactDetails.filter(contact => contact.type === type);
+    return items.length ? items[0].value : '';
+  }
+
+  formsInvalid(): boolean {
+    return (!this.officeForm.pristine && this.officeForm.invalid) ||
+    (!this.contactForm.pristine && this.contactForm.invalid)
+      || this.detailForm.invalid;
+  }
+
+  save(): void {
+    this.onSave.emit({
+      detailForm: this.detailForm.value,
+      contactForm: this.contactForm.value,
+      officeForm: this.officeForm.value
+    });
+  }
+
+  cancel(): void {
+    this.onCancel.emit();
+  }
+
+  searchOffice(searchTerm: string): void {
+    const fetchRequest: FetchRequest = {
+      searchTerm
+    };
+    this.store.dispatch({ type: SEARCH_OFFICE, payload: fetchRequest });
+  }
+
+  assignOffice(selections: string[]): void {
+    this.setFormValue(this.officeForm, {'assignedOffice': selections && selections.length > 0 ? selections[0] : undefined});
+  }
+
+  fetchRoles(): void {
+    this.store.dispatch({ type: SEARCH_ROLE });
+  }
+
+  private setFormValue(form: FormGroup, value: any) {
+    form.setValue(value);
+    form.markAsDirty();
+  }
+}
diff --git a/src/app/employees/form/form.mapper.ts b/src/app/employees/form/form.mapper.ts
new file mode 100644
index 0000000..eb6339f
--- /dev/null
+++ b/src/app/employees/form/form.mapper.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 {EmployeeSaveEvent} from './form.component';
+import {ContactDetail, ContactDetailType} from '../../services/domain/contact/contact-detail.model';
+import {Employee} from '../../services/office/domain/employee.model';
+import {UserWithPassword} from '../../services/identity/domain/user-with-password.model';
+
+function buildContactDetail(type: ContactDetailType, value: string): ContactDetail {
+  return {
+    group: 'BUSINESS',
+    type: type,
+    value: value,
+    preferenceLevel: 1
+  };
+}
+
+export function mapContactDetails(contactForm: any): ContactDetail[] {
+  const contactDetails: ContactDetail[] = [];
+
+  if (contactForm.phone) {
+    contactDetails.push(buildContactDetail('PHONE', contactForm.phone));
+  }
+
+  if (contactForm.mobile) {
+    contactDetails.push(buildContactDetail('MOBILE', contactForm.mobile));
+  }
+
+  if (contactForm.email) {
+    contactDetails.push(buildContactDetail('EMAIL', contactForm.email));
+  }
+
+  return contactDetails;
+}
+
+export function mapEmployee(event: EmployeeSaveEvent): Employee {
+  const assignedOffice = event.officeForm.assignedOffice;
+
+  const contactDetails: ContactDetail[] = mapContactDetails(event.contactForm);
+
+  const employee: Employee = {
+    identifier: event.detailForm.identifier,
+    givenName: event.detailForm.firstName,
+    middleName: event.detailForm.middleName,
+    surname: event.detailForm.lastName,
+    contactDetails: contactDetails,
+    assignedOffice: assignedOffice ? assignedOffice : undefined
+  };
+
+  return employee;
+}
+
+export function mapUser(event: EmployeeSaveEvent): UserWithPassword {
+  const userWithPassword: UserWithPassword = {
+    identifier: event.detailForm.identifier,
+    password: event.detailForm.password,
+    role: event.detailForm.role
+  };
+
+  return userWithPassword;
+}
diff --git a/src/app/employees/store/effects/notification.effects.ts b/src/app/employees/store/effects/notification.effects.ts
new file mode 100644
index 0000000..3de745e
--- /dev/null
+++ b/src/app/employees/store/effects/notification.effects.ts
@@ -0,0 +1,47 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 employeeActions from '../employee.actions';
+import {NotificationService, NotificationType} from '../../../services/notification/notification.service';
+
+@Injectable()
+export class EmployeeNotificationEffects {
+
+  @Effect({ dispatch: false })
+  createEmployeeSuccess$: Observable<Action> = this.actions$
+    .ofType(employeeActions.CREATE_SUCCESS, employeeActions.UPDATE_SUCCESS)
+    .do(() => this.notificationService.send({
+      type: NotificationType.MESSAGE,
+      message: 'Employee is going to be saved'
+    }));
+
+  @Effect({ dispatch: false })
+  deleteEmployeeSuccess$: Observable<Action> = this.actions$
+    .ofType(employeeActions.DELETE_SUCCESS)
+    .do(() => this.notificationService.send({
+      type: NotificationType.MESSAGE,
+      message: 'Employee is going to be deleted'
+    }));
+
+  constructor(private actions$: Actions, private notificationService: NotificationService) {}
+}
+
diff --git a/src/app/employees/store/effects/route.effects.ts b/src/app/employees/store/effects/route.effects.ts
new file mode 100644
index 0000000..3e35dd1
--- /dev/null
+++ b/src/app/employees/store/effects/route.effects.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 {Actions, Effect} from '@ngrx/effects';
+import {Observable} from 'rxjs/Observable';
+import {Action} from '@ngrx/store';
+import * as employeeActions from '../employee.actions';
+import {Router} from '@angular/router';
+
+@Injectable()
+export class EmployeeRouteEffects {
+
+  @Effect({ dispatch: false })
+  createEmployeeSuccess$: Observable<Action> = this.actions$
+    .ofType(employeeActions.CREATE_SUCCESS, employeeActions.UPDATE_SUCCESS)
+    .map(action => action.payload)
+    .do(payload => this.router.navigate(['../'], { relativeTo: payload.activatedRoute} ));
+
+  @Effect({ dispatch: false })
+  deleteEmployeeSuccess$: Observable<Action> = this.actions$
+    .ofType(employeeActions.DELETE_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/employees/store/effects/service.effects.spec.ts b/src/app/employees/store/effects/service.effects.spec.ts
new file mode 100644
index 0000000..5afcd87
--- /dev/null
+++ b/src/app/employees/store/effects/service.effects.spec.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 {fakeAsync, TestBed, tick} from '@angular/core/testing';
+import {EffectsRunner, EffectsTestingModule} from '@ngrx/effects/testing';
+import {EmployeeApiEffects} from './service.effects';
+import {OfficeService} from '../../../services/office/office.service';
+import {IdentityService} from '../../../services/identity/identity.service';
+import {Observable} from 'rxjs/Observable';
+import {UpdateEmployeeAction, UpdateEmployeeSuccessAction} from '../employee.actions';
+import {Employee} from '../../../services/office/domain/employee.model';
+
+describe('Account Search Api Effects', () => {
+  beforeEach(() => {
+
+    TestBed.configureTestingModule({
+      imports: [
+        EffectsTestingModule
+      ],
+      providers: [
+        EmployeeApiEffects,
+        {
+          provide: OfficeService,
+          useValue: jasmine.createSpyObj('officeService', ['createEmployee', 'updateEmployee', 'setContactDetails'])
+        },
+        {
+          provide: IdentityService,
+          useValue: jasmine.createSpyObj('identityService', ['createUser', 'changeUserRole', 'changePassword'])
+        }
+      ]
+    });
+
+  });
+
+  describe('updateEmployee$', () => {
+
+    function setup() {
+      const officeService = TestBed.get(OfficeService);
+
+      officeService.updateEmployee.and.returnValue(Observable.of({}));
+
+      return {
+        runner: TestBed.get(EffectsRunner),
+        officeService: officeService,
+        identityService: TestBed.get(IdentityService),
+        employeeEffects: TestBed.get(EmployeeApiEffects)
+      };
+    }
+
+    it('should update employee', fakeAsync(() => {
+      const { runner, officeService, employeeEffects } = setup();
+
+      const employee: Employee = {
+        identifier: '',
+        givenName: '',
+        surname: '',
+        contactDetails: []
+      };
+
+      const expectedResult = new UpdateEmployeeSuccessAction({
+        resource: employee,
+        activatedRoute: null
+      });
+
+      runner.queue(new UpdateEmployeeAction({
+        employee: employee,
+        activatedRoute: null
+      }));
+
+      let result = null;
+      employeeEffects.updateEmployee$.subscribe(_result => result = _result);
+
+      tick();
+
+      expect(result).toEqual(expectedResult);
+
+      expect(officeService.updateEmployee).toHaveBeenCalled();
+    }));
+
+    it('should update contact details', fakeAsync(() => {
+      const { runner, officeService, employeeEffects } = setup();
+
+      officeService.setContactDetails.and.returnValue(Observable.of({}));
+
+      const employee: Employee = {
+        identifier: '',
+        givenName: '',
+        surname: '',
+        contactDetails: []
+      };
+
+      const expectedResult = new UpdateEmployeeSuccessAction({
+        resource: employee,
+        activatedRoute: null
+      });
+
+      runner.queue(new UpdateEmployeeAction({
+        employee: employee,
+        contactDetails: [
+          {
+            type: 'EMAIL',
+            group: 'BUSINESS',
+            value: 'dont@call.me',
+            preferenceLevel: 0
+          }
+        ],
+        activatedRoute: null
+      }));
+
+      let result = null;
+      employeeEffects.updateEmployee$.subscribe(_result => result = _result);
+
+      tick();
+
+      expect(result).toEqual(expectedResult);
+
+      expect(officeService.setContactDetails).toHaveBeenCalled();
+    }));
+
+    it('should update password', fakeAsync(() => {
+      const { runner, identityService, employeeEffects } = setup();
+
+      identityService.changePassword.and.returnValue(Observable.of({}));
+
+      const employee: Employee = {
+        identifier: '',
+        givenName: '',
+        surname: '',
+        contactDetails: []
+      };
+
+      const expectedResult = new UpdateEmployeeSuccessAction({
+        resource: employee,
+        activatedRoute: null
+      });
+
+      runner.queue(new UpdateEmployeeAction({
+        employee: employee,
+        password: 'test',
+        activatedRoute: null
+      }));
+
+      let result = null;
+      employeeEffects.updateEmployee$.subscribe(_result => result = _result);
+
+      tick();
+
+      expect(result).toEqual(expectedResult);
+
+      expect(identityService.changePassword).toHaveBeenCalled();
+    }));
+
+    it('should update role', fakeAsync(() => {
+      const { runner, identityService, employeeEffects } = setup();
+
+      identityService.changeUserRole.and.returnValue(Observable.of({}));
+
+      const employee: Employee = {
+        identifier: '',
+        givenName: '',
+        surname: '',
+        contactDetails: []
+      };
+
+      const expectedResult = new UpdateEmployeeSuccessAction({
+        resource: employee,
+        activatedRoute: null
+      });
+
+      runner.queue(new UpdateEmployeeAction({
+        employee: employee,
+        role: 'test',
+        activatedRoute: null
+      }));
+
+      let result = null;
+      employeeEffects.updateEmployee$.subscribe(_result => result = _result);
+
+      tick();
+
+      expect(result).toEqual(expectedResult);
+
+      expect(identityService.changeUserRole).toHaveBeenCalled();
+    }));
+  });
+});
diff --git a/src/app/employees/store/effects/service.effects.ts b/src/app/employees/store/effects/service.effects.ts
new file mode 100644
index 0000000..0a17834
--- /dev/null
+++ b/src/app/employees/store/effects/service.effects.ts
@@ -0,0 +1,95 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 '../../../services/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 {IdentityService} from '../../../services/identity/identity.service';
+import {RoleIdentifier} from '../../../services/identity/domain/role-identifier.model';
+import {Password} from '../../../services/identity/domain/password.model';
+
+@Injectable()
+export class EmployeeApiEffects {
+
+  @Effect()
+  createEmployee$: Observable<Action> = this.actions$
+    .ofType(employeeActions.CREATE)
+    .map((action: employeeActions.CreateEmployeeAction) => action.payload)
+    .mergeMap(payload =>
+      this.identityService.createUser(payload.user)
+        .mergeMap(() => this.officeService.createEmployee(payload.employee) )
+        .map(() => new employeeActions.CreateEmployeeSuccessAction({
+          resource: payload.employee,
+          activatedRoute: payload.activatedRoute
+        }))
+        .catch((error) => of(new employeeActions.CreateEmployeeFailAction(error)))
+    );
+
+  @Effect()
+  updateEmployee$: Observable<Action> = this.actions$
+    .ofType(employeeActions.UPDATE)
+    .map((action: employeeActions.UpdateEmployeeAction) => action.payload)
+    .mergeMap(payload => {
+        const employee = payload.employee;
+        const httpCalls: Observable<any>[] = [];
+
+        httpCalls.push(this.officeService.updateEmployee(employee));
+
+        if (payload.contactDetails) {
+          httpCalls.push(this.officeService.setContactDetails(employee.identifier, payload.contactDetails));
+        }
+
+        if (payload.role) {
+          httpCalls.push(this.identityService.changeUserRole(employee.identifier, new RoleIdentifier(payload.role)));
+        }
+
+        if (payload.password) {
+          httpCalls.push(this.identityService.changePassword(employee.identifier, new Password(payload.password)));
+        }
+
+        return Observable.forkJoin(httpCalls)
+          .map(() => new employeeActions.UpdateEmployeeSuccessAction({
+            resource: payload.employee,
+            activatedRoute: payload.activatedRoute
+          }))
+          .catch((error) => of(new employeeActions.UpdateEmployeeFailAction(error)));
+    });
+
+  @Effect()
+  deleteEmployee$: Observable<Action> = this.actions$
+    .ofType(employeeActions.DELETE)
+    .map((action: employeeActions.DeleteEmployeeAction) => action.payload)
+    .mergeMap(payload => {
+      return Observable.forkJoin(
+        this.officeService.deleteEmployee(payload.employee.identifier),
+        this.identityService.changeUserRole(payload.employee.identifier, new RoleIdentifier('deactivated'))
+      )
+        .map(() => new employeeActions.DeleteEmployeeSuccessAction({
+          resource: payload.employee,
+          activatedRoute: payload.activatedRoute
+        }))
+        .catch((error) => of(new employeeActions.DeleteEmployeeFailAction(error)));
+      }
+    );
+
+  constructor(private actions$: Actions, private officeService: OfficeService, private identityService: IdentityService) { }
+}
diff --git a/src/app/employees/store/employee.actions.ts b/src/app/employees/store/employee.actions.ts
new file mode 100644
index 0000000..9083240
--- /dev/null
+++ b/src/app/employees/store/employee.actions.ts
@@ -0,0 +1,149 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 {Employee} from '../../services/office/domain/employee.model';
+import {Error} from '../../services/domain/error.model';
+import {ContactDetail} from '../../services/domain/contact/contact-detail.model';
+import {UserWithPassword} from '../../services/identity/domain/user-with-password.model';
+import {RoutePayload} from '../../common/store/route-payload';
+import {
+  CreateResourceSuccessPayload,
+  DeleteResourceSuccessPayload,
+  LoadResourcePayload,
+  SelectResourcePayload,
+  UpdateResourceSuccessPayload
+} from '../../common/store/resource.reducer';
+
+export const LOAD = type('[Employee] Load');
+export const SELECT = type('[Employee] Select');
+
+export const CREATE = type('[Employee] Create');
+export const CREATE_SUCCESS = type('[Employee] Create Success');
+export const CREATE_FAIL = type('[Employee] Create Fail');
+
+export const UPDATE = type('[Employee] Update');
+export const UPDATE_SUCCESS = type('[Employee] Update Success');
+export const UPDATE_FAIL = type('[Employee] Update Fail');
+
+export const DELETE = type('[Employee] Delete');
+export const DELETE_SUCCESS = type('[Employee] Delete Success');
+export const DELETE_FAIL = type('[Employee] Delete Fail');
+
+export const RESET_FORM = type('[Employee] Reset Form');
+
+export interface EmployeeRoutePayload extends RoutePayload {
+  employee: Employee;
+}
+
+export interface CreateEmployeePayload extends EmployeeRoutePayload {
+  user: UserWithPassword;
+}
+
+export interface UpdateEmployeePayload extends EmployeeRoutePayload {
+  contactDetails?: ContactDetail[];
+  password?: string;
+  role?: string;
+}
+
+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 CreateEmployeeAction implements Action {
+  readonly type = CREATE;
+
+  constructor(public payload: CreateEmployeePayload) { }
+}
+
+export class CreateEmployeeSuccessAction implements Action {
+  readonly type = CREATE_SUCCESS;
+
+  constructor(public payload: CreateResourceSuccessPayload) { }
+}
+
+export class CreateEmployeeFailAction implements Action {
+  readonly type = CREATE_FAIL;
+
+  constructor(public payload: Error) { }
+}
+
+export class UpdateEmployeeAction implements Action {
+  readonly type = UPDATE;
+
+  constructor(public payload: UpdateEmployeePayload) { }
+}
+
+export class UpdateEmployeeSuccessAction implements Action {
+  readonly type = UPDATE_SUCCESS;
+
+  constructor(public payload: UpdateResourceSuccessPayload) { }
+}
+
+export class UpdateEmployeeFailAction implements Action {
+  readonly type = UPDATE_FAIL;
+
+  constructor(public payload: Error) { }
+}
+
+export class DeleteEmployeeAction implements Action {
+  readonly type = DELETE;
+
+  constructor(public payload: EmployeeRoutePayload) { }
+}
+
+export class DeleteEmployeeSuccessAction implements Action {
+  readonly type = DELETE_SUCCESS;
+
+  constructor(public payload: DeleteResourceSuccessPayload) { }
+}
+
+export class DeleteEmployeeFailAction implements Action {
+  readonly type = DELETE_FAIL;
+
+  constructor(public payload: Error) { }
+}
+
+export class ResetEmployeeFormAction implements Action {
+  readonly type = RESET_FORM;
+
+  constructor() { }
+}
+
+export type Actions
+  = LoadAction
+  | SelectAction
+  | CreateEmployeeAction
+  | CreateEmployeeSuccessAction
+  | CreateEmployeeFailAction
+  | UpdateEmployeeAction
+  | UpdateEmployeeSuccessAction
+  | UpdateEmployeeFailAction
+  | DeleteEmployeeAction
+  | DeleteEmployeeSuccessAction
+  | DeleteEmployeeFailAction
+  | ResetEmployeeFormAction;
diff --git a/src/app/employees/store/index.ts b/src/app/employees/store/index.ts
new file mode 100644
index 0000000..6b4ea4e
--- /dev/null
+++ b/src/app/employees/store/index.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 * as fromRoot from '../../store';
+import {ActionReducer, Store} from '@ngrx/store';
+import {createReducer} from '../../store/index';
+import {createSelector} from 'reselect';
+import {createResourceReducer, getResourceLoadedAt, getResourceSelected, ResourceState} from '../../common/store/resource.reducer';
+import {createFormReducer, FormState, getFormError} from '../../common/store/form.reducer';
+
+export interface State extends fromRoot.State {
+  employees: ResourceState;
+  employeeForm: FormState;
+}
+
+const reducers = {
+  employees: createResourceReducer('Employee'),
+  employeeForm: createFormReducer('Employee')
+};
+
+export const employeeModuleReducer: ActionReducer<State> = createReducer(reducers);
+
+export const getEmployeesState = (state: State) => state.employees;
+
+export const getEmployeeFormState = (state: State) => state.employeeForm;
+export const getEmployeeFormError = createSelector(getEmployeeFormState, getFormError);
+
+export const getEmployeesLoadedAt = createSelector(getEmployeesState, getResourceLoadedAt);
+export const getSelectedEmployee = createSelector(getEmployeesState, getResourceSelected);
+
+export class EmployeesStore extends Store<State> {}
+
+export function employeeStoreFactory(appStore: Store<fromRoot.State>) {
+  appStore.replaceReducer(employeeModuleReducer);
+  return appStore;
+}
diff --git a/src/app/employees/user.resolver.ts b/src/app/employees/user.resolver.ts
new file mode 100644
index 0000000..34f29cd
--- /dev/null
+++ b/src/app/employees/user.resolver.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 {Injectable} from '@angular/core';
+import {ActivatedRouteSnapshot, Resolve} from '@angular/router';
+import {Observable} from 'rxjs/Observable';
+import {IdentityService} from '../services/identity/identity.service';
+import {User} from '../services/identity/domain/user.model';
+
+@Injectable()
+export class UserResolver implements Resolve<User> {
+
+  constructor(private identityService: IdentityService) {}
+
+  resolve(route: ActivatedRouteSnapshot): Observable<User> {
+    return this.identityService.getUser(route.params['id']);
+  }
+}
diff --git a/src/app/environment.ts b/src/app/environment.ts
new file mode 100644
index 0000000..9a16843
--- /dev/null
+++ b/src/app/environment.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.
+ */
+
+// The file for the current environment will overwrite this one during build
+// Different environments can be found in config/environment.{dev|prod}.ts
+// The build system defaults to the dev environment
+
+export const environment: any = {
+  production: false,
+};
diff --git a/src/app/index.ts b/src/app/index.ts
new file mode 100644
index 0000000..2df8576
--- /dev/null
+++ b/src/app/index.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 {environment} from './environment';
+export {AppModule} from './app.module';
diff --git a/src/app/loans/products/charges/charge-exists.guard.ts b/src/app/loans/products/charges/charge-exists.guard.ts
new file mode 100644
index 0000000..68ae830
--- /dev/null
+++ b/src/app/loans/products/charges/charge-exists.guard.ts
@@ -0,0 +1,69 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, CanActivate, RouterStateSnapshot} from '@angular/router';
+import {Injectable} from '@angular/core';
+import * as fromProducts from '../store';
+import {Observable} from 'rxjs/Observable';
+import {LoadAction} from '../store/charges/charge.actions';
+import {of} from 'rxjs/observable/of';
+import {PortfolioStore} from '../store/index';
+import {PortfolioService} from '../../../services/portfolio/portfolio.service';
+import {ExistsGuardService} from '../../../common/guards/exists-guard';
+
+@Injectable()
+export class ProductChargeExistsGuard implements CanActivate {
+
+  constructor(private store: PortfolioStore,
+              private portfolioService: PortfolioService,
+              private existsGuardService: ExistsGuardService) {}
+
+  hasChargeInStore(id: string): Observable<boolean> {
+    const timestamp$ = this.store.select(fromProducts.getProductChargesLoadedAt)
+      .map(loadedAt => loadedAt[id]);
+
+    return this.existsGuardService.isWithinExpiry(timestamp$);
+  }
+
+  hasChargeInApi(productId: string, chargeId: string): Observable<boolean> {
+    const getChargeDefinition = this.portfolioService.getChargeDefinition(productId, chargeId)
+      .map(chargeEntity => new LoadAction({
+        resource: chargeEntity
+      }))
+      .do((action: LoadAction) => this.store.dispatch(action))
+      .map(charge => !!charge);
+
+    return this.existsGuardService.routeTo404OnError(getChargeDefinition);
+  }
+
+  hasCharge(productId: string, chargeId: string): Observable<boolean> {
+    return this.hasChargeInStore(chargeId)
+      .switchMap(inStore => {
+        if (inStore) {
+          return of(inStore);
+        }
+
+        return this.hasChargeInApi(productId, chargeId);
+      });
+  }
+
+  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
+    return this.hasCharge(route.parent.params['productId'], route.params['chargeId']);
+  }
+}
diff --git a/src/app/loans/products/charges/charge.detail.component.html b/src/app/loans/products/charges/charge.detail.component.html
new file mode 100644
index 0000000..158185e
--- /dev/null
+++ b/src/app/loans/products/charges/charge.detail.component.html
@@ -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.
+-->
+
+<fims-layout-card-over [title]="charge.name" [subTitle]="charge.description" [navigateBackTo]="['../../']">
+  <fims-layout-card-over-header-menu>
+    <button mat-icon-button (click)="deleteCharge()" title="{{'Delete this fee' | translate}}" *hasPermission="{ id: 'portfolio_products', accessLevel: 'CHANGE'}"><mat-icon>delete</mat-icon></button>
+  </fims-layout-card-over-header-menu>
+  <div class="mat-content inset" flex>
+    <div layout="row">
+      <mat-list>
+        <mat-list-item>
+          <h3 matLine translate>Action</h3>
+          <p matLine>{{charge.chargeAction}}</p>
+        </mat-list-item>
+        <mat-list-item>
+          <h3 matLine translate>Amount</h3>
+          <p matLine>{{charge.amount | number}}</p>
+        </mat-list-item>
+        <mat-list-item>
+          <h3 matLine translate>Charge method</h3>
+          <p matLine>{{charge.chargeMethod}}</p>
+        </mat-list-item>
+        <mat-list-item>
+          <h3 matLine translate>Proportional to</h3>
+          <p matLine>{{charge.proportionalTo}}</p>
+        </mat-list-item>
+        <mat-list-item>
+          <h3 matLine translate>Cycle</h3>
+          <p matLine>{{charge.forCycleSizeUnit}}</p>
+        </mat-list-item>
+      </mat-list>
+    </div>
+  </div>
+</fims-layout-card-over>
+<fims-fab-button title="{{'Edit fee' | translate}}" icon="mode_edit" [link]="['edit']" [permission]="{ id: 'portfolio_products', accessLevel: 'CHANGE'}"></fims-fab-button>
diff --git a/src/app/loans/products/charges/charge.detail.component.ts b/src/app/loans/products/charges/charge.detail.component.ts
new file mode 100644
index 0000000..aa72d0d
--- /dev/null
+++ b/src/app/loans/products/charges/charge.detail.component.ts
@@ -0,0 +1,87 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, OnDestroy, OnInit} from '@angular/core';
+import {ActivatedRoute} from '@angular/router';
+import {ChargeDefinition} from '../../../services/portfolio/domain/charge-definition.model';
+import {Observable} from 'rxjs/Observable';
+import {Subscription} from 'rxjs/Subscription';
+import {TdDialogService} from '@covalent/core';
+import {DELETE, SelectAction} from '../store/charges/charge.actions';
+import {PortfolioStore} from '../store/index';
+import * as fromPortfolio from '../store';
+import {FimsProduct} from '../store/model/fims-product.model';
+
+@Component({
+  templateUrl: './charge.detail.component.html'
+})
+export class ProductChargeDetailComponent implements OnInit, OnDestroy {
+
+  private actionsSubscription: Subscription;
+
+  private productSubscription: Subscription;
+
+  private chargeSubscription: Subscription;
+
+  private product: FimsProduct;
+
+  charge: ChargeDefinition;
+
+  constructor(private route: ActivatedRoute, private dialogService: TdDialogService, private portfolioStore: PortfolioStore) {}
+
+  ngOnInit(): void {
+    this.actionsSubscription = this.route.params
+      .map(params => new SelectAction(params['chargeId']))
+      .subscribe(this.portfolioStore);
+
+    this.productSubscription = this.portfolioStore.select(fromPortfolio.getSelectedProduct)
+      .filter(product => !!product)
+      .subscribe(product => this.product = product);
+
+    this.chargeSubscription = this.portfolioStore.select(fromPortfolio.getSelectedProductCharge)
+      .filter(charge => !!charge)
+      .subscribe(charge => this.charge = charge);
+  }
+
+  ngOnDestroy(): void {
+    this.actionsSubscription.unsubscribe();
+    this.productSubscription.unsubscribe();
+    this.chargeSubscription.unsubscribe();
+  }
+
+  confirmDeletion(): Observable<boolean> {
+    return this.dialogService.openConfirm({
+      message: 'Do you want to delete fee "' + this.charge.identifier + '"?',
+      title: 'Confirm deletion',
+      acceptButton: 'DELETE FEE',
+    }).afterClosed();
+  }
+
+  deleteCharge(): void {
+    this.confirmDeletion()
+      .filter(accept => accept)
+      .subscribe(() => {
+        this.portfolioStore.dispatch({ type: DELETE, payload: {
+          productId: this.product.identifier,
+          charge: this.charge,
+          activatedRoute: this.route
+        }});
+      });
+  }
+
+}
diff --git a/src/app/loans/products/charges/charge.list.component.html b/src/app/loans/products/charges/charge.list.component.html
new file mode 100644
index 0000000..610037c
--- /dev/null
+++ b/src/app/loans/products/charges/charge.list.component.html
@@ -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.
+-->
+
+<fims-layout-card-over title="{{'Manage loan product fees' | translate}}" [navigateBackTo]="['../']">
+  <fims-two-column-layout>
+    <mat-nav-list left>
+      <h3 mat-subheader translate>Management</h3>
+      <a mat-list-item [routerLink]="['./ranges']">
+        <mat-icon matListAvatar>assignment</mat-icon>
+        <h3 matLine translate>Ranges</h3>
+        <p matLine translate>Manage ranges</p>
+      </a>
+    </mat-nav-list>
+    <fims-data-table right flex (onFetch)="fetchCharges($event)" (onActionCellClick)="rowSelect($event)" [columns]="columns" [data]="chargesData$ | async" [sortable]="false"></fims-data-table>
+  </fims-two-column-layout>
+
+</fims-layout-card-over>
diff --git a/src/app/loans/products/charges/charge.list.component.ts b/src/app/loans/products/charges/charge.list.component.ts
new file mode 100644
index 0000000..bdcce6d
--- /dev/null
+++ b/src/app/loans/products/charges/charge.list.component.ts
@@ -0,0 +1,88 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, OnDestroy, OnInit} from '@angular/core';
+import {ActivatedRoute, Router} from '@angular/router';
+import {TableData, TableFetchRequest} from '../../../common/data-table/data-table.component';
+import {ChargeDefinition} from '../../../services/portfolio/domain/charge-definition.model';
+import {ITdDataTableColumn} from '@covalent/core';
+import {ActionOption, ActionOptions} from '../../../common/domain/action-option.model';
+import {PortfolioStore} from '../store/index';
+import * as fromPortfolio from '../store';
+import {Observable} from 'rxjs/Observable';
+import {Subscription} from 'rxjs/Subscription';
+import {LOAD_ALL} from '../store/charges/charge.actions';
+import {FimsProduct} from '../store/model/fims-product.model';
+
+@Component({
+  templateUrl: './charge.list.component.html'
+})
+export class ProductChargeListComponent implements OnInit, OnDestroy {
+
+  private productSubscription: Subscription;
+
+  private product: FimsProduct;
+
+  chargesData$: Observable<TableData>;
+
+  columns: ITdDataTableColumn[] = [
+    { name: 'identifier', label: 'Id' },
+    { name: 'name', label: 'Name' },
+    { name: 'amount', label: 'Amount', numeric: true, format: value => value.toFixed(2) },
+    { name: 'chargeAction', label: 'Applied when', format: value => {
+      const result: ActionOption = ActionOptions.find((option) => {
+        return option.type === value;
+      });
+      return result.label;
+    } }
+  ];
+
+  constructor(private router: Router, private route: ActivatedRoute, private portfolioStore: PortfolioStore) {}
+
+  ngOnInit(): void {
+    this.productSubscription = this.portfolioStore.select(fromPortfolio.getSelectedProduct)
+      .filter(product => !!product)
+      .subscribe(product => {
+        this.product = product;
+        this.fetchCharges();
+      });
+
+    this.chargesData$ = this.portfolioStore.select(fromPortfolio.getAllProductChargeEntities)
+      .map(charges => {
+        const data = charges.filter(charge => !charge.readOnly);
+
+        return {
+          totalElements: data.length,
+          totalPages: 1,
+          data
+        };
+      });
+  }
+
+  ngOnDestroy(): void {
+    this.productSubscription.unsubscribe();
+  }
+
+  fetchCharges(event?: TableFetchRequest): void {
+    this.portfolioStore.dispatch({ type: LOAD_ALL, payload: this.product.identifier });
+  }
+
+  rowSelect(chargeDefinition: ChargeDefinition): void {
+    this.router.navigate(['detail', chargeDefinition.identifier], { relativeTo: this.route });
+  }
+}
diff --git a/src/app/loans/products/charges/form/create.component.html b/src/app/loans/products/charges/form/create.component.html
new file mode 100644
index 0000000..bdf8ab2
--- /dev/null
+++ b/src/app/loans/products/charges/form/create.component.html
@@ -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.
+-->
+
+<fims-layout-card-over title="{{'Add new fee' | translate}}">
+  <fims-product-charge-form-component
+    (onSave)="onSave($event)"
+    (onCancel)="onCancel()"
+    [charge]="charge"
+    [ranges]="ranges$ | async">
+  </fims-product-charge-form-component>
+</fims-layout-card-over>
diff --git a/src/app/loans/products/charges/form/create.component.ts b/src/app/loans/products/charges/form/create.component.ts
new file mode 100644
index 0000000..19db22a
--- /dev/null
+++ b/src/app/loans/products/charges/form/create.component.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 {Component, OnDestroy, OnInit} from '@angular/core';
+import {ChargeDefinition} from '../../../../services/portfolio/domain/charge-definition.model';
+import {ActivatedRoute, Router} from '@angular/router';
+import * as fromPortfolio from '../../store/index';
+import {PortfolioStore} from '../../store/index';
+import {Subscription} from 'rxjs/Subscription';
+import {CREATE} from '../../store/charges/charge.actions';
+import {FimsProduct} from '../../store/model/fims-product.model';
+import {Observable} from 'rxjs/Observable';
+import {RangeActions} from '../../store/ranges/range.actions';
+import {FimsRange} from '../../../../services/portfolio/domain/range-model';
+
+@Component({
+  templateUrl: './create.component.html'
+})
+export class ProductChargeCreateFormComponent implements OnInit, OnDestroy {
+
+  private productSubscription: Subscription;
+
+  private product: FimsProduct;
+
+  ranges$: Observable<FimsRange[]>;
+
+  charge: ChargeDefinition = {
+    identifier: '',
+    name: '',
+    description: '',
+    chargeAction: 'OPEN',
+    amount: 0,
+    chargeMethod: 'FIXED',
+    fromAccountDesignator: '',
+    toAccountDesignator: '',
+    forCycleSizeUnit: 'WEEKS',
+    proportionalTo: ''
+  };
+
+  constructor(private router: Router, private route: ActivatedRoute, private portfolioStore: PortfolioStore) {}
+
+  ngOnInit(): void {
+    this.productSubscription = this.portfolioStore.select(fromPortfolio.getSelectedProduct)
+      .do(product => this.portfolioStore.dispatch(RangeActions.loadAllAction(product.identifier)))
+      .subscribe(product => this.product = product);
+
+    this.ranges$ = this.portfolioStore.select(fromPortfolio.getAllProductChargeRangeEntities);
+  }
+
+  ngOnDestroy(): void {
+    this.productSubscription.unsubscribe();
+  }
+
+  onSave(charge: ChargeDefinition): void {
+    this.portfolioStore.dispatch({ type: CREATE, payload: {
+      productId: this.product.identifier,
+      charge: charge,
+      activatedRoute: this.route
+    }});
+  }
+
+  onCancel(): void {
+    this.navigateAway();
+  }
+
+  navigateAway(): void {
+    this.router.navigate(['../'], { relativeTo: this.route });
+  }
+}
diff --git a/src/app/loans/products/charges/form/edit.component.html b/src/app/loans/products/charges/form/edit.component.html
new file mode 100644
index 0000000..26efa77
--- /dev/null
+++ b/src/app/loans/products/charges/form/edit.component.html
@@ -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.
+-->
+
+<fims-layout-card-over title="{{'Edit fee' | translate}}">
+  <fims-product-charge-form-component
+    (onSave)="onSave($event)"
+    (onCancel)="onCancel()"
+    [charge]="charge$ | async"
+    [ranges]="ranges$ | async"
+    [editMode]="true">
+  </fims-product-charge-form-component>
+</fims-layout-card-over>
diff --git a/src/app/loans/products/charges/form/edit.component.ts b/src/app/loans/products/charges/form/edit.component.ts
new file mode 100644
index 0000000..32625f6
--- /dev/null
+++ b/src/app/loans/products/charges/form/edit.component.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, OnDestroy, OnInit} from '@angular/core';
+import {ChargeDefinition} from '../../../../services/portfolio/domain/charge-definition.model';
+import {ActivatedRoute, Router} from '@angular/router';
+import {Subscription} from 'rxjs/Subscription';
+import {UPDATE} from '../../store/charges/charge.actions';
+import * as fromPortfolio from '../../store';
+import {PortfolioStore} from '../../store/index';
+import {FimsProduct} from '../../store/model/fims-product.model';
+import {Observable} from 'rxjs/Observable';
+import {RangeActions} from '../../store/ranges/range.actions';
+import {FimsRange} from '../../../../services/portfolio/domain/range-model';
+
+@Component({
+  templateUrl: './edit.component.html'
+})
+export class ProductChargeEditFormComponent implements OnInit, OnDestroy {
+
+  private productSubscription: Subscription;
+
+  private product: FimsProduct;
+
+  charge$: Observable<ChargeDefinition>;
+
+  ranges$: Observable<FimsRange[]>;
+
+  constructor(private router: Router, private route: ActivatedRoute, private portfolioStore: PortfolioStore) {}
+
+  ngOnInit(): void {
+    this.productSubscription = this.portfolioStore.select(fromPortfolio.getSelectedProduct)
+      .filter(product => !!product)
+      .do(product => this.portfolioStore.dispatch(RangeActions.loadAllAction(product.identifier)))
+      .subscribe(product => this.product = product);
+
+    this.charge$ = this.portfolioStore.select(fromPortfolio.getSelectedProductCharge)
+      .filter(charge => !!charge);
+
+    this.ranges$ = this.portfolioStore.select(fromPortfolio.getAllProductChargeRangeEntities);
+  }
+
+  ngOnDestroy(): void {
+    this.productSubscription.unsubscribe();
+  }
+
+  onSave(charge: ChargeDefinition): void {
+    this.portfolioStore.dispatch({ type: UPDATE, payload: {
+      productId: this.product.identifier,
+      charge: charge,
+      activatedRoute: this.route
+    }});
+  }
+
+  onCancel(): void {
+    this.navigateAway();
+  }
+
+  navigateAway(): void {
+    this.router.navigate(['../'], { relativeTo: this.route });
+  }
+}
diff --git a/src/app/loans/products/charges/form/form.component.html b/src/app/loans/products/charges/form/form.component.html
new file mode 100644
index 0000000..83f43e1
--- /dev/null
+++ b/src/app/loans/products/charges/form/form.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.
+-->
+
+<td-steps mode="'vertical'">
+  <td-step #detailsStep label="{{'Fee details' | translate}}" [state]="detailForm.valid ? 'complete' : detailForm.pristine ? 'none' : 'required'" [active]="true">
+    <form [formGroup]="detailForm" layout="column">
+      <fims-id-input [form]="detailForm" controlName="identifier" [readonly]="editMode"></fims-id-input>
+      <fims-text-input [form]="detailForm" controlName="name" placeholder="{{'Name' | translate}}"></fims-text-input>
+      <mat-form-field layout-margin flex>
+        <textarea matInput placeholder="{{'Description' | translate}}" formControlName="description"></textarea>
+        <mat-error *ngIf="detailForm.get('description').hasError('required')" translate>
+          Required
+        </mat-error>
+      </mat-form-field>
+      <div layout="row">
+        <fims-text-input type="number" [form]="detailForm" controlName="amount" placeholder="{{'Amount' | translate}}"></fims-text-input>
+        <mat-radio-group formControlName="chargeMethod">
+          <mat-radio-button *ngFor="let basis of chargeMethodOptions" [value]="basis.type" layout-margin>
+            {{basis.label}}
+          </mat-radio-button>
+        </mat-radio-group>
+        <mat-form-field layout-margin>
+          <mat-select formControlName="proportionalTo" placeholder="{{ 'Proportional to' | translate }}">
+            <mat-option *ngFor="let proportionalTo of proportionalToOptions" [value]="proportionalTo.designator">
+              {{proportionalTo.label}}
+            </mat-option>
+          </mat-select>
+        </mat-form-field>
+      </div>
+      <mat-slide-toggle formControlName="rangeEnabled" layout-margin translate layout-margin>
+        Apply amount only within range?
+      </mat-slide-toggle>
+      <ng-container *ngIf="detailForm.get('rangeEnabled').value === true">
+        <mat-form-field layout-margin>
+          <mat-select formControlName="rangeIdentifier" placeholder="{{ 'Select range' | translate }}">
+            <mat-option *ngFor="let range of ranges" [value]="range.identifier">
+              {{range.identifier}}
+            </mat-option>
+          </mat-select>
+        </mat-form-field>
+        <mat-form-field layout-margin>
+          <mat-select formControlName="rangeSegmentIdentifier" placeholder="{{ 'Select range segment' | translate }}">
+            <mat-option *ngFor="let segment of selectedRange?.segments" [value]="segment.identifier">
+              {{segment.identifier}}({{segment.start | number:numberFormat}} - {{segment.end | number:numberFormat}})
+            </mat-option>
+          </mat-select>
+        </mat-form-field>
+      </ng-container>
+    </form>
+    <ng-template td-step-actions>
+      <fims-form-final-action
+        [resourceName]="'FEE'"
+        [editMode]="editMode"
+        [disabled]="!detailForm.valid"
+        (onCancel)="cancel()"
+        (onSave)="save()">
+      </fims-form-final-action>
+    </ng-template>
+  </td-step>
+</td-steps>
diff --git a/src/app/loans/products/charges/form/form.component.ts b/src/app/loans/products/charges/form/form.component.ts
new file mode 100644
index 0000000..96e51c6
--- /dev/null
+++ b/src/app/loans/products/charges/form/form.component.ts
@@ -0,0 +1,181 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, OnChanges, Output, SimpleChanges} from '@angular/core';
+import {ChargeDefinition} from '../../../../services/portfolio/domain/charge-definition.model';
+import {FormBuilder, FormGroup, Validators} from '@angular/forms';
+import {ChargeMethod} from '../../../../services/portfolio/domain/charge-method.model';
+import {temporalOptionList} from '../../../../common/domain/temporal.domain';
+import {FimsValidators} from '../../../../common/validator/validators';
+import {ChargeProportionalDesignators} from '../../../../services/portfolio/domain/individuallending/charge-proportional-designators.model';
+import {FimsRange} from '../../../../services/portfolio/domain/range-model';
+
+interface ChargeMethodOption {
+  type: ChargeMethod;
+  label: string;
+}
+
+@Component({
+  selector: 'fims-product-charge-form-component',
+  templateUrl: './form.component.html'
+})
+export class ProductChargeFormComponent implements OnChanges {
+
+  numberFormat = '1.2-2';
+
+  chargeMethodOptions: ChargeMethodOption[] = [
+    { type: 'FIXED', label: 'Fixed'},
+    { type: 'PROPORTIONAL', label: 'Proportional'}
+  ];
+
+  proportionalToOptions: any[] = [
+    { label: 'Maximum balance', designator: ChargeProportionalDesignators.MAXIMUM_BALANCE_DESIGNATOR },
+    { label: 'Repayment', designator: ChargeProportionalDesignators.REQUESTED_DISBURSEMENT_DESIGNATOR },
+    { label: 'Running balance', designator: ChargeProportionalDesignators.RUNNING_BALANCE_DESIGNATOR }
+  ];
+
+  temporalOptions = temporalOptionList;
+
+  detailForm: FormGroup;
+
+  selectedRange: FimsRange;
+
+  @Input() editMode: boolean;
+
+  @Input() charge: ChargeDefinition;
+
+  @Input() ranges: FimsRange[];
+
+  @Output('onSave') onSave = new EventEmitter<ChargeDefinition>();
+
+  @Output('onCancel') onCancel = new EventEmitter<void>();
+
+  constructor(private formBuilder: FormBuilder) {
+    this.detailForm = this.formBuilder.group({
+      identifier: ['', [Validators.required, Validators.minLength(3), Validators.maxLength(32), FimsValidators.urlSafe]],
+      name: ['', [Validators.required]],
+      description: ['', [Validators.required]],
+      chargeMethod: ['', [Validators.required]],
+      proportionalTo: ['', [Validators.required]],
+      amount: [0, [Validators.required]],
+      rangeEnabled: [false],
+      rangeIdentifier: ['', Validators.required],
+      rangeSegmentIdentifier: ['', Validators.required]
+    });
+
+    this.detailForm.get('chargeMethod').valueChanges
+      .subscribe(value => this.toggleChargeMethod(value));
+
+    this.detailForm.get('rangeIdentifier').valueChanges
+      .subscribe(identifier => this.toggleRange(identifier));
+
+    this.detailForm.get('rangeEnabled').valueChanges
+      .subscribe(enabled => this.toggleRangeEnabled(enabled));
+  }
+
+  ngOnChanges(changes: SimpleChanges): void {
+    if (changes.charge) {
+      const chargeData: any = {
+        identifier: this.charge.identifier,
+        name: this.charge.name,
+        description: this.charge.description,
+        chargeMethod: this.charge.chargeMethod,
+        proportionalTo: this.charge.proportionalTo,
+        amount: this.charge.amount
+      };
+
+      if (this.charge.forSegmentSet) {
+        chargeData.rangeEnabled = true;
+        chargeData.rangeIdentifier = this.charge.forSegmentSet;
+        chargeData.rangeSegmentIdentifier = this.charge.fromSegment ? [this.charge.fromSegment] : [];
+      }
+
+      this.detailForm.reset(chargeData);
+    }
+
+    if (changes.ranges) {
+      this.toggleRange(this.detailForm.get('rangeIdentifier').value);
+    }
+  }
+
+  private toggleChargeMethod(chargeMethod: ChargeMethod): void {
+    const proportionalTo = this.detailForm.get('proportionalTo');
+
+    if (chargeMethod === 'PROPORTIONAL') {
+      proportionalTo.enable();
+      proportionalTo.setValidators(Validators.required);
+    } else {
+      proportionalTo.disable();
+      proportionalTo.clearValidators();
+    }
+    proportionalTo.updateValueAndValidity();
+  }
+
+  toggleRange(identifier: string): void {
+    if (this.ranges) {
+      this.selectedRange = this.ranges.find(range => range.identifier === identifier);
+    }
+  }
+
+  toggleRangeEnabled(enabled: boolean): void {
+    const rangeIdentifier = this.detailForm.get('rangeIdentifier');
+    const rangeSegmentIdentifier = this.detailForm.get('rangeSegmentIdentifier');
+
+    if (enabled) {
+      rangeIdentifier.setValidators(Validators.required);
+      rangeSegmentIdentifier.setValidators(Validators.required);
+    } else {
+      rangeIdentifier.clearValidators();
+      rangeSegmentIdentifier.clearValidators();
+    }
+
+    rangeIdentifier.updateValueAndValidity();
+    rangeSegmentIdentifier.updateValueAndValidity();
+  }
+
+  save(): void {
+    const chargeMethod: ChargeMethod = this.detailForm.get('chargeMethod').value;
+
+    const charge: ChargeDefinition = Object.assign({}, this.charge, {
+      identifier: this.detailForm.get('identifier').value,
+      name: this.detailForm.get('name').value,
+      description: this.detailForm.get('description').value,
+      chargeMethod,
+      amount: this.detailForm.get('amount').value,
+      proportionalTo: chargeMethod === 'PROPORTIONAL' ? this.detailForm.get('proportionalTo').value : undefined
+    });
+
+    if (this.detailForm.get('rangeEnabled').value) {
+      charge.forSegmentSet = this.detailForm.get('rangeIdentifier').value;
+      const segmentIdentifier = this.detailForm.get('rangeSegmentIdentifier').value;
+      charge.fromSegment = segmentIdentifier;
+      charge.toSegment = segmentIdentifier;
+    } else {
+      delete charge.forSegmentSet;
+      delete charge.fromSegment;
+      delete charge.toSegment;
+    }
+
+    this.onSave.emit(charge);
+  }
+
+  cancel(): void {
+    this.onCancel.emit();
+  }
+
+}
diff --git a/src/app/loans/products/charges/ranges/form/create.component.html b/src/app/loans/products/charges/ranges/form/create.component.html
new file mode 100644
index 0000000..2f352cb
--- /dev/null
+++ b/src/app/loans/products/charges/ranges/form/create.component.html
@@ -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.
+-->
+
+<fims-layout-card-over title="{{'Create range' | translate}}">
+  <fims-product-charge-range-form-component
+    [range]="range"
+    (onCancel)="cancel()"
+    (onSave)="save($event)">
+  </fims-product-charge-range-form-component>
+</fims-layout-card-over>
diff --git a/src/app/loans/products/charges/ranges/form/create.component.ts b/src/app/loans/products/charges/ranges/form/create.component.ts
new file mode 100644
index 0000000..40a66e5
--- /dev/null
+++ b/src/app/loans/products/charges/ranges/form/create.component.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 {Component, OnDestroy, OnInit} from '@angular/core';
+import {ActivatedRoute, Router} from '@angular/router';
+import * as fromPortfolio from '../../../store/index';
+import {PortfolioStore} from '../../../store/index';
+import {RangeActions} from '../../../store/ranges/range.actions';
+import {FimsProduct} from '../../../store/model/fims-product.model';
+import {Subscription} from 'rxjs/Subscription';
+import {FimsRange} from '../../../../../services/portfolio/domain/range-model';
+
+@Component({
+  templateUrl: './create.component.html'
+})
+export class CreateProductChargeRangeFormComponent implements OnInit, OnDestroy {
+
+  private productSubscription: Subscription;
+
+  private product: FimsProduct;
+
+  range: FimsRange = {
+    identifier: '',
+    segments: [
+      { identifier: 'Start', start: 0 }
+    ]
+  };
+
+  constructor(private router: Router, private route: ActivatedRoute, private portfolioStore: PortfolioStore) {}
+
+  ngOnInit(): void {
+    this.productSubscription = this.portfolioStore.select(fromPortfolio.getSelectedProduct)
+      .filter(product => !!product)
+      .subscribe(product => this.product = product);
+  }
+
+  ngOnDestroy(): void {
+    this.productSubscription.unsubscribe();
+  }
+
+  save(resource: FimsRange): void {
+    this.portfolioStore.dispatch(RangeActions.createAction({
+      resource,
+      data: {
+        productIdentifier: this.product.identifier,
+        activatedRoute: this.route
+      }
+    }));
+  }
+
+  cancel(): void {
+    this.router.navigate(['../'], { relativeTo: this.route });
+  }
+
+}
diff --git a/src/app/loans/products/charges/ranges/form/edit.component.html b/src/app/loans/products/charges/ranges/form/edit.component.html
new file mode 100644
index 0000000..475a26a
--- /dev/null
+++ b/src/app/loans/products/charges/ranges/form/edit.component.html
@@ -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.
+-->
+
+<fims-layout-card-over title="{{'Edit range' | translate}}">
+  <fims-product-charge-range-form-component
+    [range]="range$ | async"
+    (onCancel)="cancel()"
+    (onSave)="save($event)"
+    [editMode]="true">
+  </fims-product-charge-range-form-component>
+</fims-layout-card-over>
diff --git a/src/app/loans/products/charges/ranges/form/edit.component.ts b/src/app/loans/products/charges/ranges/form/edit.component.ts
new file mode 100644
index 0000000..f15fe47
--- /dev/null
+++ b/src/app/loans/products/charges/ranges/form/edit.component.ts
@@ -0,0 +1,69 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, OnDestroy, OnInit} from '@angular/core';
+import * as fromPortfolio from '../../../store/index';
+import {PortfolioStore} from '../../../store/index';
+import {ActivatedRoute, Router} from '@angular/router';
+import {RangeActions} from '../../../store/ranges/range.actions';
+import {Subscription} from 'rxjs/Subscription';
+import {FimsProduct} from '../../../store/model/fims-product.model';
+import {Observable} from 'rxjs/Observable';
+import {FimsRange} from '../../../../../services/portfolio/domain/range-model';
+
+@Component({
+  templateUrl: './edit.component.html'
+})
+export class EditProductChargeRangeFormComponent implements OnInit, OnDestroy {
+
+  private productSubscription: Subscription;
+
+  private product: FimsProduct;
+
+  range$: Observable<FimsRange[]>;
+
+  constructor(private router: Router, private route: ActivatedRoute, private portfolioStore: PortfolioStore) {}
+
+  ngOnInit(): void {
+    this.productSubscription = this.portfolioStore.select(fromPortfolio.getSelectedProduct)
+      .filter(product => !!product)
+      .subscribe(product => this.product = product);
+
+    this.range$ = this.portfolioStore.select(fromPortfolio.getSelectedProductChargeRange);
+  }
+
+  ngOnDestroy(): void {
+    this.productSubscription.unsubscribe();
+  }
+
+  save(resource: FimsRange): void {
+    this.portfolioStore.dispatch(RangeActions.updateAction({
+      resource,
+      data: {
+        productIdentifier: this.product.identifier,
+        activatedRoute: this.route
+      }
+    }));
+  }
+
+
+  cancel(): void {
+    this.router.navigate(['../'], { relativeTo: this.route });
+  }
+
+}
diff --git a/src/app/loans/products/charges/ranges/form/form.component.html b/src/app/loans/products/charges/ranges/form/form.component.html
new file mode 100644
index 0000000..f42cfc9
--- /dev/null
+++ b/src/app/loans/products/charges/ranges/form/form.component.html
@@ -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.
+-->
+
+<td-steps mode="'vertical'">
+  <td-step #detailsStep label="{{'Range details' | translate}}" [state]="form.valid ? 'complete' : form.pristine ? 'none' : 'required'" [active]="true">
+    <form [formGroup]="form">
+      <fims-id-input [form]="form" controlName="identifier" [readonly]="editMode"></fims-id-input>
+      <ng-container formArrayName="rangeSegments">
+        <div layout="row" *ngFor="let range of rangeSegmentControls; let i=index" [formGroupName]="i">
+          <fims-id-input [form]="getFormGroup(i)" controlName="identifier" [readonly]="editMode"></fims-id-input>
+          <mat-form-field layout-margin>
+            <input matInput type="number" formControlName="start" placeholder="Range start"/>
+          </mat-form-field>
+          <mat-form-field layout-margin>
+            <input matInput formControlName="end" placeholder="Range end" [value]="getNextSegmentValue(i)"/>
+          </mat-form-field>
+          <button mat-button (click)="removeRangeSegment(i)" *ngIf="i > 0">{{'REMOVE RANGE' | translate}}</button>
+        </div>
+      </ng-container>
+      <div layout="row">
+        <button flex mat-button mat-raised-button (click)="addRangeSegment()">{{'ADD RANGE' | translate}}</button>
+      </div>
+    </form>
+    <ng-template td-step-actions>
+      <fims-form-final-action
+        [resourceName]="'RANGE'"
+        [editMode]="editMode"
+        [disabled]="!form.valid"
+        (onCancel)="cancel()"
+        (onSave)="save()">
+      </fims-form-final-action>
+    </ng-template>
+  </td-step>
+</td-steps>
diff --git a/src/app/loans/products/charges/ranges/form/form.component.spec.ts b/src/app/loans/products/charges/ranges/form/form.component.spec.ts
new file mode 100644
index 0000000..51da6c0
--- /dev/null
+++ b/src/app/loans/products/charges/ranges/form/form.component.spec.ts
@@ -0,0 +1,18 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
diff --git a/src/app/loans/products/charges/ranges/form/form.component.ts b/src/app/loans/products/charges/ranges/form/form.component.ts
new file mode 100644
index 0000000..c6f06e9
--- /dev/null
+++ b/src/app/loans/products/charges/ranges/form/form.component.ts
@@ -0,0 +1,116 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 {AbstractControl, FormArray, FormBuilder, FormGroup, Validators} from '@angular/forms';
+import {FimsValidators} from '../../../../../common/validator/validators';
+import {FimsRange} from '../../../../../services/portfolio/domain/range-model';
+
+@Component({
+  selector: 'fims-product-charge-range-form-component',
+  templateUrl: './form.component.html'
+})
+export class ProductChargeRangeFormComponent {
+
+  form: FormGroup;
+
+  @Input() set range(range: FimsRange) {
+    this.form = this.formBuilder.group({
+      identifier: [range.identifier, [Validators.required, Validators.minLength(3), Validators.maxLength(32), FimsValidators.urlSafe]],
+      rangeSegments: this.initRangeSegments(range)
+    });
+
+    // TODO: Validate unique identifier and ranges across range segments
+  };
+
+  @Input() editMode: boolean;
+
+  @Output('onSave') onSave = new EventEmitter<FimsRange>();
+
+  @Output('onCancel') onCancel = new EventEmitter<void>();
+
+  constructor(private formBuilder: FormBuilder) {}
+
+  private initRangeSegments(range: FimsRange): FormArray {
+    const formControls: FormGroup[] = [];
+
+    range.segments.forEach((segment, index) => {
+      formControls.push(this.initRange(segment.identifier, segment.start, index === 0));
+    });
+
+    return this.formBuilder.array(formControls);
+  }
+
+  private initRange(identifier: string = '', start: number = 0, disabled: boolean = false): FormGroup {
+    return this.formBuilder.group({
+      identifier: [identifier, [Validators.required, Validators.minLength(3), Validators.maxLength(32), FimsValidators.urlSafe]],
+      start: [{ value: start, disabled }, [Validators.required, FimsValidators.minValue(0)]],
+      end: [{ value: 0, disabled: true }, [Validators.required, FimsValidators.minValue(0)]],
+    });
+  }
+
+  get rangeSegments(): FormArray {
+    return this.form.get('rangeSegments') as FormArray;
+  }
+
+  addRangeSegment(): void {
+    this.rangeSegments.push(this.initRange());
+  }
+
+  removeRangeSegment(index: number): void {
+    this.rangeSegments.removeAt(index);
+  }
+
+  get rangeSegmentControls(): AbstractControl[] {
+    return this.rangeSegments.controls;
+  }
+
+  getFormGroup(index: number): FormGroup {
+    return this.rangeSegments.at(index) as FormGroup;
+  }
+
+  getNextSegmentValue(index: number): string {
+    const nextIndex = index + 1;
+
+    if (nextIndex >= this.rangeSegments.length) {
+      return '-';
+    }
+
+    const formGroup: FormGroup = this.getFormGroup(nextIndex);
+    return formGroup.get('start').value;
+  }
+
+  save(): void {
+    const formValue = this.form.getRawValue();
+
+    const range: FimsRange = {
+      identifier: formValue.identifier,
+      segments: formValue.rangeSegments.map(segment => ({
+        identifier: segment.identifier,
+        start: segment.start,
+        end: segment.end
+      }))
+    };
+
+    this.onSave.emit(range);
+  }
+
+  cancel(): void {
+    this.onCancel.emit();
+  }
+}
diff --git a/src/app/loans/products/charges/ranges/range-exists.guard.ts b/src/app/loans/products/charges/ranges/range-exists.guard.ts
new file mode 100644
index 0000000..6d52903
--- /dev/null
+++ b/src/app/loans/products/charges/ranges/range-exists.guard.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 {ActivatedRouteSnapshot, CanActivate, RouterStateSnapshot} from '@angular/router';
+import {Injectable} from '@angular/core';
+import * as fromProducts from '../../store';
+import {Observable} from 'rxjs/Observable';
+import {of} from 'rxjs/observable/of';
+import {ExistsGuardService} from '../../../../common/guards/exists-guard';
+import {PortfolioService} from '../../../../services/portfolio/portfolio.service';
+import {PortfolioStore} from '../../store/index';
+import {RangeActions} from '../../store/ranges/range.actions';
+import {FimsRange} from '../../../../services/portfolio/domain/range-model';
+
+@Injectable()
+export class ProductChargeRangeExistsGuard implements CanActivate {
+
+  constructor(private store: PortfolioStore,
+              private portfolioService: PortfolioService,
+              private existsGuardService: ExistsGuardService) {}
+
+  hasRangeInStore(id: string): Observable<boolean> {
+    const timestamp$ = this.store.select(fromProducts.getProductChargeRangesLoadedAt)
+      .map(loadedAt => loadedAt[id]);
+
+    return this.existsGuardService.isWithinExpiry(timestamp$);
+  }
+
+  hasRangeInApi(productId: string, rangeId: string): Observable<boolean> {
+    const getRange = this.portfolioService.getRange(productId, rangeId)
+      .do((resource: FimsRange) => this.store.dispatch(RangeActions.loadAction({
+        resource
+      })))
+      .map(resource => !!resource);
+
+    return this.existsGuardService.routeTo404OnError(getRange);
+  }
+
+  hasRange(productId: string, rangeId: string): Observable<boolean> {
+    return this.hasRangeInStore(rangeId)
+      .switchMap(inStore => {
+        if (inStore) {
+          return of(inStore);
+        }
+
+        return this.hasRangeInApi(productId, rangeId);
+      });
+  }
+
+  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
+    return this.hasRange(route.parent.params['productId'], route.params['rangeId']);
+  }
+}
diff --git a/src/app/loans/products/charges/ranges/range.detail.component.html b/src/app/loans/products/charges/ranges/range.detail.component.html
new file mode 100644
index 0000000..ad17279
--- /dev/null
+++ b/src/app/loans/products/charges/ranges/range.detail.component.html
@@ -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.
+-->
+
+<fims-layout-card-over [title]="range.identifier" [navigateBackTo]="['../../../../../../../']" *ngIf="(range$ | async) as range">
+  <fims-layout-card-over-header-menu>
+    <button mat-icon-button (click)="deleteRange(range)" title="{{'Delete this range' | translate}}" *hasPermission="{ id: 'portfolio_products', accessLevel: 'CHANGE'}"><mat-icon>delete</mat-icon></button>
+  </fims-layout-card-over-header-menu>
+  <div class="mat-content inset" flex>
+    <div layout="row">
+      <td-data-table
+        flex
+        [data]="range?.segments"
+        [columns]="rangeColumns"
+        [selectable]="false"
+        [clickable]="false"
+        [multiple]="false"
+        [sortable]="false">
+      </td-data-table>
+    </div>
+  </div>
+</fims-layout-card-over>
+<fims-fab-button title="{{'Edit range' | translate}}" icon="mode_edit" [link]="['edit']" [permission]="{ id: 'portfolio_products', accessLevel: 'CHANGE'}"></fims-fab-button>
diff --git a/src/app/loans/products/charges/ranges/range.detail.component.ts b/src/app/loans/products/charges/ranges/range.detail.component.ts
new file mode 100644
index 0000000..71eb006
--- /dev/null
+++ b/src/app/loans/products/charges/ranges/range.detail.component.ts
@@ -0,0 +1,79 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, OnInit} from '@angular/core';
+import {PortfolioStore} from '../../store/index';
+import * as fromPortfolio from '../../store';
+import {Observable} from 'rxjs/Observable';
+import {RangeActions} from '../../store/ranges/range.actions';
+import {ActivatedRoute} from '@angular/router';
+import {ITdDataTableColumn, TdDialogService} from '@covalent/core';
+import {FimsProduct} from '../../store/model/fims-product.model';
+import {Subscription} from 'rxjs/Subscription';
+import {FimsRange} from '../../../../services/portfolio/domain/range-model';
+
+@Component({
+  templateUrl: './range.detail.component.html'
+})
+export class ProductChargeRangeDetailComponent implements OnInit {
+
+  private productSubscription: Subscription;
+
+  private product: FimsProduct;
+
+  range$: Observable<FimsRange>;
+
+  rangeColumns: ITdDataTableColumn[] = [
+    { name: 'identifier', label: 'Identifier' },
+    { name: 'start', label: 'Start', numeric: true, format: value => value ? value.toFixed(2) : '-' },
+    { name: 'end', label: 'End', numeric: true, format: value => value ? value.toFixed(2) : '-' },
+  ];
+
+  constructor(private portfolioStore: PortfolioStore, private route: ActivatedRoute, private dialogService: TdDialogService) {}
+
+  ngOnInit(): void {
+    this.productSubscription = this.portfolioStore.select(fromPortfolio.getSelectedProduct)
+      .filter(product => !!product)
+      .subscribe(product => this.product = product);
+
+    this.range$ = this.portfolioStore.select(fromPortfolio.getSelectedProductChargeRange)
+      .filter(range => !!range);
+  }
+
+  confirmDeletion(): Observable<boolean> {
+    return this.dialogService.openConfirm({
+      message: 'Do you want to this range?',
+      title: 'Confirm deletion',
+      acceptButton: 'DELETE RANGE',
+    }).afterClosed();
+  }
+
+  deleteRange(resource: FimsRange): void {
+    this.confirmDeletion()
+      .filter(accept => accept)
+      .subscribe(() => {
+        this.portfolioStore.dispatch(RangeActions.deleteAction({
+          resource,
+          data: {
+            productIdentifier: this.product.identifier,
+            activatedRoute: this.route
+          }
+        }));
+      });
+  }
+}
diff --git a/src/app/loans/products/charges/ranges/range.index.component.html b/src/app/loans/products/charges/ranges/range.index.component.html
new file mode 100644
index 0000000..ca721b3
--- /dev/null
+++ b/src/app/loans/products/charges/ranges/range.index.component.html
@@ -0,0 +1,18 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<router-outlet></router-outlet>
diff --git a/src/app/loans/products/charges/ranges/range.index.component.ts b/src/app/loans/products/charges/ranges/range.index.component.ts
new file mode 100644
index 0000000..898c73e
--- /dev/null
+++ b/src/app/loans/products/charges/ranges/range.index.component.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 {Component, OnDestroy, OnInit} from '@angular/core';
+import {Subscription} from 'rxjs/Subscription';
+import {ActivatedRoute} from '@angular/router';
+import {RangeActions} from '../../store/ranges/range.actions';
+import {PortfolioStore} from '../../store/index';
+
+@Component({
+  templateUrl: './range.index.component.html'
+})
+export class ProductChargeRangeIndexComponent implements OnInit, OnDestroy {
+
+  private actionsSubscription: Subscription;
+
+  constructor(private route: ActivatedRoute, private store: PortfolioStore) {}
+
+  ngOnInit(): void {
+    this.actionsSubscription = this.route.params
+      .map(params => RangeActions.selectAction(params['rangeId']))
+      .subscribe(this.store);
+  }
+
+  ngOnDestroy(): void {
+    this.actionsSubscription.unsubscribe();
+  }
+}
diff --git a/src/app/loans/products/charges/ranges/range.list.component.html b/src/app/loans/products/charges/ranges/range.list.component.html
new file mode 100644
index 0000000..1fac59a
--- /dev/null
+++ b/src/app/loans/products/charges/ranges/range.list.component.html
@@ -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.
+-->
+
+<fims-layout-card-over title="{{ 'Manage ranges' | translate }}" [navigateBackTo]="['../../../../']">
+  <fims-data-table right flex
+                   (onFetch)="fetchRanges($event)"
+                   (onActionCellClick)="rowSelect($event)"
+                   [columns]="columns"
+                   [data]="rangesData$ | async"
+                   [sortable]="false">
+  </fims-data-table>
+</fims-layout-card-over>
+<fims-fab-button title="{{'Create new range' | translate}}" icon="add" [link]="['create']" [permission]="{ id: 'portfolio_products', accessLevel: 'CHANGE'}"></fims-fab-button>
diff --git a/src/app/loans/products/charges/ranges/range.list.component.ts b/src/app/loans/products/charges/ranges/range.list.component.ts
new file mode 100644
index 0000000..1059b44
--- /dev/null
+++ b/src/app/loans/products/charges/ranges/range.list.component.ts
@@ -0,0 +1,75 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, OnDestroy, OnInit} from '@angular/core';
+import {ITdDataTableColumn} from '@covalent/core';
+import {TableData, TableFetchRequest} from '../../../../common/data-table/data-table.component';
+import {Observable} from 'rxjs/Observable';
+import {PortfolioStore} from '../../store/index';
+import {RangeActions} from '../../store/ranges/range.actions';
+import {ActivatedRoute, Router} from '@angular/router';
+import * as fromPortfolio from '../../store';
+import {FimsRange} from '../../../../services/portfolio/domain/range-model';
+import {Subscription} from 'rxjs/Subscription';
+import {FimsProduct} from '../../store/model/fims-product.model';
+
+@Component({
+  templateUrl: './range.list.component.html'
+})
+export class ProductChargeRangeListComponent implements OnInit, OnDestroy {
+
+  private productSubscription: Subscription;
+
+  private product: FimsProduct;
+
+  rangesData$: Observable<TableData>;
+
+  columns: ITdDataTableColumn[] = [
+    { name: 'identifier', label: 'Id' }
+  ];
+
+  constructor(private router: Router, private route: ActivatedRoute, private portfolioStore: PortfolioStore) {}
+
+  ngOnInit(): void {
+    this.rangesData$ = this.portfolioStore.select(fromPortfolio.getAllProductChargeRangeEntities)
+      .map(data => ({
+        totalElements: data.length,
+        totalPages: 1,
+        data
+      }));
+
+    this.productSubscription = this.portfolioStore.select(fromPortfolio.getSelectedProduct)
+      .filter(product => !!product)
+      .subscribe(product => {
+        this.product = product;
+        this.fetchRanges();
+      });
+  }
+
+  ngOnDestroy(): void {
+    this.productSubscription.unsubscribe();
+  }
+
+  fetchRanges(event?: TableFetchRequest): void {
+    this.portfolioStore.dispatch(RangeActions.loadAllAction(this.product.identifier));
+  }
+
+  rowSelect(range: FimsRange): void {
+    this.router.navigate(['detail', range.identifier], { relativeTo: this.route });
+  }
+}
diff --git a/src/app/loans/products/components/term/term.component.html b/src/app/loans/products/components/term/term.component.html
new file mode 100644
index 0000000..bbd7198
--- /dev/null
+++ b/src/app/loans/products/components/term/term.component.html
@@ -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.
+-->
+
+<form [formGroup]="form">
+  <div layout-gt-xs="row">
+    <fims-text-input type="number" [form]="form" controlName="term" placeholder="{{'Term' | translate}}"></fims-text-input>
+    <mat-radio-group formControlName="temporalUnit">
+      <mat-radio-button *ngFor="let basis of temporalOptions" [value]="basis.type" layout-margin>
+        {{basis.label}}
+      </mat-radio-button>
+    </mat-radio-group>
+  </div>
+</form>
diff --git a/src/app/loans/products/components/term/term.component.ts b/src/app/loans/products/components/term/term.component.ts
new file mode 100644
index 0000000..11b1b8b
--- /dev/null
+++ b/src/app/loans/products/components/term/term.component.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 {Component, Input} from '@angular/core';
+import {FormComponent} from '../../../../common/forms/form.component';
+import {FormBuilder, Validators} from '@angular/forms';
+import {ChronoUnit} from '../../../../services/portfolio/domain/chrono-unit.model';
+import {temporalOptionList} from '../../../../common/domain/temporal.domain';
+import {FimsValidators} from '../../../../common/validator/validators';
+
+export interface TermRangeFormData {
+  temporalUnit: ChronoUnit;
+  term: number;
+}
+
+@Component({
+  selector: 'fims-product-term-form',
+  templateUrl: './term.component.html'
+})
+export class ProductTermFormComponent extends FormComponent<TermRangeFormData> {
+
+  temporalOptions = temporalOptionList;
+
+  @Input() set formData(termRange: TermRangeFormData) {
+    this.form = this.formBuilder.group({
+      term: [termRange.term, [ Validators.required, FimsValidators.minValue(0) ]],
+      temporalUnit: [termRange.temporalUnit, Validators.required],
+    });
+  };
+
+  constructor(private formBuilder: FormBuilder) {
+    super();
+  }
+
+  get formData(): TermRangeFormData {
+    return this.form.getRawValue();
+  }
+
+}
diff --git a/src/app/loans/products/form/create.component.html b/src/app/loans/products/form/create.component.html
new file mode 100644
index 0000000..7dfe563
--- /dev/null
+++ b/src/app/loans/products/form/create.component.html
@@ -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.
+-->
+
+<fims-layout-card-over title="{{'Create new product' | translate}}">
+  <fims-product-form-component #form
+                                  (onSave)="onSave($event)"
+                                  (onCancel)="onCancel()"
+                                  [currencies]="currencies$ | async"
+                                  [product]="product"
+                                  [error]="error$ | async">
+  </fims-product-form-component>
+</fims-layout-card-over>
diff --git a/src/app/loans/products/form/create.component.ts b/src/app/loans/products/form/create.component.ts
new file mode 100644
index 0000000..d28b445
--- /dev/null
+++ b/src/app/loans/products/form/create.component.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 {Component, OnDestroy, OnInit, ViewChild} from '@angular/core';
+import {ActivatedRoute, Router} from '@angular/router';
+import {ProductFormComponent} from './form.component';
+import {PortfolioStore} from '../store/index';
+import {CREATE, RESET_FORM} from '../store/product.actions';
+import * as fromPortfolio from '../store';
+import {Error} from '../../../services/domain/error.model';
+import {FimsProduct} from '../store/model/fims-product.model';
+import {Currency} from '../../../services/currency/domain/currency.model';
+import {CurrencyService} from '../../../services/currency/currency.service';
+import {Observable} from 'rxjs/Observable';
+
+@Component({
+  templateUrl: './create.component.html'
+})
+export class ProductCreateComponent implements OnInit, OnDestroy {
+
+  @ViewChild('form') formComponent: ProductFormComponent;
+
+  currencies$: Observable<Currency[]>;
+
+  error$: Observable<Error>;
+
+  product: FimsProduct = {
+    identifier: '',
+    name: '',
+    termRange: {
+      temporalUnit: 'MONTHS',
+      maximum: 1
+    },
+    balanceRange: {
+      minimum: 0,
+      maximum: 0
+    },
+    interestRange: {
+      minimum: 0,
+      maximum: 0
+    },
+    interestBasis: 'CURRENT_BALANCE',
+    patternPackage: 'org.apache.fineract.cn.portfolio.individuallending.v1',
+    description: '',
+    accountAssignments: [],
+    currencyCode: 'USD',
+    minorCurrencyUnitDigits: 2,
+    enabled: false,
+    parameters: {
+      minimumDispersalAmount: 0,
+      maximumDispersalAmount: 0,
+      maximumDispersalCount: 0,
+      moratoriums: []
+    }
+  };
+
+  constructor(private router: Router, private route: ActivatedRoute, private portfolioStore: PortfolioStore,
+              private currencyService: CurrencyService) {}
+
+  ngOnInit(): void {
+    this.error$ = this.portfolioStore.select(fromPortfolio.getProductFormError)
+      .filter(error => !!error);
+
+    this.currencies$ = this.currencyService.fetchCurrencies();
+  }
+
+  ngOnDestroy(): void {
+    this.portfolioStore.dispatch({ type: RESET_FORM });
+  }
+
+  onSave(product: FimsProduct): void {
+    this.portfolioStore.dispatch({ type: CREATE, payload: {
+      product: product,
+      activatedRoute: this.route
+    }});
+  }
+
+  onCancel() {
+    this.navigateAway();
+  }
+
+  navigateAway(): void {
+    this.router.navigate(['../'], { relativeTo: this.route });
+  }
+
+}
diff --git a/src/app/loans/products/form/detail/detail.component.html b/src/app/loans/products/form/detail/detail.component.html
new file mode 100644
index 0000000..20b2001
--- /dev/null
+++ b/src/app/loans/products/form/detail/detail.component.html
@@ -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.
+-->
+
+<form [formGroup]="form" layout="column">
+  <fims-id-input [form]="form" [placeholder]="'Short name'" controlName="identifier" [readonly]="editMode"></fims-id-input>
+  <fims-text-input [form]="form" controlName="name" placeholder="{{'Name' | translate}}"></fims-text-input>
+  <mat-form-field layout-margin flex>
+    <textarea matInput placeholder="{{'Description' | translate}}" formControlName="description"></textarea>
+    <mat-error *ngIf="form.get('description').hasError('required')" translate>
+      Required
+    </mat-error>
+    <mat-error *ngIf="form.get('description').hasError('maxlength')">
+      {{ 'Only characters allowed.' | translate:{ value: form.get('description').getError('maxlength')['requiredLength']} }}
+    </mat-error>
+  </mat-form-field>
+  <mat-form-field layout-margin>
+    <mat-select formControlName="currencyCode" placeholder="{{ 'Currency' | translate }}">
+      <mat-option *ngFor="let currency of currencies" [value]="currency.code">
+        {{currency.code}}
+      </mat-option>
+    </mat-select>
+  </mat-form-field>
+  <fims-min-max flex
+                minPlaceholder="{{'Minimum principal amount' | translate}}"
+                maxPlaceholder="{{'Maximum principal amount' | translate}}"
+                [form]="form"
+                minControlName="minimumBalance"
+                maxControlName="maximumBalance"
+                [requireDecimal]="false"
+                [decimalLimit]="2">
+  </fims-min-max>
+  <div layout="row">
+    <fims-text-input type="number" [form]="form" controlName="term" placeholder="{{'Maximum Term' | translate}}"></fims-text-input>
+    <mat-radio-group formControlName="temporalUnit">
+      <mat-radio-button *ngFor="let basis of temporalOptions" [value]="basis.type" layout-margin>
+        {{basis.label}}
+      </mat-radio-button>
+    </mat-radio-group>
+  </div>
+</form>
diff --git a/src/app/loans/products/form/detail/detail.component.spec.ts b/src/app/loans/products/form/detail/detail.component.spec.ts
new file mode 100644
index 0000000..95cc5f7
--- /dev/null
+++ b/src/app/loans/products/form/detail/detail.component.spec.ts
@@ -0,0 +1,105 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, TestBed} from '@angular/core/testing';
+import {TranslateModule} from '@ngx-translate/core';
+import {Component, ViewChild} from '@angular/core';
+import {ReactiveFormsModule} from '@angular/forms';
+import {NoopAnimationsModule} from '@angular/platform-browser/animations';
+import {DetailFormData, ProductDetailFormComponent} from './detail.component';
+import {FimsSharedModule} from '../../../../common/common.module';
+import {MatInputModule, MatRadioModule, MatSelectModule} from '@angular/material';
+
+describe('Test product detail component', () => {
+
+  const validFormData: DetailFormData = {
+    identifier: 'test',
+    minimumBalance: '1000',
+    maximumBalance: '2000',
+    temporalUnit: 'WEEKS',
+    description: 'description',
+    currencyCode: 'USD',
+    name: 'test',
+    term: 12
+  };
+
+  const invalidFormData: DetailFormData = {
+    identifier: 'test',
+    minimumBalance: '2000',
+    maximumBalance: '1000',
+    temporalUnit: 'WEEKS',
+    description: 'description',
+    currencyCode: 'USD',
+    name: 'test',
+    term: 12
+  };
+
+  let component: TestComponent;
+  let fixture: ComponentFixture<TestComponent>;
+
+  beforeEach(() => {
+    TestBed.configureTestingModule({
+      imports: [
+        TranslateModule.forRoot(),
+        ReactiveFormsModule,
+        MatRadioModule,
+        MatInputModule,
+        MatSelectModule,
+        FimsSharedModule,
+        NoopAnimationsModule
+      ],
+      declarations: [
+        TestComponent,
+        ProductDetailFormComponent
+      ]
+    });
+
+    fixture = TestBed.createComponent(TestComponent);
+
+    component = fixture.componentInstance;
+  });
+
+  it('should return same interest form data', () => {
+    component.formData = validFormData;
+    fixture.detectChanges();
+    expect(component.form.formData).toEqual(component.formData);
+  });
+
+  it('should mark form as valid when range is valid', () => {
+    component.formData = validFormData;
+    fixture.detectChanges();
+    expect(component.form.valid).toBeTruthy();
+  });
+
+  it('should mark form as invalid when range is invalid', () => {
+    component.formData = invalidFormData;
+    fixture.detectChanges();
+    expect(component.form.valid).toBeFalsy();
+  });
+
+});
+
+@Component({
+  template: '<fims-product-detail-form #form [formData]="formData"></fims-product-detail-form>'
+})
+class TestComponent {
+
+  @ViewChild('form') form: ProductDetailFormComponent;
+
+  formData: DetailFormData;
+}
diff --git a/src/app/loans/products/form/detail/detail.component.ts b/src/app/loans/products/form/detail/detail.component.ts
new file mode 100644
index 0000000..bf01bea
--- /dev/null
+++ b/src/app/loans/products/form/detail/detail.component.ts
@@ -0,0 +1,96 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, Input, OnChanges, SimpleChanges} from '@angular/core';
+import {FormComponent} from '../../../../common/forms/form.component';
+import {FormBuilder, Validators} from '@angular/forms';
+import {FimsValidators} from '../../../../common/validator/validators';
+import {ChronoUnit} from '../../../../services/portfolio/domain/chrono-unit.model';
+import {Currency} from '../../../../services/currency/domain/currency.model';
+import {Error} from '../../../../services/domain/error.model';
+import {TemporalOption} from '../../../../common/domain/temporal.domain';
+
+export interface DetailFormData {
+  identifier: string;
+  name: string;
+  description: string;
+  currencyCode: string;
+  minimumBalance: string;
+  maximumBalance: string;
+  term: number;
+  temporalUnit: ChronoUnit;
+}
+
+@Component({
+  selector: 'fims-product-detail-form',
+  templateUrl: './detail.component.html'
+})
+export class ProductDetailFormComponent extends FormComponent<DetailFormData> implements OnChanges {
+
+  private _formData: DetailFormData;
+
+  @Input('formData') set formData(formData: DetailFormData) {
+    this._formData = formData;
+  };
+
+  @Input('editMode') editMode: boolean;
+
+  @Input('temporalOptions') temporalOptions: TemporalOption[];
+
+  @Input('currencies') currencies: Currency[];
+
+  @Input('error') error: Error;
+
+  constructor(private formBuilder: FormBuilder) {
+    super();
+
+    this.form = this.formBuilder.group({
+      identifier: ['', [Validators.required, Validators.minLength(3), Validators.maxLength(32), FimsValidators.urlSafe]],
+      name: ['', [Validators.required, Validators.maxLength(256)]],
+      description: ['', [Validators.required, Validators.maxLength(4096)]],
+      currencyCode: ['', [Validators.required]],
+      minimumBalance: ['', [Validators.required, FimsValidators.minValue(0)]],
+      maximumBalance: ['', [Validators.required, FimsValidators.minValue(0)]],
+      term: ['', [ Validators.required, FimsValidators.minValue(1), FimsValidators.maxScale(0)]],
+      temporalUnit: ['', Validators.required]
+    }, { validator: FimsValidators.greaterThanEquals('minimumBalance', 'maximumBalance') });
+  }
+
+  ngOnChanges(changes: SimpleChanges): void {
+    if (changes.formData) {
+      this.form.reset({
+        identifier: this._formData.identifier,
+        name: this._formData.name,
+        description: this._formData.description,
+        currencyCode: this._formData.currencyCode,
+        minimumBalance: this._formData.minimumBalance,
+        maximumBalance: this._formData.maximumBalance,
+        term: this._formData.term,
+        temporalUnit: this._formData.temporalUnit
+      });
+    }
+
+    if (changes.error) {
+      this.setError('identifier', 'unique', true);
+    }
+  }
+
+  get formData(): DetailFormData {
+    return this.form.getRawValue();
+  }
+}
diff --git a/src/app/loans/products/form/edit.component.html b/src/app/loans/products/form/edit.component.html
new file mode 100644
index 0000000..76bb79e
--- /dev/null
+++ b/src/app/loans/products/form/edit.component.html
@@ -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.
+-->
+
+<fims-layout-card-over title="{{'Edit product' | translate}}">
+  <fims-product-form-component #form
+                                  (onSave)="onSave($event)"
+                                  (onCancel)="onCancel()"
+                                  [currencies]="currencies$ | async"
+                                  [product]="product"
+                                  [editMode]="true">
+  </fims-product-form-component>
+</fims-layout-card-over>
diff --git a/src/app/loans/products/form/edit.component.ts b/src/app/loans/products/form/edit.component.ts
new file mode 100644
index 0000000..acb7311
--- /dev/null
+++ b/src/app/loans/products/form/edit.component.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 {Component, OnDestroy, OnInit} from '@angular/core';
+import {Product} from '../../../services/portfolio/domain/product.model';
+import {ActivatedRoute, Router} from '@angular/router';
+import {PortfolioStore} from '../store/index';
+import {RESET_FORM, UPDATE} from '../store/product.actions';
+import {Subscription} from 'rxjs/Subscription';
+import * as fromPortfolio from '../store';
+import {FimsProduct} from '../store/model/fims-product.model';
+import {Currency} from '../../../services/currency/domain/currency.model';
+import {CurrencyService} from '../../../services/currency/currency.service';
+import {Observable} from 'rxjs/Observable';
+
+@Component({
+  templateUrl: './edit.component.html'
+})
+export class ProductEditComponent implements OnInit, OnDestroy {
+
+  private productSubscription: Subscription;
+
+  currencies$: Observable<Currency[]>;
+
+  product: FimsProduct;
+
+  constructor(private router: Router, private route: ActivatedRoute, private portfolioStore: PortfolioStore,
+              private currencyService: CurrencyService) {}
+
+  ngOnInit() {
+    this.productSubscription = this.portfolioStore.select(fromPortfolio.getSelectedProduct)
+      .subscribe(product => this.product = product);
+
+    this.currencies$ = this.currencyService.fetchCurrencies();
+  }
+
+  ngOnDestroy(): void {
+    this.productSubscription.unsubscribe();
+
+    this.portfolioStore.dispatch({ type: RESET_FORM });
+  }
+
+  onSave(product: Product) {
+    this.portfolioStore.dispatch({ type: UPDATE, payload: {
+      product: product,
+      activatedRoute: this.route
+    }});
+  }
+
+  onCancel() {
+    this.navigateAway();
+  }
+
+  private navigateAway() {
+    this.router.navigate(['../'], { relativeTo: this.route });
+  }
+}
diff --git a/src/app/loans/products/form/fees/fee.component.html b/src/app/loans/products/form/fees/fee.component.html
new file mode 100644
index 0000000..821a0b5
--- /dev/null
+++ b/src/app/loans/products/form/fees/fee.component.html
@@ -0,0 +1,59 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<form [formGroup]="form">
+  <fims-account-select title="Processing fee income account(Revenue accounts only)" formControlName="processingFeeAccount" [type]="'REVENUE'">
+    <ng-container *ngIf="!form.get('processingFeeAccount').pristine && form.get('processingFeeAccount').hasError('required')" translate>
+      Required
+    </ng-container>
+    <ng-container *ngIf="form.get('processingFeeAccount').hasError('invalidAccount')" translate>
+      Invalid account
+    </ng-container>
+  </fims-account-select>
+  <fims-account-select title="Loan origination fee income account(Revenue accounts only)" formControlName="originationFeeAccount" [type]="'REVENUE'">
+    <ng-container *ngIf="!form.get('originationFeeAccount').pristine && form.get('originationFeeAccount').hasError('required')" translate>
+      Required
+    </ng-container>
+    <ng-container *ngIf="form.get('originationFeeAccount').hasError('invalidAccount')" translate>
+      Invalid account
+    </ng-container>
+  </fims-account-select>
+  <fims-account-select title="Disbursement fee income account(Revenue accounts only)" formControlName="disbursementFeeAccount" [type]="'REVENUE'">
+    <ng-container *ngIf="!form.get('disbursementFeeAccount').pristine && form.get('disbursementFeeAccount').hasError('required')" translate>
+      Required
+    </ng-container>
+    <ng-container *ngIf="form.get('disbursementFeeAccount').hasError('invalidAccount')" translate>
+      Invalid account
+    </ng-container>
+  </fims-account-select>
+  <fims-account-select title="Late fee income account(Revenue accounts only)" formControlName="lateFeeIncomeAccount" [type]="'REVENUE'">
+    <ng-container *ngIf="!form.get('lateFeeIncomeAccount').pristine && form.get('lateFeeIncomeAccount').hasError('required')" translate>
+      Required
+    </ng-container>
+    <ng-container *ngIf="form.get('lateFeeIncomeAccount').hasError('invalidAccount')" translate>
+      Invalid account
+    </ng-container>
+  </fims-account-select>
+  <fims-account-select title="Late fee accrual account(Asset accounts only)" formControlName="lateFeeAccrualAccount" [type]="'ASSET'">
+    <ng-container *ngIf="!form.get('lateFeeAccrualAccount').pristine && form.get('lateFeeAccrualAccount').hasError('required')" translate>
+      Required
+    </ng-container>
+    <ng-container *ngIf="form.get('lateFeeAccrualAccount').hasError('invalidAccount')" translate>
+      Invalid account
+    </ng-container>
+  </fims-account-select>
+</form>
diff --git a/src/app/loans/products/form/fees/fee.component.ts b/src/app/loans/products/form/fees/fee.component.ts
new file mode 100644
index 0000000..35c0e12
--- /dev/null
+++ b/src/app/loans/products/form/fees/fee.component.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 {Component, Input} from '@angular/core';
+import {FormComponent} from '../../../../common/forms/form.component';
+import {FormBuilder, Validators} from '@angular/forms';
+import {AccountingService} from '../../../../services/accounting/accounting.service';
+import {accountExists} from '../../../../common/validator/account-exists.validator';
+
+export interface FeeFormData {
+  processingFeeAccount: string;
+  originationFeeAccount: string;
+  disbursementFeeAccount: string;
+  lateFeeIncomeAccount: string;
+  lateFeeAccrualAccount: string;
+}
+
+@Component({
+  selector: 'fims-product-fee-form',
+  templateUrl: './fee.component.html'
+})
+export class ProductFeeFormComponent extends FormComponent<FeeFormData> {
+
+  @Input() set formData(feeFormData: FeeFormData) {
+    this.form = this.formBuilder.group({
+      processingFeeAccount: [feeFormData.processingFeeAccount, [Validators.required], accountExists(this.accountingService)],
+      originationFeeAccount: [feeFormData.originationFeeAccount, [Validators.required], accountExists(this.accountingService)],
+      disbursementFeeAccount: [feeFormData.disbursementFeeAccount, [Validators.required], accountExists(this.accountingService)],
+      lateFeeIncomeAccount: [feeFormData.lateFeeIncomeAccount, [Validators.required], accountExists(this.accountingService)],
+      lateFeeAccrualAccount: [feeFormData.lateFeeAccrualAccount, [Validators.required], accountExists(this.accountingService)]
+    });
+  }
+
+  constructor(private formBuilder: FormBuilder, private accountingService: AccountingService) {
+    super();
+  }
+
+  get formData(): FeeFormData {
+    return this.form.getRawValue();
+  }
+
+}
diff --git a/src/app/loans/products/form/form.component.html b/src/app/loans/products/form/form.component.html
new file mode 100644
index 0000000..3287d74
--- /dev/null
+++ b/src/app/loans/products/form/form.component.html
@@ -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.
+-->
+
+<td-steps mode="'vertical'">
+  <td-step #detailsStep label="{{'Product details' | translate}}" [state]="detailForm.valid ? 'complete' : detailForm.pristine ? 'none' : 'required'">
+    <fims-product-detail-form #detailForm [formData]="detailFormData" [currencies]="currencies" [temporalOptions]="temporalOptions" [error]="error" [editMode]="editMode"></fims-product-detail-form>
+    <ng-template td-step-actions>
+      <fims-form-continue-action (onContinue)="settingsStep.open()"></fims-form-continue-action>
+    </ng-template>
+  </td-step>
+  <td-step #settingsStep label="{{'Ledger and account settings' | translate}}" [state]="settingsForm.valid ? 'complete' : settingsForm.pristine ? 'none' : 'required'">
+    <fims-product-settings-form #settingsForm [formData]="settingsFormData"></fims-product-settings-form>
+    <ng-template td-step-actions>
+      <fims-form-continue-action (onContinue)="interestStep.open()"></fims-form-continue-action>
+    </ng-template>
+  </td-step>
+  <td-step #interestStep label="{{'Interest settings' | translate}}" [state]="interestForm.valid ? 'complete' : interestForm.pristine ? 'none' : 'required'">
+    <fims-product-interests-form #interestForm [formData]="interestFormData"></fims-product-interests-form>
+    <ng-template td-step-actions>
+      <fims-form-continue-action (onContinue)="feesStep.open()"></fims-form-continue-action>
+    </ng-template>
+  </td-step>
+  <td-step #feesStep label="{{'Fee income accounts' | translate}}" [state]="feeForm.valid ? 'complete' : feeForm.pristine ? 'none' : 'required'">
+    <fims-product-fee-form #feeForm [formData]="feeFormData"></fims-product-fee-form>
+    <ng-template td-step-actions>
+      <fims-form-continue-action (onContinue)="arrearsStep.open()"></fims-form-continue-action>
+    </ng-template>
+  </td-step>
+  <td-step #arrearsStep label="{{'Arrears allowance reserve account' | translate}}" [state]="arrearsAllowanceForm.valid ? 'complete' : arrearsAllowanceForm.pristine ? 'none' : 'required'">
+    <form [formGroup]="arrearsAllowanceForm">
+      <fims-account-select title="{{'Arrears allowance(Expense accounts only)' | translate}}" formControlName="account" [type]="'EXPENSE'">
+        <ng-container *ngIf="!arrearsAllowanceForm.get('account').pristine && arrearsAllowanceForm.get('account').hasError('required')" translate>
+          Required
+        </ng-container>
+        <ng-container *ngIf="arrearsAllowanceForm.get('account').hasError('invalidAccount')" translate>
+          Invalid account
+        </ng-container>
+      </fims-account-select>
+    </form>
+  </td-step>
+
+  <td-step label="{{'Final step' | translate}}" [state]="'complete'">
+    <ng-template td-step-summary>
+      <fims-form-final-action
+        [resourceName]="'PRODUCT'"
+        [editMode]="editMode"
+        [disabled]="!isValid"
+        (onCancel)="cancel()"
+        (onSave)="save()">
+      </fims-form-final-action>
+    </ng-template>
+  </td-step>
+</td-steps>
diff --git a/src/app/loans/products/form/form.component.ts b/src/app/loans/products/form/form.component.ts
new file mode 100644
index 0000000..4cb49e0
--- /dev/null
+++ b/src/app/loans/products/form/form.component.ts
@@ -0,0 +1,232 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, OnInit, Output, ViewChild} from '@angular/core';
+import {FormBuilder, FormGroup, Validators} from '@angular/forms';
+import {TdStepComponent} from '@covalent/core';
+import {InterestFormData, ProductInterestFormComponent} from './interests/interests.component';
+import {AccountAssignment} from '../../../services/portfolio/domain/account-assignment.model';
+import {AccountDesignators} from '../../../services/portfolio/domain/individuallending/account-designators.model';
+import {FeeFormData, ProductFeeFormComponent} from './fees/fee.component';
+import {ProductParameters} from '../../../services/portfolio/domain/individuallending/product-parameters.model';
+import {AccountingService} from '../../../services/accounting/accounting.service';
+import {FimsProduct} from '../store/model/fims-product.model';
+import {accountExists} from '../../../common/validator/account-exists.validator';
+import {ProductSettingsFormComponent, SettingsFormData} from './settings/settings.component';
+import {temporalOptionList} from '../../../common/domain/temporal.domain';
+import {Currency} from '../../../services/currency/domain/currency.model';
+import {
+  accountIdentifier,
+  createAccountAssignment,
+  createLedgerAssignment,
+  findAccountDesignator,
+  ledgerIdentifier
+} from '../../../common/util/account-assignments';
+import {DetailFormData, ProductDetailFormComponent} from './detail/detail.component';
+import {Error} from '../../../services/domain/error.model';
+
+@Component({
+  selector: 'fims-product-form-component',
+  templateUrl: './form.component.html'
+})
+export class ProductFormComponent implements OnInit {
+
+  private _error: Error;
+
+  temporalOptions = temporalOptionList;
+
+  arrearsAllowanceForm: FormGroup;
+
+  @ViewChild('detailsStep') step: TdStepComponent;
+
+  @Input('editMode') editMode: boolean;
+
+  @Input('product') set product(product: FimsProduct) {
+    this.prepareDetailForm(product);
+    this.prepareSettingsForm(product);
+    this.prepareInterestForm(product);
+    this.prepareFeeForm(product);
+    this.prepareAllowanceForm(product);
+  };
+
+  @Input('currencies') currencies: Currency[];
+
+  @Input('error') set error(error: Error) {
+    this._error = error;
+    this.step.open();
+  };
+
+  @Output('onSave') onSave = new EventEmitter<FimsProduct>();
+  @Output('onCancel') onCancel = new EventEmitter<void>();
+
+  @ViewChild('detailForm') detailForm: ProductDetailFormComponent;
+  detailFormData: DetailFormData;
+
+  @ViewChild('settingsForm') settingsForm: ProductSettingsFormComponent;
+  settingsFormData: SettingsFormData;
+
+  @ViewChild('interestForm') interestForm: ProductInterestFormComponent;
+  interestFormData: InterestFormData;
+
+  @ViewChild('feeForm') feeForm: ProductFeeFormComponent;
+  feeFormData: FeeFormData;
+
+  constructor(private formBuilder: FormBuilder, private accountingService: AccountingService) {}
+
+  ngOnInit(): void {
+    this.step.open();
+  }
+
+  get isValid(): boolean {
+    return this.detailForm.valid &&
+      this.settingsForm.valid &&
+      this.interestForm.valid &&
+      this.feeForm.valid &&
+      this.arrearsAllowanceForm.valid;
+  }
+
+  save(): void {
+    const parameters: ProductParameters = {
+      minimumDispersalAmount: 0,
+      maximumDispersalAmount: 0,
+      maximumDispersalCount: 0,
+      moratoriums: []
+    };
+
+    const foundCurrency = this.currencies.find(currency => currency.code === this.detailForm.formData.currencyCode);
+
+    const product: FimsProduct = {
+      identifier: this.detailForm.formData.identifier,
+      name: this.detailForm.formData.name,
+      description: this.detailForm.formData.description,
+      minorCurrencyUnitDigits: foundCurrency.digits,
+      currencyCode: foundCurrency.code,
+      interestBasis: 'CURRENT_BALANCE',
+      interestRange: {
+        minimum: parseFloat(this.interestForm.formData.minimum),
+        maximum: parseFloat(this.interestForm.formData.maximum),
+      },
+      termRange: {
+        maximum: this.detailForm.formData.term,
+        temporalUnit: this.detailForm.formData.temporalUnit
+      },
+      balanceRange: {
+        minimum: parseFloat(this.detailForm.formData.minimumBalance),
+        maximum: parseFloat(this.detailForm.formData.maximumBalance)
+      },
+      parameters,
+      patternPackage: 'org.apache.fineract.cn.individuallending.api.v1',
+      accountAssignments: this.collectAccountAssignments()
+    };
+
+    this.onSave.emit(product);
+  }
+
+  private collectAccountAssignments(): AccountAssignment[] {
+    const assignments: AccountAssignment[] = [];
+
+    assignments.push(createAccountAssignment(this.settingsForm.formData.loanFundAccount, AccountDesignators.LOAN_FUNDS_SOURCE));
+
+    assignments.push(createLedgerAssignment(this.settingsForm.formData.customerLoanLedger, AccountDesignators.CUSTOMER_LOAN_PRINCIPAL));
+    assignments.push(createLedgerAssignment(this.settingsForm.formData.customerLoanLedger, AccountDesignators.CUSTOMER_LOAN_INTEREST));
+    assignments.push(createLedgerAssignment(this.settingsForm.formData.customerLoanLedger, AccountDesignators.CUSTOMER_LOAN_FEES));
+
+    assignments.push(createLedgerAssignment(this.settingsForm.formData.customerLoanLedger, AccountDesignators.CUSTOMER_LOAN_FEES));
+
+    assignments.push(createAccountAssignment(this.feeForm.formData.processingFeeAccount, AccountDesignators.PROCESSING_FEE_INCOME));
+    assignments.push(createAccountAssignment(this.feeForm.formData.disbursementFeeAccount, AccountDesignators.DISBURSEMENT_FEE_INCOME));
+    assignments.push(createAccountAssignment(this.feeForm.formData.lateFeeIncomeAccount, AccountDesignators.LATE_FEE_INCOME));
+    assignments.push(createAccountAssignment(this.feeForm.formData.lateFeeAccrualAccount, AccountDesignators.LATE_FEE_ACCRUAL));
+    assignments.push(createAccountAssignment(this.feeForm.formData.originationFeeAccount, AccountDesignators.ORIGINATION_FEE_INCOME));
+
+    assignments.push(createAccountAssignment(this.interestForm.formData.incomeAccount, AccountDesignators.INTEREST_INCOME));
+    assignments.push(createAccountAssignment(this.interestForm.formData.accrualAccount, AccountDesignators.INTEREST_ACCRUAL));
+
+    assignments.push(createAccountAssignment(this.arrearsAllowanceForm.get('account').value, AccountDesignators.GENERAL_LOSS_ALLOWANCE));
+
+    return assignments;
+  }
+
+  cancel(): void {
+    this.onCancel.emit();
+  }
+
+  prepareDetailForm(product: FimsProduct): void {
+    this.detailFormData = {
+      identifier: product.identifier,
+      name: product.name,
+      description: product.description,
+      currencyCode: product.currencyCode,
+      minimumBalance: product.balanceRange.minimum.toFixed(2),
+      maximumBalance: product.balanceRange.maximum.toFixed(2),
+      term: product.termRange.maximum,
+      temporalUnit: product.termRange.temporalUnit
+    };
+  }
+
+  prepareSettingsForm(product: FimsProduct) {
+    const loanFoundAccount = findAccountDesignator(product.accountAssignments, AccountDesignators.LOAN_FUNDS_SOURCE);
+    const customerLoanLedger = findAccountDesignator(product.accountAssignments, AccountDesignators.CUSTOMER_LOAN_PRINCIPAL);
+
+    this.settingsFormData = {
+      loanFundAccount: accountIdentifier(loanFoundAccount),
+      customerLoanLedger: ledgerIdentifier(customerLoanLedger),
+    };
+  }
+
+  private prepareInterestForm(product: FimsProduct) {
+    const interestIncome = findAccountDesignator(product.accountAssignments, AccountDesignators.INTEREST_INCOME);
+    const interestAccrual = findAccountDesignator(product.accountAssignments, AccountDesignators.INTEREST_ACCRUAL);
+    const interestRange = product.interestRange;
+
+    this.interestFormData = {
+      minimum: interestRange.minimum.toFixed(2),
+      maximum: interestRange.maximum.toFixed(2),
+      incomeAccount: accountIdentifier(interestIncome),
+      accrualAccount: accountIdentifier(interestAccrual)
+    };
+  }
+
+  private prepareFeeForm(product: FimsProduct) {
+    const processingFeeDesignator = findAccountDesignator(product.accountAssignments, AccountDesignators.PROCESSING_FEE_INCOME);
+    const disbursementFeeDesignator = findAccountDesignator(product.accountAssignments, AccountDesignators.DISBURSEMENT_FEE_INCOME);
+    const lateFeeIncomeDesignator = findAccountDesignator(product.accountAssignments, AccountDesignators.LATE_FEE_INCOME);
+    const lateFeeAccrualDesignator = findAccountDesignator(product.accountAssignments, AccountDesignators.LATE_FEE_ACCRUAL);
+    const loanOriginationFeeDesignator = findAccountDesignator(product.accountAssignments, AccountDesignators.ORIGINATION_FEE_INCOME);
+
+    this.feeFormData = {
+      disbursementFeeAccount: accountIdentifier(disbursementFeeDesignator),
+      lateFeeIncomeAccount: accountIdentifier(lateFeeIncomeDesignator),
+      lateFeeAccrualAccount: accountIdentifier(lateFeeAccrualDesignator),
+      processingFeeAccount: accountIdentifier(processingFeeDesignator),
+      originationFeeAccount: accountIdentifier(loanOriginationFeeDesignator)
+    };
+  }
+
+  private prepareAllowanceForm(product: FimsProduct) {
+    const allowanceDesignator = findAccountDesignator(product.accountAssignments, AccountDesignators.GENERAL_LOSS_ALLOWANCE);
+    this.arrearsAllowanceForm = this.formBuilder.group({
+      account: [accountIdentifier(allowanceDesignator), [Validators.required], accountExists(this.accountingService)],
+    });
+  }
+
+  get error(): Error {
+    return this._error;
+  }
+
+}
diff --git a/src/app/loans/products/form/interests/interest.component.spec.ts b/src/app/loans/products/form/interests/interest.component.spec.ts
new file mode 100644
index 0000000..cea60a1
--- /dev/null
+++ b/src/app/loans/products/form/interests/interest.component.spec.ts
@@ -0,0 +1,102 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, TestBed} from '@angular/core/testing';
+import {TranslateModule} from '@ngx-translate/core';
+import {InterestFormData, ProductInterestFormComponent} from './interests.component';
+import {Component, CUSTOM_ELEMENTS_SCHEMA, ViewChild} from '@angular/core';
+import {ReactiveFormsModule} from '@angular/forms';
+import {AccountingService} from '../../../../services/accounting/accounting.service';
+import {FimsSharedModule} from '../../../../common/common.module';
+import {NoopAnimationsModule} from '@angular/platform-browser/animations';
+import {MatInputModule, MatRadioModule, MatSlideToggleModule} from '@angular/material';
+
+describe('Test product interest component', () => {
+
+  const validFormData: InterestFormData = {
+    minimum: '1.23',
+    maximum: '4.56',
+    accrualAccount: 'accrualAccount',
+    incomeAccount: 'incomeAccount'
+  };
+
+  const invalidFormData: InterestFormData = {
+    minimum: '4.56',
+    maximum: '1.23',
+    accrualAccount: 'accrualAccount',
+    incomeAccount: 'incomeAccount'
+  };
+
+  let component: TestComponent;
+  let fixture: ComponentFixture<TestComponent>;
+
+  beforeEach(() => {
+    TestBed.configureTestingModule({
+      imports: [
+        TranslateModule.forRoot(),
+        ReactiveFormsModule,
+        MatInputModule,
+        MatRadioModule,
+        MatSlideToggleModule,
+        FimsSharedModule,
+        NoopAnimationsModule
+      ],
+      declarations: [
+        TestComponent,
+        ProductInterestFormComponent
+      ],
+      providers: [
+        { provide: AccountingService, useValue: jasmine.createSpyObj('accountingService', ['findAccount', 'fetchAccounts']) }
+      ],
+      schemas: [CUSTOM_ELEMENTS_SCHEMA]
+    });
+
+    fixture = TestBed.createComponent(TestComponent);
+
+    component = fixture.componentInstance;
+  });
+
+  it('should return same interest form data', () => {
+    component.formData = validFormData;
+    fixture.detectChanges();
+    expect(component.form.formData).toEqual(component.formData);
+  });
+
+  it('should mark form as valid when range is valid', () => {
+    component.formData = validFormData;
+    fixture.detectChanges();
+    expect(component.form.valid).toBeTruthy();
+  });
+
+  it('should mark form as invalid when range is invalid', () => {
+    component.formData = invalidFormData;
+    fixture.detectChanges();
+    expect(component.form.valid).toBeFalsy();
+  });
+
+});
+
+@Component({
+  template: '<fims-product-interests-form #form [formData]="formData"></fims-product-interests-form>'
+})
+class TestComponent {
+
+  @ViewChild('form') form: ProductInterestFormComponent;
+
+  formData: InterestFormData;
+}
diff --git a/src/app/loans/products/form/interests/interests.component.html b/src/app/loans/products/form/interests/interests.component.html
new file mode 100644
index 0000000..1a786ad
--- /dev/null
+++ b/src/app/loans/products/form/interests/interests.component.html
@@ -0,0 +1,47 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<form [formGroup]="form">
+  <div layout="row">
+    <mat-slide-toggle formControlName="interestRangeEnabled" layout-margin translate>
+      Interest range?
+    </mat-slide-toggle>
+  </div>
+  <div layout="row" *ngIf="form.get('interestRangeEnabled').value === false">
+    <fims-number-input [form]="form" controlName="minimum" placeholder="{{'Interest rate' | translate}}"></fims-number-input>
+  </div>
+  <div layout="row" *ngIf="form.get('interestRangeEnabled').value === true">
+    <fims-min-max minPlaceholder="{{'Minimum interest rate' | translate}}" maxPlaceholder="{{'Maximum interest rate' | translate}}" [form]="form" minControlName="minimum" maxControlName="maximum"></fims-min-max>
+  </div>
+  <fims-account-select title="{{'Interest income account(Revenue accounts only)' | translate}}" formControlName="incomeAccount" [type]="'REVENUE'">
+    <ng-container *ngIf="!form.get('incomeAccount').pristine && form.get('incomeAccount').hasError('required')">
+      Required
+    </ng-container>
+    <ng-container *ngIf="form.get('incomeAccount').hasError('invalidAccount')">
+      Invalid account
+    </ng-container>
+  </fims-account-select>
+  <fims-account-select title="{{'Interest accrual account(Asset accounts only)' | translate}}" formControlName="accrualAccount" [type]="'ASSET'">
+    <ng-container *ngIf="!form.get('accrualAccount').pristine && form.get('accrualAccount').hasError('required')">
+      Required
+    </ng-container>
+    <ng-container *ngIf="form.get('accrualAccount').hasError('invalidAccount')">
+      Invalid account
+    </ng-container>
+  </fims-account-select>
+  <p class="text-md" translate>Interests will be calculated on a daily basis</p>
+</form>
diff --git a/src/app/loans/products/form/interests/interests.component.ts b/src/app/loans/products/form/interests/interests.component.ts
new file mode 100644
index 0000000..6d75f84
--- /dev/null
+++ b/src/app/loans/products/form/interests/interests.component.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 {Component, Input, OnChanges, SimpleChanges} from '@angular/core';
+import {FormComponent} from '../../../../common/forms/form.component';
+import {FormBuilder, FormControl, ValidatorFn, Validators} from '@angular/forms';
+import {FimsValidators} from '../../../../common/validator/validators';
+import {AccountingService} from '../../../../services/accounting/accounting.service';
+import {accountExists} from '../../../../common/validator/account-exists.validator';
+
+export interface InterestFormData {
+  minimum: string;
+  maximum: string;
+  incomeAccount: string;
+  accrualAccount: string;
+}
+
+@Component({
+  selector: 'fims-product-interests-form',
+  templateUrl: './interests.component.html'
+})
+export class ProductInterestFormComponent extends FormComponent<InterestFormData> implements OnChanges {
+
+  private _formData: InterestFormData;
+
+  private minMaxValidators: ValidatorFn[] = [Validators.required, FimsValidators.minValue(0), FimsValidators.scale(2)];
+
+  @Input() set formData(formData: InterestFormData) {
+    this._formData = formData;
+  };
+
+  constructor(private formBuilder: FormBuilder, private accountingService: AccountingService) {
+    super();
+
+    this.form = this.formBuilder.group({
+      interestRangeEnabled: [false],
+      minimum: ['', this.minMaxValidators],
+      maximum: [''],
+      incomeAccount: ['', [Validators.required], accountExists(this.accountingService)],
+      accrualAccount: ['', [Validators.required], accountExists(this.accountingService)]
+    });
+
+    this.form.get('interestRangeEnabled').valueChanges
+      .subscribe(enabled => this.toggleInterestRange(enabled));
+  }
+
+  ngOnChanges(changes: SimpleChanges): void {
+    const interestRangeEnabled: boolean = this.hasInterestRange(this._formData.minimum, this._formData.maximum);
+
+    this.form.reset({
+      interestRangeEnabled: interestRangeEnabled,
+      minimum: this._formData.minimum,
+      maximum: this._formData.maximum,
+      incomeAccount: this._formData.incomeAccount,
+      accrualAccount: this._formData.accrualAccount
+    });
+  }
+
+  get formData(): InterestFormData {
+    return {
+      minimum: this.form.get('minimum').value,
+      maximum: this.form.get('interestRangeEnabled').value ? this.form.get('maximum').value : this.form.get('minimum').value,
+      incomeAccount: this.form.get('incomeAccount').value,
+      accrualAccount: this.form.get('accrualAccount').value
+    };
+  }
+
+  private toggleInterestRange(enabled: boolean): void {
+    const maximumControl: FormControl = this.form.get('maximum') as FormControl;
+
+    if (enabled) {
+      maximumControl.setValidators(this.minMaxValidators);
+      this.form.setValidators(FimsValidators.greaterThan('minimum', 'maximum'));
+    } else {
+      maximumControl.clearValidators();
+      this.form.clearValidators();
+    }
+
+    maximumControl.updateValueAndValidity();
+    this.form.updateValueAndValidity();
+  }
+
+  private hasInterestRange(min: string, max: string): boolean {
+    return this.hasValue(min) &&
+        this.hasValue(max) &&
+        min !== max;
+  }
+
+  private hasValue(value: string): boolean {
+    return value !== undefined;
+  }
+
+
+}
diff --git a/src/app/loans/products/form/moratorium/moratorium.component.html b/src/app/loans/products/form/moratorium/moratorium.component.html
new file mode 100644
index 0000000..0477b07
--- /dev/null
+++ b/src/app/loans/products/form/moratorium/moratorium.component.html
@@ -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.
+-->
+
+<form [formGroup]="form">
+  <div layout-gt-xs="column" layout-margin formArrayName="moratoriums" flex="30">
+    <div *ngFor="let moratorium of moratoriums; let i=index" layout="row" [formGroupName]="i">
+      <fims-text-input flex="20" type="number" [form]="form" controlName="period" placeholder="{{'Period' | translate}}"></fims-text-input>
+      <mat-form-field layout-margin>
+        <mat-select formControlName="temporalUnit">
+          <mat-option *ngFor="let basis of temporalOptions" [value]="basis.type">
+            {{basis.label}}
+          </mat-option>
+        </mat-select>
+      </mat-form-field>
+      <fims-text-input [form]="moratorium" controlName="chargeTask" placeholder="{{'Fee task' | translate}}"></fims-text-input>
+      <button mat-button mat-raised-button (click)="removeMoratorium(i)">{{'Remove' | translate}}</button>
+    </div>
+    <div layout="row">
+      <button flex mat-button mat-raised-button (click)="addMoratorium()">{{'Add moratorium' | translate}}</button>
+    </div>
+  </div>
+</form>
diff --git a/src/app/loans/products/form/moratorium/moratorium.component.ts b/src/app/loans/products/form/moratorium/moratorium.component.ts
new file mode 100644
index 0000000..54397a5
--- /dev/null
+++ b/src/app/loans/products/form/moratorium/moratorium.component.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 {FormComponent} from '../../../../common/forms/form.component';
+import {Component, Input} from '@angular/core';
+import {Moratorium} from '../../../../services/portfolio/domain/individuallending/moratorium.model';
+import {AbstractControl, FormArray, FormBuilder, FormGroup, Validators} from '@angular/forms';
+import {temporalOptionList} from '../../../../common/domain/temporal.domain';
+
+@Component({
+  selector: 'fims-product-moratorium-form',
+  templateUrl: './moratorium.component.html'
+})
+export class ProductMoratoriumFormComponent extends FormComponent<Moratorium[]> {
+
+  temporalOptions = temporalOptionList;
+
+  @Input('formData') set formData(moratoriums: Moratorium[]){
+    moratoriums = moratoriums || [];
+    this.form = this.formBuilder.group({
+      moratoriums: this.initMoratoriums(moratoriums),
+    });
+  }
+
+  constructor(private formBuilder: FormBuilder) {
+    super();
+  }
+
+  get formData(): Moratorium[] {
+    return null;
+  }
+
+  private initMoratoriums(moratoriums: Moratorium[]): FormArray {
+    const formControls: FormGroup[] = [];
+    moratoriums.forEach(value => formControls.push(this.initMoratorium(value)));
+    return this.formBuilder.array(formControls);
+  }
+
+  private initMoratorium(moratorium?: Moratorium): FormGroup {
+    return this.formBuilder.group({
+      period: [moratorium ? moratorium.period : '1', Validators.required],
+      chargeTask: [moratorium ? moratorium.chargeTask : '', Validators.required],
+      temporalUnit: [moratorium ? moratorium.temporalUnit : 'WEEKS', Validators.required]
+    });
+  }
+
+  addMoratorium(): void {
+    const moratoriums: FormArray = this.form.get('moratoriums') as FormArray;
+    moratoriums.push(this.initMoratorium());
+  }
+
+  removeMoratorium(index: number): void {
+    const moratoriums: FormArray = this.form.get('moratoriums') as FormArray;
+    moratoriums.removeAt(index);
+  }
+
+  get moratoriums(): AbstractControl[] {
+    const moratoriums: FormArray = this.form.get('moratoriums') as FormArray;
+    return moratoriums.controls;
+  }
+}
diff --git a/src/app/loans/products/form/settings/settings.component.html b/src/app/loans/products/form/settings/settings.component.html
new file mode 100644
index 0000000..afa7983
--- /dev/null
+++ b/src/app/loans/products/form/settings/settings.component.html
@@ -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.
+-->
+
+<form [formGroup]="form">
+  <fims-account-select title="{{'Cash/fund account(Asset accounts only)' | translate}}" formControlName="loanFundAccount" [type]="'ASSET'">
+    <ng-container *ngIf="!form.get('loanFundAccount').pristine && form.get('loanFundAccount').hasError('required')" translate>
+      Required
+    </ng-container>
+    <ng-container *ngIf="form.get('loanFundAccount').hasError('invalidAccount')" translate>
+      Invalid account
+    </ng-container>
+  </fims-account-select>
+  <fims-ledger-select title="{{'Member loan ledger(Asset ledgers only)' | translate}}" formControlName="customerLoanLedger" [type]="'ASSET'">
+    <ng-container *ngIf="!form.get('customerLoanLedger').pristine && form.get('customerLoanLedger').hasError('required')" translate>
+      Required
+    </ng-container>
+    <ng-container *ngIf="form.get('customerLoanLedger').hasError('invalidLedger')" translate>
+      Invalid ledger
+    </ng-container>
+  </fims-ledger-select>
+  <span layout-margin class="text-md" translate>
+    Ledger used to create member loan account.
+  </span>
+</form>
diff --git a/src/app/loans/products/form/settings/settings.component.ts b/src/app/loans/products/form/settings/settings.component.ts
new file mode 100644
index 0000000..b4edac9
--- /dev/null
+++ b/src/app/loans/products/form/settings/settings.component.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 {Component, Input} from '@angular/core';
+import {FormComponent} from '../../../../common/forms/form.component';
+import {accountExists} from '../../../../common/validator/account-exists.validator';
+import {FormBuilder, Validators} from '@angular/forms';
+import {AccountingService} from '../../../../services/accounting/accounting.service';
+import {ledgerExists} from '../../../../common/validator/ledger-exists.validator';
+
+export interface SettingsFormData {
+  loanFundAccount: string;
+  customerLoanLedger: string;
+}
+
+@Component({
+  selector: 'fims-product-settings-form',
+  templateUrl: './settings.component.html'
+})
+export class ProductSettingsFormComponent extends FormComponent<SettingsFormData> {
+
+  @Input() set formData(settingsFormData: SettingsFormData) {
+    this.form = this.formBuilder.group({
+      loanFundAccount: [settingsFormData.loanFundAccount, [Validators.required], accountExists(this.accountingService)],
+      customerLoanLedger: [settingsFormData.customerLoanLedger, [Validators.required], ledgerExists(this.accountingService)],
+    });
+  }
+
+  constructor(private formBuilder: FormBuilder, private accountingService: AccountingService) {
+    super();
+  }
+
+  get formData(): SettingsFormData {
+    return this.form.getRawValue();
+  }
+
+}
diff --git a/src/app/loans/products/lossProvision/form/create.component.html b/src/app/loans/products/lossProvision/form/create.component.html
new file mode 100644
index 0000000..9c548bb
--- /dev/null
+++ b/src/app/loans/products/lossProvision/form/create.component.html
@@ -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.
+-->
+
+<fims-layout-card-over title="{{'Loss provision configuration' | translate}}" *ngIf="product$ | async as product">
+  <fims-product-loss-provision-form
+    [lossProvisionSteps]="lossProvisionSteps$ | async"
+    (onSave)="save(product.identifier, $event)"
+    (onCancel)="cancel()"
+    [editMode]="true">
+  </fims-product-loss-provision-form>
+</fims-layout-card-over>
+
diff --git a/src/app/loans/products/lossProvision/form/create.component.ts b/src/app/loans/products/lossProvision/form/create.component.ts
new file mode 100644
index 0000000..00452b8
--- /dev/null
+++ b/src/app/loans/products/lossProvision/form/create.component.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 {Component} from '@angular/core';
+import {LossProvisionStep} from '../../../../services/portfolio/domain/loss-provision-step.model';
+import * as fromPortfolio from '../../store/index';
+import {PortfolioStore} from '../../store/index';
+import {ActivatedRoute, Router} from '@angular/router';
+import {UPDATE} from '../../store/lossProvision/loss-provision.actions';
+import {Observable} from 'rxjs/Observable';
+import {FimsProduct} from '../../store/model/fims-product.model';
+
+@Component({
+  templateUrl: './create.component.html'
+})
+export class CreateProductLossProvisionFormComponent {
+
+  product$: Observable<FimsProduct>;
+
+  lossProvisionSteps$: Observable<LossProvisionStep[]>;
+
+  constructor(private store: PortfolioStore, private router: Router, private route: ActivatedRoute) {
+    this.product$ = store.select(fromPortfolio.getSelectedProduct);
+
+    this.lossProvisionSteps$ = store.select(fromPortfolio.getProductLossProvisionConfiguration)
+      .map(configuration => configuration.lossProvisionSteps);
+  }
+
+  save(productIdentifier: string, lossProvisionSteps: LossProvisionStep[]): void {
+    this.store.dispatch({
+      type: UPDATE,
+      payload: {
+        productIdentifier,
+        configuration: {
+          lossProvisionSteps
+        },
+        activatedRoute: this.route
+      }
+    });
+  }
+
+  cancel(): void {
+    this.router.navigate(['../'], { relativeTo: this.route });
+  }
+}
diff --git a/src/app/loans/products/lossProvision/form/form.component.html b/src/app/loans/products/lossProvision/form/form.component.html
new file mode 100644
index 0000000..76c4072
--- /dev/null
+++ b/src/app/loans/products/lossProvision/form/form.component.html
@@ -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.
+-->
+
+<td-steps mode="'vertical'">
+  <td-step #detailsStep label="{{'Loss provision' | translate}}" [state]="form.valid ? 'complete' : form.pristine ? 'none' : 'required'" [active]="true">
+    <form [formGroup]="form">
+      <ng-container formArrayName="steps">
+        <div layout="row" *ngFor="let step of steps; let i=index" [formGroupName]="i">
+          <fims-text-input type="number" [form]="step" controlName="daysLate" placeholder="{{'Days late' | translate}}"></fims-text-input>
+          <fims-text-input type="number" [form]="step" controlName="percentProvision" placeholder="{{'Percent provision' | translate}}"></fims-text-input>
+          <button mat-button (click)="removeStep(i)" *ngIf="i > 0">{{'REMOVE STEP' | translate}}</button>
+        </div>
+      </ng-container>
+      <p *ngIf="form.get('steps').hasError('daysLateUnique')" class="tc-red-600" translate>
+        Days late can't overlap with other days late.
+      </p>
+      <div layout="row">
+        <button flex mat-button mat-raised-button (click)="addStep()">{{'ADD STEP' | translate}}</button>
+      </div>
+    </form>
+    <ng-template td-step-actions>
+      <fims-form-final-action
+        [resourceName]="'LOSS PROVISION'"
+        [editMode]="editMode"
+        [disabled]="!form.valid"
+        (onCancel)="cancel()"
+        (onSave)="save()">
+      </fims-form-final-action>
+    </ng-template>
+  </td-step>
+</td-steps>
diff --git a/src/app/loans/products/lossProvision/form/form.component.ts b/src/app/loans/products/lossProvision/form/form.component.ts
new file mode 100644
index 0000000..797966f
--- /dev/null
+++ b/src/app/loans/products/lossProvision/form/form.component.ts
@@ -0,0 +1,99 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, OnChanges, Output, SimpleChanges} from '@angular/core';
+import {AbstractControl, FormArray, FormBuilder, FormGroup, Validators} from '@angular/forms';
+import {FimsValidators} from '../../../../common/validator/validators';
+import {LossProvisionStep} from '../../../../services/portfolio/domain/loss-provision-step.model';
+import {daysLateUnique} from './validator/days-late-unique.validator';
+
+@Component({
+  selector: 'fims-product-loss-provision-form',
+  templateUrl: './form.component.html'
+})
+export class ProductLossProvisionFormComponent implements OnChanges {
+
+  form: FormGroup;
+
+  @Input() lossProvisionSteps: LossProvisionStep[];
+
+  @Input() editMode: boolean;
+
+  @Output('onSave') onSave = new EventEmitter<LossProvisionStep[]>();
+
+  @Output('onCancel') onCancel = new EventEmitter<void>();
+
+  constructor(private formBuilder: FormBuilder) {
+    this.form = formBuilder.group({
+      steps: this.initSteps([])
+    });
+  }
+
+  ngOnChanges(changes: SimpleChanges): void {
+    if (changes.lossProvisionSteps) {
+      this.lossProvisionSteps.forEach(step => this.addStep(step));
+    }
+  }
+
+  private initSteps(steps: LossProvisionStep[]): FormArray {
+    const formControls: FormGroup[] = [];
+
+    steps.forEach(step => {
+      formControls.push(this.initStep(step));
+    });
+
+    return this.formBuilder.array(formControls, daysLateUnique);
+  }
+
+  private initStep(step?: LossProvisionStep): FormGroup {
+    return this.formBuilder.group({
+      daysLate: [step ? step.daysLate : 0, [Validators.required, FimsValidators.minValue(0)]],
+      percentProvision: [step ? step.percentProvision : 0, [Validators.required, FimsValidators.minValue(0), FimsValidators.maxValue(100)]]
+    });
+  }
+
+  addStep(step?: LossProvisionStep): void {
+    const steps: FormArray = this.form.get('steps') as FormArray;
+    steps.push(this.initStep(step));
+  }
+
+  removeStep(index: number): void {
+    const steps: FormArray = this.form.get('steps') as FormArray;
+    steps.removeAt(index);
+  }
+
+  get steps(): AbstractControl[] {
+    const steps: FormArray = this.form.get('steps') as FormArray;
+    return steps.controls;
+  }
+
+  save(): void {
+    const formValue = this.form.getRawValue();
+
+    const steps = formValue.steps.map(step => ({
+      daysLate: parseInt(step.daysLate, 10),
+      percentProvision: parseInt(step.percentProvision, 10)
+    }));
+
+    this.onSave.emit(steps);
+  }
+
+  cancel(): void {
+    this.onCancel.emit();
+  }
+}
diff --git a/src/app/loans/products/lossProvision/form/validator/days-late-unique.validator.spec.ts b/src/app/loans/products/lossProvision/form/validator/days-late-unique.validator.spec.ts
new file mode 100644
index 0000000..c781388
--- /dev/null
+++ b/src/app/loans/products/lossProvision/form/validator/days-late-unique.validator.spec.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 {FormArray, FormControl, FormGroup} from '@angular/forms';
+import {daysLateUnique} from './days-late-unique.validator';
+
+describe('days late unique validator', () => {
+
+  function setup(daysLate: number[]): FormArray {
+    const steps = new FormArray([]);
+    daysLate.forEach(number => steps.push(
+      new FormGroup({
+        daysLate: new FormControl(number),
+      })));
+
+    return steps;
+  }
+
+  it('should not return error when no days late overlap', () => {
+    const group = setup([1, 2, 3]);
+
+    const result = daysLateUnique(group);
+
+    expect(result).toBeNull();
+  });
+
+  it('should return error when days late overlap', () => {
+    const group = setup([1, 2, 2]);
+
+    const result = daysLateUnique(group);
+
+    expect(result).toEqual({
+      daysLateUnique: true
+    });
+  });
+
+});
diff --git a/src/app/loans/products/lossProvision/form/validator/days-late-unique.validator.ts b/src/app/loans/products/lossProvision/form/validator/days-late-unique.validator.ts
new file mode 100644
index 0000000..9f513ed
--- /dev/null
+++ b/src/app/loans/products/lossProvision/form/validator/days-late-unique.validator.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 {FormArray, FormGroup, ValidationErrors} from '@angular/forms';
+
+export function daysLateUnique(array: FormArray): ValidationErrors | null {
+  const steps: FormGroup[] = array.controls as FormGroup[];
+
+  const values = steps
+    .map(optionGroup => parseInt(optionGroup.get('daysLate').value, 10));
+
+  const set = new Set();
+
+  values.forEach(daysLate => set.add(daysLate));
+
+  if (set.size !== values.length) {
+    return {
+      daysLateUnique: true
+    };
+  }
+
+  return null;
+}
diff --git a/src/app/loans/products/lossProvision/loss-provision-exists.guard.ts b/src/app/loans/products/lossProvision/loss-provision-exists.guard.ts
new file mode 100644
index 0000000..0923cb7
--- /dev/null
+++ b/src/app/loans/products/lossProvision/loss-provision-exists.guard.ts
@@ -0,0 +1,63 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, RouterStateSnapshot} from '@angular/router';
+import {PortfolioService} from '../../../services/portfolio/portfolio.service';
+import {ExistsGuardService} from '../../../common/guards/exists-guard';
+import * as fromPortfolio from '../store/index';
+import {PortfolioStore} from '../store/index';
+import {Observable} from 'rxjs/Observable';
+import {LoadAction} from '../store/lossProvision/loss-provision.actions';
+import {of} from 'rxjs/observable/of';
+
+@Injectable()
+export class LoanLossProvisionExistsGuard implements CanActivate {
+
+  constructor(private store: PortfolioStore,
+              private portfolioService: PortfolioService,
+              private existsGuardService: ExistsGuardService) {
+  }
+
+  hasConfigurationInStore(): Observable<boolean> {
+    const timestamp$: Observable<number> = this.store.select(fromPortfolio.getProductLossProvisionConfigurationLoadedAt);
+
+    return this.existsGuardService.isWithinExpiry(timestamp$);
+  }
+
+  hasConfigurationInApi(id: string): Observable<boolean> {
+    return this.portfolioService.getLossProvisionConfiguration(id)
+      .map(configuration => new LoadAction(configuration))
+      .do((action: LoadAction) => this.store.dispatch(action))
+      .map(catalog => !!catalog);
+  }
+
+  hasConfiguration(id: string): Observable<boolean> {
+    return this.hasConfigurationInStore()
+      .switchMap(inStore => {
+        if (inStore) {
+          return of(inStore);
+        }
+        return this.hasConfigurationInApi(id);
+      });
+  }
+
+  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
+    return this.hasConfiguration(route.parent.params['productId']);
+  }
+}
diff --git a/src/app/loans/products/lossProvision/loss-provision.detail.component.html b/src/app/loans/products/lossProvision/loss-provision.detail.component.html
new file mode 100644
index 0000000..90b2750
--- /dev/null
+++ b/src/app/loans/products/lossProvision/loss-provision.detail.component.html
@@ -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.
+-->
+
+<fims-layout-card-over title="{{'Loss provision configuration' | translate}}" [navigateBackTo]="['../../../../']">
+  <fims-data-table flex
+                   [columns]="columns"
+                   [data]="stepData$ | async"
+                   [actionColumn]="false">
+  </fims-data-table>
+</fims-layout-card-over>
+<fims-fab-button
+  title="{{'Edit configuration' | translate}}"
+  icon="mode_edit"
+  [link]="['edit']"
+  [permission]="{ id: 'portfolio_loss_provision', accessLevel: 'CHANGE' }">
+</fims-fab-button>
diff --git a/src/app/loans/products/lossProvision/loss-provision.detail.component.ts b/src/app/loans/products/lossProvision/loss-provision.detail.component.ts
new file mode 100644
index 0000000..e9d3981
--- /dev/null
+++ b/src/app/loans/products/lossProvision/loss-provision.detail.component.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 {Component} from '@angular/core';
+import * as fromPortfolio from '../store/index';
+import {PortfolioStore} from '../store/index';
+import {Observable} from 'rxjs/Observable';
+import {TableData} from '../../../common/data-table/data-table.component';
+
+@Component({
+  templateUrl: './loss-provision.detail.component.html'
+})
+export class LossProvisionDetailComponent {
+
+  stepData$: Observable<TableData>;
+
+  columns: any[] = [
+    { name: 'daysLate', label: 'Days late' },
+    { name: 'percentProvision', label: 'Percent', format: v => `${v}%`}
+  ];
+
+  constructor(private store: PortfolioStore) {
+    this.stepData$ = store.select(fromPortfolio.getProductLossProvisionConfiguration)
+      .map(configuration => ({
+        data: configuration.lossProvisionSteps,
+        totalElements: configuration.lossProvisionSteps.length,
+        totalPages: 1
+      }));
+  }
+
+}
diff --git a/src/app/loans/products/product-exists.guard.ts b/src/app/loans/products/product-exists.guard.ts
new file mode 100644
index 0000000..2740a49
--- /dev/null
+++ b/src/app/loans/products/product-exists.guard.ts
@@ -0,0 +1,69 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, CanActivate, RouterStateSnapshot} from '@angular/router';
+import {Injectable} from '@angular/core';
+import * as fromProducts from './store';
+import {Observable} from 'rxjs/Observable';
+import {LoadAction} from './store/product.actions';
+import {of} from 'rxjs/observable/of';
+import {PortfolioStore} from './store/index';
+import {PortfolioService} from '../../services/portfolio/portfolio.service';
+import {mapToFimsProduct} from './store/model/fims-product.mapper';
+import {ExistsGuardService} from '../../common/guards/exists-guard';
+
+@Injectable()
+export class ProductExistsGuard implements CanActivate {
+
+  constructor(private store: PortfolioStore,
+              private portfolioService: PortfolioService,
+              private existsGuardService: ExistsGuardService) {}
+
+  hasProductInStore(id: string): Observable<boolean> {
+    const timestamp$ = this.store.select(fromProducts.getProductsLoadedAt)
+      .map(loadedAt => loadedAt[id]);
+
+    return this.existsGuardService.isWithinExpiry(timestamp$);
+  }
+
+  hasProductInApi(id: string): Observable<boolean> {
+    const getProduct = this.portfolioService.getProduct(id)
+      .map(productEntity => new LoadAction({
+        resource: mapToFimsProduct(productEntity)
+      }))
+      .do((action: LoadAction) => this.store.dispatch(action))
+      .map(product => !!product);
+
+    return this.existsGuardService.routeTo404OnError(getProduct);
+  }
+
+  hasProduct(id: string): Observable<boolean> {
+    return this.hasProductInStore(id)
+      .switchMap(inStore => {
+        if (inStore) {
+          return of(inStore);
+        }
+
+        return this.hasProductInApi(id);
+      });
+  }
+
+  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
+    return this.hasProduct(route.params['productId']);
+  }
+}
diff --git a/src/app/loans/products/product.detail.component.html b/src/app/loans/products/product.detail.component.html
new file mode 100644
index 0000000..8e9f86a
--- /dev/null
+++ b/src/app/loans/products/product.detail.component.html
@@ -0,0 +1,75 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<fims-layout-card-over [title]="product.name" [subTitle]="product.description" [navigateBackTo]="['../../../']">
+  <fims-layout-card-over-header-menu>
+    <button mat-icon-button (click)="deleteProduct()" title="{{'Delete this product' | translate}}" *ngIf="canDelete$ | async"><mat-icon>delete</mat-icon></button>
+  </fims-layout-card-over-header-menu>
+  <td-message *ngIf="!product.enabled" label="{{'Product not enabled' | translate }}" sublabel="{{'To assign this product to a member it needs to be enabled first' | translate }}" color="warn" icon="error">
+    <button td-message-actions mat-button (click)="enableProduct()" *hasPermission="{ id: 'portfolio_product_operations', accessLevel: 'CHANGE'}" translate>ENABLE PRODUCT</button>
+  </td-message>
+  <td-message *ngIf="product.enabled" label="{{'Product enabled' | translate }}" sublabel="{{'This product can be assigned to a member ' | translate }}" color="accent" icon="check">
+    <button td-message-actions mat-button (click)="disableProduct()" *hasPermission="{ id: 'portfolio_product_operations', accessLevel: 'CHANGE'}" translate>DISABLE PRODUCT</button>
+  </td-message>
+  <fims-two-column-layout>
+    <mat-nav-list left>
+      <h3 mat-subheader translate>Management</h3>
+      <a mat-list-item [routerLink]="['./charges']">
+        <mat-icon matListAvatar>assignment</mat-icon>
+        <h3 matLine translate>Fees</h3>
+        <p matLine translate>Manage fees</p>
+      </a>
+      <a mat-list-item [routerLink]="['./tasks']">
+        <mat-icon matListAvatar>playlist_add_check</mat-icon>
+        <h3 matLine translate>Tasks</h3>
+        <p matLine translate>Manage tasks of this product</p>
+      </a>
+      <a mat-list-item [routerLink]="['./lossProvision']" *hasPermission="{ id: 'portfolio_loss_provision', accessLevel: 'READ' }">
+        <mat-icon matListAvatar>trending_down</mat-icon>
+        <h3 matLine translate>Loss provision configuration</h3>
+      </a>
+    </mat-nav-list>
+    <mat-list right>
+      <mat-list-item>
+        <h3 matLine translate>Balance range</h3>
+        <p matLine>{{product.balanceRange?.minimum | number:numberFormat}} - {{product.balanceRange?.maximum | number:numberFormat}}</p>
+      </mat-list-item>
+      <mat-list-item>
+        <h3 matLine translate>Interest range</h3>
+        <p matLine>{{product.interestBasis | translate}}</p>
+        <p matLine>{{product.interestRange?.minimum | number:numberFormat}} - {{product.interestRange?.maximum | number:numberFormat}}</p>
+      </mat-list-item>
+      <mat-list-item>
+        <h3 matLine translate>Term</h3>
+        <p matLine>{{product.termRange?.maximum | number}} {{product.termRange?.temporalUnit | translate}}</p>
+      </mat-list-item>
+      <mat-list-item>
+        <h3 matLine translate>Currency</h3>
+        <p matLine>{{product.currencyCode}}</p>
+      </mat-list-item>
+      <mat-list-item>
+        <h3 matLine translate>Created by</h3>
+        <p matLine>{{product.createdBy}} - {{product.createdOn | date:'medium'}}</p>
+      </mat-list-item>
+      <mat-list-item>
+        <h3 matLine translate>Last modified by</h3>
+        <p matLine>{{product.lastModifiedBy}} - {{product.lastModifiedOn | date:'medium'}}</p>
+      </mat-list-item>
+    </mat-list>
+  </fims-two-column-layout>
+</fims-layout-card-over>
+<fims-fab-button title="{{'Edit product' | translate}}" icon="mode_edit" [link]="['edit']" *ngIf="canEdit$ | async"></fims-fab-button>
diff --git a/src/app/loans/products/product.detail.component.spec.ts b/src/app/loans/products/product.detail.component.spec.ts
new file mode 100644
index 0000000..9f8746e
--- /dev/null
+++ b/src/app/loans/products/product.detail.component.spec.ts
@@ -0,0 +1,144 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, TestBed} from '@angular/core/testing';
+import {ProductDetailComponent} from './product.detail.component';
+import {ActivatedRouteStub, RouterLinkStubDirective} from '../../common/testing/router-stubs';
+import {TranslateModule} from '@ngx-translate/core';
+import {ActivatedRoute} from '@angular/router';
+import {PortfolioStore} from './store/index';
+import {CUSTOM_ELEMENTS_SCHEMA, DebugElement} from '@angular/core';
+import {TdDialogService} from '@covalent/core';
+import {FimsProduct} from './store/model/fims-product.model';
+import {FimsPermission} from '../../services/security/authz/fims-permission.model';
+import * as fromPortfolio from './store';
+import * as fromRoot from '../../store';
+import {Observable} from 'rxjs/Observable';
+import {By} from '@angular/platform-browser';
+import {FimsPermissionStubDirective} from '../../common/testing/permission-stubs';
+import {MatDialogModule} from '@angular/material';
+
+describe('Test product list component', () => {
+
+  let component: ProductDetailComponent;
+  let fixture: ComponentFixture<ProductDetailComponent>;
+
+  beforeEach(() => {
+    const activatedRoute = new ActivatedRouteStub();
+
+    TestBed.configureTestingModule({
+      imports: [
+        TranslateModule.forRoot(),
+        MatDialogModule
+      ],
+      declarations: [
+        FimsPermissionStubDirective,
+        RouterLinkStubDirective,
+        ProductDetailComponent
+      ],
+      providers: [
+        TdDialogService,
+        { provide: ActivatedRoute, useValue: activatedRoute },
+        { provide: PortfolioStore, useValue: jasmine.createSpyObj('portfolioStore', ['select', 'dispatch']) }
+      ],
+      schemas: [CUSTOM_ELEMENTS_SCHEMA]
+    });
+
+    fixture = TestBed.createComponent(ProductDetailComponent);
+
+    component = fixture.componentInstance;
+  });
+
+  function setup(enabled: boolean, hasChangePermission: boolean) {
+    const product: FimsProduct = {
+      identifier: 'test',
+      name: 'test',
+      termRange: { temporalUnit: 'MONTHS', maximum: 1 },
+      balanceRange: { minimum: 1, maximum: 2 },
+      interestRange: { minimum: 1, maximum: 2 },
+      interestBasis: 'BEGINNING_BALANCE',
+      patternPackage: 'test',
+      description: '',
+      accountAssignments: [],
+      parameters: {
+        moratoriums: [],
+        minimumDispersalAmount: 0,
+        maximumDispersalAmount: 1,
+        maximumDispersalCount: 1
+      },
+      currencyCode: 'USD',
+      minorCurrencyUnitDigits: 1,
+      enabled
+    };
+
+    const permissions: FimsPermission[] = [];
+
+    if (hasChangePermission) {
+      permissions.push({
+        id: 'portfolio_products',
+        accessLevel: 'CHANGE'
+      });
+    }
+
+    const portfolioStore = TestBed.get(PortfolioStore);
+
+    portfolioStore.select.and.callFake(selector => {
+      if (selector === fromPortfolio.getSelectedProduct) {
+        return Observable.of(product);
+      }
+      if (selector === fromRoot.getPermissions) {
+        return Observable.of(permissions);
+      }
+    });
+  }
+
+  function getCreateButton(): DebugElement {
+    return fixture.debugElement.query(By.css('fims-fab-button'));
+  }
+
+  it('should display edit button when product is not enabled and has change permission', () => {
+    setup(false, true);
+
+    fixture.detectChanges();
+
+    const button = getCreateButton();
+
+    expect(button).not.toBeNull();
+  });
+
+  it('should not display edit button when product is enabled and has no change permission', () => {
+    setup(true, false);
+
+    fixture.detectChanges();
+
+    const button = getCreateButton();
+
+    expect(button).toBeNull();
+  });
+
+  it('should not display edit button when product is enabled and has change permission', () => {
+    setup(true, true);
+
+    fixture.detectChanges();
+
+    const button = getCreateButton();
+
+    expect(button).toBeNull();
+  });
+
+});
diff --git a/src/app/loans/products/product.detail.component.ts b/src/app/loans/products/product.detail.component.ts
new file mode 100644
index 0000000..d87298a
--- /dev/null
+++ b/src/app/loans/products/product.detail.component.ts
@@ -0,0 +1,133 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, OnDestroy, OnInit} from '@angular/core';
+import {ActivatedRoute} from '@angular/router';
+import {PortfolioStore} from './store/index';
+import {DELETE, ENABLE} from './store/product.actions';
+import {Subscription} from 'rxjs/Subscription';
+import * as fromPortfolio from './store';
+import * as fromRoot from '../../store';
+import {FimsProduct} from './store/model/fims-product.model';
+import {FimsPermission} from '../../services/security/authz/fims-permission.model';
+import {Observable} from 'rxjs/Observable';
+import {TdDialogService} from '@covalent/core';
+
+@Component({
+  templateUrl: './product.detail.component.html'
+})
+export class ProductDetailComponent implements OnInit, OnDestroy {
+
+  private productSubscription: Subscription;
+
+  product: FimsProduct;
+
+  canEdit$: Observable<boolean>;
+
+  canDelete$: Observable<boolean>;
+
+  constructor(private route: ActivatedRoute, private portfolioStore: PortfolioStore, private dialogService: TdDialogService) {}
+
+  ngOnInit(): void {
+    const product$: Observable<FimsProduct> = this.portfolioStore.select(fromPortfolio.getSelectedProduct)
+      .filter(product => !!product);
+
+    this.productSubscription = product$
+      .subscribe(product => this.product = product);
+
+    const permissions$ = this.portfolioStore.select(fromRoot.getPermissions);
+
+    this.canEdit$ = Observable.combineLatest(
+      permissions$,
+      product$,
+      (permissions, product) => ({
+        hasPermission: this.hasChangePermission(permissions),
+        isEnabled: product.enabled
+      }))
+      .map(result => result.hasPermission && !result.isEnabled);
+
+    this.canDelete$ = Observable.combineLatest(
+      permissions$,
+      product$,
+      (permissions, product) => ({
+        hasPermission: this.hasDeletePermission(permissions),
+        isEnabled: product.enabled
+      }))
+      .map(result => result.hasPermission && !result.isEnabled);
+  }
+
+  ngOnDestroy(): void {
+    this.productSubscription.unsubscribe();
+  }
+
+  enableProduct(): void {
+    this.portfolioStore.dispatch({ type: ENABLE, payload: {
+      product: this.product,
+      enable: true
+    } });
+  }
+
+  disableProduct(): void {
+    this.portfolioStore.dispatch({ type: ENABLE, payload: {
+      product: this.product,
+      enable: false
+    } });
+  }
+
+  confirmDeletion(): Observable<boolean> {
+    return this.dialogService.openConfirm({
+      message: 'Do you want to delete this product?',
+      title: 'Confirm deletion',
+      acceptButton: 'DELETE PRODUCT',
+    }).afterClosed();
+  }
+
+  deleteProduct(): void {
+    this.confirmDeletion()
+      .filter(accept => accept)
+      .subscribe(() => this.portfolioStore.dispatch({
+        type: DELETE, payload: {
+          product: this.product,
+          activatedRoute: this.route
+        }
+      }));
+  }
+
+  get numberFormat(): string {
+    let digits = 2;
+    if (this.product) {
+      digits = this.product.minorCurrencyUnitDigits;
+    }
+    return `1.${digits}-${digits}`;
+  }
+
+  private hasChangePermission(permissions: FimsPermission[]): boolean {
+    return permissions.filter(permission =>
+        permission.id === 'portfolio_products' &&
+        permission.accessLevel === 'CHANGE'
+      ).length > 0;
+  }
+
+  private hasDeletePermission(permissions: FimsPermission[]): boolean {
+    return permissions.filter(permission =>
+        permission.id === 'portfolio_products' &&
+        permission.accessLevel === 'DELETE'
+      ).length > 0;
+  }
+
+}
diff --git a/src/app/loans/products/product.index.component.html b/src/app/loans/products/product.index.component.html
new file mode 100644
index 0000000..ca721b3
--- /dev/null
+++ b/src/app/loans/products/product.index.component.html
@@ -0,0 +1,18 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<router-outlet></router-outlet>
diff --git a/src/app/loans/products/product.index.component.ts b/src/app/loans/products/product.index.component.ts
new file mode 100644
index 0000000..787159b
--- /dev/null
+++ b/src/app/loans/products/product.index.component.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 {Component, OnDestroy, OnInit} from '@angular/core';
+import {Subscription} from 'rxjs/Subscription';
+import {ActivatedRoute} from '@angular/router';
+import {PortfolioStore} from './store/index';
+import {SelectAction} from './store/product.actions';
+
+@Component({
+  templateUrl: './product.index.component.html'
+})
+export class ProductIndexComponent implements OnInit, OnDestroy {
+
+  private actionsSubscription: Subscription;
+
+  constructor(private route: ActivatedRoute, private store: PortfolioStore) {}
+
+  ngOnInit(): void {
+    this.actionsSubscription = this.route.params
+      .map(params => new SelectAction(params['productId']))
+      .subscribe(this.store);
+  }
+
+  ngOnDestroy(): void {
+    this.actionsSubscription.unsubscribe();
+  }
+}
diff --git a/src/app/loans/products/product.list.component.html b/src/app/loans/products/product.list.component.html
new file mode 100644
index 0000000..496ad8e
--- /dev/null
+++ b/src/app/loans/products/product.list.component.html
@@ -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.
+-->
+
+<fims-layout-card-over title="{{'Manage loan products' | translate}}">
+  <fims-data-table flex
+                   (onFetch)="fetchProducts($event)"
+                   (onActionCellClick)="rowSelect($event)"
+                   [columns]="columns"
+                   [sortable]="true"
+                   [pageable]="true"
+                   [data]="productData | async">
+  </fims-data-table>
+</fims-layout-card-over>
+<fims-fab-button title="{{'Create new product' | translate}}" icon="add" [link]="['create']" [permission]="{ id: 'portfolio_products', accessLevel: 'CHANGE'}"></fims-fab-button>
diff --git a/src/app/loans/products/product.list.component.ts b/src/app/loans/products/product.list.component.ts
new file mode 100644
index 0000000..3fa1314
--- /dev/null
+++ b/src/app/loans/products/product.list.component.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 {Component, OnInit} from '@angular/core';
+import {ActivatedRoute, Router} from '@angular/router';
+import {TableData} from '../../common/data-table/data-table.component';
+import {FetchRequest} from '../../services/domain/paging/fetch-request.model';
+import {PortfolioStore} from './store/index';
+import * as fromPortfolio from './store';
+import {Observable} from 'rxjs/Observable';
+import {SEARCH} from './store/product.actions';
+import {FimsProduct} from './store/model/fims-product.model';
+
+@Component({
+  templateUrl: './product.list.component.html'
+})
+export class ProductListComponent implements OnInit {
+
+  productData: Observable<TableData>;
+
+  columns: any[] = [
+    { name: 'identifier', label: 'Id' },
+    { name: 'name', label: 'Name' },
+    { name: 'enabled', label: 'Enabled'}
+  ];
+
+  constructor(private router: Router, private route: ActivatedRoute, private store: PortfolioStore) {}
+
+  ngOnInit(): void {
+    this.productData = this.store.select(fromPortfolio.getProductSearchResults)
+      .map(productPage => ({
+        data: productPage.products,
+        totalElements: productPage.totalElements,
+        totalPages: productPage.totalPages
+      }));
+    this.fetchProducts();
+  }
+
+  fetchProducts(fetchRequest?: FetchRequest): void {
+    this.store.dispatch({ type: SEARCH, payload: fetchRequest });
+  }
+
+  rowSelect(product: FimsProduct): void {
+    this.router.navigate(['detail', product.identifier], { relativeTo: this.route });
+  }
+}
diff --git a/src/app/loans/products/product.module.ts b/src/app/loans/products/product.module.ts
new file mode 100644
index 0000000..88a1822
--- /dev/null
+++ b/src/app/loans/products/product.module.ts
@@ -0,0 +1,187 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 {NgModule} from '@angular/core';
+import {FimsSharedModule} from '../../common/common.module';
+import {ProductRoutes} from './product.routes';
+import {RouterModule} from '@angular/router';
+import {ProductListComponent} from './product.list.component';
+import {ProductCreateComponent} from './form/create.component';
+import {ProductDetailComponent} from './product.detail.component';
+import {ProductEditComponent} from './form/edit.component';
+import {ProductFormComponent} from './form/form.component';
+import {ProductFeeFormComponent} from './form/fees/fee.component';
+import {ProductInterestFormComponent} from './form/interests/interests.component';
+import {ProductTermFormComponent} from './components/term/term.component';
+import {ProductChargeListComponent} from './charges/charge.list.component';
+import {ProductChargeDetailComponent} from './charges/charge.detail.component';
+import {ProductChargeFormComponent} from './charges/form/form.component';
+import {ProductChargeCreateFormComponent} from './charges/form/create.component';
+import {ProductStatusComponent} from './status/status.component';
+import {ProductStatusCreateFormComponent} from './status/form/create.component';
+import {ProductTaskFormComponent} from './status/form/form.component';
+import {ProductStatusEditFormComponent} from './status/form/edit.component';
+import {ProductStatusDetailComponent} from './status/status.detail.component';
+import {ProductMoratoriumFormComponent} from './form/moratorium/moratorium.component';
+import {ProductChargeEditFormComponent} from './charges/form/edit.component';
+import {Store} from '@ngrx/store';
+import {PortfolioStore, portfolioStoreFactory} from './store/index';
+import {ProductExistsGuard} from './product-exists.guard';
+import {ProductTaskExistsGuard} from './status/task-exists.guard';
+import {ProductChargeExistsGuard} from './charges/charge-exists.guard';
+import {ProductSettingsFormComponent} from './form/settings/settings.component';
+import {ProductChargesNotificationEffects} from './store/charges/effects/notification.effects';
+import {ProductChargesRouteEffects} from './store/charges/effects/route.effects';
+import {EffectsModule} from '@ngrx/effects';
+import {ProductChargesApiEffects} from './store/charges/effects/service.effects';
+import {ProductTasksNotificationEffects} from './store/tasks/effects/notification.effects';
+import {ProductTasksRouteEffects} from './store/tasks/effects/route.effects';
+import {ProductTasksApiEffects} from './store/tasks/effects/service.effects';
+import {ProductNotificationEffects} from './store/effects/notification.effects';
+import {ProductRouteEffects} from './store/effects/route.effects';
+import {ProductApiEffects} from './store/effects/service.effects';
+import {TranslateModule} from '@ngx-translate/core';
+import {CommonModule} from '@angular/common';
+import {ReactiveFormsModule} from '@angular/forms';
+import {
+  MatButtonModule,
+  MatCheckboxModule,
+  MatIconModule,
+  MatInputModule,
+  MatListModule,
+  MatOptionModule,
+  MatRadioModule,
+  MatSelectModule,
+  MatSlideToggleModule,
+  MatToolbarModule
+} from '@angular/material';
+import {CovalentDataTableModule, CovalentMessageModule, CovalentStepsModule} from '@covalent/core';
+import {ProductIndexComponent} from './product.index.component';
+import {ProductDetailFormComponent} from './form/detail/detail.component';
+import {ProductChargeRangeListComponent} from './charges/ranges/range.list.component';
+import {ProductChargeRangeDetailComponent} from './charges/ranges/range.detail.component';
+import {ProductChargeRangeFormComponent} from './charges/ranges/form/form.component';
+import {EditProductChargeRangeFormComponent} from './charges/ranges/form/edit.component';
+import {CreateProductChargeRangeFormComponent} from './charges/ranges/form/create.component';
+import {ProductChargeRangesRouteEffects} from './store/ranges/effects/route.effects';
+import {ProductChargeRangesApiEffects} from './store/ranges/effects/service.effects';
+import {ProductChargeRangeExistsGuard} from './charges/ranges/range-exists.guard';
+import {ProductChargeRangeIndexComponent} from './charges/ranges/range.index.component';
+import {ProductChargeRangesNotificationEffects} from './store/ranges/effects/notification.effects';
+import {ProductLossProvisionApiEffects} from './store/lossProvision/effects/service.effects';
+import {ProductLossProvisionRouteEffects} from './store/lossProvision/effects/route.effects';
+import {ProductLossProvisionNotificationEffects} from './store/lossProvision/effects/notification.effects';
+import {LoanLossProvisionExistsGuard} from './lossProvision/loss-provision-exists.guard';
+import {CreateProductLossProvisionFormComponent} from './lossProvision/form/create.component';
+import {ProductLossProvisionFormComponent} from './lossProvision/form/form.component';
+import {LossProvisionDetailComponent} from './lossProvision/loss-provision.detail.component';
+
+@NgModule({
+  imports: [
+    RouterModule.forChild(ProductRoutes),
+    FimsSharedModule,
+    TranslateModule,
+    CommonModule,
+    ReactiveFormsModule,
+    MatIconModule,
+    MatListModule,
+    MatToolbarModule,
+    MatInputModule,
+    MatButtonModule,
+    MatSlideToggleModule,
+    MatRadioModule,
+    MatOptionModule,
+    MatSelectModule,
+    MatCheckboxModule,
+    CovalentDataTableModule,
+    CovalentStepsModule,
+    CovalentMessageModule,
+
+    EffectsModule.run(ProductApiEffects),
+    EffectsModule.run(ProductRouteEffects),
+    EffectsModule.run(ProductNotificationEffects),
+
+    EffectsModule.run(ProductTasksApiEffects),
+    EffectsModule.run(ProductTasksRouteEffects),
+    EffectsModule.run(ProductTasksNotificationEffects),
+
+    EffectsModule.run(ProductChargesApiEffects),
+    EffectsModule.run(ProductChargesRouteEffects),
+    EffectsModule.run(ProductChargesNotificationEffects),
+
+    EffectsModule.run(ProductChargeRangesApiEffects),
+    EffectsModule.run(ProductChargeRangesRouteEffects),
+    EffectsModule.run(ProductChargeRangesNotificationEffects),
+
+    EffectsModule.run(ProductLossProvisionApiEffects),
+    EffectsModule.run(ProductLossProvisionRouteEffects),
+    EffectsModule.run(ProductLossProvisionNotificationEffects),
+  ],
+  declarations: [
+    // product
+    ProductListComponent,
+    ProductIndexComponent,
+    ProductDetailComponent,
+    ProductFormComponent,
+    ProductCreateComponent,
+    ProductEditComponent,
+    ProductDetailFormComponent,
+    ProductFeeFormComponent,
+    ProductInterestFormComponent,
+    ProductTermFormComponent,
+    ProductMoratoriumFormComponent,
+    ProductSettingsFormComponent,
+
+    // charge
+    ProductChargeListComponent,
+    ProductChargeDetailComponent,
+    ProductChargeFormComponent,
+    ProductChargeCreateFormComponent,
+    ProductChargeEditFormComponent,
+
+    // ranges
+    ProductChargeRangeListComponent,
+    ProductChargeRangeIndexComponent,
+    ProductChargeRangeDetailComponent,
+    ProductChargeRangeFormComponent,
+    CreateProductChargeRangeFormComponent,
+    EditProductChargeRangeFormComponent,
+
+    // status
+    ProductStatusComponent,
+    ProductTaskFormComponent,
+    ProductStatusCreateFormComponent,
+    ProductStatusEditFormComponent,
+    ProductStatusDetailComponent,
+
+    // Loss provision
+    LossProvisionDetailComponent,
+    ProductLossProvisionFormComponent,
+    CreateProductLossProvisionFormComponent,
+    ProductChargeDetailComponent
+  ],
+  providers: [
+    ProductExistsGuard,
+    ProductTaskExistsGuard,
+    ProductChargeExistsGuard,
+    ProductChargeRangeExistsGuard,
+    LoanLossProvisionExistsGuard,
+    { provide: PortfolioStore, useFactory: portfolioStoreFactory, deps: [Store]}
+  ]
+})
+export class ProductModule {}
diff --git a/src/app/loans/products/product.routes.ts b/src/app/loans/products/product.routes.ts
new file mode 100644
index 0000000..2db26ba
--- /dev/null
+++ b/src/app/loans/products/product.routes.ts
@@ -0,0 +1,151 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 {Routes} from '@angular/router';
+import {ProductListComponent} from './product.list.component';
+import {ProductCreateComponent} from './form/create.component';
+import {ProductDetailComponent} from './product.detail.component';
+import {ProductEditComponent} from './form/edit.component';
+import {ProductChargeListComponent} from './charges/charge.list.component';
+import {ProductChargeDetailComponent} from './charges/charge.detail.component';
+import {ProductChargeCreateFormComponent} from './charges/form/create.component';
+import {ProductStatusComponent} from './status/status.component';
+import {ProductStatusCreateFormComponent} from './status/form/create.component';
+import {ProductStatusEditFormComponent} from './status/form/edit.component';
+import {ProductStatusDetailComponent} from './status/status.detail.component';
+import {ProductChargeEditFormComponent} from './charges/form/edit.component';
+import {ProductExistsGuard} from './product-exists.guard';
+import {ProductTaskExistsGuard} from './status/task-exists.guard';
+import {ProductChargeExistsGuard} from './charges/charge-exists.guard';
+import {ProductIndexComponent} from './product.index.component';
+import {ProductChargeRangeListComponent} from './charges/ranges/range.list.component';
+import {CreateProductChargeRangeFormComponent} from './charges/ranges/form/create.component';
+import {EditProductChargeRangeFormComponent} from './charges/ranges/form/edit.component';
+import {ProductChargeRangeExistsGuard} from './charges/ranges/range-exists.guard';
+import {ProductChargeRangeIndexComponent} from './charges/ranges/range.index.component';
+import {ProductChargeRangeDetailComponent} from './charges/ranges/range.detail.component';
+import {LoanLossProvisionExistsGuard} from './lossProvision/loss-provision-exists.guard';
+import {CreateProductLossProvisionFormComponent} from './lossProvision/form/create.component';
+import {LossProvisionDetailComponent} from './lossProvision/loss-provision.detail.component';
+
+export const ProductRoutes: Routes = [
+  {path: '', component: ProductListComponent, data: {hasPermission: {id: 'portfolio_products', accessLevel: 'READ'}} /* List */},
+  {
+    path: 'create',
+    component: ProductCreateComponent,
+    data: {hasPermission: {id: 'portfolio_products', accessLevel: 'CHANGE'}} /* Create */
+  },
+  {
+    path: 'detail/:productId', /* Parent view to resolve product */
+    component: ProductIndexComponent,
+    canActivate: [ProductExistsGuard],
+    children: [
+      {
+        path: '',
+        component: ProductDetailComponent /* Detail */
+      },
+      {
+        path: 'edit',
+        component: ProductEditComponent,
+        data: { hasPermission: { id: 'portfolio_products', accessLevel: 'CHANGE' } }
+      },
+      {
+        path: 'charges',
+        component: ProductChargeListComponent /* Charges list view */
+      },
+      {
+        path: 'charges/ranges',
+        children: [
+          {
+            path: '',
+            component: ProductChargeRangeListComponent
+          },
+          {
+            path: 'create',
+            component: CreateProductChargeRangeFormComponent
+          },
+          {
+            path: 'detail/:rangeId',
+            component: ProductChargeRangeIndexComponent,
+            canActivate: [ProductChargeRangeExistsGuard],
+            children: [
+              {
+                path: '',
+                component: ProductChargeRangeDetailComponent
+              },
+              {
+                path: 'edit',
+                component: EditProductChargeRangeFormComponent
+              }
+            ]
+          }
+        ]
+      },
+      {
+        path: 'charges/create',
+        component: ProductChargeCreateFormComponent,
+        data: { hasPermission: { id: 'portfolio_products', accessLevel: 'CHANGE' } } /* Charges create view */},
+      {
+        path: 'charges/detail/:chargeId',
+        component: ProductChargeDetailComponent,
+        canActivate: [ProductChargeExistsGuard]/* Charges detail view */
+      },
+      {
+        path: 'charges/detail/:chargeId/edit',
+        component: ProductChargeEditFormComponent,
+        canActivate: [ProductChargeExistsGuard],
+        data: { hasPermission: { id: 'portfolio_products', accessLevel: 'CHANGE' } }/* Charges detail view */
+      },
+      {
+        path: 'tasks',
+        component: ProductStatusComponent
+      },
+      {
+        path: 'tasks/create', component: ProductStatusCreateFormComponent,
+        data: { hasPermission: { id: 'portfolio_products', accessLevel: 'CHANGE' } }
+      },
+      {
+        path: 'tasks/detail/:taskId',
+        component: ProductStatusDetailComponent,
+        canActivate: [ProductTaskExistsGuard]
+      },
+      {
+        path: 'tasks/detail/:taskId/edit',
+        component: ProductStatusEditFormComponent,
+        canActivate: [ProductTaskExistsGuard],
+        data: { hasPermission: { id: 'portfolio_products', accessLevel: 'CHANGE' } }
+      },
+      {
+        path: 'lossProvision',
+        canActivate: [LoanLossProvisionExistsGuard],
+        data: { hasPermission: { id: 'portfolio_loss_provision', accessLevel: 'READ' } },
+        children: [
+          {
+            path: '',
+            component: LossProvisionDetailComponent
+          },
+          {
+            path: 'edit',
+            component: CreateProductLossProvisionFormComponent,
+            data: { hasPermission: { id: 'portfolio_loss_provision', accessLevel: 'CHANGE' } },
+          }
+        ]
+      }
+    ]
+  }
+];
diff --git a/src/app/loans/products/status/form/create.component.html b/src/app/loans/products/status/form/create.component.html
new file mode 100644
index 0000000..8764bf7
--- /dev/null
+++ b/src/app/loans/products/status/form/create.component.html
@@ -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.
+-->
+
+<fims-layout-card-over title="{{'Add new task definition' | translate}}">
+  <fims-product-task-form-component #form (onSave)="onSave($event)" (onCancel)="onCancel()" [task]="task"></fims-product-task-form-component>
+</fims-layout-card-over>
diff --git a/src/app/loans/products/status/form/create.component.ts b/src/app/loans/products/status/form/create.component.ts
new file mode 100644
index 0000000..b2a7a5a
--- /dev/null
+++ b/src/app/loans/products/status/form/create.component.ts
@@ -0,0 +1,91 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, OnDestroy, OnInit, ViewChild} from '@angular/core';
+import {ActivatedRoute, Router} from '@angular/router';
+import {TaskDefinition} from '../../../../services/portfolio/domain/task-definition.model';
+import {ProductTaskFormComponent} from './form.component';
+import {Subscription} from 'rxjs/Subscription';
+import {PortfolioStore} from '../../store/index';
+import * as fromPortfolio from '../../store';
+import {CREATE, RESET_FORM} from '../../store/tasks/task.actions';
+import {Error} from '../../../../services/domain/error.model';
+import {FimsProduct} from '../../store/model/fims-product.model';
+
+@Component({
+  templateUrl: './create.component.html'
+})
+export class ProductStatusCreateFormComponent implements OnInit, OnDestroy {
+
+  private productSubscription: Subscription;
+
+  private formStateSubscription: Subscription;
+
+  @ViewChild('form') formComponent: ProductTaskFormComponent;
+
+  private product: FimsProduct;
+
+  task: TaskDefinition = {
+    identifier: '',
+    name: '',
+    description: '',
+    actions: ['OPEN'],
+    fourEyes: false,
+    mandatory: false
+  };
+
+  constructor(private router: Router, private route: ActivatedRoute, private portfolioStore: PortfolioStore) {}
+
+  ngOnInit(): void {
+    this.productSubscription = this.portfolioStore.select(fromPortfolio.getSelectedProduct)
+      .subscribe(product => this.product = product);
+
+    this.formStateSubscription = this.portfolioStore.select(fromPortfolio.getProductTaskFormError)
+      .filter((error: Error) => !!error)
+      .subscribe((error: Error) => {
+        const detailForm = this.formComponent.detailForm;
+        const errors = detailForm.get('identifier').errors || {};
+        errors['unique'] = true;
+        detailForm.get('identifier').setErrors(errors);
+        this.formComponent.openDetailsStep();
+      });
+  }
+
+  ngOnDestroy(): void {
+    this.productSubscription.unsubscribe();
+    this.formStateSubscription.unsubscribe();
+
+    this.portfolioStore.dispatch({ type: RESET_FORM });
+  }
+
+  onSave(task: TaskDefinition): void {
+    this.portfolioStore.dispatch({ type: CREATE, payload: {
+      productId: this.product.identifier,
+      task: task,
+      activatedRoute: this.route
+    }});
+  }
+
+  onCancel(): void {
+    this.navigateAway();
+  }
+
+  navigateAway(): void {
+    this.router.navigate(['../'], { relativeTo: this.route });
+  }
+}
diff --git a/src/app/loans/products/status/form/edit.component.html b/src/app/loans/products/status/form/edit.component.html
new file mode 100644
index 0000000..add688f
--- /dev/null
+++ b/src/app/loans/products/status/form/edit.component.html
@@ -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.
+-->
+
+<fims-layout-card-over title="{{'Edit task definition' | translate}}">
+  <fims-product-task-form-component (onSave)="onSave($event)" (onCancel)="onCancel()" [task]="task" [editMode]="true"></fims-product-task-form-component>
+</fims-layout-card-over>
diff --git a/src/app/loans/products/status/form/edit.component.ts b/src/app/loans/products/status/form/edit.component.ts
new file mode 100644
index 0000000..2a81e5d
--- /dev/null
+++ b/src/app/loans/products/status/form/edit.component.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 {Component, OnDestroy, OnInit} from '@angular/core';
+import {TaskDefinition} from '../../../../services/portfolio/domain/task-definition.model';
+import {ActivatedRoute, Router} from '@angular/router';
+import {PortfolioStore} from '../../store/index';
+import {Subscription} from 'rxjs/Subscription';
+import * as fromPortfolio from '../../store';
+import {UPDATE} from '../../store/tasks/task.actions';
+import {FimsProduct} from '../../store/model/fims-product.model';
+
+@Component({
+  templateUrl: './edit.component.html'
+})
+export class ProductStatusEditFormComponent implements OnInit, OnDestroy {
+
+  private taskSubscription: Subscription;
+
+  private productSubscription: Subscription;
+
+  private product: FimsProduct;
+
+  task: TaskDefinition;
+
+  constructor(private router: Router, private route: ActivatedRoute, private portfolioStore: PortfolioStore) {}
+
+  ngOnInit(): void {
+    this.taskSubscription = this.portfolioStore.select(fromPortfolio.getSelectedProductTask)
+      .subscribe(task => this.task = task);
+
+    this.productSubscription = this.portfolioStore.select(fromPortfolio.getSelectedProduct)
+      .subscribe(product => this.product = product);
+  }
+
+  ngOnDestroy(): void {
+    this.taskSubscription.unsubscribe();
+    this.productSubscription.unsubscribe();
+  }
+
+  onSave(task: TaskDefinition): void {
+    this.portfolioStore.dispatch({ type: UPDATE, payload: {
+      productId: this.product.identifier,
+      task: task,
+      activatedRoute: this.route
+    }});
+  }
+
+  onCancel(): void {
+    this.navigateAway();
+  }
+
+  navigateAway(): void {
+    this.router.navigate(['../'], { relativeTo: this.route });
+  }
+}
diff --git a/src/app/loans/products/status/form/form.component.html b/src/app/loans/products/status/form/form.component.html
new file mode 100644
index 0000000..5012c50
--- /dev/null
+++ b/src/app/loans/products/status/form/form.component.html
@@ -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.
+-->
+
+<td-steps mode="'vertical'">
+  <td-step #detailsStep label="{{'Task details' | translate}}" [state]="detailForm.valid ? 'complete' : detailForm.pristine ? 'none' : 'required'">
+    <form [formGroup]="detailForm" layout="column">
+      <fims-id-input flex [form]="detailForm" controlName="identifier" [readonly]="editMode"></fims-id-input>
+      <fims-text-input [form]="detailForm" controlName="name" placeholder="{{'Name' | translate}}"></fims-text-input>
+      <mat-form-field layout-margin flex>
+        <textarea matInput placeholder="{{'Description' | translate}}" formControlName="description"></textarea>
+      </mat-form-field>
+      <mat-checkbox formControlName="mandatory" layout-margin translate>
+        Mandatory
+      </mat-checkbox>
+      <mat-checkbox formControlName="fourEyes" layout-margin translate>
+        Four eyes
+      </mat-checkbox>
+      <div layout-gt-xs="column" layout-margin formArrayName="actions">
+        <h4 translate>Task needs to be executed, before loan</h4>
+        <div *ngFor="let address of actions; let i=index" layout="row" [formGroupName]="i">
+          <mat-form-field>
+            <mat-select formControlName="action">
+              <mat-option *ngFor="let actionOption of actionOptions" [value]="actionOption.type">
+                {{actionOption.label}}
+              </mat-option>
+            </mat-select>
+          </mat-form-field>
+          <button mat-button (click)="removeAction(i)">{{'Remove' | translate}}</button>
+        </div>
+        <button mat-button (click)="addAction()">{{'Add action' | translate}}</button>
+      </div>
+    </form>
+    <ng-template td-step-actions>
+      <fims-form-final-action
+        [resourceName]="'TASK'"
+        [editMode]="editMode"
+        [disabled]="!detailForm.valid"
+        (onCancel)="cancel()"
+        (onSave)="save()">
+      </fims-form-final-action>
+    </ng-template>
+  </td-step>
+</td-steps>
diff --git a/src/app/loans/products/status/form/form.component.ts b/src/app/loans/products/status/form/form.component.ts
new file mode 100644
index 0000000..9e9a486
--- /dev/null
+++ b/src/app/loans/products/status/form/form.component.ts
@@ -0,0 +1,126 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, OnInit, Output, ViewChild} from '@angular/core';
+import {TaskDefinition} from '../../../../services/portfolio/domain/task-definition.model';
+import {TdStepComponent} from '@covalent/core';
+import {AbstractControl, FormArray, FormBuilder, FormGroup, Validators} from '@angular/forms';
+import {ActionOption} from '../../../../common/domain/action-option.model';
+import {WorkflowAction} from '../../../../services/portfolio/domain/individuallending/workflow-action.model';
+import {FimsValidators} from '../../../../common/validator/validators';
+
+@Component({
+  selector: 'fims-product-task-form-component',
+  templateUrl: './form.component.html'
+})
+export class ProductTaskFormComponent implements OnInit {
+
+  @Input('task') set task(task: TaskDefinition){
+    this.prepareDetailForm(task);
+  };
+
+  @Input('editMode') editMode: boolean;
+
+  @Output('onSave') onSave = new EventEmitter<TaskDefinition>();
+
+  @Output('onCancel') onCancel = new EventEmitter<void>();
+
+  @ViewChild('detailsStep') detailsStep: TdStepComponent;
+
+  detailForm: FormGroup;
+
+  actionOptions: ActionOption[] = [
+    { type: 'OPEN', label: 'can be opened' },
+    { type: 'DENY', label: 'can be denied' },
+    { type: 'APPROVE', label: 'can be approved' },
+    { type: 'DISBURSE', label: 'can be disbursed' },
+    { type: 'WRITE_OFF', label: 'can be written off' },
+    { type: 'CLOSE', label: 'can be closed' },
+    { type: 'RECOVER', label: 'can recovered' },
+    { type: 'ACCEPT_PAYMENT', label: 'can be repayed' },
+  ];
+
+  constructor(private formBuilder: FormBuilder) {}
+
+  ngOnInit(): void {
+    this.openDetailsStep();
+  }
+
+  openDetailsStep(): void {
+    this.detailsStep.open();
+  }
+
+  private prepareDetailForm(task: TaskDefinition) {
+    this.detailForm = this.formBuilder.group({
+      identifier: [task.identifier, [Validators.required, Validators.minLength(3), Validators.maxLength(32), FimsValidators.urlSafe]],
+      name: [task.name, [Validators.required]],
+      description: [task.description, [Validators.required]],
+      actions: this.initActions(task.actions),
+      fourEyes: [task.fourEyes, [Validators.required]],
+      mandatory: [task.mandatory, [Validators.required]],
+    });
+  }
+
+  private initActions(values: string[]): FormArray {
+    const formControls: FormGroup[] = [];
+    values.forEach(value => formControls.push(this.initAction(value)));
+    return this.formBuilder.array(formControls);
+  }
+
+  private initAction(value?: string): FormGroup {
+    return this.formBuilder.group({
+      action: [value ? value : '', Validators.required]
+    });
+  }
+
+  addAction(): void {
+    const actions: FormArray = this.detailForm.get('actions') as FormArray;
+    actions.push(this.initAction());
+  }
+
+  removeAction(index: number): void {
+    const actions: FormArray = this.detailForm.get('actions') as FormArray;
+    actions.removeAt(index);
+  }
+
+  get actions(): AbstractControl[] {
+    const actions: FormArray = this.detailForm.get('actions') as FormArray;
+    return actions.controls;
+  }
+
+  save(): void {
+    const actions: any[] = this.detailForm.get('actions').value;
+    const rawActions: WorkflowAction[] = [];
+    actions.forEach(action => rawActions.push(action.action));
+
+    const task: TaskDefinition = {
+      identifier: this.detailForm.get('identifier').value,
+      name: this.detailForm.get('name').value,
+      description: this.detailForm.get('description').value,
+      actions: rawActions,
+      fourEyes: this.detailForm.get('fourEyes').value,
+      mandatory: this.detailForm.get('mandatory').value
+    };
+    this.onSave.emit(task);
+  }
+
+  cancel(): void {
+    this.onCancel.emit();
+  }
+
+}
diff --git a/src/app/loans/products/status/status.component.html b/src/app/loans/products/status/status.component.html
new file mode 100644
index 0000000..165df99
--- /dev/null
+++ b/src/app/loans/products/status/status.component.html
@@ -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.
+-->
+
+<fims-layout-card-over title="{{'Manage task definitions' | translate}}" [navigateBackTo]="['../']">
+  <fims-data-table flex (onFetch)="fetchTasks($event)" (onActionCellClick)="rowSelect($event)" [columns]="columns" [data]="tasksData$ | async" [sortable]="false"></fims-data-table>
+</fims-layout-card-over>
+<fims-fab-button title="{{'Create new task' | translate}}" icon="add" [link]="['create']" [permission]="{ id: 'portfolio_products', accessLevel: 'CHANGE'}"></fims-fab-button>
diff --git a/src/app/loans/products/status/status.component.ts b/src/app/loans/products/status/status.component.ts
new file mode 100644
index 0000000..f4acb06
--- /dev/null
+++ b/src/app/loans/products/status/status.component.ts
@@ -0,0 +1,75 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, OnDestroy, OnInit} from '@angular/core';
+import {TaskDefinition} from '../../../services/portfolio/domain/task-definition.model';
+import {ActivatedRoute, Router} from '@angular/router';
+import {TableData, TableFetchRequest} from '../../../common/data-table/data-table.component';
+import {PortfolioStore} from '../store/index';
+import {Observable} from 'rxjs/Observable';
+import {Subscription} from 'rxjs/Subscription';
+import * as fromPortfolio from '../store';
+import {LOAD_ALL} from '../store/tasks/task.actions';
+import {FimsProduct} from '../store/model/fims-product.model';
+
+@Component({
+  templateUrl: './status.component.html'
+})
+export class ProductStatusComponent implements OnInit, OnDestroy {
+
+  private productSubscription: Subscription;
+
+  tasksData$: Observable<TableData>;
+
+  columns: any[] = [
+    { name: 'identifier', label: 'Id' },
+    { name: 'name', label: 'Name' }
+  ];
+
+  private product: FimsProduct;
+
+  constructor(private router: Router, private route: ActivatedRoute, private portfolioStore: PortfolioStore) {}
+
+  ngOnInit(): void {
+    this.productSubscription = this.portfolioStore.select(fromPortfolio.getSelectedProduct)
+      .subscribe(product => {
+        this.product = product;
+        this.fetchTasks();
+      });
+
+    this.tasksData$ = this.portfolioStore.select(fromPortfolio.getAllProductTaskEntities)
+      .map(tasks => ({
+        totalElements: tasks.length,
+        totalPages: 1,
+        data: tasks
+      }));
+
+  }
+
+  ngOnDestroy(): void {
+    this.productSubscription.unsubscribe();
+  }
+
+  fetchTasks(event?: TableFetchRequest): void {
+    this.portfolioStore.dispatch({ type: LOAD_ALL, payload: this.product.identifier });
+  }
+
+  rowSelect(taskDefinition: TaskDefinition): void {
+    this.router.navigate(['detail', taskDefinition.identifier], { relativeTo: this.route });
+  }
+}
diff --git a/src/app/loans/products/status/status.detail.component.html b/src/app/loans/products/status/status.detail.component.html
new file mode 100644
index 0000000..82cc2a2
--- /dev/null
+++ b/src/app/loans/products/status/status.detail.component.html
@@ -0,0 +1,44 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<fims-layout-card-over [title]="task.name" [subTitle]="task.description" *ngIf="task$ | async as task" [navigateBackTo]="['../../']">
+  <fims-layout-card-over-header-menu *ngIf="product$ | async as product">
+    <button mat-icon-button (click)="deleteTask(product, task)" title="{{'Delete this task' | translate}}" *hasPermission="{ id: 'portfolio_products', accessLevel: 'CHANGE' }"><mat-icon>delete</mat-icon></button>
+  </fims-layout-card-over-header-menu>
+  <div class="mat-content inset" flex>
+    <div layout="row">
+      <mat-list>
+        <mat-list-item>
+          <mat-icon matListAvatar>account_balance</mat-icon>
+          <h3 matLine translate>Actions</h3>
+          <p matLine>{{task.actions.join(',')}}</p>
+        </mat-list-item>
+        <mat-list-item>
+          <mat-icon matListAvatar>person</mat-icon>
+          <h3 matLine translate>Mandatory</h3>
+          <p matLine>{{task.mandatory}}</p>
+        </mat-list-item>
+        <mat-list-item>
+          <mat-icon matListAvatar>person</mat-icon>
+          <h3 matLine translate>Four eyes</h3>
+          <p matLine>{{task.fourEyes}}</p>
+        </mat-list-item>
+      </mat-list>
+    </div>
+  </div>
+</fims-layout-card-over>
+<fims-fab-button title="{{'Edit task' | translate}}" icon="mode_edit" [link]="['edit']" [permission]="{ id: 'portfolio_products', accessLevel: 'CHANGE'}"></fims-fab-button>
diff --git a/src/app/loans/products/status/status.detail.component.ts b/src/app/loans/products/status/status.detail.component.ts
new file mode 100644
index 0000000..269e129
--- /dev/null
+++ b/src/app/loans/products/status/status.detail.component.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, OnDestroy, OnInit} from '@angular/core';
+import {ActivatedRoute} from '@angular/router';
+import {TaskDefinition} from '../../../services/portfolio/domain/task-definition.model';
+import {DELETE, SelectAction} from '../store/tasks/task.actions';
+import {PortfolioStore} from '../store/index';
+import {Subscription} from 'rxjs/Subscription';
+import * as fromPortfolio from '../store';
+import {Observable} from 'rxjs/Observable';
+import {TdDialogService} from '@covalent/core';
+import {Product} from '../../../services/portfolio/domain/product.model';
+import {FimsProduct} from '../store/model/fims-product.model';
+
+@Component({
+  templateUrl: './status.detail.component.html'
+})
+export class ProductStatusDetailComponent implements OnInit, OnDestroy {
+
+  private actionsSubscription: Subscription;
+
+  task$: Observable<TaskDefinition>;
+
+  product$: Observable<FimsProduct>;
+
+  constructor(private route: ActivatedRoute, private portfolioStore: PortfolioStore, private dialogService: TdDialogService) {}
+
+  ngOnInit(): void {
+    this.actionsSubscription = this.route.params
+    .map(params => new SelectAction(params['taskId']))
+    .subscribe(this.portfolioStore);
+
+    this.task$ = this.portfolioStore.select(fromPortfolio.getSelectedProductTask);
+    this.product$ = this.portfolioStore.select(fromPortfolio.getSelectedProduct);
+  }
+
+  ngOnDestroy(): void {
+    this.actionsSubscription.unsubscribe();
+  }
+
+  confirmDeletion(): Observable<boolean> {
+    return this.dialogService.openConfirm({
+      message: 'Do you want to delete this task?',
+      title: 'Confirm deletion',
+      acceptButton: 'DELETE TASK',
+    }).afterClosed();
+  }
+
+  deleteTask(product: Product, task: TaskDefinition): void {
+    this.confirmDeletion()
+      .filter(accept => accept)
+      .subscribe(() => {
+        this.portfolioStore.dispatch({ type: DELETE, payload: {
+          productId: product.identifier,
+          task,
+          activatedRoute: this.route
+        } });
+      });
+  }
+}
diff --git a/src/app/loans/products/status/task-exists.guard.ts b/src/app/loans/products/status/task-exists.guard.ts
new file mode 100644
index 0000000..adecb20
--- /dev/null
+++ b/src/app/loans/products/status/task-exists.guard.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 {ActivatedRouteSnapshot, CanActivate, RouterStateSnapshot} from '@angular/router';
+import {Injectable} from '@angular/core';
+import * as fromProducts from '../store';
+import {Observable} from 'rxjs/Observable';
+import {LoadAction} from '../store/tasks/task.actions';
+import {of} from 'rxjs/observable/of';
+import {PortfolioStore} from '../store/index';
+import {PortfolioService} from '../../../services/portfolio/portfolio.service';
+import {ExistsGuardService} from '../../../common/guards/exists-guard';
+
+@Injectable()
+export class ProductTaskExistsGuard implements CanActivate {
+
+  constructor(private store: PortfolioStore,
+              private portfolioService: PortfolioService,
+              private existsGuardService: ExistsGuardService) {}
+
+  hasTaskInStore(id: string): Observable<boolean> {
+    const timestamp$ = this.store.select(fromProducts.getProductTasksLoadedAt)
+      .map(loadedAt => loadedAt[id]);
+
+    return this.existsGuardService.isWithinExpiry(timestamp$);
+  }
+
+  hasTaskInApi(productId: string, taskId: string): Observable<boolean> {
+    const getTaskDefintion$ = this.portfolioService.getTaskDefinition(productId, taskId)
+      .map(taskEntity => new LoadAction({
+        resource: taskEntity
+      }))
+      .do((action: LoadAction) => this.store.dispatch(action))
+      .map(task => !!task);
+
+    return this.existsGuardService.routeTo404OnError(getTaskDefintion$);
+  }
+
+  hasTask(productId: string, taskId: string): Observable<boolean> {
+    return this.hasTaskInStore(taskId)
+      .switchMap(inStore => {
+        if (inStore) {
+          return of(inStore);
+        }
+
+        return this.hasTaskInApi(productId, taskId);
+      });
+  }
+
+  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
+    return this.hasTask(route.parent.params['productId'], route.params['taskId']);
+  }
+}
diff --git a/src/app/loans/products/store/charges/charge.actions.ts b/src/app/loans/products/store/charges/charge.actions.ts
new file mode 100644
index 0000000..4b5d29a
--- /dev/null
+++ b/src/app/loans/products/store/charges/charge.actions.ts
@@ -0,0 +1,155 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 {Error} from '../../../../services/domain/error.model';
+import {type} from '../../../../store/util';
+import {RoutePayload} from '../../../../common/store/route-payload';
+import {ChargeDefinition} from '../../../../services/portfolio/domain/charge-definition.model';
+import {
+  CreateResourceSuccessPayload,
+  DeleteResourceSuccessPayload,
+  LoadResourcePayload,
+  SelectResourcePayload,
+  UpdateResourceSuccessPayload
+} from '../../../../common/store/resource.reducer';
+
+export const LOAD_ALL = type('[Product Charge] Load All');
+export const LOAD_ALL_COMPLETE = type('[Product Charge] Load All Complete');
+
+export const LOAD = type('[Product Charge] Load');
+export const SELECT = type('[Product Charge] Select');
+
+export const CREATE = type('[Product Charge] Create');
+export const CREATE_SUCCESS = type('[Product Charge] Create Success');
+export const CREATE_FAIL = type('[Product Charge] Create Fail');
+
+export const UPDATE = type('[Product Charge] Update');
+export const UPDATE_SUCCESS = type('[Product Charge] Update Success');
+export const UPDATE_FAIL = type('[Product Charge] Update Fail');
+
+export const DELETE = type('[Product Charge] Delete');
+export const DELETE_SUCCESS = type('[Product Charge] Delete Success');
+export const DELETE_FAIL = type('[Product Charge] Delete Fail');
+
+export const RESET_FORM = type('[Product Charge] Reset Form');
+
+export interface ChargeRoutePayload extends RoutePayload {
+  productId: string;
+  charge: ChargeDefinition;
+}
+
+export class LoadAllAction implements Action {
+  readonly type = LOAD_ALL;
+
+  constructor(public payload: string) { }
+}
+
+export class LoadAllCompleteAction implements Action {
+  readonly type = LOAD_ALL_COMPLETE;
+
+  constructor(public payload: ChargeDefinition[]) { }
+}
+
+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 CreateChargeAction implements Action {
+  readonly type = CREATE;
+
+  constructor(public payload: ChargeRoutePayload) { }
+}
+
+export class CreateChargeSuccessAction implements Action {
+  readonly type = CREATE_SUCCESS;
+
+  constructor(public payload: CreateResourceSuccessPayload) { }
+}
+
+export class CreateChargeFailAction implements Action {
+  readonly type = CREATE_FAIL;
+
+  constructor(public payload: Error) { }
+}
+
+export class UpdateChargeAction implements Action {
+  readonly type = UPDATE;
+
+  constructor(public payload: ChargeRoutePayload) { }
+}
+
+export class UpdateChargeSuccessAction implements Action {
+  readonly type = UPDATE_SUCCESS;
+
+  constructor(public payload: UpdateResourceSuccessPayload) { }
+}
+
+export class UpdateChargeFailAction implements Action {
+  readonly type = UPDATE_FAIL;
+
+  constructor(public payload: Error) { }
+}
+
+export class DeleteChargeAction implements Action {
+  readonly type = DELETE;
+
+  constructor(public payload: ChargeRoutePayload) { }
+}
+
+export class DeleteChargeSuccessAction implements Action {
+  readonly type = DELETE_SUCCESS;
+
+  constructor(public payload: DeleteResourceSuccessPayload) { }
+}
+
+export class DeleteChargeFailAction implements Action {
+  readonly type = DELETE_FAIL;
+
+  constructor(public payload: Error) { }
+}
+
+export class ResetChargeFormAction implements Action {
+  readonly type = RESET_FORM;
+
+  constructor() {}
+}
+
+export type Actions
+  = LoadAllAction
+  | LoadAllCompleteAction
+  | LoadAction
+  | SelectAction
+  | CreateChargeAction
+  | CreateChargeSuccessAction
+  | CreateChargeFailAction
+  | UpdateChargeAction
+  | UpdateChargeSuccessAction
+  | UpdateChargeFailAction
+  | DeleteChargeAction
+  | DeleteChargeSuccessAction
+  | DeleteChargeFailAction
+  | ResetChargeFormAction;
diff --git a/src/app/loans/products/store/charges/charges.reducer.ts b/src/app/loans/products/store/charges/charges.reducer.ts
new file mode 100644
index 0000000..dc3df87
--- /dev/null
+++ b/src/app/loans/products/store/charges/charges.reducer.ts
@@ -0,0 +1,66 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 charge from './charge.actions';
+import {ChargeDefinition} from '../../../../services/portfolio/domain/charge-definition.model';
+import {ResourceState} from '../../../../common/store/resource.reducer';
+import {idsToHashWithCurrentTimestamp, resourcesToHash} from '../../../../common/store/reducer.helper';
+
+export interface State extends ResourceState {
+  ids: string[];
+  entities: { [id: string]: ChargeDefinition };
+  selectedId: string | null;
+}
+
+export const initialState: State = {
+  ids: [],
+  entities: {},
+  loadedAt: {},
+  selectedId: null,
+};
+
+export function reducer(state = initialState, action: charge.Actions): ResourceState {
+
+  switch (action.type) {
+
+    case charge.LOAD_ALL: {
+      return initialState;
+    }
+
+    case charge.LOAD_ALL_COMPLETE: {
+      const chargeDefinitions: ChargeDefinition[] = action.payload;
+
+      const ids = chargeDefinitions.map(chargeDefinition => chargeDefinition.identifier);
+
+      const entities = resourcesToHash(chargeDefinitions);
+
+      const loadedAt = idsToHashWithCurrentTimestamp(ids);
+
+      return {
+        ids: [ ...ids ],
+        entities: entities,
+        loadedAt: loadedAt,
+        selectedId: state.selectedId
+      };
+    }
+
+    default: {
+      return state;
+    }
+  }
+}
diff --git a/src/app/loans/products/store/charges/effects/notification.effects.ts b/src/app/loans/products/store/charges/effects/notification.effects.ts
new file mode 100644
index 0000000..01139ba
--- /dev/null
+++ b/src/app/loans/products/store/charges/effects/notification.effects.ts
@@ -0,0 +1,47 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 {NotificationService, NotificationType} from '../../../../../services/notification/notification.service';
+import {Action} from '@ngrx/store';
+import {Observable} from 'rxjs/Observable';
+import {Actions, Effect} from '@ngrx/effects';
+import {Injectable} from '@angular/core';
+import * as chargeActions from '../charge.actions';
+
+@Injectable()
+export class ProductChargesNotificationEffects {
+
+  @Effect({dispatch: false})
+  createUpdateCustomerChargeSuccess$: Observable<Action> = this.actions$
+    .ofType(chargeActions.CREATE_SUCCESS, chargeActions.UPDATE)
+    .do(() => this.notificationService.send({
+      type: NotificationType.MESSAGE,
+      message: 'Fee is going to be saved'
+    }));
+
+  @Effect({dispatch: false})
+  deleteCustomerChargeSuccess$: Observable<Action> = this.actions$
+    .ofType(chargeActions.DELETE_SUCCESS)
+    .do(() => this.notificationService.send({
+      type: NotificationType.MESSAGE,
+      message: 'Fee is going to be deleted'
+    }));
+
+  constructor(private actions$: Actions, private notificationService: NotificationService) {}
+}
+
diff --git a/src/app/loans/products/store/charges/effects/route.effects.ts b/src/app/loans/products/store/charges/effects/route.effects.ts
new file mode 100644
index 0000000..b5f4b12
--- /dev/null
+++ b/src/app/loans/products/store/charges/effects/route.effects.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 {Injectable} from '@angular/core';
+import {Actions, Effect} from '@ngrx/effects';
+import {Observable} from 'rxjs/Observable';
+import {Action} from '@ngrx/store';
+import * as chargeActions from '../charge.actions';
+import {Router} from '@angular/router';
+
+@Injectable()
+export class ProductChargesRouteEffects {
+
+  @Effect({ dispatch: false })
+  createUpdateProductChargeSuccess$: Observable<Action> = this.actions$
+    .ofType(chargeActions.CREATE_SUCCESS, chargeActions.UPDATE_SUCCESS)
+    .map(action => action.payload)
+    .do(payload => this.router.navigate(['../'], { relativeTo: payload.activatedRoute }));
+
+  @Effect({ dispatch: false })
+  deleteChargeSuccess$: Observable<Action> = this.actions$
+    .ofType(chargeActions.DELETE_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/loans/products/store/charges/effects/service.effects.ts b/src/app/loans/products/store/charges/effects/service.effects.ts
new file mode 100644
index 0000000..3e88033
--- /dev/null
+++ b/src/app/loans/products/store/charges/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 {Actions, Effect} from '@ngrx/effects';
+import {Observable} from 'rxjs/Observable';
+import {Action} from '@ngrx/store';
+import {of} from 'rxjs/observable/of';
+import * as chargeActions from '../charge.actions';
+import {PortfolioService} from '../../../../../services/portfolio/portfolio.service';
+
+@Injectable()
+export class ProductChargesApiEffects {
+
+  @Effect()
+  loadAll$: Observable<Action> = this.actions$
+    .ofType(chargeActions.LOAD_ALL)
+    .debounceTime(300)
+    .map((action: chargeActions.LoadAllAction) => action.payload)
+    .switchMap(id => {
+      const nextSearch$ = this.actions$.ofType(chargeActions.LOAD_ALL).skip(1);
+
+      return this.portfolioService.findAllChargeDefinitionsForProduct(id)
+        .takeUntil(nextSearch$)
+        .map(chargeDefinitions => new chargeActions.LoadAllCompleteAction(chargeDefinitions))
+        .catch(() => of(new chargeActions.LoadAllCompleteAction([])));
+    });
+
+  @Effect()
+  createCharge$: Observable<Action> = this.actions$
+    .ofType(chargeActions.CREATE)
+    .map((action: chargeActions.CreateChargeAction) => action.payload)
+    .mergeMap(payload =>
+      this.portfolioService.createChargeDefinition(payload.productId, payload.charge)
+        .map(() => new chargeActions.CreateChargeSuccessAction({
+          resource: payload.charge,
+          activatedRoute: payload.activatedRoute
+        }))
+        .catch((error) => of(new chargeActions.CreateChargeFailAction(error)))
+    );
+
+  @Effect()
+  updateCharge$: Observable<Action> = this.actions$
+    .ofType(chargeActions.UPDATE)
+    .map((action: chargeActions.UpdateChargeAction) => action.payload)
+    .mergeMap(payload =>
+      this.portfolioService.changeChargeDefinition(payload.productId, payload.charge)
+        .map(() => new chargeActions.UpdateChargeSuccessAction({
+          resource: payload.charge,
+          activatedRoute: payload.activatedRoute
+        }))
+        .catch((error) => of(new chargeActions.UpdateChargeFailAction(error)))
+    );
+
+  @Effect()
+  deleteCharge$: Observable<Action> = this.actions$
+    .ofType(chargeActions.DELETE)
+    .map((action: chargeActions.DeleteChargeAction) => action.payload)
+    .mergeMap(payload =>
+      this.portfolioService.deleteChargeDefinition(payload.productId, payload.charge.identifier)
+        .map(() => new chargeActions.DeleteChargeSuccessAction({
+          resource: payload.charge,
+          activatedRoute: payload.activatedRoute
+        }))
+        .catch((error) => of(new chargeActions.DeleteChargeFailAction(error)))
+    );
+
+  constructor(private actions$: Actions, private portfolioService: PortfolioService) { }
+}
diff --git a/src/app/loans/products/store/effects/notification.effects.ts b/src/app/loans/products/store/effects/notification.effects.ts
new file mode 100644
index 0000000..3361111
--- /dev/null
+++ b/src/app/loans/products/store/effects/notification.effects.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 {Injectable} from '@angular/core';
+import {Actions, Effect, toPayload} from '@ngrx/effects';
+import {Observable} from 'rxjs/Observable';
+import {Action} from '@ngrx/store';
+import * as productActions from '../product.actions';
+import {NotificationService, NotificationType} from '../../../../services/notification/notification.service';
+
+@Injectable()
+export class ProductNotificationEffects {
+
+  @Effect({ dispatch: false })
+  createProductSuccess$: Observable<Action> = this.actions$
+    .ofType(productActions.CREATE_SUCCESS, productActions.UPDATE_SUCCESS)
+    .do(() => this.notificationService.send({
+      type: NotificationType.MESSAGE,
+      message: 'Product is going to be saved'
+    }));
+
+  @Effect({ dispatch: false })
+  deleteProductSuccess$: Observable<Action> = this.actions$
+    .ofType(productActions.DELETE_SUCCESS)
+    .do(() => this.notificationService.send({
+      type: NotificationType.MESSAGE,
+      message: 'Product is going to be deleted'
+    }));
+
+  @Effect({ dispatch: false })
+  deleteProductFail$: Observable<Action> = this.actions$
+    .ofType(productActions.DELETE_FAIL)
+    .do(() => this.notificationService.send({
+      type: NotificationType.ALERT,
+      title: 'Product can\'t be deleted',
+      message: 'Product is already assigned to a member.'
+    }));
+
+  @Effect({ dispatch: false })
+  enableProductSuccess$: Observable<Action> = this.actions$
+    .ofType(productActions.ENABLE_SUCCESS)
+    .map(toPayload)
+    .do(payload => {
+      const action: string = payload.enable ? 'enabled' : 'disabled';
+      this.notificationService.send({
+        type: NotificationType.MESSAGE,
+        message: `Product is going to be ${action}`
+      });
+    });
+
+  @Effect({ dispatch: false })
+  enableProductFail$: Observable<Action> = this.actions$
+    .ofType(productActions.ENABLE_FAIL)
+    .map(toPayload)
+    .do(payload => this.notificationService.send({
+      type: NotificationType.ALERT,
+      message: 'Product could not be enabled'
+    }));
+
+  constructor(private actions$: Actions, private notificationService: NotificationService) {}
+}
diff --git a/src/app/loans/products/store/effects/route.effects.ts b/src/app/loans/products/store/effects/route.effects.ts
new file mode 100644
index 0000000..0b76373
--- /dev/null
+++ b/src/app/loans/products/store/effects/route.effects.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 {Injectable} from '@angular/core';
+import {Actions, Effect} from '@ngrx/effects';
+import {Observable} from 'rxjs/Observable';
+import {Action} from '@ngrx/store';
+import * as productActions from '../product.actions';
+import {Router} from '@angular/router';
+
+@Injectable()
+export class ProductRouteEffects {
+
+  @Effect({ dispatch: false })
+  createProductSuccess$: Observable<Action> = this.actions$
+    .ofType(productActions.CREATE_SUCCESS, productActions.UPDATE_SUCCESS)
+    .map(action => action.payload)
+    .do(payload => this.router.navigate(['../'], { relativeTo: payload.activatedRoute} ));
+
+  @Effect({ dispatch: false })
+  deleteProductSuccess$: Observable<Action> = this.actions$
+    .ofType(productActions.DELETE_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/loans/products/store/effects/service.effects.ts b/src/app/loans/products/store/effects/service.effects.ts
new file mode 100644
index 0000000..c53ce52
--- /dev/null
+++ b/src/app/loans/products/store/effects/service.effects.ts
@@ -0,0 +1,102 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 {PortfolioService} from '../../../../services/portfolio/portfolio.service';
+import {Action} from '@ngrx/store';
+import {Observable} from 'rxjs/Observable';
+import {Actions, Effect} from '@ngrx/effects';
+import * as productActions from '../product.actions';
+import {of} from 'rxjs/observable/of';
+import {mapToFimsProducts, mapToProduct} from '../model/fims-product.mapper';
+import {emptySearchResult} from '../../../../common/store/search.reducer';
+
+@Injectable()
+export class ProductApiEffects {
+
+  @Effect()
+  search$: Observable<Action> = this.actions$
+    .ofType(productActions.SEARCH)
+    .map((action: productActions.SelectAction) => action.payload)
+    .debounceTime(300)
+    .switchMap(fetchRequest => {
+      const nextSearch$ = this.actions$.ofType(productActions.SEARCH).skip(1);
+
+      return this.portfolioService.findAllProducts(true, fetchRequest)
+        .takeUntil(nextSearch$)
+        .map(productPage => new productActions.SearchCompleteAction({
+          elements: mapToFimsProducts(productPage.elements),
+          totalElements: productPage.totalElements,
+          totalPages: productPage.totalPages
+        }))
+        .catch(() => of(new productActions.SearchCompleteAction(emptySearchResult())));
+    });
+
+  @Effect()
+  createProduct$: Observable<Action> = this.actions$
+    .ofType(productActions.CREATE)
+    .map((action: productActions.CreateProductAction) => action.payload)
+    .mergeMap(payload =>
+      this.portfolioService.createProduct(mapToProduct(payload.product))
+        .map(() => new productActions.CreateProductSuccessAction({
+          resource: payload.product,
+          activatedRoute: payload.activatedRoute
+        }))
+        .catch((error) => of(new productActions.CreateProductFailAction(error)))
+    );
+
+  @Effect()
+  updateProduct$: Observable<Action> = this.actions$
+    .ofType(productActions.UPDATE)
+    .map((action: productActions.UpdateProductAction) => action.payload)
+    .mergeMap(payload =>
+      this.portfolioService.changeProduct(mapToProduct(payload.product))
+        .map(() => new productActions.UpdateProductSuccessAction({
+          resource: payload.product,
+          activatedRoute: payload.activatedRoute
+        }))
+        .catch((error) => of(new productActions.UpdateProductFailAction(error)))
+    );
+
+  @Effect()
+  deleteProduct$: Observable<Action> = this.actions$
+    .ofType(productActions.DELETE)
+    .map((action: productActions.DeleteProductAction) => action.payload)
+    .mergeMap(payload =>
+      this.portfolioService.deleteProduct(payload.product.identifier)
+        .map(() => new productActions.DeleteProductSuccessAction({
+          resource: payload.product,
+          activatedRoute: payload.activatedRoute
+        }))
+        .catch((error) => of(new productActions.DeleteProductFailAction(error)))
+    );
+
+  @Effect()
+  enableProduct$: Observable<Action> = this.actions$
+    .ofType(productActions.ENABLE)
+    .map((action: productActions.EnableProductAction) => action.payload)
+    .mergeMap(payload =>
+      this.portfolioService.enableProduct(payload.product.identifier, payload.enable)
+        .map(() => new productActions.EnableProductSuccessAction(payload))
+        .catch((error) =>
+          this.portfolioService.incompleteaccountassignments(payload.product.identifier)
+            .map(accountAssignments => new productActions.EnableProductFailAction(accountAssignments)))
+    );
+
+  constructor(private actions$: Actions, private portfolioService: PortfolioService) { }
+}
diff --git a/src/app/loans/products/store/index.ts b/src/app/loans/products/store/index.ts
new file mode 100644
index 0000000..7576882
--- /dev/null
+++ b/src/app/loans/products/store/index.ts
@@ -0,0 +1,151 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 {ActionReducer, Store} from '@ngrx/store';
+import {createSelector} from 'reselect';
+import {createReducer} from '../../../store/index';
+import * as fromProducts from './products.reducer';
+import * as fromProductTasks from './tasks/tasks.reducer';
+import * as fromProductCharges from './charges/charges.reducer';
+import * as fromProductChargeRanges from './ranges/ranges.reducer';
+import * as fromProductLossProvision from './lossProvision/loss-provision.reducer';
+import {getLossProvisionConfiguration, getLossProvisionConfigurationLoadedAt} from './lossProvision/loss-provision.reducer';
+
+import {
+  createResourceReducer,
+  getResourceAll,
+  getResourceLoadedAt,
+  getResourceSelected,
+  ResourceState
+} from '../../../common/store/resource.reducer';
+import {
+  createSearchReducer,
+  getSearchEntities,
+  getSearchTotalElements,
+  getSearchTotalPages,
+  SearchState
+} from '../../../common/store/search.reducer';
+import {createFormReducer, FormState, getFormError} from '../../../common/store/form.reducer';
+
+export interface State extends fromRoot.State {
+  products: ResourceState;
+  productSearch: SearchState;
+  productForm: FormState;
+  productTasks: ResourceState;
+  productTaskForm: FormState;
+  productCharges: ResourceState;
+  productChargeForm: FormState;
+  productChargeRanges: ResourceState;
+  productLossProvision: fromProductLossProvision.State;
+}
+
+const reducers = {
+  products: createResourceReducer('Product', fromProducts.reducer),
+  productSearch: createSearchReducer('Product'),
+  productForm: createFormReducer('Product'),
+  productTasks: createResourceReducer('Product Task', fromProductTasks.reducer),
+  productTaskForm: createFormReducer('Product Task'),
+  productCharges: createResourceReducer('Product Charge', fromProductCharges.reducer),
+  productChargeForm: createFormReducer('Product Charge'),
+  productChargeRanges: createResourceReducer('Product Charge Range', fromProductChargeRanges.reducer),
+  productLossProvision: fromProductLossProvision.reducer
+};
+
+export const portfolioModuleReducer: ActionReducer<State> = createReducer(reducers);
+
+export class PortfolioStore extends Store<State> {}
+
+export function portfolioStoreFactory(appStore: Store<fromRoot.State>) {
+  appStore.replaceReducer(portfolioModuleReducer);
+  return appStore;
+}
+
+/**
+ * Product selectors
+ */
+export const getProductsState = (state: State) => state.products;
+
+export const getProductFormState = (state: State) => state.productForm;
+export const getProductFormError = createSelector(getProductFormState, getFormError);
+
+export const getProductsLoadedAt = createSelector(getProductsState, getResourceLoadedAt);
+export const getSelectedProduct = createSelector(getProductsState, getResourceSelected);
+
+/**
+ * Product search selector
+ */
+export const getProductSearchState = (state: State) => state.productSearch;
+
+export const getSearchProducts = createSelector(getProductSearchState, getSearchEntities);
+export const getProductSearchTotalElements = createSelector(getProductSearchState, getSearchTotalElements);
+export const getProductSearchTotalPages = createSelector(getProductSearchState, getSearchTotalPages);
+
+export const getProductSearchResults = createSelector(getSearchProducts, getProductSearchTotalPages, getProductSearchTotalElements,
+  (products, totalPages, totalElements) => {
+  return {
+    products: products,
+    totalPages: totalPages,
+    totalElements: totalElements
+  };
+});
+
+/**
+ * Product Task Selectors
+ */
+export const getProductTasksState = (state: State) => state.productTasks;
+
+export const getProductTaskFormState = (state: State) => state.productTaskForm;
+export const getProductTaskFormError = createSelector(getProductTaskFormState, getFormError);
+
+export const getProductTasksLoadedAt = createSelector(getProductTasksState, getResourceLoadedAt);
+export const getSelectedProductTask = createSelector(getProductTasksState, getResourceSelected);
+
+export const getAllProductTaskEntities = createSelector(getProductTasksState, getResourceAll);
+
+/**
+ * Product Charge Selectors
+ */
+export const getProductChargesState = (state: State) => state.productCharges;
+
+export const getProductChargesLoadedAt = createSelector(getProductChargesState, getResourceLoadedAt);
+export const getSelectedProductCharge = createSelector(getProductChargesState, getResourceSelected);
+
+export const getAllProductChargeEntities = createSelector(getProductChargesState, getResourceAll);
+
+/**
+ * Product Charge Range Selectors
+ */
+
+export const getProductChargeRangesState = (state: State) => state.productChargeRanges;
+
+export const getProductChargeRangesLoadedAt = createSelector(getProductChargeRangesState, getResourceLoadedAt);
+export const getSelectedProductChargeRange = createSelector(getProductChargeRangesState, getResourceSelected);
+
+export const getAllProductChargeRangeEntities = createSelector(getProductChargeRangesState, getResourceAll);
+
+/**
+ * Product Loss Configuration Selectors
+ */
+
+export const getProductLossProvisionState = (state: State) => state.productLossProvision;
+
+export const getProductLossProvisionConfigurationLoadedAt = createSelector(
+  getProductLossProvisionState, getLossProvisionConfigurationLoadedAt
+);
+export const getProductLossProvisionConfiguration = createSelector(getProductLossProvisionState, getLossProvisionConfiguration);
diff --git a/src/app/loans/products/store/lossProvision/effects/notification.effects.ts b/src/app/loans/products/store/lossProvision/effects/notification.effects.ts
new file mode 100644
index 0000000..d218443
--- /dev/null
+++ b/src/app/loans/products/store/lossProvision/effects/notification.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 {Actions, Effect} from '@ngrx/effects';
+import {NotificationService, NotificationType} from '../../../../../services/notification/notification.service';
+import {Observable} from 'rxjs/Observable';
+import {Action} from '@ngrx/store';
+import * as lossProvisionActions from '../loss-provision.actions';
+
+@Injectable()
+export class ProductLossProvisionNotificationEffects {
+
+  @Effect({ dispatch: false })
+  updateLossProvisionSuccess$: Observable<Action> = this.actions$
+    .ofType(lossProvisionActions.UPDATE_SUCCESS)
+    .do(() => this.notificationService.send({
+      type: NotificationType.MESSAGE,
+      message: 'Loss provision configuration is going to be saved'
+    }));
+
+  constructor(private actions$: Actions, private notificationService: NotificationService) { }
+}
diff --git a/src/app/loans/products/store/lossProvision/effects/route.effects.ts b/src/app/loans/products/store/lossProvision/effects/route.effects.ts
new file mode 100644
index 0000000..2acae17
--- /dev/null
+++ b/src/app/loans/products/store/lossProvision/effects/route.effects.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 {Injectable} from '@angular/core';
+import {Actions, Effect} from '@ngrx/effects';
+import {Observable} from 'rxjs/Observable';
+import * as lossProvisionActions from '../loss-provision.actions';
+import {Action} from '@ngrx/store';
+import {Router} from '@angular/router';
+
+@Injectable()
+export class ProductLossProvisionRouteEffects {
+
+  @Effect({ dispatch: false })
+  updateLossProvisionSuccess$: Observable<Action> = this.actions$
+    .ofType(lossProvisionActions.UPDATE_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/loans/products/store/lossProvision/effects/service.effects.ts b/src/app/loans/products/store/lossProvision/effects/service.effects.ts
new file mode 100644
index 0000000..9ca4205
--- /dev/null
+++ b/src/app/loans/products/store/lossProvision/effects/service.effects.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 {Actions, Effect} from '@ngrx/effects';
+import {Observable} from 'rxjs/Observable';
+import {Action} from '@ngrx/store';
+import {of} from 'rxjs/observable/of';
+import {PortfolioService} from '../../../../../services/portfolio/portfolio.service';
+import * as lossProvisionActions from '../loss-provision.actions';
+
+@Injectable()
+export class ProductLossProvisionApiEffects {
+
+  @Effect()
+  updateConfiguration$: Observable<Action> = this.actions$
+    .ofType(lossProvisionActions.UPDATE)
+    .map((action: lossProvisionActions.UpdateLossProvisionAction) => action.payload)
+    .mergeMap(payload =>
+      this.portfolioService.changeLossProvisionConfiguration(payload.productIdentifier, payload.configuration)
+        .map(() => new lossProvisionActions.UpdateLossProvisionSuccessAction(payload))
+        .catch(error => of(new lossProvisionActions.UpdateLossProvisionFailAction(error)))
+    );
+
+  constructor(private actions$: Actions, private portfolioService: PortfolioService) { }
+
+}
diff --git a/src/app/loans/products/store/lossProvision/loss-provision.actions.ts b/src/app/loans/products/store/lossProvision/loss-provision.actions.ts
new file mode 100644
index 0000000..1b3ea7d
--- /dev/null
+++ b/src/app/loans/products/store/lossProvision/loss-provision.actions.ts
@@ -0,0 +1,63 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 {Action} from '@ngrx/store';
+import {RoutePayload} from '../../../../common/store/route-payload';
+import {LossProvisionConfiguration} from '../../../../services/portfolio/domain/loss-provision-configuration.model';
+
+export const LOAD = type('[Product Loss Provision] Load');
+
+export const UPDATE = type('[Product Loss Provision] Update');
+export const UPDATE_SUCCESS = type('[Product Loss Provision] Update Success');
+export const UPDATE_FAIL = type('[Product Loss Provision] Update Fail');
+
+export interface LossProvisionPayload extends RoutePayload {
+  productIdentifier: string;
+  configuration: LossProvisionConfiguration;
+}
+
+export class LoadAction implements Action {
+  readonly type = LOAD;
+
+  constructor(public payload: LossProvisionConfiguration) { }
+}
+
+export class UpdateLossProvisionAction implements Action {
+  readonly type = UPDATE;
+
+  constructor(public payload: LossProvisionPayload) { }
+}
+
+export class UpdateLossProvisionSuccessAction implements Action {
+  readonly type = UPDATE_SUCCESS;
+
+  constructor(public payload: LossProvisionPayload) { }
+}
+
+export class UpdateLossProvisionFailAction implements Action {
+  readonly type = UPDATE_FAIL;
+
+  constructor(public payload: Error) { }
+}
+
+export type Actions
+  = LoadAction
+  | UpdateLossProvisionAction
+  | UpdateLossProvisionSuccessAction
+  | UpdateLossProvisionFailAction;
diff --git a/src/app/loans/products/store/lossProvision/loss-provision.reducer.ts b/src/app/loans/products/store/lossProvision/loss-provision.reducer.ts
new file mode 100644
index 0000000..5f5d2db
--- /dev/null
+++ b/src/app/loans/products/store/lossProvision/loss-provision.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 provisionActions from './loss-provision.actions';
+import {LossProvisionConfiguration} from '../../../../services/portfolio/domain/loss-provision-configuration.model';
+
+export interface State {
+  configuration: LossProvisionConfiguration;
+  loadedAt: number;
+}
+
+const initialState: State = {
+  configuration: null,
+  loadedAt: null
+};
+
+export function reducer(state: State = initialState, action: provisionActions.Actions): State {
+
+  switch (action.type) {
+
+    case provisionActions.LOAD: {
+      const configuration: LossProvisionConfiguration = action.payload;
+
+      return Object.assign({}, state, {
+        configuration,
+        loadedAt: Date.now()
+      });
+    }
+
+    case provisionActions.UPDATE_SUCCESS: {
+      const configuration: LossProvisionConfiguration = action.payload.configuration;
+
+      return Object.assign({}, state, {
+        configuration,
+        loadedAt: state.loadedAt
+      });
+    }
+
+    default: {
+      return state;
+    }
+  }
+}
+
+export const getLossProvisionConfiguration = (state: State) => state.configuration;
+export const getLossProvisionConfigurationLoadedAt = (state: State) => state.loadedAt;
diff --git a/src/app/loans/products/store/model/fims-product.mapper.ts b/src/app/loans/products/store/model/fims-product.mapper.ts
new file mode 100644
index 0000000..7b1445c
--- /dev/null
+++ b/src/app/loans/products/store/model/fims-product.mapper.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 {Product} from '../../../../services/portfolio/domain/product.model';
+import {FimsProduct} from './fims-product.model';
+
+export function mapToProduct(product: FimsProduct): Product {
+  return Object.assign({}, product, {
+    parameters: JSON.stringify(product.parameters)
+  });
+}
+
+export function mapToFimsProduct(product: Product): FimsProduct {
+  return Object.assign({}, product, {
+    parameters: JSON.parse(product.parameters)
+  });
+}
+
+export function mapToFimsProducts(products: Product[]): FimsProduct[] {
+  const fimsProducts: FimsProduct[] = [];
+
+  for (const product of products){
+    fimsProducts.push(mapToFimsProduct(product));
+  }
+
+  return fimsProducts;
+}
diff --git a/src/app/loans/products/store/model/fims-product.model.ts b/src/app/loans/products/store/model/fims-product.model.ts
new file mode 100644
index 0000000..e366338
--- /dev/null
+++ b/src/app/loans/products/store/model/fims-product.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 {ProductParameters} from '../../../../services/portfolio/domain/individuallending/product-parameters.model';
+import {AccountAssignment} from '../../../../services/portfolio/domain/account-assignment.model';
+import {InterestBasis} from '../../../../services/portfolio/domain/interest-basis.model';
+import {InterestRange} from '../../../../services/portfolio/domain/interest-range.model';
+import {BalanceRange} from '../../../../services/portfolio/domain/balance-range.model';
+import {TermRange} from '../../../../services/portfolio/domain/term-range.model';
+
+/**
+ * Model interface with concrete ProductParameters instead of JSON string.
+ */
+
+export interface FimsProduct {
+  identifier: string;
+  name: string;
+  termRange: TermRange;
+  balanceRange: BalanceRange;
+  interestRange: InterestRange;
+  interestBasis: InterestBasis;
+  patternPackage: string;
+  description: string;
+  accountAssignments: AccountAssignment[];
+  parameters: ProductParameters;
+  currencyCode: string;
+  minorCurrencyUnitDigits: number;
+  enabled?: boolean;
+  createdOn?: string;
+  createdBy?: string;
+  lastModifiedOn?: string;
+  lastModifiedBy?: string;
+}
+
+
diff --git a/src/app/loans/products/store/product.actions.ts b/src/app/loans/products/store/product.actions.ts
new file mode 100644
index 0000000..cad72f3
--- /dev/null
+++ b/src/app/loans/products/store/product.actions.ts
@@ -0,0 +1,186 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 {Error} from '../../../services/domain/error.model';
+import {type} from '../../../store/util';
+import {RoutePayload} from '../../../common/store/route-payload';
+import {FimsProduct} from './model/fims-product.model';
+import {SearchResult} from '../../../common/store/search.reducer';
+import {
+  CreateResourceSuccessPayload,
+  DeleteResourceSuccessPayload,
+  LoadResourcePayload,
+  SelectResourcePayload,
+  UpdateResourceSuccessPayload
+} from '../../../common/store/resource.reducer';
+import {AccountAssignment} from '../../../services/portfolio/domain/account-assignment.model';
+import {FetchRequest} from '../../../services/domain/paging/fetch-request.model';
+
+export const SEARCH = type('[Product] Search');
+export const SEARCH_COMPLETE = type('[Product] Search Complete');
+
+export const LOAD = type('[Product] Load');
+export const SELECT = type('[Product] Select');
+
+export const CREATE = type('[Product] Create');
+export const CREATE_SUCCESS = type('[Product] Create Success');
+export const CREATE_FAIL = type('[Product] Create Fail');
+
+export const UPDATE = type('[Product] Update');
+export const UPDATE_SUCCESS = type('[Product] Update Success');
+export const UPDATE_FAIL = type('[Product] Update Fail');
+
+export const DELETE = type('[Product] Delete');
+export const DELETE_SUCCESS = type('[Product] Delete Success');
+export const DELETE_FAIL = type('[Product] Delete Fail');
+
+export const ENABLE = type('[Product] Enable');
+export const ENABLE_SUCCESS = type('[Product] Enable Success');
+export const ENABLE_FAIL = type('[Product] Enable Fail');
+
+export const RESET_FORM = type('[Product] Reset Form');
+
+export interface ProductRoutePayload extends RoutePayload {
+  product: FimsProduct;
+}
+
+export interface EnableProductPayload {
+  product: FimsProduct;
+  enable: boolean;
+}
+
+export class SearchAction implements Action {
+  readonly type = SEARCH;
+
+  constructor(payload: FetchRequest) { }
+}
+
+export class SearchCompleteAction implements Action {
+  readonly type = SEARCH_COMPLETE;
+
+  constructor(public payload: SearchResult) { }
+}
+
+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 CreateProductAction implements Action {
+  readonly type = CREATE;
+
+  constructor(public payload: ProductRoutePayload) { }
+}
+
+export class CreateProductSuccessAction implements Action {
+  readonly type = CREATE_SUCCESS;
+
+  constructor(public payload: CreateResourceSuccessPayload) { }
+}
+
+export class CreateProductFailAction implements Action {
+  readonly type = CREATE_FAIL;
+
+  constructor(public payload: Error) { }
+}
+
+export class UpdateProductAction implements Action {
+  readonly type = UPDATE;
+
+  constructor(public payload: ProductRoutePayload) { }
+}
+
+export class UpdateProductSuccessAction implements Action {
+  readonly type = UPDATE_SUCCESS;
+
+  constructor(public payload: UpdateResourceSuccessPayload) { }
+}
+
+export class UpdateProductFailAction implements Action {
+  readonly type = UPDATE_FAIL;
+
+  constructor(public payload: Error) { }
+}
+
+export class DeleteProductAction implements Action {
+  readonly type = DELETE;
+
+  constructor(public payload: ProductRoutePayload) { }
+}
+
+export class DeleteProductSuccessAction implements Action {
+  readonly type = DELETE_SUCCESS;
+
+  constructor(public payload: DeleteResourceSuccessPayload) { }
+}
+
+export class DeleteProductFailAction implements Action {
+  readonly type = DELETE_FAIL;
+
+  constructor(public payload: Error) { }
+}
+
+export class EnableProductAction implements Action {
+  readonly type = ENABLE;
+
+  constructor(public payload: EnableProductPayload) { }
+}
+
+export class EnableProductSuccessAction implements Action {
+  readonly type = ENABLE_SUCCESS;
+
+  constructor(public payload: EnableProductPayload) { }
+}
+
+export class EnableProductFailAction implements Action {
+  readonly type = ENABLE_FAIL;
+
+  constructor(public payload: AccountAssignment[]) { }
+}
+
+export class ResetProductFormAction implements Action {
+  readonly type = RESET_FORM;
+
+  constructor() {}
+}
+
+export type Actions
+  = SearchAction
+  | SearchCompleteAction
+  | LoadAction
+  | SelectAction
+  | CreateProductAction
+  | CreateProductSuccessAction
+  | CreateProductFailAction
+  | UpdateProductAction
+  | UpdateProductSuccessAction
+  | UpdateProductFailAction
+  | DeleteProductAction
+  | DeleteProductSuccessAction
+  | DeleteProductFailAction
+  | EnableProductAction
+  | EnableProductSuccessAction
+  | EnableProductFailAction;
diff --git a/src/app/loans/products/store/products.reducer.ts b/src/app/loans/products/store/products.reducer.ts
new file mode 100644
index 0000000..b14430b
--- /dev/null
+++ b/src/app/loans/products/store/products.reducer.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 * as product from './product.actions';
+import {ResourceState} from '../../../common/store/resource.reducer';
+
+export const initialState: ResourceState = {
+  ids: [],
+  entities: {},
+  loadedAt: {},
+  selectedId: null,
+};
+
+export function reducer(state = initialState, action: product.Actions): ResourceState {
+  switch (action.type) {
+
+    case product.ENABLE_SUCCESS: {
+      const product = action.payload.product;
+
+      return {
+        ids: [ ...state.ids ],
+        entities: Object.assign({}, state.entities, {
+          [product.identifier]: Object.assign({}, product, {
+            enabled: action.payload.enable
+          })
+        }),
+        selectedId: state.selectedId,
+        loadedAt: state.loadedAt
+      };
+    }
+
+    default: {
+      return state;
+    }
+  }
+}
diff --git a/src/app/loans/products/store/ranges/effects/notification.effects.ts b/src/app/loans/products/store/ranges/effects/notification.effects.ts
new file mode 100644
index 0000000..606d05d
--- /dev/null
+++ b/src/app/loans/products/store/ranges/effects/notification.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 {NotificationService, NotificationType} from '../../../../../services/notification/notification.service';
+import {Observable} from 'rxjs/Observable';
+import {Action} from '@ngrx/store';
+import {RangeActions} from '../range.actions';
+
+@Injectable()
+export class ProductChargeRangesNotificationEffects {
+
+  @Effect({ dispatch: false })
+  createRangeSuccess$: Observable<Action> = this.actions$
+    .ofType(RangeActions.CREATE_SUCCESS, RangeActions.UPDATE_SUCCESS)
+    .do(() => this.notificationService.send({
+      type: NotificationType.MESSAGE,
+      message: 'Range is going to be saved'
+    }));
+
+  @Effect({ dispatch: false })
+  deleteProductSuccess$: Observable<Action> = this.actions$
+    .ofType(RangeActions.DELETE_SUCCESS)
+    .do(() => this.notificationService.send({
+      type: NotificationType.MESSAGE,
+      message: 'Range is going to be deleted'
+    }));
+
+  constructor(private actions$: Actions, private notificationService: NotificationService) { }
+}
diff --git a/src/app/loans/products/store/ranges/effects/route.effects.ts b/src/app/loans/products/store/ranges/effects/route.effects.ts
new file mode 100644
index 0000000..dc8d53d
--- /dev/null
+++ b/src/app/loans/products/store/ranges/effects/route.effects.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 {Actions, Effect} from '@ngrx/effects';
+import {Router} from '@angular/router';
+import {Observable} from 'rxjs/Observable';
+import {Action} from '@ngrx/store';
+import {RangeActions} from '../range.actions';
+
+@Injectable()
+export class ProductChargeRangesRouteEffects {
+
+  @Effect({ dispatch: false })
+  createRangeSuccess$: Observable<Action> = this.actions$
+    .ofType(RangeActions.CREATE_SUCCESS, RangeActions.UPDATE_SUCCESS)
+    .map(action => action.payload)
+    .do(payload => this.router.navigate(['../'], { relativeTo: payload.data.activatedRoute} ));
+
+  @Effect({ dispatch: false })
+  deleteRangeSuccess$: Observable<Action> = this.actions$
+    .ofType(RangeActions.DELETE_SUCCESS)
+    .map(action => action.payload)
+    .do(payload => this.router.navigate(['../../../../../../../'], { relativeTo: payload.data.activatedRoute} ));
+
+  constructor(private actions$: Actions, private router: Router) { }
+}
diff --git a/src/app/loans/products/store/ranges/effects/service.effects.ts b/src/app/loans/products/store/ranges/effects/service.effects.ts
new file mode 100644
index 0000000..55c8c7d
--- /dev/null
+++ b/src/app/loans/products/store/ranges/effects/service.effects.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 {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 resourceActions from '../../../../../common/store/action-creator/actions';
+import {PortfolioService} from '../../../../../services/portfolio/portfolio.service';
+import {RangeActions} from '../range.actions';
+import {FimsRange} from '../../../../../services/portfolio/domain/range-model';
+
+@Injectable()
+export class ProductChargeRangesApiEffects {
+
+  @Effect()
+  loadAll$: Observable<Action> = this.actions$
+    .ofType(RangeActions.LOAD_ALL)
+    .debounceTime(300)
+    .map((action: resourceActions.LoadAllAction) => action.payload)
+    .switchMap(id => {
+      const nextSearch$ = this.actions$.ofType(RangeActions.LOAD_ALL).skip(1);
+
+      return this.portfolioService.findAllRanges(id)
+        .takeUntil(nextSearch$)
+        .map(resources => RangeActions.loadAllCompleteAction({
+          resources
+        }))
+        .catch(() => of(RangeActions.loadAllCompleteAction({
+          resources: []
+        })));
+    });
+
+  @Effect()
+  createRange$: Observable<Action> = this.actions$
+    .ofType(RangeActions.CREATE)
+    .map((action: resourceActions.ResourceAction<FimsRange>) => action.payload)
+    .mergeMap(payload =>
+      this.portfolioService.createRange(payload.data.productIdentifier, payload.resource)
+        .map(() => RangeActions.createSuccessAction(payload))
+        .catch((error) => of(RangeActions.createFailAction({
+          resource: payload.resource,
+          data: payload.data,
+          error
+        })))
+    );
+
+  @Effect()
+  updateRange$: Observable<Action> = this.actions$
+    .ofType(RangeActions.UPDATE)
+    .map((action: resourceActions.ResourceAction<FimsRange>) => action.payload)
+    .mergeMap(payload =>
+      this.portfolioService.changeRange(payload.data.productIdentifier, payload.resource)
+        .map(() => RangeActions.updateSuccessAction(payload))
+        .catch((error) => of(RangeActions.updateFailAction({
+          resource: payload.resource,
+          data: payload.data,
+          error
+        })))
+    );
+
+  @Effect()
+  deleteRange$: Observable<Action> = this.actions$
+    .ofType(RangeActions.DELETE)
+    .map((action: resourceActions.ResourceAction<FimsRange>) => action.payload)
+    .mergeMap(payload =>
+      this.portfolioService.deleteRange(payload.data.productIdentifier, payload.resource.identifier)
+        .map(() => RangeActions.deleteSuccessAction(payload))
+        .catch((error) => of(RangeActions.deleteFailAction({
+          resource: payload.resource,
+          data: payload.data,
+          error
+        })))
+    );
+
+  constructor(private actions$: Actions, private portfolioService: PortfolioService) { }
+
+}
diff --git a/src/app/loans/products/store/ranges/range.actions.ts b/src/app/loans/products/store/ranges/range.actions.ts
new file mode 100644
index 0000000..a39fb69
--- /dev/null
+++ b/src/app/loans/products/store/ranges/range.actions.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.
+ */
+import {createResourceActions} from '../../../../common/store/action-creator/action-creator';
+import {FimsRange} from '../../../../services/portfolio/domain/range-model';
+
+export const RangeActions = createResourceActions<FimsRange>('Product Charge Range');
diff --git a/src/app/loans/products/store/ranges/ranges.reducer.ts b/src/app/loans/products/store/ranges/ranges.reducer.ts
new file mode 100644
index 0000000..a7a7824
--- /dev/null
+++ b/src/app/loans/products/store/ranges/ranges.reducer.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 {ResourceState} from '../../../../common/store/resource.reducer';
+import {RangeActions} from './range.actions';
+import {idsToHashWithCurrentTimestamp, resourcesToHash} from '../../../../common/store/reducer.helper';
+import {Actions} from '../../../../common/store/action-creator/action-creator';
+import {FimsRange} from '../../../../services/portfolio/domain/range-model';
+
+
+export interface State extends ResourceState {
+  ids: string[];
+  entities: { [id: string]: FimsRange };
+  selectedId: string | null;
+}
+
+export const initialState: State = {
+  ids: [],
+  entities: {},
+  loadedAt: {},
+  selectedId: null,
+};
+
+export function reducer(state = initialState, action: Actions<FimsRange>): ResourceState {
+
+  switch (action.type) {
+
+    case RangeActions.LOAD_ALL: {
+      return initialState;
+    }
+
+    case RangeActions.LOAD_ALL_COMPLETE: {
+      const ranges: FimsRange[] = action.payload.resources;
+
+      const ids = ranges.map(chargeDefinition => chargeDefinition.identifier);
+
+      const entities = resourcesToHash(ranges);
+
+      const loadedAt = idsToHashWithCurrentTimestamp(ids);
+
+      return {
+        ids: [ ...ids ],
+        entities: entities,
+        loadedAt: loadedAt,
+        selectedId: state.selectedId
+      };
+    }
+
+    default: {
+      return state;
+    }
+  }
+}
diff --git a/src/app/loans/products/store/tasks/effects/notification.effects.ts b/src/app/loans/products/store/tasks/effects/notification.effects.ts
new file mode 100644
index 0000000..3b798d5
--- /dev/null
+++ b/src/app/loans/products/store/tasks/effects/notification.effects.ts
@@ -0,0 +1,47 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 {NotificationService, NotificationType} from '../../../../../services/notification/notification.service';
+import {Action} from '@ngrx/store';
+import {Observable} from 'rxjs/Observable';
+import {Actions, Effect} from '@ngrx/effects';
+import {Injectable} from '@angular/core';
+import * as taskActions from '../task.actions';
+
+@Injectable()
+export class ProductTasksNotificationEffects {
+
+  @Effect({dispatch: false})
+  createUpdateCustomerTaskSuccess$: Observable<Action> = this.actions$
+    .ofType(taskActions.CREATE_SUCCESS, taskActions.UPDATE_SUCCESS)
+    .do(() => this.notificationService.send({
+      type: NotificationType.MESSAGE,
+      message: 'Task is going to be created'
+    }));
+
+  @Effect({dispatch: false})
+  deleteCustomerTaskSuccess$: Observable<Action> = this.actions$
+    .ofType(taskActions.DELETE_SUCCESS)
+    .do(() => this.notificationService.send({
+      type: NotificationType.MESSAGE,
+      message: 'Task is going to be deleted'
+    }));
+
+  constructor(private actions$: Actions, private notificationService: NotificationService) {}
+}
+
diff --git a/src/app/loans/products/store/tasks/effects/route.effects.ts b/src/app/loans/products/store/tasks/effects/route.effects.ts
new file mode 100644
index 0000000..2a64003
--- /dev/null
+++ b/src/app/loans/products/store/tasks/effects/route.effects.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 {Injectable} from '@angular/core';
+import {Actions, Effect} from '@ngrx/effects';
+import {Observable} from 'rxjs/Observable';
+import {Action} from '@ngrx/store';
+import * as taskActions from '../task.actions';
+import {Router} from '@angular/router';
+
+@Injectable()
+export class ProductTasksRouteEffects {
+
+  @Effect({ dispatch: false })
+  createUpdateProductTaskSuccess$: Observable<Action> = this.actions$
+    .ofType(taskActions.CREATE_SUCCESS, taskActions.UPDATE_SUCCESS)
+    .map(action => action.payload)
+    .do(payload => this.router.navigate(['../'], { relativeTo: payload.activatedRoute }));
+
+  @Effect({ dispatch: false })
+  deleteProductTaskSuccess$: Observable<Action> = this.actions$
+    .ofType(taskActions.DELETE)
+    .map(action => action.payload)
+    .do(payload => this.router.navigate(['../../'], { relativeTo: payload.activatedRoute }));
+
+  constructor(private actions$: Actions, private router: Router) { }
+
+}
diff --git a/src/app/loans/products/store/tasks/effects/service.effects.ts b/src/app/loans/products/store/tasks/effects/service.effects.ts
new file mode 100644
index 0000000..b368e3d
--- /dev/null
+++ b/src/app/loans/products/store/tasks/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 {Actions, Effect} from '@ngrx/effects';
+import {Observable} from 'rxjs/Observable';
+import {Action} from '@ngrx/store';
+import {of} from 'rxjs/observable/of';
+import * as taskActions from '../task.actions';
+import {PortfolioService} from '../../../../../services/portfolio/portfolio.service';
+
+@Injectable()
+export class ProductTasksApiEffects {
+
+  @Effect()
+  loadAll$: Observable<Action> = this.actions$
+    .ofType(taskActions.LOAD_ALL)
+    .debounceTime(300)
+    .map((action: taskActions.LoadAllAction) => action.payload)
+    .switchMap(id => {
+      const nextSearch$ = this.actions$.ofType(taskActions.LOAD_ALL).skip(1);
+
+      return this.portfolioService.findAllTaskDefinitionsForProduct(id)
+        .takeUntil(nextSearch$)
+        .map(taskDefinitions => new taskActions.LoadAllCompleteAction(taskDefinitions))
+        .catch(() => of(new taskActions.LoadAllCompleteAction([])));
+    });
+
+  @Effect()
+  createTask$: Observable<Action> = this.actions$
+    .ofType(taskActions.CREATE)
+    .map((action: taskActions.CreateTaskAction) => action.payload)
+    .mergeMap(payload =>
+      this.portfolioService.createTaskDefinition(payload.productId, payload.task)
+        .map(() => new taskActions.CreateTaskSuccessAction({
+          resource: payload.task,
+          activatedRoute: payload.activatedRoute
+        }))
+        .catch((error) => of(new taskActions.CreateTaskFailAction(error)))
+    );
+
+  @Effect()
+  updateTask$: Observable<Action> = this.actions$
+    .ofType(taskActions.UPDATE)
+    .map((action: taskActions.UpdateTaskAction) => action.payload)
+    .mergeMap(payload =>
+      this.portfolioService.changeTaskDefinition(payload.productId, payload.task)
+        .map(() => new taskActions.UpdateTaskSuccessAction({
+          resource: payload.task,
+          activatedRoute: payload.activatedRoute
+        }))
+        .catch((error) => of(new taskActions.UpdateTaskFailAction(error)))
+    );
+
+  @Effect()
+  deleteTask$: Observable<Action> = this.actions$
+    .ofType(taskActions.DELETE)
+    .map((action: taskActions.DeleteTaskAction) => action.payload)
+    .mergeMap(payload =>
+      this.portfolioService.deleteTaskDefinition(payload.productId, payload.task.identifier)
+        .map(() => new taskActions.DeleteTaskSuccessAction({
+          resource: payload.task,
+          activatedRoute: payload.activatedRoute
+        }))
+        .catch((error) => of(new taskActions.DeleteTaskFailAction(error)))
+    );
+
+  constructor(private actions$: Actions, private portfolioService: PortfolioService) { }
+}
diff --git a/src/app/loans/products/store/tasks/task.actions.ts b/src/app/loans/products/store/tasks/task.actions.ts
new file mode 100644
index 0000000..3a8ae76
--- /dev/null
+++ b/src/app/loans/products/store/tasks/task.actions.ts
@@ -0,0 +1,155 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 {Error} from '../../../../services/domain/error.model';
+import {type} from '../../../../store/util';
+import {TaskDefinition} from '../../../../services/portfolio/domain/task-definition.model';
+import {RoutePayload} from '../../../../common/store/route-payload';
+import {
+  CreateResourceSuccessPayload,
+  DeleteResourceSuccessPayload,
+  LoadResourcePayload,
+  SelectResourcePayload,
+  UpdateResourceSuccessPayload
+} from '../../../../common/store/resource.reducer';
+
+export const LOAD_ALL = type('[Product Task] Load All');
+export const LOAD_ALL_COMPLETE = type('[Product Task] Load All Complete');
+
+export const LOAD = type('[Product Task] Load');
+export const SELECT = type('[Product Task] Select');
+
+export const CREATE = type('[Product Task] Create');
+export const CREATE_SUCCESS = type('[Product Task] Create Success');
+export const CREATE_FAIL = type('[Product Task] Create Fail');
+
+export const UPDATE = type('[Product Task] Update');
+export const UPDATE_SUCCESS = type('[Product Task] Update Success');
+export const UPDATE_FAIL = type('[Product Task] Update Fail');
+
+export const DELETE = type('[Product Task] Delete');
+export const DELETE_SUCCESS = type('[Product Task] Delete Success');
+export const DELETE_FAIL = type('[Product Task] Delete Fail');
+
+export const RESET_FORM = type('[Product Task] Reset Form');
+
+export interface TaskRoutePayload extends RoutePayload {
+  productId: string;
+  task: TaskDefinition;
+}
+
+export class LoadAllAction implements Action {
+  readonly type = LOAD_ALL;
+
+  constructor(public payload: string) { }
+}
+
+export class LoadAllCompleteAction implements Action {
+  readonly type = LOAD_ALL_COMPLETE;
+
+  constructor(public payload: TaskDefinition[]) { }
+}
+
+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 CreateTaskAction implements Action {
+  readonly type = CREATE;
+
+  constructor(public payload: TaskRoutePayload) { }
+}
+
+export class CreateTaskSuccessAction implements Action {
+  readonly type = CREATE_SUCCESS;
+
+  constructor(public payload: CreateResourceSuccessPayload) { }
+}
+
+export class CreateTaskFailAction implements Action {
+  readonly type = CREATE_FAIL;
+
+  constructor(public payload: Error) { }
+}
+
+export class UpdateTaskAction implements Action {
+  readonly type = UPDATE;
+
+  constructor(public payload: TaskRoutePayload) { }
+}
+
+export class UpdateTaskSuccessAction implements Action {
+  readonly type = UPDATE_SUCCESS;
+
+  constructor(public payload: UpdateResourceSuccessPayload) { }
+}
+
+export class UpdateTaskFailAction implements Action {
+  readonly type = UPDATE_FAIL;
+
+  constructor(public payload: Error) { }
+}
+
+export class DeleteTaskAction implements Action {
+  readonly type = DELETE;
+
+  constructor(public payload: TaskRoutePayload) { }
+}
+
+export class DeleteTaskSuccessAction implements Action {
+  readonly type = DELETE_SUCCESS;
+
+  constructor(public payload: DeleteResourceSuccessPayload) { }
+}
+
+export class DeleteTaskFailAction implements Action {
+  readonly type = DELETE_FAIL;
+
+  constructor(public payload: Error) { }
+}
+
+export class ResetTaskFormAction implements Action {
+  readonly type = RESET_FORM;
+
+  constructor() {}
+}
+
+export type Actions
+  = LoadAllAction
+  | LoadAllCompleteAction
+  | LoadAction
+  | SelectAction
+  | CreateTaskAction
+  | CreateTaskSuccessAction
+  | CreateTaskFailAction
+  | UpdateTaskAction
+  | UpdateTaskSuccessAction
+  | UpdateTaskFailAction
+  | DeleteTaskAction
+  | DeleteTaskSuccessAction
+  | DeleteTaskFailAction
+  | ResetTaskFormAction;
diff --git a/src/app/loans/products/store/tasks/tasks.reducer.ts b/src/app/loans/products/store/tasks/tasks.reducer.ts
new file mode 100644
index 0000000..e9cee51
--- /dev/null
+++ b/src/app/loans/products/store/tasks/tasks.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 * as task from './task.actions';
+import {TaskDefinition} from '../../../../services/portfolio/domain/task-definition.model';
+import {ResourceState} from '../../../../common/store/resource.reducer';
+import {idsToHashWithCurrentTimestamp, resourcesToHash} from '../../../../common/store/reducer.helper';
+
+export const initialState: ResourceState = {
+  ids: [],
+  entities: {},
+  loadedAt: {},
+  selectedId: null
+};
+
+export function reducer(state = initialState, action: task.Actions): ResourceState {
+
+  switch (action.type) {
+
+    case task.LOAD_ALL: {
+      return initialState;
+    }
+
+    case task.LOAD_ALL_COMPLETE: {
+      const taskDefinitions: TaskDefinition[] = action.payload;
+
+      const ids = taskDefinitions.map(task => task.identifier);
+
+      const entities = resourcesToHash(taskDefinitions);
+
+      const loadedAt = idsToHashWithCurrentTimestamp(ids);
+
+      return {
+        ids: [ ...ids ],
+        entities: entities,
+        loadedAt: loadedAt,
+        selectedId: state.selectedId
+      };
+    }
+
+    default: {
+      return state;
+    }
+  }
+}
diff --git a/src/app/login/login.component.html b/src/app/login/login.component.html
index 4560c05..8aea8b0 100644
--- a/src/app/login/login.component.html
+++ b/src/app/login/login.component.html
@@ -1,3 +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.
+-->
+
 <div class="main-background" style="background-image:url('assets/Background.png');">
   <div style="margin-top:5%; margin-left:25%;">
     <div class="welcome">
diff --git a/src/app/login/login.component.spec.ts b/src/app/login/login.component.spec.ts
index aa23f4f..e3c8fb5 100644
--- a/src/app/login/login.component.spec.ts
+++ b/src/app/login/login.component.spec.ts
@@ -1,3 +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.
+ */
 import {async, ComponentFixture, inject, TestBed} from '@angular/core/testing';
 import {LoginComponent} from './login.component';
 import {Observable} from 'rxjs/Observable';
@@ -146,4 +164,3 @@
   })));
 
 });
-
diff --git a/src/app/login/login.component.ts b/src/app/login/login.component.ts
index 6b92113..69e5038 100644
--- a/src/app/login/login.component.ts
+++ b/src/app/login/login.component.ts
@@ -1,24 +1,40 @@
-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';
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, OnDestroy, OnInit} from '@angular/core';
+import {ITdLoadingConfig, LoadingType, TdLoadingService} from '@covalent/core';
+import {TranslateService} from '@ngx-translate/core';
+import {FormBuilder, FormGroup, Validators} from '@angular/forms';
 import * as fromRoot from '../store';
-import { TranslateService } from '@ngx-translate/core';
-import {FormsModule, ReactiveFormsModule} from '@angular/forms';
+import {Store} from '@ngrx/store';
+import {LOGIN} from '../store/security/security.actions';
+import {Subscription} from 'rxjs/Subscription';
 import {TRANSLATE_STORAGE_KEY} from '../common/i18n/translate';
+import {Observable} from 'rxjs/Observable';
+import {MatSelectChange} from '@angular/material';
 
 @Component({
-  selector: 'app-login',
+  selector: 'fims-login',
   templateUrl: './login.component.html',
   styleUrls: ['./login.component.scss'],
-  
 })
-export class LoginComponent implements OnInit {
+export class LoginComponent implements OnInit, OnDestroy {
+
   private loadingSubscription: Subscription;
 
   currentLanguage: string;
@@ -27,9 +43,8 @@
     {id: 'en', label: 'Welcome to fims'},
     {id: 'es', label: 'Bienvenido a fims'}
   ];
-  
- hide= true;
- form: FormGroup;
+
+  form: FormGroup;
 
   error$: Observable<string>;
 
diff --git a/src/app/login/login.module.ts b/src/app/login/login.module.ts
new file mode 100644
index 0000000..9d82311
--- /dev/null
+++ b/src/app/login/login.module.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 {NgModule} from '@angular/core';
+import {LoginComponent} from './login.component';
+import {LoginRoutes} from './login.routing';
+import {RouterModule} from '@angular/router';
+import {FormsModule, ReactiveFormsModule} from '@angular/forms';
+import {MatButtonModule, MatCardModule, MatIconModule, MatInputModule, MatSelectModule, MatTooltipModule, MatFormFieldModule} from '@angular/material';
+import {CommonModule} from '@angular/common';
+import {TranslateModule} from '@ngx-translate/core';
+import {CovalentLoadingModule} from '@covalent/core';
+
+@NgModule({
+  imports: [
+    RouterModule.forChild(LoginRoutes),
+    TranslateModule,
+    CommonModule,
+    FormsModule,
+    ReactiveFormsModule,
+    MatIconModule,
+    MatCardModule,
+    MatInputModule,
+    MatButtonModule,
+    MatSelectModule,
+    MatTooltipModule,
+    MatFormFieldModule,
+    CovalentLoadingModule
+  ],
+  declarations: [
+    LoginComponent
+  ]
+})
+export class LoginModule {}
diff --git a/src/app/login/login.routing.ts b/src/app/login/login.routing.ts
new file mode 100644
index 0000000..2bfd0b1
--- /dev/null
+++ b/src/app/login/login.routing.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 {Routes} from '@angular/router';
+import {LoginComponent} from './login.component';
+
+export const LoginRoutes: Routes = [
+  {
+    path: '',
+    component: LoginComponent,
+    pathMatch: 'full'
+  }
+];
diff --git a/src/app/main/access.denied.component.html b/src/app/main/access.denied.component.html
new file mode 100644
index 0000000..052566f
--- /dev/null
+++ b/src/app/main/access.denied.component.html
@@ -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.
+-->
+
+<div style="padding: 20%;text-align:center;" translate>
+  Sorry, but you are not allowed to see this page.
+</div>
diff --git a/src/app/main/access.denied.component.ts b/src/app/main/access.denied.component.ts
new file mode 100644
index 0000000..144a4ee
--- /dev/null
+++ b/src/app/main/access.denied.component.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 {Component} from '@angular/core';
+
+@Component({
+  templateUrl: './access.denied.component.html'
+})
+export class AccessDeniedComponent {}
diff --git a/src/app/main/main.component.html b/src/app/main/main.component.html
new file mode 100644
index 0000000..758205a
--- /dev/null
+++ b/src/app/main/main.component.html
@@ -0,0 +1,140 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="flex-container" fullscreen>
+      <mat-progress-bar *ngIf="isLoading$ | async" mode="indeterminate"></mat-progress-bar>
+  <mat-toolbar class="my-toolbar">
+    <div>
+      <div class="search2">
+    <ul id="menu" class="menu1">
+      <li>
+        <button mat-icon-button (click)="sidenav.toggle()" class="dropdown-button">
+          <mat-icon class="fineract-toolicon">menu</mat-icon>
+        </button>
+      </li>
+      <li>
+        
+        <span style="color:purple">{{tenant$ | async}}</span>
+      </li>
+      <li>
+       
+        <button mat-button [matMenuTriggerFor]="client" class="dropdown-button"> 
+          <mat-icon>group</mat-icon>Clients
+          <mat-icon>arrow_drop_down</mat-icon>
+        </button>
+        <mat-menu #client="matMenu">
+          <a mat-menu-item [routerLink]="['/customers']">Client</a>
+          <a mat-menu-item [routerLink]="['/centers']">Group</a>
+          <a mat-menu-item [routerLink]="['/']">Center</a>
+        </mat-menu>
+      </li>
+      <li>
+        <button mat-button  [routerLink]="['/accounting']"  class="dropdown-button">Accounting
+        </button>
+      </li>
+      <li>
+        <button mat-button [matMenuTriggerFor]="report" class="dropdown-button">Reports
+          <mat-icon>arrow_drop_down</mat-icon>
+        </button>
+        <mat-menu #report="matMenu">
+          <a mat-menu-item [routerLink]="['/reporting']">Member</a>
+          <a mat-menu-item [routerLink]="['/reporting/deposits']">Deposit</a>
+         
+        </mat-menu>
+      </li>
+      <li>
+       
+        <button mat-button [matMenuTriggerFor]="admin" class="dropdown-button">
+           <mat-icon>build</mat-icon>Admin
+          <mat-icon>arrow_drop_down</mat-icon>
+        </button>
+        <mat-menu #admin="matMenu">
+          <button mat-menu-item [routerLink]="['/employees']">Users</button>
+          <button mat-menu-item [routerLink]="['/teller']">Teller</button>
+          <button mat-menu-item [routerLink]="['/roles']">Roles/Permissions</button>
+          <button mat-menu-item [routerLink]="['/navbar/admin/products']">Products</button>
+          
+        </mat-menu>
+      </li>
+  
+    </ul>
+    </div>
+    <div class="search1">
+    <ul id="menu">
+      <li>
+        <input matInput placeholder="click or press alt + X to Search" type="text" class="fineract-toolsearch">
+      </li>
+     
+      <li class="dropdown-button">
+        <button mat-icon-button [routerLink]="['/navbar/notification']">
+          <mat-icon>notifications</mat-icon>
+        </button>
+      </li>
+      <li>
+        <button mat-button [matMenuTriggerFor]="mifos" class="dropdown-button">Fineract CN
+          <mat-icon>arrow_drop_down</mat-icon>
+        </button>
+        <mat-menu #mifos="matMenu">
+            <button mat-menu-item [disabled]="true">
+                <mat-icon>account_circle</mat-icon>
+                <span>{{username$ | async}}</span>
+              </button>
+              <hr>
+              <button mat-menu-item (click)="goToSettings()">
+                <mat-icon>settings</mat-icon>
+                <span>{{'Settings' | translate}}</span>
+              </button>
+              <button mat-menu-item (click)="logout()">
+                <mat-icon>exit_to_app</mat-icon>
+                <span>{{'Sign Out' | translate}}</span>
+              </button>
+        </mat-menu>
+      </li>
+    </ul>
+    </div>
+    </div>
+  </mat-toolbar>
+  </div>
+  
+  <mat-sidenav-container class="app-container" style="background-image:url('assets/Background.png');">
+    <mat-sidenav #sidenav class="app-sidenav mat-elevation-z2" mode="side" opened="true">
+     
+      <mat-nav-list>
+          <ng-container *ngFor="let menuItem of menuItems; let i = index">
+            <a mat-list-item [routerLink]="[menuItem.routerLink]" routerLinkActive="active" *hasPermission="menuItem.permission">
+              <mat-icon matListAvatar>{{menuItem.icon}}</mat-icon>
+              <h3 matLine>{{menuItem.title | translate}}</h3>
+              <p matLine>{{menuItem.description | translate}}</p>
+            </a>
+            <mat-divider *ngIf="i === 0"></mat-divider>
+          </ng-container>
+        </mat-nav-list>
+  
+    </mat-sidenav>
+    <div class="app-sidenav-content">
+      <div class="wrapper" >
+        <router-outlet></router-outlet>
+      </div>
+  
+    </div>
+    <fims-notification></fims-notification>
+  </mat-sidenav-container>
+ 
+  
+  
+  
+  
\ No newline at end of file
diff --git a/src/app/main/main.component.scss b/src/app/main/main.component.scss
new file mode 100644
index 0000000..94b91a5
--- /dev/null
+++ b/src/app/main/main.component.scss
@@ -0,0 +1,109 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+ .dropdown-button{
+
+  color:#3498dB;
+}
+::placeholder{
+  color: #3498dB;
+  font-size: 12px;
+}
+.app-container {
+  flex: 1;
+  width: 100%;
+  height:100%;
+  min-width: 100%;
+  position:fixed;  
+}
+.my-toolbar{
+  height:20px;
+  background-color: #363636;
+  opacity:100%;
+}
+
+.app-sidenav-content{
+  display:flex;
+  min-height:100%;
+  
+  flex-direction:column;
+  opacity: 40%;
+ 
+}
+.app-sidenav{
+  display:flex;
+  height:100%;
+  flex-direction:column;
+  
+  opacity:40%;
+  
+  
+}
+mat-list > a{
+
+  color:white;
+}
+.fineract-toolicon{
+  color:white;
+}
+.wrapper{
+  margin:50px;
+  
+}
+ul#menu li{
+  display: inline;
+}
+.search1{
+  
+  margin-left:8%;
+  
+}
+.fineract-toolsearch{
+  width:230px;
+  max-height: 10px;
+}
+
+
+.fineract-logo{
+  width:40px;
+  height:37px;
+  vertical-align: bottom;
+  
+}
+.logo-title{
+  vertical-align: middle;
+  font-size:11px;
+  color:white;
+}
+
+.search1,.search2{
+  display: inline-block;
+}
+
+.menu1{
+  padding:0;
+}
+
+.test{
+  padding-left:0;
+  padding-right: 0;
+}
+.flex-container{
+  height: 45px;
+}
diff --git a/src/app/main/main.component.ts b/src/app/main/main.component.ts
new file mode 100644
index 0000000..9e74fa9
--- /dev/null
+++ b/src/app/main/main.component.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 {AfterViewInit, Component, OnDestroy, OnInit, ViewChild} from '@angular/core';
+import {ActivatedRoute, NavigationEnd, Router, RouterState} from '@angular/router';
+import {Title} from '@angular/platform-browser';
+import {Action, HttpClient} from '../services/http/http.service';
+import {Store} from '@ngrx/store';
+import * as fromRoot from '../store';
+import {LOGOUT} from '../store/security/security.actions';
+import {Observable} from 'rxjs/Observable';
+import {FimsPermission} from '../services/security/authz/fims-permission.model';
+import {CountryService} from '../services/country/country.service';
+import {TdMediaService} from '@covalent/core';
+import {Subscription} from 'rxjs/Subscription';
+import {MatSidenav} from '@angular/material';
+
+interface MenuItem {
+  permission?: FimsPermission;
+  icon: string;
+  title: string;
+  description?: string;
+  routerLink: string;
+}
+
+@Component({
+  selector: 'fims-main',
+  templateUrl: './main.component.html',
+  styleUrls: ['main.component.scss']
+})
+export class MainComponent implements OnInit, OnDestroy, AfterViewInit {
+
+  private routerEventSubscription: Subscription;
+
+  @ViewChild(MatSidenav) sidenav: MatSidenav;
+
+  menuItems: MenuItem[] = [
+    {title: 'Quick access', icon: 'dashboard', routerLink: '/quickAccess'},
+    {
+      title: 'Offices',
+      description: 'Manage offices',
+      icon: 'store',
+      routerLink: '/offices',
+      permission: {id: 'office_offices', accessLevel: 'READ'}
+    },
+    {
+      title: 'Roles/Permissions',
+      description: 'Manage roles and permissions',
+      icon: 'https',
+      routerLink: '/roles',
+      permission: {id: 'identity_roles', accessLevel: 'READ'}
+    },
+    {
+      title: 'Employees',
+      description: 'Manage employees',
+      icon: 'group',
+      routerLink: '/employees',
+      permission: {id: 'office_employees', accessLevel: 'READ'}
+    },
+    {
+      title: 'Accounting',
+      description: 'Manage ledger accounts',
+      icon: 'receipt',
+      routerLink: '/accounting',
+      permission: {id: 'accounting_ledgers', accessLevel: 'READ'}
+    },
+    {
+      title: 'Member',
+      description: 'Manage members',
+      icon: 'face',
+      routerLink: '/customers',
+      permission: {id: 'customer_customers', accessLevel: 'READ'}
+    },
+    {
+      title: 'Loan products',
+      description: 'Manage loan products',
+      icon: 'credit_card',
+      routerLink: '/loans',
+      permission: {id: 'portfolio_products', accessLevel: 'READ'}
+    },
+    {
+      title: 'Deposit',
+      description: 'Account management',
+      icon: 'attach_money',
+      routerLink: '/deposits',
+      permission: {id: 'deposit_definitions', accessLevel: 'READ'}
+    },
+    {
+      title: 'Teller',
+      description: 'Teller management',
+      icon: 'person',
+      routerLink: '/teller',
+      permission: {id: 'teller_operations', accessLevel: 'READ'}
+    },
+    {
+      title: 'Reports',
+      description: 'View reports',
+      icon: 'show_chart',
+      routerLink: '/reports',
+      permission: {id: 'reporting_management', accessLevel: 'READ'}
+    },
+    {
+      title: 'Collection Sheet',
+      description: 'View collection sheet',
+      icon: 'event_note',
+      routerLink: '/collection',
+      permission: {id: 'accounting_ledgers', accessLevel: 'READ'}
+
+    },
+    {
+      title: 'Centers',
+      description: 'View centers',
+      icon: 'event_note',
+      routerLink: '/centers',
+      permission: {id: 'office_employees', accessLevel: 'READ'}
+
+    },
+   
+  ];
+
+  isLoading$: Observable<boolean>;
+
+  isOpened$: Observable<boolean>;
+
+  tenant$: Observable<string>;
+
+  username$: Observable<string>;
+
+  constructor(private router: Router, private titleService: Title, private httpClient: HttpClient, private countryService: CountryService,
+              private store: Store<fromRoot.State>, private media: TdMediaService) {}
+
+  ngOnInit(): void {
+    this.routerEventSubscription = this.router.events
+      .filter(event => event instanceof NavigationEnd)
+      .map(() => this.getTitle(this.router.routerState, this.router.routerState.root).join(' - '))
+      .subscribe(title => this.titleService.setTitle(title));
+
+    this.tenant$ = this.store.select(fromRoot.getTenant);
+    this.username$ = this.store.select(fromRoot.getUsername);
+    this.isOpened$ = this.media.registerQuery('gt-md');
+
+    this.countryService.init();
+  }
+
+  ngOnDestroy(): void {
+    this.routerEventSubscription.unsubscribe();
+  }
+
+  ngAfterViewInit(): void {
+    this.isLoading$ = this.httpClient.process
+      .debounceTime(1000)
+      .map((action: Action) => action === Action.QueryStart);
+
+    this.media.broadcast();
+  }
+
+  getTitle(state: RouterState, parent: ActivatedRoute): string[] {
+    const data = [];
+
+    if (parent && parent.snapshot.data) {
+      const dataProperty: any = parent.snapshot.data;
+
+      if (dataProperty.title) {
+        data.push(dataProperty.title);
+      }
+    }
+
+    if (state && parent) {
+      data.push(... this.getTitle(state, parent.firstChild));
+    }
+
+    return data;
+  }
+
+  toggleSideNav(): void {
+    this.sidenav.toggle(!this.sidenav.opened);
+  }
+
+  logout(): void {
+    this.store.dispatch({ type: LOGOUT });
+  }
+
+  goToSettings(): void {
+    this.router.navigate(['/user']);
+  }
+}
diff --git a/src/app/main/main.module.ts b/src/app/main/main.module.ts
new file mode 100644
index 0000000..dd6b56e
--- /dev/null
+++ b/src/app/main/main.module.ts
@@ -0,0 +1,73 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 {NgModule} from '@angular/core';
+import {RouterModule} from '@angular/router';
+import {MainComponent} from './main.component';
+import {MainRoutes, mainRoutingProviders} from './main.routing';
+import {AccessDeniedComponent} from './access.denied.component';
+import {FimsSharedModule} from '../common/common.module';
+import {NotificationComponent} from './notification.component';
+import {QuickAccessComponent} from '../quickAccess/quick-access.component';
+import {
+  MatButtonModule,
+  MatCardModule,
+  MatIconModule,
+  MatListModule,
+  MatMenuModule,
+  MatProgressBarModule,
+  MatSidenavModule,
+  MatToolbarModule,
+  MatTooltipModule,
+  MatFormFieldModule
+} from '@angular/material';
+import {TranslateModule} from '@ngx-translate/core';
+import {CommonModule} from '@angular/common';
+import {CovalentLayoutModule, CovalentMediaModule} from '@covalent/core';
+
+@NgModule({
+  imports: [
+    RouterModule.forChild(MainRoutes),
+    FimsSharedModule,
+    TranslateModule,
+    CommonModule,
+    MatButtonModule,
+    MatToolbarModule,
+    MatSidenavModule,
+    MatListModule,
+    MatProgressBarModule,
+    MatIconModule,
+    MatMenuModule,
+    MatTooltipModule,
+    MatCardModule,
+    MatFormFieldModule,
+    CovalentLayoutModule,
+    CovalentMediaModule
+  ],
+  declarations: [
+    MainComponent,
+    QuickAccessComponent,
+    AccessDeniedComponent,
+    NotificationComponent
+  ],
+  providers: [
+    mainRoutingProviders
+  ],
+})
+export class MainModule {}
diff --git a/src/app/main/main.routing.ts b/src/app/main/main.routing.ts
new file mode 100644
index 0000000..d94d87b
--- /dev/null
+++ b/src/app/main/main.routing.ts
@@ -0,0 +1,55 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 {Routes} from '@angular/router';
+import {MainComponent} from './main.component';
+import {ChangePasswordGuard} from '../services/security/change.password.service';
+import {AccessDeniedComponent} from './access.denied.component';
+import {PermissionGuard} from '../services/security/authz/permission.guard';
+import {QuickAccessComponent} from '../quickAccess/quick-access.component';
+
+export const MainRoutes: Routes = [
+  {
+    path: '', component: MainComponent, canActivateChild: [ChangePasswordGuard, PermissionGuard], children: [
+      { path: '', redirectTo: '/quickAccess', pathMatch: 'full'},
+      { path: 'quickAccess', component: QuickAccessComponent, data: { title: 'Quick access' } },
+      { path: 'offices', loadChildren: './../offices/office.module#OfficeModule' },
+      { path: 'employees', loadChildren: './../employees/employee.module#EmployeeModule' },
+      { path: 'roles', loadChildren: './../roles/role.module#RoleModule' },
+      { path: 'user', loadChildren: './../user/user.module#UserModule' },
+      { path: 'customers', loadChildren: './../customers/customer.module#CustomerModule' },
+      { path: 'accounting', loadChildren: './../accounting/accounting.module#AccountingModule' },
+      { path: 'loans', loadChildren: './../loans/products/product.module#ProductModule' },
+      { path: 'deposits', loadChildren: './../depositAccount/deposit-account.module#DepositAccountModule' },
+      { path: 'teller', loadChildren: './../teller/teller.module#TellerModule' },
+      { path: 'reports', loadChildren: './../reporting/reporting.module#ReportingModule' },
+      { path: 'denied', component: AccessDeniedComponent, data: { title: 'Not allowed' }},
+      { path: 'centers', loadChildren: './../centers/center.module#CenterModule' }
+    
+    ]
+  },
+  {
+    path: 'changePassword', loadChildren: './../user/user.module#UserModule', data: { title: 'Change password' }
+  }
+
+];
+
+export const mainRoutingProviders: any[] = [
+  ChangePasswordGuard,
+  PermissionGuard
+];
diff --git a/src/app/main/notification.component.ts b/src/app/main/notification.component.ts
new file mode 100644
index 0000000..8abd7cf
--- /dev/null
+++ b/src/app/main/notification.component.ts
@@ -0,0 +1,114 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 {AfterViewInit, Component, OnDestroy, OnInit, ViewContainerRef} from '@angular/core';
+import {NotificationEvent, NotificationService, NotificationType} from '../services/notification/notification.service';
+import {TranslateService} from '@ngx-translate/core';
+import {HttpClient} from '../services/http/http.service';
+import {TdDialogService} from '@covalent/core';
+import {Subscription} from 'rxjs/Subscription';
+import {MatSnackBar, MatSnackBarConfig, MatSnackBarRef} from '@angular/material';
+
+@Component({
+  selector: 'fims-notification',
+  template: ''
+})
+export class NotificationComponent implements OnInit, OnDestroy, AfterViewInit {
+
+  private notificationSubscription: Subscription;
+  private errorSubscription: Subscription;
+
+  constructor(private notificationService: NotificationService, private translate: TranslateService, private httpClient: HttpClient,
+              private snackBar: MatSnackBar, private viewContainerRef: ViewContainerRef, private dialogService: TdDialogService) {}
+
+  ngOnInit(): void {
+    const config: MatSnackBarConfig = new MatSnackBarConfig();
+    config.viewContainerRef = this.viewContainerRef;
+    this.notificationSubscription = this.notificationService.notifications$
+      .subscribe((notification: NotificationEvent) => this.showNotification(notification, config));
+  }
+
+  ngOnDestroy(): void {
+    this.notificationSubscription.unsubscribe();
+    this.errorSubscription.unsubscribe();
+  }
+
+  ngAfterViewInit(): void {
+    this.errorSubscription = this.httpClient.error.subscribe((error: any) => this.showError(error));
+  }
+
+  private showNotification(notification: NotificationEvent, config: MatSnackBarConfig): void {
+    switch (notification.type) {
+      case NotificationType.MESSAGE:
+        this.showMessage(notification.message, config);
+        break;
+      case NotificationType.ALERT:
+        this.showAlert(notification.title, notification.message);
+        break;
+      default:
+        break;
+    }
+  }
+
+  private showError(error: any): void {
+    switch (error.status) {
+      case 400:
+        this.showAlert('Unexpected error',
+          'We are very sorry, it seems the request you sent could not be accepted by our servers.'
+        );
+        break;
+      case 404:
+        this.showAlert('Resource not available',
+          'It seems the resource you requested is either not available or you don\'t have the permission to access it.'
+        );
+        break;
+      case 504:
+      case 500:
+        this.showAlert('Service not available',
+          'We are very sorry, it seems there is a problem with our servers. Please contact your administrator if the problem occurs.'
+        );
+        break;
+      default:
+        break;
+    }
+  }
+
+  private showMessage(message: string, config: MatSnackBarConfig): void {
+    this.translate.get(message)
+      .take(1)
+      .subscribe((result) => {
+        const snackBarRef: MatSnackBarRef<any> = this.snackBar.open(result, '', config);
+        setTimeout(() => snackBarRef.dismiss(), 3000);
+      });
+  }
+
+  private showAlert(title: string = '', message: string): void {
+    this.translate.get([title, message, 'OK'])
+      .take(1)
+      .subscribe((result) => {
+        this.dialogService.openAlert({
+          message: result[message],
+          viewContainerRef: this.viewContainerRef,
+          title: result[title],
+          closeButton: result['OK']
+        });
+    });
+
+  }
+}
diff --git a/src/app/navbar/navbar.component.html b/src/app/navbar/navbar.component.html
deleted file mode 100644
index a793414..0000000
--- a/src/app/navbar/navbar.component.html
+++ /dev/null
@@ -1,167 +0,0 @@
-
-<div class="flex-container" fullscreen>
-<mat-toolbar class="my-toolbar">
-  <div>
-    <div class="search2">
-  <ul id="menu" class="menu1">
-    <li>
-      <button mat-icon-button (click)="sidenav.toggle()" class="dropdown-button">
-        <mat-icon class="fineract-toolicon">menu</mat-icon>
-      </button>
-    </li>
-    <li>
-      
-      <span style="color:purple">Tenant Name</span>
-    </li>
-    <li>
-     
-      <button mat-button [matMenuTriggerFor]="client" class="dropdown-button"> 
-        <mat-icon>group</mat-icon>Clients
-        <mat-icon>arrow_drop_down</mat-icon>
-      </button>
-      <mat-menu #client="matMenu">
-        <a mat-menu-item [routerLink]="['/navbar/manage_members']">Client</a>
-        <a mat-menu-item [routerLink]="['/navbar/group']">Group</a>
-        <a mat-menu-item [routerLink]="['/navbar/center']">Center</a>
-      </mat-menu>
-    </li>
-    <li>
-      <button mat-button  [routerLink]="['/navbar/accounting']"  class="dropdown-button">Accounting
-      </button>
-    </li>
-    <li>
-      <button mat-button [matMenuTriggerFor]="report" class="dropdown-button">Reports
-        <mat-icon>arrow_drop_down</mat-icon>
-      </button>
-      <mat-menu #report="matMenu">
-        <a mat-menu-item [routerLink]="['/navbar/reports/allreports']">All</a>
-        <a mat-menu-item [routerLink]="['/navbar/reports/clients']">Clients</a>
-        <a mat-menu-item [routerLink]="['/navbar/reports/loans']">Loans</a>
-        <a mat-menu-item [routerLink]="['/navbar/reports/savings']">Savings</a>
-        <a mat-menu-item [routerLink]="['/navbar/reports/funds']">Funds</a>
-        <a mat-menu-item [routerLink]="['/navbar/reports/accounting']">Accounting</a>
-        <a mat-menu-item [routerLink]="['/navbar/reports/xbrl']">XBRL</a>
-      </mat-menu>
-    </li>
-    <li>
-     
-      <button mat-button [matMenuTriggerFor]="admin" class="dropdown-button">
-         <mat-icon>build</mat-icon>Admin
-        <mat-icon>arrow_drop_down</mat-icon>
-      </button>
-      <mat-menu #admin="matMenu">
-        <button mat-menu-item [routerLink]="['/navbar/user']">Users</button>
-        <button mat-menu-item [routerLink]="['/navbar/organisation']">Organisations</button>
-        <button mat-menu-item [routerLink]="['/navbar/system']">Systems</button>
-        <button mat-menu-item [routerLink]="['/navbar/admin/products']">Products</button>
-        <button mat-menu-item [routerLink]="['/navbar/admin/templates']">Template</button>
-
-      </mat-menu>
-    </li>
-
-  </ul>
-  </div>
-  <div class="search1">
-  <ul id="menu">
-    <li>
-      <input placeholder="click or press alt + X to Search" type="text" class="fineract-toolsearch">
-    </li>
-   
-    <li class="dropdown-button">
-      <button mat-icon-button [routerLink]="['/navbar/notification']">
-        <mat-icon>notifications</mat-icon>
-      </button>
-    </li>
-    <li>
-      <button mat-button [matMenuTriggerFor]="mifos" class="dropdown-button">Fineract CN
-        <mat-icon>arrow_drop_down</mat-icon>
-      </button>
-      <mat-menu #mifos="matMenu">
-        <button mat-menu-item><mat-icon>help</mat-icon>Help</button>
-        <button mat-menu-item><mat-icon>person</mat-icon>Profile</button>
-        <button mat-menu-item><mat-icon>settings</mat-icon>Settings</button>
-        <button mat-menu-item (click)="onLogout()"><mat-icon>settings_power</mat-icon>Log Out</button>
-
-      </mat-menu>
-    </li>
-  </ul>
-  </div>
-  </div>
-</mat-toolbar>
-</div>
-
-<mat-sidenav-container class="app-container" style="background-image:url('assets/Background.png');">
-  <mat-sidenav #sidenav class="app-sidenav mat-elevation-z2" mode="side" opened="true">
-    <mat-toolbar>
-      <button mat-button [routerLink]="['/navbar/dashboard']">Quick Access</button>
-    </mat-toolbar>
-    <mat-list>
-     
-      <a mat-list-item href="badges.html">
-        <button mat-button>
-          <mat-icon>event_note</mat-icon>
-          collection sheet</button>
-      </a>
-      <mat-divider></mat-divider>
-      <a mat-list-item [routerLink]="['/navbar/individualcollection']">
-        <button mat-button>
-          <mat-icon>event_note</mat-icon>
-          individual collection </button>
-      </a>
-     
-      <mat-divider></mat-divider>
-      <a mat-list-item [routerLink]="['/navbar/addjournalentry']">
-        <button mat-button>
-          <mat-icon>add</mat-icon>
-          add journal entry</button>
-      </a>
-      <mat-divider></mat-divider>
-      <a mat-list-item [routerLink]="['/navbar/accountclosure']">
-        <button mat-button>
-          <mat-icon>folder</mat-icon>
-          closing entry</button>
-      </a>
-      <mat-divider></mat-divider>
-      <a mat-list-item [routerLink]="['/navbar/chartaccount']">
-        <button mat-button>
-          <mat-icon>insert_chart</mat-icon>
-          chart of account</button>
-      </a>
-      <mat-divider></mat-divider>
-      <a mat-list-item [routerLink]="['/navbar/createclient']">
-        <button mat-button>
-          <mat-icon>person_add</mat-icon>
-          client</button>
-      </a>
-      <mat-divider></mat-divider>
-      <a mat-list-item [routerLink]="['/navbar/creategroup']">
-        <button mat-button>
-          <mat-icon>group_add</mat-icon>
-          group</button>
-      </a>
-      <mat-divider></mat-divider>
-      <a mat-list-item [routerLink]="['/navbar/createcenter']">
-        <button mat-button>
-          <mat-icon>place</mat-icon>
-          center</button>
-      </a>
-      <mat-divider></mat-divider>
-      <a mat-list-item href="badges.html">
-        <button mat-button>
-          <mat-icon>help</mat-icon>
-          help</button>
-      </a>
-
-
-    </mat-list>
-
-
-  </mat-sidenav>
-  <div class="app-sidenav-content">
-    <div class="wrapper" >
-      <router-outlet></router-outlet>
-    </div>
-
-  </div>
-</mat-sidenav-container>
-
diff --git a/src/app/navbar/navbar.component.scss b/src/app/navbar/navbar.component.scss
deleted file mode 100644
index 1f920b3..0000000
--- a/src/app/navbar/navbar.component.scss
+++ /dev/null
@@ -1,86 +0,0 @@
-.dropdown-button{
-
-    color:#3498dB;
-}
-::placeholder{
-    color: #3498dB;
-}
-.app-container {
-    flex: 1;
-    width: 100%;
-    height:100%;
-    min-width: 100%;
-    position:fixed;  
-  }
-  .my-toolbar{
-      height:37px;
-      background-color: #363636;
-      opacity:100%;
-  }
-  
-
-.app-sidenav-content{
-    display:flex;
-    min-height:100%;
-    
-    flex-direction:column;
-    opacity: 40%;
-   
-}
-.app-sidenav{
-    display:flex;
-    height:100%;
-    flex-direction:column;
-    background-color: #363636;
-    opacity:40%;
-    
-    
-}
-mat-list > a{
-
-    color:white;
-}
-.fineract-toolicon{
-    color:white;
-}
-.wrapper{
-    margin:50px;
-    
-}
-ul#menu li{
-    display: inline;
-}
-.search1{
-    
-    margin-left:8%;
-    
-}
-.fineract-toolsearch{
-    width:210px;
-}
-
-
-.fineract-logo{
-    width:40px;
-    height:37px;
-    vertical-align: bottom;
-    
-}
-.logo-title{
-    vertical-align: middle;
-    font-size:11px;
-    color:white;
-}
-
-.search1,.search2{
-    display: inline-block;
-}
-  
-.menu1{
-    padding:0;
-}
-
-.test{
-    padding-left:0;
-    padding-right: 0;
-}
\ No newline at end of file
diff --git a/src/app/navbar/navbar.component.spec.ts b/src/app/navbar/navbar.component.spec.ts
deleted file mode 100644
index 9032ad2..0000000
--- a/src/app/navbar/navbar.component.spec.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-import { async, ComponentFixture, TestBed } from '@angular/core/testing';
-
-import { NavbarComponent } from './navbar.component';
-
-describe('NavbarComponent', () => {
-  let component: NavbarComponent;
-  let fixture: ComponentFixture<NavbarComponent>;
-
-  beforeEach(async(() => {
-    TestBed.configureTestingModule({
-      declarations: [ NavbarComponent ]
-    })
-    .compileComponents();
-  }));
-
-  beforeEach(() => {
-    fixture = TestBed.createComponent(NavbarComponent);
-    component = fixture.componentInstance;
-    fixture.detectChanges();
-  });
-
-  it('should create', () => {
-    expect(component).toBeTruthy();
-  });
-});
diff --git a/src/app/navbar/navbar.component.ts b/src/app/navbar/navbar.component.ts
deleted file mode 100644
index a1b4d25..0000000
--- a/src/app/navbar/navbar.component.ts
+++ /dev/null
@@ -1,15 +0,0 @@
-import { Component, OnInit } from '@angular/core';
-
-@Component({
-  selector: 'app-navbar',
-  templateUrl: './navbar.component.html',
-  styleUrls: ['./navbar.component.scss']
-})
-export class NavbarComponent implements OnInit {
-
-  constructor() { }
-
-  ngOnInit() {
-  }
-
-}
diff --git a/src/app/office/add-office/add-office.component.html b/src/app/office/add-office/add-office.component.html
deleted file mode 100644
index 0a8ab41..0000000
--- a/src/app/office/add-office/add-office.component.html
+++ /dev/null
@@ -1,44 +0,0 @@
-<div class="main-div mat-elevation-z2">
-  <h3 class="heading">Add Office</h3>
-  <mat-divider></mat-divider>
-  <span>OFFICCE INFORMATION</span>
-<form class="fineract-form">
-  <mat-form-field>
-    <input matInput placeholder="Identifier">
-  </mat-form-field>
-<br/>
-  <mat-form-field>
-    <input matInput placeholder="Name">
-  </mat-form-field>
-<br/>
-  <mat-form-field>
-    <input matInput placeholder="Description">
-  </mat-form-field>
-</form>
-<span>ADDRESS</span>
-<form class="fineract-form">
-<mat-form-field>
-  <input matInput placeholder="Street">
-</mat-form-field>
-<br/>
-<mat-form-field>
-  <input matInput placeholder="City">
-</mat-form-field>
-<br/>
-<mat-form-field>
-  <input matInput placeholder="Postal code">
-</mat-form-field>
-<br/>
-<mat-form-field>
-  <input matInput placeholder="Country">
-</mat-form-field>
-<br/>
-<mat-form-field>
-  <input matInput placeholder="Region">
-</mat-form-field>
-</form>
-  <div>
-  <button mat-raised-button class="button1" color="primary">Submit</button>
-  <button mat-raised-button class="button2" color="warn">Cancel</button>
-  </div>
-</div>
\ No newline at end of file
diff --git a/src/app/office/add-office/add-office.component.scss b/src/app/office/add-office/add-office.component.scss
deleted file mode 100644
index 7109ed8..0000000
--- a/src/app/office/add-office/add-office.component.scss
+++ /dev/null
@@ -1,13 +0,0 @@
-.fineract-form {
-    min-width: 150px;
-    max-width: 500px;
-    width: 100%;
-  }
-  .button1{
-      margin-left: 50%;
-      
-      }
-  .button1, .button2{
-      width:20px;
-      display: inline-block;
-  }
\ No newline at end of file
diff --git a/src/app/office/add-office/add-office.component.spec.ts b/src/app/office/add-office/add-office.component.spec.ts
deleted file mode 100644
index c45da18..0000000
--- a/src/app/office/add-office/add-office.component.spec.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-import { async, ComponentFixture, TestBed } from '@angular/core/testing';
-
-import { AddOfficeComponent } from './add-office.component';
-
-describe('AddOfficeComponent', () => {
-  let component: AddOfficeComponent;
-  let fixture: ComponentFixture<AddOfficeComponent>;
-
-  beforeEach(async(() => {
-    TestBed.configureTestingModule({
-      declarations: [ AddOfficeComponent ]
-    })
-    .compileComponents();
-  }));
-
-  beforeEach(() => {
-    fixture = TestBed.createComponent(AddOfficeComponent);
-    component = fixture.componentInstance;
-    fixture.detectChanges();
-  });
-
-  it('should create', () => {
-    expect(component).toBeTruthy();
-  });
-});
diff --git a/src/app/office/add-office/add-office.component.ts b/src/app/office/add-office/add-office.component.ts
deleted file mode 100644
index 79d64af..0000000
--- a/src/app/office/add-office/add-office.component.ts
+++ /dev/null
@@ -1,15 +0,0 @@
-import { Component, OnInit } from '@angular/core';
-
-@Component({
-  selector: 'app-add-office',
-  templateUrl: './add-office.component.html',
-  styleUrls: ['./add-office.component.scss']
-})
-export class AddOfficeComponent implements OnInit {
-
-  constructor() { }
-
-  ngOnInit() {
-  }
-
-}
diff --git a/src/app/office/store/effects/notification.effects.ts b/src/app/office/store/effects/notification.effects.ts
deleted file mode 100644
index 20b4446..0000000
--- a/src/app/office/store/effects/notification.effects.ts
+++ /dev/null
@@ -1,57 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT 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 '../../../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
deleted file mode 100644
index acf9e87..0000000
--- a/src/app/office/store/effects/route.effects.ts
+++ /dev/null
@@ -1,61 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT 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
deleted file mode 100644
index cdc351b..0000000
--- a/src/app/office/store/effects/service.effects.ts
+++ /dev/null
@@ -1,84 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT 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 '../../../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
deleted file mode 100644
index 2b18056..0000000
--- a/src/app/office/store/index.ts
+++ /dev/null
@@ -1,81 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT 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
deleted file mode 100644
index d4c8ad7..0000000
--- a/src/app/office/store/office.actions.ts
+++ /dev/null
@@ -1,145 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT 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 'app/sevices/office/domain/office.model';
-import {Error} from '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
deleted file mode 100644
index c47110a..0000000
--- a/src/app/office/store/teller/denomination/denomination.actions.ts
+++ /dev/null
@@ -1,77 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT 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 '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
deleted file mode 100644
index 255a75d..0000000
--- a/src/app/office/store/teller/denomination/denominations.reducer.ts
+++ /dev/null
@@ -1,61 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT 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 '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
deleted file mode 100644
index 890ffb8..0000000
--- a/src/app/office/store/teller/denomination/effects/notification.effects.ts
+++ /dev/null
@@ -1,39 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT 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 '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
deleted file mode 100644
index 21ece39..0000000
--- a/src/app/office/store/teller/denomination/effects/route.effects.ts
+++ /dev/null
@@ -1,40 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT 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
deleted file mode 100644
index c1ca732..0000000
--- a/src/app/office/store/teller/denomination/effects/service.effects.ts
+++ /dev/null
@@ -1,52 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT 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 '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
deleted file mode 100644
index f536bba..0000000
--- a/src/app/office/store/teller/effects/notification.effects.ts
+++ /dev/null
@@ -1,72 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT 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 '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
deleted file mode 100644
index b662790..0000000
--- a/src/app/office/store/teller/effects/route.effects.ts
+++ /dev/null
@@ -1,49 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT 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';
-import {Teller} from 'app/sevices/teller/domain/teller.model' 
-
-@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
deleted file mode 100644
index 18071a9..0000000
--- a/src/app/office/store/teller/effects/service.effects.ts
+++ /dev/null
@@ -1,82 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT 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 '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
deleted file mode 100644
index b43755d..0000000
--- a/src/app/office/store/teller/teller.actions.ts
+++ /dev/null
@@ -1,161 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT 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 '../../../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 '../../../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
deleted file mode 100644
index 341a6d9..0000000
--- a/src/app/office/store/teller/tellers.reducer.spec.ts
+++ /dev/null
@@ -1,83 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT 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 '../../../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
deleted file mode 100644
index 415b20d..0000000
--- a/src/app/office/store/teller/tellers.reducer.ts
+++ /dev/null
@@ -1,92 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT 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 '../../../sevices/teller/domain/teller.model';
-import {idsToHashWithCurrentTimestamp, resourcesToHash} from '../../../common/store/reducer.helper';
-import {TellerManagementCommand} from '../../../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
deleted file mode 100644
index bb12198..0000000
--- a/src/app/office/view-offices/view-offices.component.html
+++ /dev/null
@@ -1,43 +0,0 @@
-<div class="main-div mat-elevation-z2">
-<h3 class="heading">View Offices</h3>
-<mat-divider></mat-divider>
-<div class="fineract-button">
-<a mat-raised-button  color="primary" [routerLink]="['/navbar/add_office']">
-  <mat-icon>add</mat-icon>Add Office</a>
-  </div>
-<mat-form-field>
-  <input matInput (keyup)="applyFilter($event.target.value)" placeholder="Filter by name">
-</mat-form-field>
-
-<br>
-<mat-divider></mat-divider>
-
-<mat-table #table [dataSource]="datasource" >
-
-<!-- Position Column -->
-<ng-container matColumnDef="id">
-  <mat-header-cell *matHeaderCellDef>Code</mat-header-cell>
-  <mat-cell *matCellDef="let element">
-    <a routerLink="{{element.id}}">{{element.id}}</a>
-  </mat-cell>
-</ng-container>
-
-<!-- Name Column -->
-<ng-container matColumnDef="name">
-  <mat-header-cell *matHeaderCellDef> Name</mat-header-cell>
-  <mat-cell *matCellDef="let element"> {{element.name}} </mat-cell>
-</ng-container>
-
-<ng-container matColumnDef="description">
-    <mat-header-cell *matHeaderCellDef>Description</mat-header-cell>
-    <mat-cell *matCellDef="let element"> {{element.description}} </mat-cell>
-  </ng-container>
-
-<mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
-<mat-row *matRowDef="let row; columns: displayedColumns;"></mat-row>
-</mat-table>
-
-
-</div>
-
-
diff --git a/src/app/office/view-offices/view-offices.component.scss b/src/app/office/view-offices/view-offices.component.scss
deleted file mode 100644
index 87b71f6..0000000
--- a/src/app/office/view-offices/view-offices.component.scss
+++ /dev/null
@@ -1,16 +0,0 @@
-.my-div{
-    margin-left: 2%;
-    margin-right:2%;
-    border-radius: 5px 5px 5px 5px;
-    background-color: #e6e6ff;
-    min-height: 100%;
-   
-  }
-  table {
-      width: 100%;
-    }
-    
-    .mat-form-field {
-      font-size: 14px;
-      width: 100%;
-    }
\ No newline at end of file
diff --git a/src/app/office/view-offices/view-offices.component.spec.ts b/src/app/office/view-offices/view-offices.component.spec.ts
deleted file mode 100644
index 2d0d3c7..0000000
--- a/src/app/office/view-offices/view-offices.component.spec.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-import { async, ComponentFixture, TestBed } from '@angular/core/testing';
-
-import { ViewOfficesComponent } from './view-offices.component';
-
-describe('ViewOfficesComponent', () => {
-  let component: ViewOfficesComponent;
-  let fixture: ComponentFixture<ViewOfficesComponent>;
-
-  beforeEach(async(() => {
-    TestBed.configureTestingModule({
-      declarations: [ ViewOfficesComponent ]
-    })
-    .compileComponents();
-  }));
-
-  beforeEach(() => {
-    fixture = TestBed.createComponent(ViewOfficesComponent);
-    component = fixture.componentInstance;
-    fixture.detectChanges();
-  });
-
-  it('should create', () => {
-    expect(component).toBeTruthy();
-  });
-});
diff --git a/src/app/office/view-offices/view-offices.component.ts b/src/app/office/view-offices/view-offices.component.ts
deleted file mode 100644
index 3ac70b7..0000000
--- a/src/app/office/view-offices/view-offices.component.ts
+++ /dev/null
@@ -1,36 +0,0 @@
-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);
-
-  applyFilter(filterValue: string) {
-    filterValue = filterValue.trim(); // Remove whitespace
-    filterValue = filterValue.toLowerCase(); // MatTableDataSource defaults to lowercase matches
-    this.dataSource.filter = filterValue;
-  }
-
-  constructor() { }
-
-  ngOnInit() {
-  }
-
-}
-export interface Element {
-  id: number;
-  name: string;
-  description: string;
-  
-}
-
-const ELEMENT_DATA: Element[] = [
-  
-];
diff --git a/src/app/offices/detail/office.detail.component.html b/src/app/offices/detail/office.detail.component.html
new file mode 100644
index 0000000..bb0e2cf
--- /dev/null
+++ b/src/app/offices/detail/office.detail.component.html
@@ -0,0 +1,73 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<ng-container *ngIf="office$ | async as office">
+  <fims-layout-card-over title="{{office.parentIdentifier ? office.name : office.name + ' (Headquarter)'}}" subTitle="{{office.description}}"
+                         [navigateBackTo]="office.parentIdentifier ? ['../../', this.office.parentIdentifier] : undefined">
+    <fims-layout-card-over-header-menu layout="row" layout-align="end center">
+      <td-search-box #searchBox placeholder="{{'Search' | translate}}" (search)="searchOffice($event)"
+                     [alwaysVisible]="false"></td-search-box>
+      <a mat-icon-button [routerLink]="['edit']" *hasPermission="{ id: 'office_offices', accessLevel: 'CHANGE' }">
+        <mat-icon>mode_edit</mat-icon>
+      </a>
+      <button mat-icon-button (click)="deleteOffice(office)" title="{{'Delete this office' | translate}}"
+              *ngIf="canDelete$ | async">
+        <mat-icon>delete</mat-icon>
+      </button>
+    </fims-layout-card-over-header-menu>
+    <fims-two-column-layout>
+      <mat-nav-list left>
+        <h3 mat-subheader translate>Management</h3>
+        <a mat-list-item [routerLink]="['tellers']" *hasPermission="{ id: 'teller_management', accessLevel: 'READ'}">
+          <mat-icon matListAvatar>person</mat-icon>
+          <h3 matLine translate>Tellers</h3>
+          <p matLine translate>Manage tellers</p>
+        </a>
+      </mat-nav-list>
+      <ng-container right>
+        <mat-list>
+          <h3 mat-subheader translate>Address</h3>
+          <mat-list-item *ngIf="office.address">
+            <mat-icon matListAvatar>location_on</mat-icon>
+            <h3 matLine>{{office.address.street}}, {{office.address.postalCode}}, {{office.address.city}}<span *ngIf="office.address.region">({{office.address.region}})</span>
+            </h3>
+            <p matLine>{{office.address.country}}</p>
+          </mat-list-item>
+          <mat-list-item *ngIf="!office.address">
+            <mat-icon matListAvatar>location_on</mat-icon>
+            <h3 matLine translate>No address available</h3>
+          </mat-list-item>
+          <h3 mat-subheader translate>Branch offices</h3>
+        </mat-list>
+
+        <fims-data-table flex
+                         (onFetch)="fetchBranches(office.identifier, $event)"
+                         (onActionCellClick)="rowSelect($event)"
+                         [columns]="columns"
+                         [data]="branchData"
+                         [sortable]="true"
+                         [pageable]="true">
+        </fims-data-table>
+      </ng-container>
+    </fims-two-column-layout>
+  </fims-layout-card-over>
+  <a mat-fab color="accent" title="{{'Create branch office' | translate}}" class="mat-fab-position-bottom-right"
+     [routerLink]="['../../../create']"
+     [queryParams]="{ parentId: office.identifier }" *hasPermission="{ id: 'office_offices', accessLevel: 'CHANGE' }">
+    <mat-icon>add</mat-icon>
+  </a>
+</ng-container>
diff --git a/src/app/offices/detail/office.detail.component.ts b/src/app/offices/detail/office.detail.component.ts
new file mode 100644
index 0000000..78b568c
--- /dev/null
+++ b/src/app/offices/detail/office.detail.component.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 {ActivatedRoute, Router} from '@angular/router';
+import {Component, OnInit} from '@angular/core';
+import {OfficeService} from '../../services/office/office.service';
+import {Office} from '../../services/office/domain/office.model';
+import {FetchRequest} from '../../services/domain/paging/fetch-request.model';
+import {OfficePage} from '../../services/office/domain/office-page.model';
+import {Observable} from 'rxjs/Observable';
+import {TdDialogService} from '@covalent/core';
+import {TableData} from '../../common/data-table/data-table.component';
+import {DELETE} from '../store/office.actions';
+import {getSelectedOffice, OfficesStore} from '../store/index';
+import {FimsPermission} from '../../services/security/authz/fims-permission.model';
+import * as fromRoot from '../../store/index';
+
+@Component({
+  templateUrl: './office.detail.component.html'
+})
+export class OfficeDetailComponent implements OnInit {
+
+  office$: Observable<Office>;
+
+  canDelete$: Observable<boolean>;
+
+  branchData: TableData = {
+    totalElements: 0,
+    totalPages: 0,
+    data: []
+  };
+
+  columns: any[] = [
+    {name: 'identifier', label: 'Id'},
+    {name: 'name', label: 'Name'},
+    {name: 'description', label: 'Description'}
+  ];
+
+  constructor(private store: OfficesStore, private route: ActivatedRoute, private router: Router, private officeService: OfficeService,
+              private dialogService: TdDialogService) {
+  }
+
+  ngOnInit(): void {
+    this.office$ = this.store.select(getSelectedOffice)
+      .filter(office => !!office)
+      .do(office => this.fetchBranches(office.identifier));
+
+    this.canDelete$ = Observable.combineLatest(
+      this.store.select(fromRoot.getPermissions),
+      this.office$,
+      (permissions, office: Office) => ({
+        hasPermission: this.hasDeletePermission(permissions),
+        noExternalReferences: !office.externalReferences
+      }))
+      .map(result => result.hasPermission && result.noExternalReferences);
+  }
+
+  fetchBranches(identifier: string, fetchRequest?: FetchRequest): void {
+    this.officeService.listBranches(identifier, fetchRequest)
+      .subscribe((officePage: OfficePage) => {
+        this.branchData = {
+          data: officePage.offices,
+          totalElements: officePage.totalElements,
+          totalPages: officePage.totalPages
+        };
+      });
+  }
+
+  rowSelect(office: Office): void {
+    this.router.navigate(['../../', office.identifier], {relativeTo: this.route});
+  }
+
+  searchOffice(searchTerm: string): void {
+    this.router.navigate(['../../../'], {queryParams: {term: searchTerm}, relativeTo: this.route});
+  }
+
+  confirmDeletion(): Observable<boolean> {
+    return this.dialogService.openConfirm({
+      message: 'Do you want to delete this office?',
+      title: 'Confirm deletion',
+      acceptButton: 'DELETE OFFICE',
+    }).afterClosed();
+  }
+
+  deleteOffice(office: Office): void {
+    this.confirmDeletion()
+      .filter(accept => accept)
+      .subscribe(() => this.store.dispatch({
+        type: DELETE, payload: {
+          office,
+          activatedRoute: this.route
+        }
+      }));
+  }
+
+  private hasDeletePermission(permissions: FimsPermission[]): boolean {
+    return permissions.filter(permission =>
+      permission.id === 'office_offices' &&
+      permission.accessLevel === 'DELETE'
+    ).length > 0;
+  }
+
+}
diff --git a/src/app/offices/detail/office.index.component.html b/src/app/offices/detail/office.index.component.html
new file mode 100644
index 0000000..ca721b3
--- /dev/null
+++ b/src/app/offices/detail/office.index.component.html
@@ -0,0 +1,18 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<router-outlet></router-outlet>
diff --git a/src/app/offices/detail/office.index.component.ts b/src/app/offices/detail/office.index.component.ts
new file mode 100644
index 0000000..30c50e7
--- /dev/null
+++ b/src/app/offices/detail/office.index.component.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 {Component, OnDestroy, OnInit} from '@angular/core';
+import {SelectAction} from '../store/office.actions';
+import {OfficesStore} from '../store/index';
+import {ActivatedRoute} from '@angular/router';
+import {Subscription} from 'rxjs/Subscription';
+
+@Component({
+  templateUrl: './office.index.component.html',
+})
+export class OfficeIndexComponent implements OnInit, OnDestroy {
+
+  private actionsSubscription: Subscription;
+
+  constructor(private store: OfficesStore, private route: ActivatedRoute) {}
+
+  ngOnInit(): void {
+    this.actionsSubscription = this.route.params
+      .map(params => new SelectAction(params['id']))
+      .subscribe(this.store);
+  }
+
+  ngOnDestroy(): void {
+    this.actionsSubscription.unsubscribe();
+  }
+}
diff --git a/src/app/offices/detail/teller/detail/balance/balance.component.html b/src/app/offices/detail/teller/detail/balance/balance.component.html
new file mode 100644
index 0000000..96d55b4
--- /dev/null
+++ b/src/app/offices/detail/teller/detail/balance/balance.component.html
@@ -0,0 +1,87 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<fims-layout-card-over title="{{'Teller balance' | translate}}" [navigateBackTo]="['../']">
+  <div layout="row">
+    <table td-data-table *ngIf="balance$ | async as balance">
+      <thead>
+        <tr td-data-table-column-row>
+          <th td-data-table-column>
+            <span translate>Transaction date</span>
+          </th>
+          <th td-data-table-column>
+            <span translate>Message</span>
+          </th>
+          <th td-data-table-column>
+            <span translate>Cash Received</span>
+          </th>
+          <th td-data-table-column>
+            <span translate>Cash Disbursed</span>
+          </th>
+          <th td-data-table-column>
+            <span translate>Cheques Received</span>
+          </th>
+        </tr>
+      </thead>
+      <tbody>
+        <tr td-data-table-row *ngFor="let row of balance.entries">
+          <td td-data-table-cell>
+            {{row['transactionDate'] | date:'short' }}
+          </td>
+          <td td-data-table-cell>
+            {{row['message'] }}
+          </td>
+          <td td-data-table-cell>
+            {{row['type'] == 'DEBIT' ? (row['amount'] | number:numberFormat) : '-'}}
+          </td>
+          <td td-data-table-cell>
+            {{row['type'] == 'CREDIT' ? (row['amount'] | number:numberFormat): '-'}}
+          </td>
+          <td td-data-table-cell>
+            {{row['type'] == 'CHEQUE' ? (row['amount'] | number:numberFormat) : '-'}}
+          </td>
+        </tr>
+        <tr td-data-table-row>
+          <td td-data-table-cell></td>
+          <td td-data-table-cell>
+            <b translate>Subtotal</b>
+          </td>
+          <td td-data-table-cell>
+            <b>{{balance.cashReceivedTotal | number:numberFormat}}</b>
+          </td>
+          <td td-data-table-cell>
+            <b>{{balance.cashDisbursedTotal | number:numberFormat}}</b>
+          </td>
+          <td td-data-table-cell>
+            <b>{{balance.chequesReceivedTotal | number:numberFormat}}</b>
+          </td>
+        </tr>
+        <tr td-data-table-row>
+          <td td-data-table-cell></td>
+          <td td-data-table-cell>
+            <b translate>Cash on hand</b>
+          </td>
+          <td td-data-table-cell>
+            <b>{{balance.cashOnHand | number:numberFormat}}</b>
+          </td>
+          <td td-data-table-cell></td>
+          <td td-data-table-cell></td>
+        </tr>
+      </tbody>
+    </table>
+  </div>
+</fims-layout-card-over>
diff --git a/src/app/offices/detail/teller/detail/balance/balance.component.ts b/src/app/offices/detail/teller/detail/balance/balance.component.ts
new file mode 100644
index 0000000..3b31642
--- /dev/null
+++ b/src/app/offices/detail/teller/detail/balance/balance.component.ts
@@ -0,0 +1,47 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, OnInit} from '@angular/core';
+import {Observable} from 'rxjs/Observable';
+import * as fromOffices from '../../../../store/index';
+import {OfficesStore} from '../../../../store/index';
+import {TellerBalance} from './services/teller-balance.model';
+import {BalanceSheetService} from './services/balance-sheet.service';
+
+@Component({
+  templateUrl: './balance.component.html'
+})
+export class TellerBalanceComponent implements OnInit {
+
+  numberFormat = '1.2-2';
+
+  balance$: Observable<TellerBalance>;
+
+  constructor(private balanceSheetService: BalanceSheetService, private store: OfficesStore) {}
+
+  ngOnInit(): void {
+    this.balance$ = Observable.combineLatest(
+      this.store.select(fromOffices.getSelectedTeller).filter(teller => !!teller),
+      this.store.select(fromOffices.getSelectedOffice).filter(office => !!office),
+      (teller, office) => ({
+        teller,
+        office
+      })
+    ).switchMap(result => this.balanceSheetService.getBalance(result.office.identifier, result.teller.code));
+  }
+}
diff --git a/src/app/offices/detail/teller/detail/balance/services/balance-sheet.service.ts b/src/app/offices/detail/teller/detail/balance/services/balance-sheet.service.ts
new file mode 100644
index 0000000..a063ae1
--- /dev/null
+++ b/src/app/offices/detail/teller/detail/balance/services/balance-sheet.service.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 {Injectable} from '@angular/core';
+import {TellerService} from '../../../../../../services/teller/teller-service';
+import {TellerBalance} from './teller-balance.model';
+import {Observable} from 'rxjs/Observable';
+import {TellerEntry} from '../../../../../../services/teller/domain/teller-entry.model';
+
+@Injectable()
+export class BalanceSheetService {
+
+  constructor(private tellerService: TellerService) {
+  }
+
+  private compareEntry(entryA: TellerEntry, entryB: TellerEntry): number {
+    return new Date(entryA.transactionDate).getTime() - new Date(entryB.transactionDate).getTime();
+  }
+
+  getBalance(officeIdentifier: string, tellerCode: string): Observable<TellerBalance> {
+    return this.tellerService.getBalance(officeIdentifier, tellerCode)
+      .map(balance => {
+        const chequeEntries = balance.chequeEntries || [];
+        const cashEntries = balance.cashEntries || [];
+
+        const entries = cashEntries.concat(chequeEntries)
+          .sort((entryA, entryB) => this.compareEntry(entryA, entryB));
+
+        return {
+          day: balance.day,
+          cashOnHand: balance.cashOnHand,
+          cashReceivedTotal: balance.cashReceivedTotal,
+          cashDisbursedTotal: balance.cashDisbursedTotal,
+          chequesReceivedTotal: balance.chequesReceivedTotal,
+          entries
+        };
+      });
+  }
+}
diff --git a/src/app/offices/detail/teller/detail/balance/services/teller-balance.model.ts b/src/app/offices/detail/teller/detail/balance/services/teller-balance.model.ts
new file mode 100644
index 0000000..e9f495f
--- /dev/null
+++ b/src/app/offices/detail/teller/detail/balance/services/teller-balance.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 {TellerEntry} from '../../../../../../services/teller/domain/teller-entry.model';
+
+export interface TellerBalance {
+  day?: string;
+  cashOnHand: string;
+  cashReceivedTotal: string;
+  cashDisbursedTotal: string;
+  chequesReceivedTotal: string;
+  entries: TellerEntry[];
+}
diff --git a/src/app/offices/detail/teller/detail/command/close.component.html b/src/app/offices/detail/teller/detail/command/close.component.html
new file mode 100644
index 0000000..3fb8d9b
--- /dev/null
+++ b/src/app/offices/detail/teller/detail/command/close.component.html
@@ -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.
+-->
+
+<td-steps mode="'vertical'">
+  <td-step #detailsStep label="{{'Close teller' | translate}}"
+           [state]="form.valid ? 'complete' : form.pristine ? 'none' : 'required'">
+    <form [formGroup]="form">
+      <fims-teller-adjustment-form
+        [form]="form"
+        [adjustmentOptions]="adjustmentOptions">
+      </fims-teller-adjustment-form>
+    </form>
+    <ng-template td-step-actions>
+      <button mat-raised-button color="primary" (click)="close()" [disabled]="form.invalid">{{'CLOSE TELLER' | translate}}</button>
+      <span flex></span>
+      <button mat-button (click)="cancel()">{{'CANCEL' | translate}}</button>
+    </ng-template>
+  </td-step>
+</td-steps>
diff --git a/src/app/offices/detail/teller/detail/command/close.component.ts b/src/app/offices/detail/teller/detail/command/close.component.ts
new file mode 100644
index 0000000..0fb07b0
--- /dev/null
+++ b/src/app/offices/detail/teller/detail/command/close.component.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 {Component, EventEmitter, OnInit, Output, ViewChild} from '@angular/core';
+import {FormBuilder, Validators} from '@angular/forms';
+import {TdStepComponent} from '@covalent/core';
+import {TellerManagementCommand} from '../../../../../services/teller/domain/teller-management-command.model';
+import {FormComponent} from '../../../../../common/forms/form.component';
+import {FimsValidators} from '../../../../../common/validator/validators';
+import {AdjustmentOption} from './model/adjustment-option.model';
+
+@Component({
+  selector: 'fims-teller-close-command',
+  templateUrl: './close.component.html'
+})
+export class CloseOfficeTellerFormComponent extends FormComponent<TellerManagementCommand> implements OnInit {
+
+  @ViewChild('detailsStep') step: TdStepComponent;
+
+  @Output() onClose = new EventEmitter<TellerManagementCommand>();
+
+  @Output() onCancel = new EventEmitter<void>();
+
+  adjustmentOptions: AdjustmentOption[] = [
+    { key: 'NONE', label: 'None' },
+    { key: 'CREDIT', label: 'Cash out' }
+  ];
+
+  constructor(private formBuilder: FormBuilder) {
+    super();
+  }
+
+  ngOnInit(): void {
+    this.form = this.formBuilder.group({
+      adjustment: ['NONE'],
+      amount: [0, [Validators.required, FimsValidators.minValue(0)]],
+    });
+
+    this.step.open();
+  }
+
+  cancel(): void {
+    this.onCancel.emit();
+  }
+
+  get formData(): TellerManagementCommand {
+    // Not needed
+    return null;
+  }
+
+  close(): void {
+    const command: TellerManagementCommand = {
+      action: 'CLOSE',
+      adjustment: this.form.get('adjustment').value,
+      amount: this.form.get('amount').value,
+    };
+
+    this.onClose.emit(command);
+  }
+
+}
diff --git a/src/app/offices/detail/teller/detail/command/command.component.html b/src/app/offices/detail/teller/detail/command/command.component.html
new file mode 100644
index 0000000..1d1dba3
--- /dev/null
+++ b/src/app/offices/detail/teller/detail/command/command.component.html
@@ -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.
+-->
+
+<fims-layout-card-over title="{{title | translate}}">
+  <fims-teller-open-command *ngIf="action === 'OPEN'" (onOpen)="onOpen($event)" (onCancel)="onCancel()"></fims-teller-open-command>
+  <fims-teller-close-command *ngIf="action === 'CLOSE'" (onClose)="onClose($event)" (onCancel)="onCancel()"></fims-teller-close-command>
+</fims-layout-card-over>
diff --git a/src/app/offices/detail/teller/detail/command/command.component.ts b/src/app/offices/detail/teller/detail/command/command.component.ts
new file mode 100644
index 0000000..d9cec26
--- /dev/null
+++ b/src/app/offices/detail/teller/detail/command/command.component.ts
@@ -0,0 +1,91 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, OnDestroy, OnInit} from '@angular/core';
+import * as fromOffices from '../../../../store/index';
+import {OfficesStore} from '../../../../store/index';
+import {Action, TellerManagementCommand} from '../../../../../services/teller/domain/teller-management-command.model';
+import {Teller} from '../../../../../services/teller/domain/teller.model';
+import {ActivatedRoute, Router} from '@angular/router';
+import {Office} from '../../../../../services/office/domain/office.model';
+import {Subscription} from 'rxjs/Subscription';
+import {EXECUTE_COMMAND} from '../../../../store/teller/teller.actions';
+
+@Component({
+  templateUrl: './command.component.html'
+})
+export class OfficeTellerCommandComponent implements OnInit, OnDestroy {
+
+  private officeSubscription: Subscription;
+
+  private tellerSubscription: Subscription;
+
+  office: Office;
+
+  teller: Teller;
+
+  action: Action = 'OPEN';
+
+  constructor(private router: Router, private route: ActivatedRoute, private store: OfficesStore) {}
+
+  ngOnInit(): void {
+    this.route.queryParams.subscribe(params => this.action = params['action']);
+
+    this.officeSubscription = this.store.select(fromOffices.getSelectedOffice)
+      .filter(office => !!office)
+      .subscribe(office => this.office = office);
+
+    this.tellerSubscription = this.store.select(fromOffices.getSelectedTeller)
+      .filter(teller => !!teller)
+      .subscribe(teller => this.teller = teller);
+  }
+
+  ngOnDestroy(): void {
+    this.officeSubscription.unsubscribe();
+    this.tellerSubscription.unsubscribe();
+  }
+
+  onOpen(command: TellerManagementCommand): void {
+    this.executeCommand(command);
+  }
+
+  onClose(command: TellerManagementCommand): void {
+    this.executeCommand(command);
+  }
+
+  private executeCommand(command: TellerManagementCommand): void {
+    this.store.dispatch({
+      type: EXECUTE_COMMAND,
+      payload: {
+        officeId: this.office.identifier,
+        tellerCode: this.teller.code,
+        activatedRoute: this.route,
+        command
+      }
+    });
+  }
+
+  onCancel(): void {
+    this.router.navigate(['../'], { relativeTo: this.route });
+  }
+
+  get title(): string {
+    return this.action === 'OPEN' ? 'Open teller' : 'Close teller';
+  }
+
+}
diff --git a/src/app/offices/detail/teller/detail/command/components/adjustment.component.html b/src/app/offices/detail/teller/detail/command/components/adjustment.component.html
new file mode 100644
index 0000000..aa58e39
--- /dev/null
+++ b/src/app/offices/detail/teller/detail/command/components/adjustment.component.html
@@ -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.
+-->
+
+<ng-container [formGroup]="form" layout="column">
+  <mat-radio-group formControlName="adjustment">
+    <mat-radio-button *ngFor="let adjustment of adjustmentOptions" [value]="adjustment.key" layout-margin>
+      {{adjustment.label}}
+    </mat-radio-button>
+  </mat-radio-group>
+  <fims-text-input type="number" [form]="form" controlName="amount" placeholder="{{'Amount' | translate}}"></fims-text-input>
+</ng-container>
diff --git a/src/app/offices/detail/teller/detail/command/components/adjustment.component.ts b/src/app/offices/detail/teller/detail/command/components/adjustment.component.ts
new file mode 100644
index 0000000..a7c4bc6
--- /dev/null
+++ b/src/app/offices/detail/teller/detail/command/components/adjustment.component.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 {Component, Input, OnInit} from '@angular/core';
+import {FormGroup} from '@angular/forms';
+import {AdjustmentOption} from '../model/adjustment-option.model';
+
+@Component({
+  selector: 'fims-teller-adjustment-form',
+  templateUrl: './adjustment.component.html'
+})
+export class AdjustmentComponent implements OnInit {
+
+  @Input() form: FormGroup;
+
+  @Input() adjustmentOptions: AdjustmentOption[];
+
+  ngOnInit(): void {
+    this.form.get('adjustment').valueChanges
+      .startWith(this.form.get('adjustment').value)
+      .subscribe(adjustment => {
+        const amountControl = this.form.get('amount');
+        if (adjustment === 'NONE') {
+          amountControl.disable();
+        } else {
+          amountControl.enable();
+        }
+      });
+  }
+}
diff --git a/src/app/offices/detail/teller/detail/command/model/adjustment-option.model.ts b/src/app/offices/detail/teller/detail/command/model/adjustment-option.model.ts
new file mode 100644
index 0000000..b16c40c
--- /dev/null
+++ b/src/app/offices/detail/teller/detail/command/model/adjustment-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.
+ */
+import {Adjustment} from '../../../../../../services/teller/domain/teller-management-command.model';
+
+export interface AdjustmentOption {
+  key: string | Adjustment;
+  label: string;
+}
diff --git a/src/app/offices/detail/teller/detail/command/open.component.html b/src/app/offices/detail/teller/detail/command/open.component.html
new file mode 100644
index 0000000..541b328
--- /dev/null
+++ b/src/app/offices/detail/teller/detail/command/open.component.html
@@ -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.
+-->
+
+<td-steps mode="'vertical'">
+  <td-step #detailsStep label="{{'Open teller' | translate}}"
+           [state]="form.valid ? 'complete' : form.pristine ? 'none' : 'required'">
+    <form [formGroup]="form">
+      <fims-teller-adjustment-form
+        [form]="form"
+        [adjustmentOptions]="adjustmentOptions">
+      </fims-teller-adjustment-form>
+      <fims-employee-auto-complete title="{{'Assigned employee' | translate}}" formControlName="assignedEmployeeIdentifier">
+        <ng-container *ngIf="!form.get('assignedEmployeeIdentifier').pristine && form.get('assignedEmployeeIdentifier').hasError('required')" translate>
+          Required
+        </ng-container>
+        <ng-container *ngIf="form.get('assignedEmployeeIdentifier').hasError('invalidEmployee')" translate>
+          Invalid employee
+        </ng-container>
+      </fims-employee-auto-complete>
+    </form>
+    <ng-template td-step-actions>
+      <button mat-raised-button color="primary" (click)="open()" [disabled]="form.invalid">{{'OPEN TELLER' | translate}}</button>
+      <span flex></span>
+      <button mat-button (click)="cancel()">{{'CANCEL' | translate}}</button>
+    </ng-template>
+  </td-step>
+</td-steps>
diff --git a/src/app/offices/detail/teller/detail/command/open.component.ts b/src/app/offices/detail/teller/detail/command/open.component.ts
new file mode 100644
index 0000000..f884664
--- /dev/null
+++ b/src/app/offices/detail/teller/detail/command/open.component.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 {Component, EventEmitter, OnInit, Output, ViewChild} from '@angular/core';
+import {FormBuilder, Validators} from '@angular/forms';
+import {TdStepComponent} from '@covalent/core';
+import {TellerManagementCommand} from '../../../../../services/teller/domain/teller-management-command.model';
+import {FormComponent} from '../../../../../common/forms/form.component';
+import {OfficeService} from '../../../../../services/office/office.service';
+import {employeeExists} from '../../../../../common/validator/employee-exists.validator';
+import {AdjustmentOption} from './model/adjustment-option.model';
+import {FimsValidators} from '../../../../../common/validator/validators';
+
+@Component({
+  selector: 'fims-teller-open-command',
+  templateUrl: './open.component.html'
+})
+export class OpenOfficeTellerFormComponent extends FormComponent<TellerManagementCommand> implements OnInit {
+
+  @ViewChild('detailsStep') step: TdStepComponent;
+
+  @Output() onOpen = new EventEmitter<TellerManagementCommand>();
+
+  @Output() onCancel = new EventEmitter<void>();
+
+  adjustmentOptions: AdjustmentOption[] = [
+    { key: 'NONE', label: 'None' },
+    { key: 'DEBIT', label: 'Cash in' },
+  ];
+
+  constructor(private formBuilder: FormBuilder, private officeService: OfficeService) {
+    super();
+  }
+
+  ngOnInit(): void {
+    this.form = this.formBuilder.group({
+      adjustment: ['NONE'],
+      amount: [0, [Validators.required, FimsValidators.minValue(0)]],
+      assignedEmployeeIdentifier: ['', [Validators.required], employeeExists(this.officeService)]
+    });
+
+    this.step.open();
+  }
+
+  cancel(): void {
+    this.onCancel.emit();
+  }
+
+  get formData(): TellerManagementCommand {
+    // Not needed
+    return null;
+  }
+
+  open(): void {
+    const command: TellerManagementCommand = {
+      action: 'OPEN',
+      assignedEmployeeIdentifier: this.form.get('assignedEmployeeIdentifier').value,
+      adjustment: this.form.get('adjustment').value,
+      amount: this.form.get('amount').value,
+    };
+
+    this.onOpen.emit(command);
+  }
+
+}
diff --git a/src/app/offices/detail/teller/detail/denomination/denomination.list.component.html b/src/app/offices/detail/teller/detail/denomination/denomination.list.component.html
new file mode 100644
index 0000000..bc6475e
--- /dev/null
+++ b/src/app/offices/detail/teller/detail/denomination/denomination.list.component.html
@@ -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.
+-->
+
+<fims-layout-card-over title="{{ 'Manage denominations' | translate}}" [navigateBackTo]="['../../../../../../../']">
+  <td-message
+    *ngIf="isTellerNotPaused$ | async"
+    label="{{'Teller is not paused' | translate }}"
+    sublabel="{{'Teller must be paused to create denominations' | translate }}"
+    color="warn"
+    icon="error">
+  </td-message>
+  <fims-data-table flex
+                   [columns]="columns"
+                   [data]="denominationData$ | async"
+                   [sortable]="false"
+                   [pageable]="false"
+                   [actionColumn]="false">
+  </fims-data-table>
+</fims-layout-card-over>
+<fims-fab-button
+  title="{{'Create denomination' | translate}}"
+  icon="attach_money" [link]="['create']"
+  [permission]="{ id: 'teller_management', accessLevel: 'CHANGE'}"
+  [disabled]="isTellerNotPaused$ | async">
+</fims-fab-button>
diff --git a/src/app/offices/detail/teller/detail/denomination/denomination.list.component.ts b/src/app/offices/detail/teller/detail/denomination/denomination.list.component.ts
new file mode 100644
index 0000000..41b7a1b
--- /dev/null
+++ b/src/app/offices/detail/teller/detail/denomination/denomination.list.component.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 {Component, OnDestroy, OnInit} from '@angular/core';
+import {TableData} from '../../../../../common/data-table/data-table.component';
+import {Subscription} from 'rxjs/Subscription';
+import {Observable} from 'rxjs/Observable';
+import * as fromOffices from '../../../../store/index';
+import {OfficesStore} from '../../../../store/index';
+import {LoadDenominationAction} from '../../../../store/teller/denomination/denomination.actions';
+import {DatePipe} from '@angular/common';
+
+@Component({
+  templateUrl: './denomination.list.component.html',
+  providers: [DatePipe]
+})
+export class TellerDenominationListComponent implements OnInit, OnDestroy {
+
+  private loadDenominationSubscription: Subscription;
+
+  denominationData$: Observable<TableData>;
+
+  isTellerNotPaused$: Observable<boolean>;
+
+  columns: any[] = [
+    {name: 'countedTotal', label: 'Counted total'},
+    {name: 'note', label: 'Note'},
+    {name: 'adjustingJournalEntry', label: 'Adjusting journal entry'},
+    {
+      name: 'createdOn', label: 'Created on', format: (v: any) => {
+      return this.datePipe.transform(v, 'short');
+    }
+    },
+    {name: 'createdBy', label: 'Created by'}
+  ];
+
+  constructor(private store: OfficesStore, private datePipe: DatePipe) {}
+
+  ngOnInit(): void {
+    const selectedTeller$ = this.store.select(fromOffices.getSelectedTeller).filter(teller => !!teller);
+
+    this.isTellerNotPaused$ = selectedTeller$.map(teller => teller.state !== 'PAUSED');
+
+    this.loadDenominationSubscription = Observable.combineLatest(
+      this.store.select(fromOffices.getSelectedOffice).filter(office => !!office),
+      selectedTeller$,
+      (office, teller) => ({
+        office,
+        teller
+      })
+    ).map(result => new LoadDenominationAction({
+      officeId: result.office.identifier,
+      tellerCode: result.teller.code
+    })).subscribe(this.store);
+
+    this.denominationData$ = this.store.select(fromOffices.getDenominationsEntities)
+      .map(tellers => ({
+        data: tellers,
+        totalElements: tellers.length,
+        totalPages: 1
+      }));
+  }
+
+  ngOnDestroy(): void {
+    this.loadDenominationSubscription.unsubscribe();
+  }
+
+}
diff --git a/src/app/offices/detail/teller/detail/denomination/form/create.form.component.html b/src/app/offices/detail/teller/detail/denomination/form/create.form.component.html
new file mode 100644
index 0000000..0414331
--- /dev/null
+++ b/src/app/offices/detail/teller/detail/denomination/form/create.form.component.html
@@ -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.
+-->
+
+<fims-layout-card-over title="{{'Create new denomination' | translate}}" *ngIf="currentSelection$ | async as selection">
+  <fims-denomination-form-component #form
+                                    [balanceSheet]="balanceSheet$ | async"
+                                    (onSave)="onSave(selection.officeId, selection.tellerId, $event)"
+                                    (onCancel)="onCancel()">
+  </fims-denomination-form-component>
+</fims-layout-card-over>
diff --git a/src/app/offices/detail/teller/detail/denomination/form/create.form.component.ts b/src/app/offices/detail/teller/detail/denomination/form/create.form.component.ts
new file mode 100644
index 0000000..2dff441
--- /dev/null
+++ b/src/app/offices/detail/teller/detail/denomination/form/create.form.component.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 {Component} from '@angular/core';
+import {ActivatedRoute, Router} from '@angular/router';
+import * as fromOffices from '../../../../../store/index';
+import {OfficesStore} from '../../../../../store/index';
+import {TellerDenomination} from '../../../../../../services/teller/domain/teller-denomination.model';
+import {CREATE_DENOMINATION} from '../../../../../store/teller/denomination/denomination.actions';
+import {Observable} from 'rxjs/Observable';
+import {TellerService} from '../../../../../../services/teller/teller-service';
+import {TellerBalanceSheet} from '../../../../../../services/teller/domain/teller-balance-sheet.model';
+
+interface CurrentSelection {
+  officeId: string;
+  tellerId: string;
+}
+
+@Component({
+  templateUrl: './create.form.component.html'
+})
+export class CreateDenominationFormComponent {
+
+  currentSelection$: Observable<CurrentSelection>;
+
+  balanceSheet$: Observable<TellerBalanceSheet>;
+
+  constructor(private router: Router, private route: ActivatedRoute, private store: OfficesStore, private tellerService: TellerService) {
+    this.currentSelection$ = Observable.combineLatest(
+      this.store.select(fromOffices.getSelectedOffice).filter(office => !!office),
+      this.store.select(fromOffices.getSelectedTeller).filter(teller => !!teller),
+      (office, teller) => ({
+        office,
+        teller
+      })).map(result => ({
+        officeId: result.office.identifier,
+        tellerId: result.teller.code
+      }));
+
+    this.balanceSheet$ = this.currentSelection$
+      .switchMap(selection => this.tellerService.getBalance(selection.officeId, selection.tellerId));
+  }
+
+  onSave(officeId: string, tellerCode: string, denomination: TellerDenomination): void {
+    this.store.dispatch({ type: CREATE_DENOMINATION, payload: {
+      officeId,
+      tellerCode,
+      denomination,
+      activatedRoute: this.route
+    }});
+  }
+
+  onCancel(): void {
+    this.navigateAway();
+  }
+
+  navigateAway(): void {
+    this.router.navigate(['../'], { relativeTo: this.route });
+  }
+
+}
diff --git a/src/app/offices/detail/teller/detail/denomination/form/form.component.html b/src/app/offices/detail/teller/detail/denomination/form/form.component.html
new file mode 100644
index 0000000..c3afb8c
--- /dev/null
+++ b/src/app/offices/detail/teller/detail/denomination/form/form.component.html
@@ -0,0 +1,79 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<td-steps mode="'vertical'">
+  <td-step #detailsStep label="{{'Denomination details' | translate}}"
+           [state]="form.valid ? 'complete' : form.pristine ? 'none' : 'required'" [active]="true">
+    <form [formGroup]="form" layout="column">
+      <mat-form-field layout-margin flex>
+        <textarea matInput placeholder="{{'Note(Optional)' | translate}}" formControlName="note"></textarea>
+        <mat-error *ngIf="form.get('note').hasError('required')" translate>
+          Required
+        </mat-error>
+      </mat-form-field>
+      <div layout-gt-xs="column" layout-margin formArrayName="details">
+        <h4 translate>Details</h4>
+        <div *ngFor="let detail of details; let i=index" [formGroupName]="i" layout="row">
+          <fims-text-input type="number" [form]="detail" controlName="value" placeholder="{{'Note value' | translate}}"></fims-text-input>
+          <fims-text-input type="number" [form]="detail" controlName="count" placeholder="{{'Count' | translate}}"></fims-text-input>
+          <mat-form-field layout-margin>
+            <input matInput
+                   placeholder="{{'Subtotal' | translate}}"
+                   [disabled]="true"
+                   [value]="getTotalValue(detail) | displayFimsNumber"
+                   flex/>
+          </mat-form-field>
+          <button mat-button (click)="removeDetail(i)" *ngIf="i > 0">{{'Remove detail' | translate}}</button>
+        </div>
+        <button mat-button (click)="addDetail()">{{'Add detail' | translate}}</button>
+        <table td-data-table>
+          <tbody>
+            <tr td-data-table-row>
+              <td td-data-table-cell>
+                <b translate>Total</b>
+              </td>
+              <td td-data-table-cell>
+                <b>{{overallTotal | displayFimsNumber}}</b>
+              </td>
+              <td td-data-table-cell></td>
+              <td td-data-table-cell></td>
+            </tr>
+            <tr td-data-table-row>
+              <td td-data-table-cell>
+                <b translate>Cash on hand</b>
+              </td>
+              <td td-data-table-cell>
+                <b>{{balanceSheet?.cashOnHand | displayFimsNumber}}</b>
+              </td>
+              <td td-data-table-cell></td>
+              <td td-data-table-cell></td>
+            </tr>
+          </tbody>
+        </table>
+      </div>
+    </form>
+    <ng-template td-step-actions>
+      <fims-form-final-action
+        [resourceName]="'DENOMINATION'"
+        [editMode]="false"
+        [disabled]="form.invalid"
+        (onCancel)="cancel()"
+        (onSave)="save()" flex>
+      </fims-form-final-action>
+    </ng-template>
+  </td-step>
+</td-steps>
diff --git a/src/app/offices/detail/teller/detail/denomination/form/form.component.ts b/src/app/offices/detail/teller/detail/denomination/form/form.component.ts
new file mode 100644
index 0000000..c89cd1f
--- /dev/null
+++ b/src/app/offices/detail/teller/detail/denomination/form/form.component.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 {AbstractControl, FormArray, FormBuilder, FormGroup, Validators} from '@angular/forms';
+import {FimsValidators} from '../../../../../../common/validator/validators';
+import {Component, EventEmitter, Input, Output} from '@angular/core';
+import {TellerDenomination} from '../../../../../../services/teller/domain/teller-denomination.model';
+import {TellerBalanceSheet} from '../../../../../../services/teller/domain/teller-balance-sheet.model';
+
+interface Detail {
+  label: string;
+  value: string;
+  units: string;
+  totalValue: string;
+}
+
+@Component({
+  selector: 'fims-denomination-form-component',
+  templateUrl: './form.component.html'
+})
+export class DenominationFormComponent {
+
+  form: FormGroup;
+
+  @Input() balanceSheet: TellerBalanceSheet;
+
+  @Output() onSave = new EventEmitter<TellerDenomination>();
+
+  @Output() onCancel = new EventEmitter<void>();
+
+  constructor(private formBuilder: FormBuilder) {
+    this.form = this.formBuilder.group({
+      note: [''],
+      details: formBuilder.array([])
+    });
+
+    this.addDetail();
+  }
+
+  initDetail(): FormGroup {
+    return this.formBuilder.group({
+      value: ['', [Validators.required, FimsValidators.minValue(0)]],
+      count: ['', [Validators.required, FimsValidators.minValue(0)]]
+    });
+  }
+
+  addDetail(): void {
+    const options: FormArray = this.form.get('details') as FormArray;
+    options.push(this.initDetail());
+  }
+
+  removeDetail(index: number): void {
+    const options: FormArray = this.form.get('details') as FormArray;
+    options.removeAt(index);
+  }
+
+  get details(): FormGroup[] {
+    const charges: FormArray = this.form.get('details') as FormArray;
+    return charges.controls as FormGroup[];
+  }
+
+  save(): void {
+    const denomination: TellerDenomination = {
+      note: this.form.get('note').value,
+      countedTotal: this.overallTotal.toString(10)
+    };
+
+    this.onSave.emit(denomination);
+  }
+
+  cancel(): void {
+    this.onCancel.emit();
+  }
+
+  getTotalValue(formGroup: FormGroup): number {
+    const value = formGroup.get('value').value;
+    const units = formGroup.get('count').value;
+    if (value && units) {
+      return parseFloat(value) * parseFloat(units);
+    }
+    return 0;
+  }
+
+  get overallTotal(): number {
+    return this.details.reduce((previous, current) => {
+      return previous + this.getTotalValue(current)
+    }, 0)
+  }
+}
diff --git a/src/app/offices/detail/teller/detail/teller.detail.component.html b/src/app/offices/detail/teller/detail/teller.detail.component.html
new file mode 100644
index 0000000..5d2098f
--- /dev/null
+++ b/src/app/offices/detail/teller/detail/teller.detail.component.html
@@ -0,0 +1,93 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<fims-layout-card-over title="{{ 'Teller' | translate }}" [navigateBackTo]="['../../../../../']">
+  <td-message *ngIf="isClosed$ | async" label="{{'Teller can be opened' | translate }}" color="warn" icon="error">
+    <a td-message-actions mat-button [routerLink]="['command']" [queryParams]="{ action: 'OPEN'}"
+            *hasPermission="{ id: 'teller_management', accessLevel: 'CHANGE'}" translate>OPEN TELLER
+    </a>
+  </td-message>
+  <td-message *ngIf="!(isClosed$ | async)" label="{{'Teller can be closed' | translate }}" color="accent" icon="check">
+    <a td-message-actions mat-button [routerLink]="['command']" [queryParams]="{ action: 'CLOSE'}"
+            *hasPermission="{ id: 'teller_management', accessLevel: 'CHANGE'}" translate>CLOSE TELLER
+    </a>
+  </td-message>
+  <fims-two-column-layout>
+    <mat-nav-list left>
+      <h3 mat-subheader translate>Management</h3>
+      <a mat-list-item [routerLink]="['balance']">
+        <mat-icon matListAvatar>account_balance</mat-icon>
+        <h3 matLine translate>Teller balance</h3>
+        <p matLine translate>View current teller balance</p>
+      </a>
+      <a mat-list-item [routerLink]="['denominations']">
+        <mat-icon matListAvatar>attach_money</mat-icon>
+        <h3 matLine translate>Denominations</h3>
+        <p matLine translate>Manage denominations</p>
+      </a>
+    </mat-nav-list>
+    <mat-list *ngIf="teller$ | async as teller" right>
+      <h3 mat-subheader translate>Current status</h3>
+      <fims-state-display [state]="teller.state"></fims-state-display>
+      <mat-list-item>
+        <h3 matLine translate>Number</h3>
+        <p matLine>{{teller.code}}</p>
+      </mat-list-item>
+      <mat-list-item>
+        <h3 matLine translate>Cash withdrawal limit</h3>
+        <p matLine>{{teller.cashdrawLimit}}</p>
+      </mat-list-item>
+      <mat-list-item>
+        <h3 matLine translate>Teller account</h3>
+        <p matLine>{{teller.tellerAccountIdentifier}}</p>
+      </mat-list-item>
+      <mat-list-item>
+        <h3 matLine translate>Vault account</h3>
+        <p matLine>{{teller.vaultAccountIdentifier}}</p>
+      </mat-list-item>
+      <mat-list-item>
+        <h3 matLine translate>Cheques receivable account</h3>
+        <p matLine>{{teller.chequesReceivableAccount}}</p>
+      </mat-list-item>
+      <mat-list-item>
+        <h3 matLine translate>Cash over short account</h3>
+        <p matLine>{{teller.cashOverShortAccount}}</p>
+      </mat-list-item>
+      <mat-list-item>
+        <h3 matLine translate>Denomination required?</h3>
+        <p matLine>{{teller.denominationRequired}}</p>
+      </mat-list-item>
+      <mat-list-item>
+        <h3 matLine translate>Assigned employee</h3>
+        <p matLine>{{teller.assignedEmployee}}</p>
+      </mat-list-item>
+      <mat-list-item>
+        <h3 matLine translate>Created by</h3>
+        <p matLine>{{teller.createdBy}} - {{teller.createdOn | date:'medium'}}</p>
+      </mat-list-item>
+      <mat-list-item>
+        <h3 matLine translate>Last modified by</h3>
+        <p matLine>{{teller.lastModifiedBy}} - {{teller.lastModifiedOn | date:'medium'}}</p>
+      </mat-list-item>
+      <mat-list-item>
+        <h3 matLine translate>Last opened by</h3>
+        <p matLine>{{teller.lastOpenedBy}} - {{teller.lastOpenedOn | date:'medium'}}</p>
+      </mat-list-item>
+    </mat-list>
+  </fims-two-column-layout>
+</fims-layout-card-over>
+<fims-fab-button title="{{'Edit teller' | translate}}" icon="mode_edit" [link]="['edit']" [permission]="{ id: 'teller_management', accessLevel: 'CHANGE'}"></fims-fab-button>
diff --git a/src/app/offices/detail/teller/detail/teller.detail.component.ts b/src/app/offices/detail/teller/detail/teller.detail.component.ts
new file mode 100644
index 0000000..9a31b90
--- /dev/null
+++ b/src/app/offices/detail/teller/detail/teller.detail.component.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 {Component, OnInit} from '@angular/core';
+import * as fromOffices from '../../../store/index';
+import {OfficesStore} from '../../../store/index';
+import {Teller} from '../../../../services/teller/domain/teller.model';
+import {Observable} from 'rxjs/Observable';
+
+@Component({
+  templateUrl: './teller.detail.component.html'
+})
+export class OfficeTellerDetailComponent implements OnInit {
+
+  teller$: Observable<Teller>;
+
+  isClosed$: Observable<boolean>;
+
+  constructor(private store: OfficesStore) {}
+
+  ngOnInit(): void {
+    this.teller$ = this.store.select(fromOffices.getSelectedTeller)
+      .filter(teller => !!teller);
+
+    this.isClosed$ = this.teller$
+      .map(teller => teller.state === 'CLOSED');
+
+  }
+
+}
diff --git a/src/app/offices/detail/teller/form/create.form.component.html b/src/app/offices/detail/teller/form/create.form.component.html
new file mode 100644
index 0000000..9d60987
--- /dev/null
+++ b/src/app/offices/detail/teller/form/create.form.component.html
@@ -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.
+-->
+
+<fims-layout-card-over title="{{'Create new teller' | translate}}">
+  <fims-teller-form-component #form
+                              (onSave)="onSave($event)"
+                              (onCancel)="onCancel()"
+                              [teller]="teller">
+  </fims-teller-form-component>
+</fims-layout-card-over>
diff --git a/src/app/offices/detail/teller/form/create.form.component.ts b/src/app/offices/detail/teller/form/create.form.component.ts
new file mode 100644
index 0000000..2cec565
--- /dev/null
+++ b/src/app/offices/detail/teller/form/create.form.component.ts
@@ -0,0 +1,86 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, OnDestroy, ViewChild} from '@angular/core';
+import {Subscription} from 'rxjs/Subscription';
+import {Teller} from '../../../../services/teller/domain/teller.model';
+import {OfficeTellerFormComponent} from './form.component';
+import {ActivatedRoute, Router} from '@angular/router';
+import * as fromTeller from '../../../store/index';
+import {OfficesStore} from '../../../store/index';
+import {CREATE_TELLER, RESET_FORM} from '../../../store/teller/teller.actions';
+import {Error} from '../../../../services/domain/error.model';
+import {Office} from '../../../../services/office/domain/office.model';
+
+@Component({
+  templateUrl: './create.form.component.html'
+})
+export class CreateOfficeTellerFormComponent implements OnDestroy {
+
+  private officeSubscription: Subscription;
+
+  private formStateSubscription: Subscription;
+
+  private office: Office;
+
+  teller: Teller = {
+    code: '',
+    password: '',
+    cashdrawLimit: undefined,
+    tellerAccountIdentifier: '',
+    vaultAccountIdentifier: '',
+    chequesReceivableAccount: '',
+    cashOverShortAccount: '',
+    denominationRequired: false
+  };
+
+  @ViewChild('form') formComponent: OfficeTellerFormComponent;
+
+  constructor(private router: Router, private route: ActivatedRoute, private store: OfficesStore) {
+    this.officeSubscription = this.store.select(fromTeller.getSelectedOffice)
+      .filter(office => !!office)
+      .subscribe(office => this.office = office);
+
+    this.formStateSubscription = store.select(fromTeller.getTellerFormError)
+      .filter((error: Error) => !!error)
+      .subscribe((error: Error) => this.formComponent.showCodeValidationError());
+  }
+
+  ngOnDestroy(): void {
+    this.formStateSubscription.unsubscribe();
+    this.officeSubscription.unsubscribe();
+    this.store.dispatch({ type: RESET_FORM });
+  }
+
+  onSave(teller: Teller): void {
+    this.store.dispatch({ type: CREATE_TELLER, payload: {
+      officeId: this.office.identifier,
+      teller,
+      activatedRoute: this.route
+    }});
+  }
+
+  onCancel(): void {
+    this.navigateAway();
+  }
+
+  navigateAway(): void {
+    this.router.navigate(['../'], { relativeTo: this.route });
+  }
+
+}
diff --git a/src/app/offices/detail/teller/form/edit.form.component.html b/src/app/offices/detail/teller/form/edit.form.component.html
new file mode 100644
index 0000000..3e176e6
--- /dev/null
+++ b/src/app/offices/detail/teller/form/edit.form.component.html
@@ -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.
+-->
+
+<fims-layout-card-over title="{{'Edit teller' | translate}}">
+  <fims-teller-form-component #form
+                              (onSave)="onSave($event)"
+                              (onCancel)="onCancel()"
+                              [teller]="teller$ | async"
+                              [editMode]="true">
+  </fims-teller-form-component>
+</fims-layout-card-over>
diff --git a/src/app/offices/detail/teller/form/edit.form.component.ts b/src/app/offices/detail/teller/form/edit.form.component.ts
new file mode 100644
index 0000000..0d0d4e5
--- /dev/null
+++ b/src/app/offices/detail/teller/form/edit.form.component.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 {Component, OnDestroy, OnInit} from '@angular/core';
+import {Teller} from '../../../../services/teller/domain/teller.model';
+import {ActivatedRoute, Router} from '@angular/router';
+import * as fromOffices from '../../../store/index';
+import {OfficesStore} from '../../../store/index';
+import {RESET_FORM, UPDATE_TELLER} from '../../../store/teller/teller.actions';
+import {Observable} from 'rxjs/Observable';
+import {Subscription} from 'rxjs/Subscription';
+import {Office} from '../../../../services/office/domain/office.model';
+
+@Component({
+  templateUrl: './edit.form.component.html'
+})
+export class EditOfficeTellerFormComponent implements OnInit, OnDestroy {
+
+  private officeSubscription: Subscription;
+
+  private office: Office;
+
+  teller$: Observable<Teller>;
+
+  constructor(private router: Router, private route: ActivatedRoute, private store: OfficesStore) {}
+
+  ngOnInit(): void {
+    this.teller$ = this.store.select(fromOffices.getSelectedTeller)
+      .filter(teller => !!teller);
+
+    this.officeSubscription = this.store.select(fromOffices.getSelectedOffice)
+      .filter(office => !!office)
+      .subscribe(office => this.office = office);
+  }
+
+  ngOnDestroy(): void {
+    this.store.dispatch({ type: RESET_FORM });
+    this.officeSubscription.unsubscribe();
+  }
+
+  onSave(teller: Teller): void {
+    this.store.dispatch({ type: UPDATE_TELLER, payload: {
+      officeId: this.office.identifier,
+      teller,
+      activatedRoute: this.route
+    }});
+  }
+
+  onCancel(): void {
+    this.navigateAway();
+  }
+
+  navigateAway(): void {
+    this.router.navigate(['../'], { relativeTo: this.route });
+  }
+
+}
diff --git a/src/app/offices/detail/teller/form/form.component.html b/src/app/offices/detail/teller/form/form.component.html
new file mode 100644
index 0000000..a82d0a0
--- /dev/null
+++ b/src/app/offices/detail/teller/form/form.component.html
@@ -0,0 +1,78 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<td-steps mode="'vertical'">
+  <td-step #detailsStep label="{{'Teller details' | translate}}"
+           [state]="form.valid ? 'complete' : form.pristine ? 'none' : 'required'">
+    <form [formGroup]="form" layout="column">
+      <fims-id-input [form]="form" [placeholder]="'Number'" controlName="code" [readonly]="editMode"></fims-id-input>
+      <mat-form-field layout-margin flex>
+        <input matInput type="password" placeholder="{{'Password' | translate}}" formControlName="password" tdAutoTrim autocomplete="new-password"/>
+        <mat-error *ngIf="form.get('password').hasError('required')" translate>Required</mat-error>
+        <mat-error *ngIf="form.get('password').hasError('minlength')">
+          {{ 'Must have at least characters.' | translate:{ value: form.get('password').getError('minlength')['requiredLength']} }}
+        </mat-error>
+        <mat-error *ngIf="form.get('password').hasError('maxlength')">
+          {{ 'Only characters allowed.' | translate:{ value: form.get('password').getError('maxlength')['requiredLength']} }}
+        </mat-error>
+      </mat-form-field>
+      <fims-text-input type="number" [form]="form" controlName="cashdrawLimit" placeholder="{{'Cash withdrawal limit' | translate}}"></fims-text-input>
+      <fims-account-select title="{{'Teller account(Asset accounts only)' | translate}}" formControlName="tellerAccountIdentifier" [type]="'ASSET'">
+        <ng-container *ngIf="!form.get('tellerAccountIdentifier').pristine && form.get('tellerAccountIdentifier').hasError('required')" translate>
+          Required
+        </ng-container>
+        <ng-container *ngIf="form.get('tellerAccountIdentifier').hasError('invalidAccount')" translate>
+          Invalid account
+        </ng-container>
+      </fims-account-select>
+      <fims-account-select title="{{'Vault account(Asset accounts only)' | translate}}" formControlName="vaultAccountIdentifier" [type]="'ASSET'">
+        <ng-container *ngIf="!form.get('vaultAccountIdentifier').pristine && form.get('vaultAccountIdentifier').hasError('required')" translate>
+          Required
+        </ng-container>
+        <ng-container *ngIf="form.get('vaultAccountIdentifier').hasError('invalidAccount')" translate>
+          Invalid account
+        </ng-container>
+      </fims-account-select>
+      <fims-account-select title="{{'Cheques receivable account(Asset accounts only)' | translate}}" formControlName="chequesReceivableAccount" [type]="'ASSET'">
+        <ng-container *ngIf="!form.get('chequesReceivableAccount').pristine && form.get('chequesReceivableAccount').hasError('required')" translate>
+          Required
+        </ng-container>
+        <ng-container *ngIf="form.get('chequesReceivableAccount').hasError('invalidAccount')" translate>
+          Invalid account
+        </ng-container>
+      </fims-account-select>
+      <fims-account-select title="{{'Cash over short account(Expense accounts only)' | translate}}" formControlName="cashOverShortAccount" [type]="'EXPENSE'">
+        <ng-container *ngIf="!form.get('cashOverShortAccount').pristine && form.get('cashOverShortAccount').hasError('required')" translate>
+          Required
+        </ng-container>
+        <ng-container *ngIf="form.get('cashOverShortAccount').hasError('invalidAccount')" translate>
+          Invalid account
+        </ng-container>
+      </fims-account-select>
+      <mat-checkbox formControlName="denominationRequired" layout-margin translate>Denomination required?</mat-checkbox>
+    </form>
+    <ng-template td-step-actions>
+      <fims-form-final-action
+        [resourceName]="'TELLER'"
+        [editMode]="editMode"
+        [disabled]="form.invalid"
+        (onCancel)="cancel()"
+        (onSave)="save()" flex>
+      </fims-form-final-action>
+    </ng-template>
+  </td-step>
+</td-steps>
diff --git a/src/app/offices/detail/teller/form/form.component.spec.ts b/src/app/offices/detail/teller/form/form.component.spec.ts
new file mode 100644
index 0000000..d5d8231
--- /dev/null
+++ b/src/app/offices/detail/teller/form/form.component.spec.ts
@@ -0,0 +1,125 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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} from '@angular/core';
+import {async, ComponentFixture, TestBed} from '@angular/core/testing';
+import {By} from '@angular/platform-browser';
+import {TranslateModule} from '@ngx-translate/core';
+import {ReactiveFormsModule} from '@angular/forms';
+import {CovalentStepsModule} from '@covalent/core';
+import {NoopAnimationsModule} from '@angular/platform-browser/animations';
+import {FimsSharedModule} from '../../../../common/common.module';
+import {OfficeTellerFormComponent} from './form.component';
+import {Teller} from '../../../../services/teller/domain/teller.model';
+import {AccountingService} from '../../../../services/accounting/accounting.service';
+import {MatCheckboxModule, MatInputModule, MatRadioModule} from '@angular/material';
+
+describe('Test teller form', () => {
+
+  let fixture: ComponentFixture<TestComponent>;
+
+  let testComponent: TestComponent;
+
+  beforeEach( async(() => {
+    TestBed.configureTestingModule({
+      imports: [
+        TranslateModule.forRoot(),
+        MatCheckboxModule,
+        MatRadioModule,
+        MatInputModule,
+        CovalentStepsModule,
+        FimsSharedModule,
+        ReactiveFormsModule,
+        NoopAnimationsModule
+      ],
+      declarations: [
+        OfficeTellerFormComponent,
+        TestComponent
+      ],
+      providers: [
+        {
+          provide: AccountingService, useClass: class {
+          findAccount = jasmine.createSpy('findAccount').and.returnValue({});
+          fetchAccounts = jasmine.createSpy('fetchAccounts').and.returnValue([]);
+        }
+        }
+      ]
+    }).compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture  = TestBed.createComponent(TestComponent);
+    testComponent = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should trigger save event', () => {
+    const button: DebugElement = fixture.debugElement.query(By.css('button[mat-raised-button]'));
+
+    button.nativeElement.click();
+
+    expect(testComponent.savedTeller).toEqual(testComponent.teller);
+  });
+
+  it('should trigger cancel event', () => {
+    const button: DebugElement = fixture.debugElement.query(By.css('button[mat-button]'));
+
+    button.nativeElement.click();
+
+    expect(testComponent.canceled).toBeTruthy();
+  });
+
+});
+
+@Component({
+  template: `
+    <fims-teller-form-component (onSave)="onSave($event)" (onCancel)="onCancel($event)" [teller]="teller" [editMode]="true">
+    </fims-teller-form-component>`
+})
+class TestComponent {
+
+  teller: Teller = {
+    code: 'code',
+    password: 'password',
+    cashdrawLimit: 10,
+    tellerAccountIdentifier: 'tellerAccountIdentifier',
+    vaultAccountIdentifier: 'vaultAccountIdentifier',
+    chequesReceivableAccount: 'chequesReceivableAccount',
+    cashOverShortAccount: 'cashOverShortAccount',
+    denominationRequired: false,
+    assignedEmployee: 'assignedEmployee',
+    createdBy: 'createdBy',
+    createdOn: 'createdOn',
+    lastModifiedBy: 'lastModifiedBy',
+    lastModifiedOn: 'lastModifiedOn',
+    state: 'ACTIVE'
+  };
+
+  savedTeller: Teller;
+
+  canceled: boolean;
+
+  onSave(teller: Teller): void {
+    this.savedTeller = teller;
+  }
+
+  onCancel(): void {
+    this.canceled = true;
+  }
+
+}
diff --git a/src/app/offices/detail/teller/form/form.component.ts b/src/app/offices/detail/teller/form/form.component.ts
new file mode 100644
index 0000000..ccfcce7
--- /dev/null
+++ b/src/app/offices/detail/teller/form/form.component.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 {Component, EventEmitter, Input, Output, ViewChild} from '@angular/core';
+import {Teller} from '../../../../services/teller/domain/teller.model';
+import {FormBuilder, Validators} from '@angular/forms';
+import {FimsValidators} from '../../../../common/validator/validators';
+import {AccountingService} from '../../../../services/accounting/accounting.service';
+import {accountExists} from '../../../../common/validator/account-exists.validator';
+import {FormComponent} from '../../../../common/forms/form.component';
+import {TdStepComponent} from '@covalent/core';
+
+@Component({
+  selector: 'fims-teller-form-component',
+  templateUrl: './form.component.html'
+})
+export class OfficeTellerFormComponent extends FormComponent<Teller> {
+
+  private _teller: Teller;
+
+  @ViewChild('detailsStep') step: TdStepComponent;
+
+  @Input() set teller(teller: Teller) {
+    this._teller = teller;
+    this.prepareForm(teller);
+  }
+
+  @Input('editMode') editMode: boolean;
+
+  @Output() onSave = new EventEmitter<Teller>();
+
+  @Output() onCancel = new EventEmitter<void>();
+
+  constructor(private formBuilder: FormBuilder, private accountService: AccountingService) {
+    super();
+  }
+
+  prepareForm(teller: Teller): void {
+    this.form = this.formBuilder.group({
+      code: [teller.code, [Validators.required, Validators.minLength(3), Validators.maxLength(32), FimsValidators.urlSafe]],
+      password: [teller.password, [Validators.required, Validators.minLength(8), Validators.maxLength(4096)]],
+      cashdrawLimit: [teller.cashdrawLimit, [Validators.required, FimsValidators.greaterThanValue(0)]],
+      tellerAccountIdentifier: [teller.tellerAccountIdentifier, [Validators.required], accountExists(this.accountService)],
+      vaultAccountIdentifier: [teller.vaultAccountIdentifier, [Validators.required], accountExists(this.accountService)],
+      chequesReceivableAccount: [teller.chequesReceivableAccount, [Validators.required], accountExists(this.accountService)],
+      cashOverShortAccount: [teller.cashOverShortAccount, [Validators.required], accountExists(this.accountService)],
+      denominationRequired: [teller.denominationRequired]
+    });
+
+    this.step.open();
+  }
+
+  save(): void {
+    const teller: Teller = Object.assign({}, this.teller, {
+      code: this.form.get('code').value,
+      password: this.form.get('password').value,
+      cashdrawLimit: this.form.get('cashdrawLimit').value,
+      tellerAccountIdentifier: this.form.get('tellerAccountIdentifier').value,
+      vaultAccountIdentifier: this.form.get('vaultAccountIdentifier').value,
+      chequesReceivableAccount: this.form.get('chequesReceivableAccount').value,
+      cashOverShortAccount: this.form.get('cashOverShortAccount').value,
+      denominationRequired: this.form.get('denominationRequired').value
+    });
+
+    this.onSave.emit(teller);
+  }
+
+  cancel(): void {
+    this.onCancel.emit();
+  }
+
+  get formData(): Teller {
+    // Not needed
+    return null;
+  }
+
+  showCodeValidationError(): void {
+    this.setError('code', 'unique', true);
+  }
+
+  get teller(): Teller {
+    return this._teller;
+  }
+}
diff --git a/src/app/offices/detail/teller/teller-exists.guard.ts b/src/app/offices/detail/teller/teller-exists.guard.ts
new file mode 100644
index 0000000..5211c6a
--- /dev/null
+++ b/src/app/offices/detail/teller/teller-exists.guard.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 {ActivatedRouteSnapshot, CanActivate, RouterStateSnapshot} from '@angular/router';
+import {Injectable} from '@angular/core';
+import * as fromOffices from '../../store';
+import {Observable} from 'rxjs/Observable';
+import {of} from 'rxjs/observable/of';
+import {OfficesStore} from '../../store/index';
+import {TellerService} from '../../../services/teller/teller-service';
+import {LoadAction} from '../../store/teller/teller.actions';
+import {ExistsGuardService} from '../../../common/guards/exists-guard';
+
+@Injectable()
+export class TellerExistsGuard implements CanActivate {
+
+  constructor(private store: OfficesStore,
+              private tellerService: TellerService,
+              private existsGuardService: ExistsGuardService) {
+  }
+
+  hasTellerInStore(id: string): Observable<boolean> {
+    const timestamp$: Observable<number> = this.store.select(fromOffices.getTellersLoadedAt)
+      .map(loadedAt => loadedAt[id]);
+
+    return this.existsGuardService.isWithinExpiry(timestamp$);
+  }
+
+  hasTellerInApi(officeId: string, tellerCode: string): Observable<boolean> {
+    const getTeller$: Observable<any> = this.tellerService.find(officeId, tellerCode)
+      .map(tellerEntity => new LoadAction({
+        resource: tellerEntity
+      }))
+      .do((action: LoadAction) => this.store.dispatch(action))
+      .map(customer => !!customer);
+
+    return this.existsGuardService.routeTo404OnError(getTeller$);
+  }
+
+  hasTeller(officeId: string, tellerCode: string): Observable<boolean> {
+    return this.hasTellerInStore(tellerCode)
+      .switchMap(inStore => {
+        if (inStore) {
+          return of(inStore);
+        }
+        return this.hasTellerInApi(officeId, tellerCode);
+      });
+  }
+
+  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
+    return this.hasTeller(route.parent.params['id'], route.params['code']);
+  }
+}
diff --git a/src/app/offices/detail/teller/teller.index.component.html b/src/app/offices/detail/teller/teller.index.component.html
new file mode 100644
index 0000000..ca721b3
--- /dev/null
+++ b/src/app/offices/detail/teller/teller.index.component.html
@@ -0,0 +1,18 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<router-outlet></router-outlet>
diff --git a/src/app/offices/detail/teller/teller.index.component.ts b/src/app/offices/detail/teller/teller.index.component.ts
new file mode 100644
index 0000000..1feabc6
--- /dev/null
+++ b/src/app/offices/detail/teller/teller.index.component.ts
@@ -0,0 +1,44 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, OnDestroy, OnInit} from '@angular/core';
+import {ActivatedRoute} from '@angular/router';
+import {Subscription} from 'rxjs/Subscription';
+import {OfficesStore} from '../../store/index';
+import {SelectAction} from '../../store/teller/teller.actions';
+
+@Component({
+  templateUrl: './teller.index.component.html',
+})
+export class OfficeTellerIndexComponent implements OnInit, OnDestroy {
+
+  private actionsSubscription: Subscription;
+
+  constructor(private store: OfficesStore, private route: ActivatedRoute) {}
+
+  ngOnInit(): void {
+    this.actionsSubscription = this.route.params
+      .map(params => new SelectAction(params['code']))
+      .subscribe(this.store);
+  }
+
+  ngOnDestroy(): void {
+    this.actionsSubscription.unsubscribe();
+  }
+}
diff --git a/src/app/offices/detail/teller/teller.list.component.html b/src/app/offices/detail/teller/teller.list.component.html
new file mode 100644
index 0000000..519ad04
--- /dev/null
+++ b/src/app/offices/detail/teller/teller.list.component.html
@@ -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.
+-->
+
+<fims-layout-card-over title="{{ 'Manage teller' | translate}}" [navigateBackTo]="['../']">
+  <fims-data-table flex
+                   (onActionCellClick)="rowSelect($event)"
+                   [columns]="columns"
+                   [data]="tellerData$ | async"
+                   [sortable]="false"
+                   [pageable]="false">
+  </fims-data-table>
+</fims-layout-card-over>
+<fims-fab-button title="{{'Create teller' | translate}}" icon="add" [link]="['create']" [permission]="{ id: 'teller_management', accessLevel: 'CHANGE'}"></fims-fab-button>
diff --git a/src/app/offices/detail/teller/teller.list.component.ts b/src/app/offices/detail/teller/teller.list.component.ts
new file mode 100644
index 0000000..1d6bb46
--- /dev/null
+++ b/src/app/offices/detail/teller/teller.list.component.ts
@@ -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.
+ */
+import {Component, OnDestroy, OnInit} from '@angular/core';
+import {TableData} from '../../../common/data-table/data-table.component';
+import {Observable} from 'rxjs/Observable';
+import {getAllTellerEntities, getSelectedOffice, OfficesStore} from '../../store/index';
+import {ActivatedRoute, Router} from '@angular/router';
+import {Subscription} from 'rxjs/Subscription';
+import {Teller} from '../../../services/teller/domain/teller.model';
+import {LoadTellerAction} from '../../store/teller/teller.actions';
+import {DatePipe} from '@angular/common';
+
+@Component({
+  templateUrl: './teller.list.component.html',
+  providers: [DatePipe]
+})
+export class OfficeTellerListComponent implements OnInit, OnDestroy {
+
+  private loadTellerSubscription: Subscription;
+
+  tellerData$: Observable<TableData>;
+
+  columns: any[] = [
+    {name: 'code', label: 'Number'},
+    {name: 'cashdrawLimit', label: 'Cash withdrawal limit'},
+    {name: 'assignedEmployee', label: 'Assigned employee'},
+    {name: 'state', label: 'Status'},
+    {
+      name: 'lastOpenedOn', label: 'Last opened on', format: (v: any) => {
+      return this.datePipe.transform(v, 'short');
+    }}
+  ];
+
+  constructor(private store: OfficesStore, private route: ActivatedRoute, private router: Router, private datePipe: DatePipe) {}
+
+  ngOnInit(): void {
+    this.loadTellerSubscription = this.store.select(getSelectedOffice)
+      .filter(office => !!office)
+      .map(office => new LoadTellerAction(office.identifier))
+      .subscribe(this.store);
+
+    this.tellerData$ = this.store.select(getAllTellerEntities)
+      .map(tellers => ({
+        data: tellers,
+        totalElements: tellers.length,
+        totalPages: 1
+      }));
+  }
+
+  ngOnDestroy(): void {
+    this.loadTellerSubscription.unsubscribe();
+  }
+
+  rowSelect(teller: Teller): void {
+    this.router.navigate(['detail', teller.code], {relativeTo: this.route});
+  }
+
+}
diff --git a/src/app/offices/form/create/create.form.component.html b/src/app/offices/form/create/create.form.component.html
new file mode 100644
index 0000000..ef6d293
--- /dev/null
+++ b/src/app/offices/form/create/create.form.component.html
@@ -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.
+-->
+
+<fims-layout-card-over title="{{'Create new office' | translate}}">
+  <fims-office-form-component #form (onSave)="onSave($event)" (onCancel)="onCancel()" [office]="office"></fims-office-form-component>
+</fims-layout-card-over>
diff --git a/src/app/offices/form/create/create.form.component.ts b/src/app/offices/form/create/create.form.component.ts
new file mode 100644
index 0000000..cb791c4
--- /dev/null
+++ b/src/app/offices/form/create/create.form.component.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 {Component, OnDestroy, OnInit, ViewChild} from '@angular/core';
+import {ActivatedRoute, Router} from '@angular/router';
+import {OfficeFormComponent} from '../form.component';
+import {Office} from '../../../services/office/domain/office.model';
+import * as fromOffice from '../../store';
+import {CREATE, CREATE_BRANCH, RESET_FORM} from '../../store/office.actions';
+import {Error} from '../../../services/domain/error.model';
+import {Subscription} from 'rxjs/Subscription';
+import {OfficesStore} from '../../store/index';
+
+@Component({
+  templateUrl: './create.form.component.html'
+})
+export class CreateOfficeFormComponent implements OnInit, OnDestroy {
+
+  private formStateSubscription: Subscription;
+
+  private parentIdentifier: string;
+
+  office: Office = { identifier: '', parentIdentifier: '', name: '' };
+
+  @ViewChild('form') formComponent: OfficeFormComponent;
+
+  constructor(private router: Router, private route: ActivatedRoute, private store: OfficesStore) {
+
+    this.formStateSubscription = store.select(fromOffice.getOfficeFormError)
+      .filter((error: Error) => !!error)
+      .subscribe((error: Error) => {
+        const officeDetailForm = this.formComponent.detailForm;
+        const errors = officeDetailForm.get('identifier').errors || {};
+        errors['unique'] = true;
+        officeDetailForm.get('identifier').setErrors(errors);
+        this.formComponent.step.open();
+    });
+  }
+
+  ngOnInit(): void {
+    this.route.queryParams.subscribe((queryParams) => {
+      this.parentIdentifier = queryParams['parentId'];
+    });
+  }
+
+  ngOnDestroy(): void {
+    this.formStateSubscription.unsubscribe();
+
+    this.store.dispatch({ type: RESET_FORM });
+  }
+
+  onSave(office: Office): void {
+    if (this.parentIdentifier) {
+      office.parentIdentifier = this.parentIdentifier;
+      this.store.dispatch({ type: CREATE_BRANCH, payload: {
+        office,
+        activatedRoute: this.route
+      }});
+    } else {
+      this.store.dispatch({ type: CREATE, payload: {
+        office,
+        activatedRoute: this.route
+      }});
+    }
+  }
+
+  onCancel(): void {
+    this.navigateAway();
+  }
+
+  navigateAway(): void {
+    if (this.parentIdentifier) {
+      this.router.navigate(['../detail', this.parentIdentifier ], { relativeTo: this.route });
+    } else {
+      this.router.navigate(['../'], { relativeTo: this.route });
+    }
+  }
+
+}
diff --git a/src/app/offices/form/edit/edit.form.component.html b/src/app/offices/form/edit/edit.form.component.html
new file mode 100644
index 0000000..f13d5da
--- /dev/null
+++ b/src/app/offices/form/edit/edit.form.component.html
@@ -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.
+-->
+
+<fims-layout-card-over title="{{'Edit office' | translate}}">
+  <fims-office-form-component #form [editMode]="true" (onSave)="onSave($event)" (onCancel)="onCancel()" [office]="office"></fims-office-form-component>
+</fims-layout-card-over>
diff --git a/src/app/offices/form/edit/edit.form.component.ts b/src/app/offices/form/edit/edit.form.component.ts
new file mode 100644
index 0000000..42e6064
--- /dev/null
+++ b/src/app/offices/form/edit/edit.form.component.ts
@@ -0,0 +1,56 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, OnDestroy, OnInit} from '@angular/core';
+import {ActivatedRoute, Router} from '@angular/router';
+import {Office} from '../../../services/office/domain/office.model';
+import {getSelectedOffice, OfficesStore} from '../../store';
+import {UPDATE} from '../../store/office.actions';
+import {Subscription} from 'rxjs/Subscription';
+
+@Component({
+  templateUrl: './edit.form.component.html'
+})
+export class EditOfficeFormComponent implements OnInit, OnDestroy {
+
+  private officeSubscription: Subscription;
+
+  office: Office;
+
+  constructor(private router: Router, private route: ActivatedRoute, private store: OfficesStore) {}
+
+  ngOnInit() {
+    this.officeSubscription = this.store.select(getSelectedOffice).subscribe((office: Office) => this.office = office);
+  }
+
+  ngOnDestroy(): void {
+    this.officeSubscription.unsubscribe();
+  }
+
+  onSave(office: Office) {
+    office.parentIdentifier = this.office.parentIdentifier;
+    this.store.dispatch({ type: UPDATE, payload: {
+      office,
+      activatedRoute: this.route
+    }});
+  };
+
+  onCancel() {
+    this.router.navigate(['../'], { relativeTo: this.route } );
+  }
+}
diff --git a/src/app/offices/form/form.component.html b/src/app/offices/form/form.component.html
new file mode 100644
index 0000000..852bf03
--- /dev/null
+++ b/src/app/offices/form/form.component.html
@@ -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.
+-->
+
+<td-steps mode="'vertical'">
+  <td-step #detailsStep label="{{'Office details' | translate}}"
+           [state]="detailForm.valid ? 'complete' : detailForm.pristine ? 'none' : 'required'">
+    <form [formGroup]="detailForm" layout="column">
+      <fims-id-input [form]="detailForm" controlName="identifier" [readonly]="editMode"></fims-id-input>
+      <fims-text-input [form]="detailForm" controlName="name" placeholder="{{'Name' | translate}}"></fims-text-input>
+      <fims-text-input [form]="detailForm" controlName="description" placeholder="{{'Description(optional)' | translate}}"></fims-text-input>
+    </form>
+    <ng-template td-step-actions>
+      <fims-form-continue-action (onContinue)="addressStep.open()"></fims-form-continue-action>
+    </ng-template>
+  </td-step>
+  <td-step #addressStep label="{{'Office Address(optional)' | translate}}"
+           [state]="addressForm.valid ? 'complete' : addressForm.pristine ? 'none' : 'required'">
+    <fims-address-form #addressForm [formData]="addressFormData"></fims-address-form>
+  </td-step>
+  <td-step label="{{'Final step' | translate}}" [state]="'complete'" class="disable-pointer">
+    <ng-template td-step-summary>
+      <fims-form-final-action
+        [resourceName]="'OFFICE'"
+        [editMode]="editMode"
+        [disabled]="formsInvalid()"
+        (onCancel)="cancel()"
+        (onSave)="save()" flex>
+      </fims-form-final-action>
+    </ng-template>
+  </td-step>
+</td-steps>
diff --git a/src/app/offices/form/form.component.spec.ts b/src/app/offices/form/form.component.spec.ts
new file mode 100644
index 0000000..0a1c024
--- /dev/null
+++ b/src/app/offices/form/form.component.spec.ts
@@ -0,0 +1,135 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, ViewChild} from '@angular/core';
+import {Office} from '../../services/office/domain/office.model';
+import {ComponentFixture, TestBed} from '@angular/core/testing';
+import {OfficeFormComponent} from './form.component';
+import {TranslateModule} from '@ngx-translate/core';
+import {CovalentStepsModule} from '@covalent/core';
+import {ReactiveFormsModule} from '@angular/forms';
+import {NoopAnimationsModule} from '@angular/platform-browser/animations';
+import {MatAutocompleteModule, MatInputModule} from '@angular/material';
+import {CountryService} from '../../services/country/country.service';
+import {Country} from '../../services/country/model/country.model';
+import {FimsSharedModule} from '../../common/common.module';
+
+const officeTemplate: Office = {
+  identifier: 'test',
+  name: 'test',
+  description: 'test',
+  address: {
+    street: 'street',
+    city: 'city',
+    region: 'region',
+    postalCode: '12345',
+    countryCode: 'CC',
+    country: 'country'
+  }
+};
+
+const country: Country = {
+  displayName: '',
+  name: officeTemplate.address.country,
+  alpha2Code: officeTemplate.address.countryCode,
+  translations: {}
+};
+
+describe('Test office form', () => {
+
+  let fixture: ComponentFixture<TestComponent>;
+
+  let testComponent: TestComponent;
+
+  beforeEach(() => {
+    TestBed.configureTestingModule({
+      imports: [
+        ReactiveFormsModule,
+        FimsSharedModule,
+        MatInputModule,
+        MatAutocompleteModule,
+        CovalentStepsModule,
+        TranslateModule.forRoot(),
+        NoopAnimationsModule
+      ],
+      providers: [
+        {
+          // Used by address component
+          provide: CountryService, useClass: class {
+            fetchByCountryCode = jasmine.createSpy('fetchByCountryCode').and.returnValue(country);
+            fetchCountries = jasmine.createSpy('fetchCountries').and.returnValue([country]);
+          }
+        }
+      ],
+      declarations: [
+        OfficeFormComponent,
+        TestComponent
+      ]
+    });
+
+    fixture = TestBed.createComponent(TestComponent);
+    testComponent = fixture.componentInstance;
+  });
+
+  it('should save address when pristine', (done: DoneFn) => {
+    fixture.detectChanges();
+    testComponent.saveEmitter.subscribe((office) => {
+      expect(office.identifier).toBe(officeTemplate.identifier);
+      expect(office.name).toBe(officeTemplate.name);
+      expect(office.description).toBe(officeTemplate.description);
+
+      expect(office.address).toBeDefined();
+
+      expect(office.address.street).toBe(officeTemplate.address.street);
+      expect(office.address.city).toBe(officeTemplate.address.city);
+      expect(office.address.region).toBe(officeTemplate.address.region);
+      expect(office.address.postalCode).toBe(officeTemplate.address.postalCode);
+      expect(office.address.countryCode).toBe(officeTemplate.address.countryCode);
+      expect(office.address.country).toBe(officeTemplate.address.country);
+
+      done();
+    });
+
+    testComponent.triggerSave();
+  });
+
+});
+
+@Component({
+  template: `
+    <fims-office-form-component #form (onSave)="onSave($event)" (onCancel)="onCancel($event)" [office]="office" [editMode]="false">
+    </fims-office-form-component>`
+})
+class TestComponent {
+
+  saveEmitter = new EventEmitter<Office>();
+
+  @ViewChild('form') formComponent: OfficeFormComponent;
+
+  office: Office = officeTemplate;
+
+  triggerSave(): void {
+    this.formComponent.save();
+  }
+
+  onSave(office: Office): void {
+    this.saveEmitter.emit(office);
+  }
+
+  onCancel(): void {}
+}
diff --git a/src/app/offices/form/form.component.ts b/src/app/offices/form/form.component.ts
new file mode 100644
index 0000000..b3ccc07
--- /dev/null
+++ b/src/app/offices/form/form.component.ts
@@ -0,0 +1,98 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, OnInit, Output, ViewChild} from '@angular/core';
+import {FormBuilder, FormGroup, Validators} from '@angular/forms';
+import {TdStepComponent} from '@covalent/core';
+import {Office} from '../../services/office/domain/office.model';
+import {FimsValidators} from '../../common/validator/validators';
+import {Address} from '../../services/domain/address/address.model';
+import {AddressFormComponent} from '../../common/address/address.component';
+
+
+@Component({
+  selector: 'fims-office-form-component',
+  templateUrl: './form.component.html'
+})
+export class OfficeFormComponent implements OnInit {
+
+  private _office: Office;
+
+  detailForm: FormGroup;
+
+  @ViewChild('detailsStep') step: TdStepComponent;
+
+  @ViewChild('addressForm') addressForm: AddressFormComponent;
+  addressFormData: Address;
+
+  @Input('editMode') editMode: boolean;
+
+  @Input('office') set office(office: Office) {
+    this._office = office;
+    this.prepareForm(office);
+  }
+
+  @Output('onSave') onSave = new EventEmitter<Office>();
+
+  @Output('onCancel') onCancel = new EventEmitter<void>();
+
+  constructor(private formBuilder: FormBuilder) {}
+
+  prepareForm(office: Office): void {
+    this.detailForm = this.formBuilder.group({
+      identifier: [office.identifier, [Validators.required, Validators.minLength(3), Validators.maxLength(32), FimsValidators.urlSafe]],
+      name: [office.name, [Validators.required, Validators.maxLength(256)]],
+      description: [office.description, Validators.maxLength(2048)]
+    });
+
+    this.addressFormData = office.address;
+  }
+
+  ngOnInit(): void {
+    this.step.open();
+  }
+
+  formsInvalid(): boolean {
+    return ((this.editMode || !this.addressForm.pristine) && !this.addressForm.valid) || this.detailForm.invalid;
+  }
+
+  save(): void {
+    const office: Office = {
+      identifier: this.detailForm.get('identifier').value,
+      name: this.detailForm.get('name').value,
+      description: this.detailForm.get('description').value
+    };
+
+    if (this.addressForm.pristine) {
+      office.address = this.office.address;
+    } else {
+      office.address = this.addressForm.formData;
+    }
+
+    this.onSave.emit(office);
+  }
+
+  cancel(): void {
+    this.onCancel.emit();
+  }
+
+  get office(): Office {
+    return this._office;
+  }
+
+}
diff --git a/src/app/offices/headquarter/headquarter-not-found.component.html b/src/app/offices/headquarter/headquarter-not-found.component.html
new file mode 100644
index 0000000..abba2c9
--- /dev/null
+++ b/src/app/offices/headquarter/headquarter-not-found.component.html
@@ -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.
+-->
+
+<mat-card>
+  <mat-card-title><i class="material-icons">warning</i><span translate>No Headquarter found</span></mat-card-title>
+  <mat-card-content>
+    <p translate>Oh no, it looks like you don't have a headquarter created yet. No worries you can do it now!</p>
+    <button mat-raised-button color="primary" [routerLink]="['../create']">{{'Create headquarter' | translate}}</button>
+  </mat-card-content>
+</mat-card>
+
diff --git a/src/app/offices/headquarter/headquarter-not-found.component.ts b/src/app/offices/headquarter/headquarter-not-found.component.ts
new file mode 100644
index 0000000..1b9e6b0
--- /dev/null
+++ b/src/app/offices/headquarter/headquarter-not-found.component.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 {Component} from '@angular/core';
+
+@Component({
+    templateUrl: './headquarter-not-found.component.html'
+})
+export class HeadquarterNotFoundComponent {}
diff --git a/src/app/offices/headquarter/headquarter.guard.ts b/src/app/offices/headquarter/headquarter.guard.ts
new file mode 100644
index 0000000..acaab86
--- /dev/null
+++ b/src/app/offices/headquarter/headquarter.guard.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, CanActivate, Router, RouterStateSnapshot} from '@angular/router';
+import {Observable} from 'rxjs/Observable';
+import {OfficeService} from '../../services/office/office.service';
+import {OfficePage} from '../../services/office/domain/office-page.model';
+import {Office} from '../../services/office/domain/office.model';
+
+@Injectable()
+export class HeadquarterGuard implements CanActivate {
+
+  constructor(private officeService: OfficeService, private router: Router) {}
+
+  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
+    const searchTerm = route.queryParams['term'];
+
+    if (searchTerm) {
+      return Observable.of(true);
+    }
+
+    return this.officeService.listOffices().map((officePage: OfficePage) => {
+      if (officePage.totalElements) {
+        const firstOffice: Office = officePage.offices[0];
+        this.router.navigate(['offices/detail', firstOffice.identifier]);
+      } else {
+        this.router.navigate(['offices/hqNotFound']);
+      }
+
+      return false;
+    });
+  }
+}
diff --git a/src/app/offices/office-exists.guard.ts b/src/app/offices/office-exists.guard.ts
new file mode 100644
index 0000000..eab0c5f
--- /dev/null
+++ b/src/app/offices/office-exists.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 {ActivatedRouteSnapshot, CanActivate, RouterStateSnapshot} from '@angular/router';
+import {Injectable} from '@angular/core';
+import {OfficeService} from '../services/office/office.service';
+import {getOfficesLoadedAt, OfficesStore} from './store';
+import {Observable} from 'rxjs/Observable';
+import {LoadAction} from './store/office.actions';
+import {of} from 'rxjs/observable/of';
+import {ExistsGuardService} from '../common/guards/exists-guard';
+
+@Injectable()
+export class OfficeExistsGuard implements CanActivate {
+
+  constructor(private store: OfficesStore,
+              private officeService: OfficeService,
+              private existsGuardService: ExistsGuardService) {}
+
+  hasOfficeInStore(id: string): Observable<boolean> {
+    const timestamp$ = this.store.select(getOfficesLoadedAt)
+      .map(loadedAt => loadedAt[id]);
+
+    return this.existsGuardService.isWithinExpiry(timestamp$);
+  }
+
+  hasOfficeInApi(id: string): Observable<boolean> {
+    const getOffice$ = this.officeService.getOffice(id)
+      .map(officeEntity => new LoadAction({
+        resource: officeEntity
+      }))
+      .do((action: LoadAction) => this.store.dispatch(action))
+      .map(office => !!office);
+
+    return this.existsGuardService.routeTo404OnError(getOffice$);
+  }
+
+  hasOffice(id: string): Observable<boolean> {
+    return this.hasOfficeInStore(id)
+      .switchMap(inStore => {
+        if (inStore) {
+          return of(inStore);
+        }
+
+        return this.hasOfficeInApi(id);
+      });
+  }
+
+  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
+    return this.hasOffice(route.params['id']);
+  }
+}
diff --git a/src/app/offices/office.component.html b/src/app/offices/office.component.html
new file mode 100644
index 0000000..32beda8
--- /dev/null
+++ b/src/app/offices/office.component.html
@@ -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.
+-->
+
+<fims-layout-card-over title="{{ 'Search results' | translate}}">
+  <fims-layout-card-over-header-menu>
+    <td-search-box #searchBox placeholder="{{ 'Search' | translate }}" (search)="search($event)" [alwaysVisible]="true"></td-search-box>
+  </fims-layout-card-over-header-menu>
+  <fims-data-table flex
+                   (onFetch)="fetchOffices($event)"
+                   (onActionCellClick)="rowSelect($event)"
+                   [columns]="columns"
+                   [data]="officeData$ | async"
+                   [loading]="loading$ | async"
+                   [sortable]="true"
+                   [pageable]="true">
+  </fims-data-table>
+</fims-layout-card-over>
diff --git a/src/app/offices/office.component.ts b/src/app/offices/office.component.ts
new file mode 100644
index 0000000..34c76b8
--- /dev/null
+++ b/src/app/offices/office.component.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 {Component, OnInit} from '@angular/core';
+import {ActivatedRoute, Params, Router} from '@angular/router';
+import {FetchRequest} from '../services/domain/paging/fetch-request.model';
+import {TableData} from '../common/data-table/data-table.component';
+import {Office} from '../services/office/domain/office.model';
+import * as fromRoot from '../store';
+
+import {Observable} from 'rxjs/Observable';
+import {SEARCH} from '../store/office/office.actions';
+import {OfficesStore} from './store/index';
+
+@Component({
+  selector: 'fims-office',
+  templateUrl: './office.component.html'
+})
+export class OfficeComponent implements OnInit {
+
+  officeData$: Observable<TableData>;
+
+  loading$: Observable<boolean>;
+
+  columns: any[] = [
+    { name: 'identifier', label: 'Id' },
+    { name: 'name', label: 'Name' },
+    { name: 'description', label: 'Description' }
+  ];
+
+  searchTerm: string;
+
+  private lastFetchRequest: FetchRequest = {};
+
+  constructor(private router: Router, private route: ActivatedRoute, private store: OfficesStore) {}
+
+  ngOnInit(): void {
+    this.route.queryParams.subscribe((params: Params) => {
+      this.search(params['term']);
+    });
+    this.officeData$ = this.store.select(fromRoot.getOfficeSearchResults)
+      .map(officePage => ({
+        data: officePage.offices,
+        totalElements: officePage.totalElements,
+        totalPages: officePage.totalPages
+      }));
+
+    this.loading$ = this.store.select(fromRoot.getOfficeSearchLoading);
+  }
+
+  rowSelect(office: Office): void {
+    this.router.navigate(['detail', office.identifier], { relativeTo: this.route });
+  }
+
+  search(searchTerm: string): void {
+    this.searchTerm = searchTerm;
+    this.fetchOffices();
+  }
+
+  fetchOffices(fetchRequest?: FetchRequest): void {
+    if (fetchRequest) {
+      this.lastFetchRequest = fetchRequest;
+    }
+
+    this.lastFetchRequest.searchTerm = this.searchTerm;
+
+    this.store.dispatch({ type: SEARCH, payload: this.lastFetchRequest});
+  }
+}
diff --git a/src/app/offices/office.module.ts b/src/app/offices/office.module.ts
new file mode 100644
index 0000000..21aadd4
--- /dev/null
+++ b/src/app/offices/office.module.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 {NgModule} from '@angular/core';
+import {RouterModule} from '@angular/router';
+import {ReactiveFormsModule} from '@angular/forms';
+import {OfficeRoutes} from './office.routing';
+import {OfficeComponent} from './office.component';
+import {OfficeFormComponent} from './form/form.component';
+import {OfficeDetailComponent} from './detail/office.detail.component';
+import {CreateOfficeFormComponent} from './form/create/create.form.component';
+import {EditOfficeFormComponent} from './form/edit/edit.form.component';
+import {FimsSharedModule} from '../common/common.module';
+import {HeadquarterGuard} from './headquarter/headquarter.guard';
+import {HeadquarterNotFoundComponent} from './headquarter/headquarter-not-found.component';
+import {OfficeExistsGuard} from './office-exists.guard';
+import {Store} from '@ngrx/store';
+import {OfficesStore, officeStoreFactory} from './store/index';
+import {OfficeNotificationEffects} from './store/effects/notification.effects';
+import {EffectsModule} from '@ngrx/effects';
+import {OfficeRouteEffects} from './store/effects/route.effects';
+import {OfficeApiEffects} from './store/effects/service.effects';
+import {TranslateModule} from '@ngx-translate/core';
+import {
+  MatButtonModule,
+  MatCardModule, MatCheckboxModule,
+  MatIconModule,
+  MatInputModule,
+  MatListModule,
+  MatRadioModule,
+  MatToolbarModule
+} from '@angular/material';
+import {CovalentDataTableModule, CovalentMessageModule, CovalentSearchModule, CovalentStepsModule} from '@covalent/core';
+import {CommonModule} from '@angular/common';
+import {TellerApiEffects} from './store/teller/effects/service.effects';
+import {OfficeTellerListComponent} from './detail/teller/teller.list.component';
+import {OfficeIndexComponent} from './detail/office.index.component';
+import {OfficeTellerFormComponent} from './detail/teller/form/form.component';
+import {CreateOfficeTellerFormComponent} from './detail/teller/form/create.form.component';
+import {EditOfficeTellerFormComponent} from './detail/teller/form/edit.form.component';
+import {TellerExistsGuard} from './detail/teller/teller-exists.guard';
+import {TellerRouteEffects} from './store/teller/effects/route.effects';
+import {TellerNotificationEffects} from './store/teller/effects/notification.effects';
+import {OfficeTellerIndexComponent} from './detail/teller/teller.index.component';
+import {TellerBalanceComponent} from './detail/teller/detail/balance/balance.component';
+import {OfficeTellerDetailComponent} from './detail/teller/detail/teller.detail.component';
+import {OpenOfficeTellerFormComponent} from './detail/teller/detail/command/open.component';
+import {CloseOfficeTellerFormComponent} from './detail/teller/detail/command/close.component';
+import {OfficeTellerCommandComponent} from './detail/teller/detail/command/command.component';
+import {AdjustmentComponent} from './detail/teller/detail/command/components/adjustment.component';
+import {BalanceSheetService} from './detail/teller/detail/balance/services/balance-sheet.service';
+import {TellerDenominationApiEffects} from './store/teller/denomination/effects/service.effects';
+import {TellerDenominationRouteEffects} from './store/teller/denomination/effects/route.effects';
+import {TellerDenominationNotificationEffects} from './store/teller/denomination/effects/notification.effects';
+import {TellerDenominationListComponent} from './detail/teller/detail/denomination/denomination.list.component';
+import {CreateDenominationFormComponent} from './detail/teller/detail/denomination/form/create.form.component';
+import {DenominationFormComponent} from './detail/teller/detail/denomination/form/form.component';
+
+@NgModule({
+  imports: [
+    RouterModule.forChild(OfficeRoutes),
+    FimsSharedModule,
+    ReactiveFormsModule,
+    TranslateModule,
+    CommonModule,
+    MatCardModule,
+    MatIconModule,
+    MatListModule,
+    MatToolbarModule,
+    MatInputModule,
+    MatButtonModule,
+    MatRadioModule,
+    MatCheckboxModule,
+    CovalentSearchModule,
+    CovalentStepsModule,
+    CovalentDataTableModule,
+    CovalentMessageModule,
+    EffectsModule.run(OfficeApiEffects),
+    EffectsModule.run(OfficeRouteEffects),
+    EffectsModule.run(OfficeNotificationEffects),
+
+    EffectsModule.run(TellerApiEffects),
+    EffectsModule.run(TellerRouteEffects),
+    EffectsModule.run(TellerNotificationEffects),
+
+    EffectsModule.run(TellerDenominationApiEffects),
+    EffectsModule.run(TellerDenominationRouteEffects),
+    EffectsModule.run(TellerDenominationNotificationEffects),
+  ],
+  declarations: [
+    OfficeComponent,
+    OfficeIndexComponent,
+    OfficeFormComponent,
+    CreateOfficeFormComponent,
+    EditOfficeFormComponent,
+    OfficeDetailComponent,
+    HeadquarterNotFoundComponent,
+    OfficeTellerListComponent,
+    OfficeTellerFormComponent,
+    OfficeTellerIndexComponent,
+    OfficeTellerDetailComponent,
+    CreateOfficeTellerFormComponent,
+    EditOfficeTellerFormComponent,
+    OfficeTellerCommandComponent,
+    OpenOfficeTellerFormComponent,
+    CloseOfficeTellerFormComponent,
+    TellerBalanceComponent,
+    AdjustmentComponent,
+    TellerDenominationListComponent,
+    CreateDenominationFormComponent,
+    DenominationFormComponent
+  ],
+  providers: [
+    HeadquarterGuard,
+    OfficeExistsGuard,
+    TellerExistsGuard,
+    BalanceSheetService,
+    { provide: OfficesStore, useFactory: officeStoreFactory, deps: [Store]}
+  ],
+  entryComponents: []
+})
+export class OfficeModule {}
diff --git a/src/app/offices/office.routing.ts b/src/app/offices/office.routing.ts
new file mode 100644
index 0000000..a376cd7
--- /dev/null
+++ b/src/app/offices/office.routing.ts
@@ -0,0 +1,128 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 {Routes} from '@angular/router';
+import {OfficeComponent} from './office.component';
+import {OfficeDetailComponent} from './detail/office.detail.component';
+import {EditOfficeFormComponent} from './form/edit/edit.form.component';
+import {CreateOfficeFormComponent} from './form/create/create.form.component';
+import {HeadquarterNotFoundComponent} from './headquarter/headquarter-not-found.component';
+import {HeadquarterGuard} from './headquarter/headquarter.guard';
+import {OfficeExistsGuard} from './office-exists.guard';
+import {OfficeIndexComponent} from './detail/office.index.component';
+import {OfficeTellerListComponent} from './detail/teller/teller.list.component';
+import {OfficeTellerIndexComponent} from './detail/teller/teller.index.component';
+import {TellerExistsGuard} from './detail/teller/teller-exists.guard';
+import {EditOfficeTellerFormComponent} from './detail/teller/form/edit.form.component';
+import {CreateOfficeTellerFormComponent} from './detail/teller/form/create.form.component';
+import {OfficeTellerDetailComponent} from './detail/teller/detail/teller.detail.component';
+import {TellerBalanceComponent} from './detail/teller/detail/balance/balance.component';
+import {OfficeTellerCommandComponent} from './detail/teller/detail/command/command.component';
+import {TellerDenominationListComponent} from './detail/teller/detail/denomination/denomination.list.component';
+import {CreateDenominationFormComponent} from './detail/teller/detail/denomination/form/create.form.component';
+
+export const OfficeRoutes: Routes = [
+  {
+    path: '',
+    component: OfficeComponent,
+    canActivate: [HeadquarterGuard],
+    data: {
+      title: 'Manage offices',
+      hasPermission: {id: 'office_offices', accessLevel: 'READ'}
+    }
+  },
+  {
+    path: 'hqNotFound', component: HeadquarterNotFoundComponent, data: {title: 'Headquarter not found'}
+  },
+  {
+    path: 'create',
+    component: CreateOfficeFormComponent,
+    data: {title: 'Create office', hasPermission: {id: 'office_offices', accessLevel: 'CHANGE'}}
+  },
+  {
+    path: 'detail/:id',
+    component: OfficeIndexComponent,
+    canActivate: [OfficeExistsGuard],
+    children: [
+      {
+        path: '',
+        component: OfficeDetailComponent,
+        data: {title: 'View office', hasPermission: {id: 'office_offices', accessLevel: 'READ'}}
+      },
+      {
+        path: 'edit',
+        component: EditOfficeFormComponent,
+        data: {title: 'Edit office', hasPermission: {id: 'office_offices', accessLevel: 'CHANGE'}}
+      },
+      {
+        path: 'tellers',
+        component: OfficeTellerListComponent,
+        data: {title: 'Manage teller', hasPermission: {id: 'office_offices', accessLevel: 'READ'}}
+      },
+      {
+        path: 'tellers/detail/:code',
+        component: OfficeTellerIndexComponent,
+        canActivate: [TellerExistsGuard],
+        data: {hasPermission: {id: 'teller_management', accessLevel: 'READ'}},
+        children: [
+          {
+            path: '',
+            component: OfficeTellerDetailComponent,
+            data: { title: 'View teller' }
+          },
+          {
+            path: 'edit',
+            component: EditOfficeTellerFormComponent,
+            data: {title: 'Edit teller', hasPermission: {id: 'teller_management', accessLevel: 'CHANGE'}}
+          },
+          {
+            path: 'command',
+            component: OfficeTellerCommandComponent,
+            data: { title: 'Manage teller', hasPermission: {id: 'teller_management', accessLevel: 'CHANGE'}}
+          },
+          {
+            path: 'balance',
+            component: TellerBalanceComponent,
+            data: { title: 'View balance'}
+          },
+          {
+            path: 'denominations',
+            children: [
+              {
+                path: '',
+                component: TellerDenominationListComponent,
+                data: { title: 'View denominations'}
+              },
+              {
+                path: 'create',
+                component: CreateDenominationFormComponent,
+                data: { title: 'Create denomination'}
+              }
+            ]
+          },
+        ]
+      },
+      {
+        path: 'tellers/create',
+        component: CreateOfficeTellerFormComponent,
+        data: {title: 'Create teller', hasPermission: {id: 'teller_management', accessLevel: 'CHANGE'}}
+      }
+    ]
+  }
+
+];
diff --git a/src/app/offices/store/effects/notification.effects.ts b/src/app/offices/store/effects/notification.effects.ts
new file mode 100644
index 0000000..f78ad4e
--- /dev/null
+++ b/src/app/offices/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 '../../../services/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/offices/store/effects/route.effects.ts b/src/app/offices/store/effects/route.effects.ts
new file mode 100644
index 0000000..8064460
--- /dev/null
+++ b/src/app/offices/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/offices/store/effects/service.effects.ts b/src/app/offices/store/effects/service.effects.ts
new file mode 100644
index 0000000..7783b23
--- /dev/null
+++ b/src/app/offices/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 '../../../services/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/offices/store/index.ts b/src/app/offices/store/index.ts
new file mode 100644
index 0000000..9ae603b
--- /dev/null
+++ b/src/app/offices/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/offices/store/office.actions.ts b/src/app/offices/store/office.actions.ts
new file mode 100644
index 0000000..dfad003
--- /dev/null
+++ b/src/app/offices/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 '../../services/office/domain/office.model';
+import {Error} from '../../services/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/offices/store/teller/denomination/denomination.actions.ts b/src/app/offices/store/teller/denomination/denomination.actions.ts
new file mode 100644
index 0000000..f310735
--- /dev/null
+++ b/src/app/offices/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 '../../../../services/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/offices/store/teller/denomination/denominations.reducer.ts b/src/app/offices/store/teller/denomination/denominations.reducer.ts
new file mode 100644
index 0000000..5e980f4
--- /dev/null
+++ b/src/app/offices/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 '../../../../services/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/offices/store/teller/denomination/effects/notification.effects.ts b/src/app/offices/store/teller/denomination/effects/notification.effects.ts
new file mode 100644
index 0000000..032f1d8
--- /dev/null
+++ b/src/app/offices/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 '../../../../../services/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/offices/store/teller/denomination/effects/route.effects.ts b/src/app/offices/store/teller/denomination/effects/route.effects.ts
new file mode 100644
index 0000000..4e09777
--- /dev/null
+++ b/src/app/offices/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/offices/store/teller/denomination/effects/service.effects.ts b/src/app/offices/store/teller/denomination/effects/service.effects.ts
new file mode 100644
index 0000000..6789b40
--- /dev/null
+++ b/src/app/offices/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 '../../../../../services/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/offices/store/teller/effects/notification.effects.ts b/src/app/offices/store/teller/effects/notification.effects.ts
new file mode 100644
index 0000000..e5eed57
--- /dev/null
+++ b/src/app/offices/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 '../../../../services/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/offices/store/teller/effects/route.effects.ts b/src/app/offices/store/teller/effects/route.effects.ts
new file mode 100644
index 0000000..daff2f4
--- /dev/null
+++ b/src/app/offices/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/offices/store/teller/effects/service.effects.ts b/src/app/offices/store/teller/effects/service.effects.ts
new file mode 100644
index 0000000..8ce8d50
--- /dev/null
+++ b/src/app/offices/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 '../../../../services/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/offices/store/teller/teller.actions.ts b/src/app/offices/store/teller/teller.actions.ts
new file mode 100644
index 0000000..51df037
--- /dev/null
+++ b/src/app/offices/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 '../../../services/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 '../../../services/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/offices/store/teller/tellers.reducer.spec.ts b/src/app/offices/store/teller/tellers.reducer.spec.ts
new file mode 100644
index 0000000..a10b17e
--- /dev/null
+++ b/src/app/offices/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 '../../../services/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/offices/store/teller/tellers.reducer.ts b/src/app/offices/store/teller/tellers.reducer.ts
new file mode 100644
index 0000000..df92f4d
--- /dev/null
+++ b/src/app/offices/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 '../../../services/teller/domain/teller.model';
+import {idsToHashWithCurrentTimestamp, resourcesToHash} from '../../../common/store/reducer.helper';
+import {TellerManagementCommand} from '../../../services/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/quickAccess/quick-access.component.html b/src/app/quickAccess/quick-access.component.html
new file mode 100644
index 0000000..87a1e7f
--- /dev/null
+++ b/src/app/quickAccess/quick-access.component.html
@@ -0,0 +1,98 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<mat-toolbar>
+  <span translate>Quick access</span>
+</mat-toolbar>
+
+<div class="mat-content" flex mat-scroll-y>
+  <div layout="column" layout-gt-sm="row" class="inset">
+    <div flex-gt-sm="50" *hasPermission="{ id: 'office_offices', accessLevel: 'READ'}">
+      <mat-card>
+        <mat-card-title translate>Offices</mat-card-title>
+        <mat-card-subtitle translate>Create and edit your offices here.</mat-card-subtitle>
+        <mat-card-actions>
+          <a mat-button color="accent" class="text-upper" [routerLink]="['/offices']">
+            <span translate>View headquarter office</span>
+          </a>
+        </mat-card-actions>
+      </mat-card>
+    </div>
+    <div flex-gt-sm="50" *hasPermission="{ id: 'office_employees', accessLevel: 'READ'}">
+      <mat-card>
+        <mat-card-title translate>Employees</mat-card-title>
+        <mat-card-subtitle translate>Create and edit your employees and assign them to offices.</mat-card-subtitle>
+        <mat-card-actions>
+          <a mat-button color="accent" class="text-upper" [routerLink]="['/employees']">
+            <span translate>View employees</span>
+          </a>
+          <a mat-button color="accent" class="text-upper" [routerLink]="['/employees/create']" *hasPermission="{ id: 'office_employees', accessLevel: 'CHANGE'}">
+            <span translate>Create new employee</span>
+          </a>
+        </mat-card-actions>
+      </mat-card>
+    </div>
+  </div>
+  <div layout="column" layout-gt-sm="row" class="inset">
+    <div flex-gt-sm="40" *hasPermission="{ id: 'customer_customers', accessLevel: 'READ'}">
+      <mat-card>
+        <mat-card-title translate>Member</mat-card-title>
+        <mat-card-subtitle translate>Create/edit members for your offices.</mat-card-subtitle>
+        <mat-card-actions>
+          <a mat-button color="accent" class="text-upper" [routerLink]="['/customers']">
+            <span translate>View member </span>
+          </a>
+          <a mat-button color="accent" class="text-upper" [routerLink]="['/customers/create']" *hasPermission="{ id: 'customer_customers', accessLevel: 'CHANGE'}">
+            <span translate>Create new member </span>
+          </a>
+        </mat-card-actions>
+      </mat-card>
+    </div>
+  </div>
+  <div layout="column" layout-gt-sm="row" class="inset">
+    <div flex-gt-sm="40" *hasPermission="{ id: 'accounting_ledgers', accessLevel: 'READ'}">
+      <mat-card>
+        <mat-card-title translate>Accounting</mat-card-title>
+        <mat-card-subtitle translate>Create/edit ledgers and accounts for your General Ledger.</mat-card-subtitle>
+        <mat-card-actions>
+          <a mat-button color="accent" class="text-upper" [routerLink]="['/accounting']">
+            <span translate>View General Ledger</span>
+          </a>
+          <a mat-button color="accent" class="text-upper" [routerLink]="['/accounting/create']" *hasPermission="{ id: 'accounting_ledgers', accessLevel: 'CHANGE'}">
+            <span translate>Create new ledger</span>
+          </a>
+        </mat-card-actions>
+      </mat-card>
+    </div>
+  </div>
+  <div layout="column" layout-gt-sm="row" class="inset">
+    <div flex-gt-sm="40" *hasPermission="{ id: 'identity_roles', accessLevel: 'READ'}">
+      <mat-card>
+        <mat-card-title translate>Roles</mat-card-title>
+        <mat-card-subtitle translate>Create and edit roles to manage access levels within fims.</mat-card-subtitle>
+        <mat-card-actions>
+          <a mat-button color="accent" class="text-upper" [routerLink]="['/roles']">
+            <span translate>View roles</span>
+          </a>
+          <a mat-button color="accent" class="text-upper" [routerLink]="['/roles/create']" *hasPermission="{ id: 'identity_roles', accessLevel: 'CHANGE'}">
+            <span translate>Create new role</span>
+          </a>
+        </mat-card-actions>
+      </mat-card>
+    </div>
+  </div>
+</div>
diff --git a/src/app/quickAccess/quick-access.component.ts b/src/app/quickAccess/quick-access.component.ts
new file mode 100644
index 0000000..af148b4
--- /dev/null
+++ b/src/app/quickAccess/quick-access.component.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 {Component} from '@angular/core';
+
+@Component({
+  templateUrl: './quick-access.component.html'
+})
+export class QuickAccessComponent {}
diff --git a/src/app/reporting/detail/criteria/criteria.component.html b/src/app/reporting/detail/criteria/criteria.component.html
new file mode 100644
index 0000000..ff4ff4e
--- /dev/null
+++ b/src/app/reporting/detail/criteria/criteria.component.html
@@ -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.
+-->
+
+<td-steps>
+  <td-step #step1 label="Criteria" sublabel="Filter your report based on criteria." [active]="true" [disabled]="false">
+    <fims-reporting-query-params #mandatoryComponent [queryParams]="mandatoryParams"></fims-reporting-query-params>
+    <ng-template td-step-actions>
+      <button mat-raised-button color="primary" (click)="step2.open()" class="text-upper">Next</button>
+    </ng-template>
+  </td-step>
+  <td-step #step2 label="Optional criteria" sublabel="Extend your criteria." [disabled]="false">
+    <fims-reporting-query-params #optionalComponent [queryParams]="optionalParams"></fims-reporting-query-params>
+    <ng-template td-step-actions>
+      <button mat-raised-button color="primary" (click)="step3.open()" class="text-upper">Next</button>
+    </ng-template>
+  </td-step>
+  <td-step #step3 label="Field selection" sublabel="Add or remove fields for your report" [disabled]="false">
+    <fims-reporting-displayable-fields #displayableFieldComponent [displayableFields]="displayableFields">
+    </fims-reporting-displayable-fields>
+  </td-step>
+  <td-step label="{{'Final step' | translate}}" [state]="'complete'">
+    <ng-template td-step-summary>
+      <button mat-raised-button color="primary" class="text-upper" [disabled]="!valid" (click)="generateReport()">Generate report</button>
+    </ng-template>
+  </td-step>
+</td-steps>
diff --git a/src/app/reporting/detail/criteria/criteria.component.ts b/src/app/reporting/detail/criteria/criteria.component.ts
new file mode 100644
index 0000000..ca40f60
--- /dev/null
+++ b/src/app/reporting/detail/criteria/criteria.component.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, EventEmitter, Input, Output, ViewChild} from '@angular/core';
+import {QueryParameter} from '../../../services/reporting/domain/query-parameter.model';
+import {ReportingQueryParamsComponent} from '../queryParams/query-params.component';
+import {DisplayableField} from '../../../services/reporting/domain/displayable-field.model';
+import {ReportingDisplayableFieldsComponent} from '../displayableFields/displayable-fields.component';
+
+export interface GenerateReportEvent {
+  queryParameter: QueryParameter[];
+  displayableFields: DisplayableField[];
+}
+
+@Component({
+  selector: 'fims-reporting-criteria',
+  templateUrl: './criteria.component.html'
+})
+export class ReportingCriteriaComponent {
+
+  mandatoryParams: QueryParameter[];
+
+  optionalParams: QueryParameter[];
+
+  @ViewChild('mandatoryComponent') mandatoryComponent: ReportingQueryParamsComponent;
+
+  @ViewChild('optionalComponent') optionalComponent: ReportingQueryParamsComponent;
+
+  @ViewChild('displayableFieldComponent') displayableFieldsComponent: ReportingDisplayableFieldsComponent;
+
+  @Input() set queryParameter(queryParameter: QueryParameter[]) {
+    if (!queryParameter) {
+      return;
+    }
+
+    this.mandatoryParams = queryParameter.filter(param => param.mandatory);
+    this.optionalParams = queryParameter.filter(param => !param.mandatory);
+  };
+
+  @Input() displayableFields: DisplayableField[];
+
+  @Output() onGenerateReport = new EventEmitter<GenerateReportEvent>();
+
+  get valid(): boolean {
+    return this.mandatoryComponent.valid && this.optionalComponent.valid;
+  }
+
+  generateReport(): void {
+    const queryParameter: QueryParameter[] = [
+      ...this.mandatoryComponent.formData,
+      ...this.optionalComponent.formData
+    ];
+
+    const displayableFields: DisplayableField[] = this.displayableFieldsComponent.formData;
+
+    this.onGenerateReport.emit({
+      queryParameter,
+      displayableFields
+    });
+  }
+
+}
diff --git a/src/app/reporting/detail/displayableFields/displayable-fields.component.html b/src/app/reporting/detail/displayableFields/displayable-fields.component.html
new file mode 100644
index 0000000..04b36de
--- /dev/null
+++ b/src/app/reporting/detail/displayableFields/displayable-fields.component.html
@@ -0,0 +1,44 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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-body-1">Locked</div>
+<td-chips [items]="[]"
+          [ngModel]="mandatoryFields"
+          [readOnly]="true"
+          stacked>
+
+  <ng-template td-chip let-chip="chip">
+    <mat-icon>lock_outline</mat-icon> {{chip.name}}
+  </ng-template>
+</td-chips>
+<div class="mat-body-1">Additional fields</div>
+<td-chips [items]="filteredOptionalFields"
+          [formControl]="optionalFormControl"
+          (inputChange)="filterOptionalFields($event)"
+          placeholder="{{'Enter field name and hit enter' | translate}}"
+          stacked
+          requireMatch>
+  <ng-template td-chip let-chip="chip">
+    <div class="tc-grey-100 bgc-teal-700" td-chip-avatar>{{chip.name.substring(0, 1).toUpperCase()}}</div> {{chip.name}}
+  </ng-template>
+  <ng-template td-autocomplete-option let-option="option">
+    <div layout="row" layout-align="start center">
+      {{option.name}}
+    </div>
+  </ng-template>
+</td-chips>
+
diff --git a/src/app/reporting/detail/displayableFields/displayable-fields.component.ts b/src/app/reporting/detail/displayableFields/displayable-fields.component.ts
new file mode 100644
index 0000000..b0ee498
--- /dev/null
+++ b/src/app/reporting/detail/displayableFields/displayable-fields.component.ts
@@ -0,0 +1,87 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 {DisplayableField} from '../../../services/reporting/domain/displayable-field.model';
+import {Component, Input, OnInit} from '@angular/core';
+import {FormControl} from '@angular/forms';
+
+@Component({
+  selector: 'fims-reporting-displayable-fields',
+  templateUrl: './displayable-fields.component.html'
+})
+export class ReportingDisplayableFieldsComponent implements OnInit {
+
+  private _displayableFields: DisplayableField[];
+
+  optionalFormControl: FormControl;
+
+  filteredOptionalFields: DisplayableField[];
+
+  mandatoryFields: DisplayableField[];
+
+  optionalFields: DisplayableField[];
+
+  @Input() set displayableFields(displayableFields: DisplayableField[]) {
+    if (!displayableFields) {
+      return;
+    }
+
+    this._displayableFields = displayableFields;
+
+    this.mandatoryFields = displayableFields.filter(field => field.mandatory);
+    this.optionalFields = displayableFields.filter(field => !field.mandatory);
+  };
+
+  constructor() {}
+
+  ngOnInit(): void {
+    this.optionalFormControl = new FormControl([]);
+  }
+
+  filterOptionalFields(value: string): void {
+    this.filteredOptionalFields = this.optionalFields.filter(field => {
+      if (value) {
+        return field.name.toLowerCase().indexOf(value.toLowerCase()) > -1;
+      } else {
+        return false;
+      }
+    }).filter((filteredObj: any) => {
+      return this.optionalFormControl.value ? this.optionalFormControl.value.indexOf(filteredObj) < 0 : true;
+    });
+  }
+
+  get formData(): DisplayableField[] {
+    const allDisplayableFields = [
+      ...this.mandatoryFields,
+      ...this.optionalFormControl.value
+    ];
+
+    // Reuse input displayable field to keep order
+    return this.displayableFields.filter(field => this.hasDisplayableField(field.name, allDisplayableFields));
+  }
+
+  private hasDisplayableField(name: string, displayableField: DisplayableField[]): boolean {
+    const found = displayableField.find(field => field.name === name);
+    return !!found;
+  }
+
+  get displayableFields(): DisplayableField[] {
+    return this._displayableFields;
+  }
+
+}
diff --git a/src/app/reporting/detail/queryParams/abstract-value-accessor.ts b/src/app/reporting/detail/queryParams/abstract-value-accessor.ts
new file mode 100644
index 0000000..9287659
--- /dev/null
+++ b/src/app/reporting/detail/queryParams/abstract-value-accessor.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 {ControlValueAccessor} from '@angular/forms';
+
+const noop: any = () => {
+  // empty method
+};
+
+export abstract class AbstractControlValueAccessor implements ControlValueAccessor {
+
+  protected _value: any = undefined;
+
+  get value(): any { return this._value; }
+
+  set value(v: any) {
+    if (v !== this._value) {
+      this._value = v;
+      this.onChange(v);
+    }
+  }
+
+  writeValue(value: any): void {
+    this.value = value;
+  }
+
+  registerOnChange(fn: any): void {
+    this.onChange = fn;
+  }
+
+  registerOnTouched(fn: any): void {
+    this.onTouched = fn;
+  }
+
+  onChange = (_: any) => noop;
+  onTouched = () => noop;
+}
diff --git a/src/app/reporting/detail/queryParams/between/between.component.html b/src/app/reporting/detail/queryParams/between/between.component.html
new file mode 100644
index 0000000..4c053b6
--- /dev/null
+++ b/src/app/reporting/detail/queryParams/between/between.component.html
@@ -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.
+-->
+
+<div layout="column" [formGroup]="formGroup">
+  <div class="pad-bottom-xs">
+    <div class="mat-body-1">{{placeholder}}</div>
+    <div layout="row">
+      <mat-form-field floatPlaceholder="always">
+        <input matInput
+               formControlName="start"
+               placeholder="Start"
+               [attr.type]="type"
+               flex>
+        <mat-error *ngIf="formGroup.get('start').hasError('required')" translate>Required</mat-error>
+      </mat-form-field>
+      <mat-form-field floatPlaceholder="always">
+        <input matInput
+               formControlName="end"
+               placeholder="End"
+               [attr.type]="type"
+               flex>
+        <mat-error *ngIf="formGroup.get('end').hasError('required')" translate>Required</mat-error>
+        <mat-hint class="tc-red-500" *ngIf="formGroup.hasError('rangeInvalid') || formGroup.hasError('greaterThan')" translate>Invalid range</mat-hint>
+      </mat-form-field>
+    </div>
+  </div>
+</div>
diff --git a/src/app/reporting/detail/queryParams/between/between.component.spec.ts b/src/app/reporting/detail/queryParams/between/between.component.spec.ts
new file mode 100644
index 0000000..252f657
--- /dev/null
+++ b/src/app/reporting/detail/queryParams/between/between.component.spec.ts
@@ -0,0 +1,97 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, TestBed} from '@angular/core/testing';
+import {FormControl, ReactiveFormsModule} from '@angular/forms';
+import {NoopAnimationsModule} from '@angular/platform-browser/animations';
+import {ReportingBetweenParamComponent} from './between.component';
+import {Component, ViewChild} from '@angular/core';
+import {TranslateModule} from '@ngx-translate/core';
+import {MatInputModule} from '@angular/material';
+
+describe('Test between component', () => {
+
+  let fixture: ComponentFixture<DateTestComponent>;
+
+  let testComponent: DateTestComponent;
+
+  beforeEach(() => {
+    TestBed.configureTestingModule({
+      imports: [
+        TranslateModule.forRoot(),
+        ReactiveFormsModule,
+        MatInputModule,
+        NoopAnimationsModule
+      ],
+      declarations: [
+        DateTestComponent,
+        ReportingBetweenParamComponent
+      ]
+    });
+
+    fixture = TestBed.createComponent(DateTestComponent);
+    testComponent = fixture.componentInstance;
+
+    fixture.detectChanges();
+  });
+
+  it('should return same value', () => {
+    const value = '2017-01-01..2017-01-01';
+
+    testComponent.formControl.setValue(value);
+
+    fixture.detectChanges();
+
+    expect(testComponent.formControl.value).toEqual(value);
+  });
+
+  it('should mark control as valid when valid range', () => {
+    const start = '2017-01-01';
+    const end = '2017-01-01';
+
+    testComponent.formControl.setValue(`${start}..${end}`);
+
+    fixture.detectChanges();
+
+    expect(testComponent.formControl.valid).toBeTruthy();
+  });
+
+  it('should mark control as invalid when invalid range', () => {
+    const start = '2017-01-02';
+    const end = '2017-01-01';
+
+    testComponent.formControl.setValue(`${start}..${end}`);
+
+    fixture.detectChanges();
+
+    expect(testComponent.formControl.invalid).toBeTruthy();
+  });
+
+});
+
+@Component({
+  template: `
+    <fims-reporting-between-param #paramComponent [formControl]="formControl" [required]="true" type="DATE">
+    </fims-reporting-between-param>`
+})
+class DateTestComponent {
+
+  @ViewChild('paramComponent') paramComponent: ReportingBetweenParamComponent;
+
+  formControl: FormControl = new FormControl();
+}
diff --git a/src/app/reporting/detail/queryParams/between/between.component.ts b/src/app/reporting/detail/queryParams/between/between.component.ts
new file mode 100644
index 0000000..893baed
--- /dev/null
+++ b/src/app/reporting/detail/queryParams/between/between.component.ts
@@ -0,0 +1,131 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, forwardRef, Input, OnDestroy, OnInit} from '@angular/core';
+import {Type} from '../../../../services/reporting/domain/type.model';
+import {
+  AbstractControl,
+  ControlValueAccessor,
+  FormBuilder,
+  FormGroup,
+  NG_VALIDATORS,
+  NG_VALUE_ACCESSOR,
+  ValidationErrors,
+  Validator,
+  Validators
+} from '@angular/forms';
+import {AbstractControlValueAccessor} from '../abstract-value-accessor';
+import {Observable} from 'rxjs/Observable';
+import {FimsValidators} from '../../../../common/validator/validators';
+import {Subscription} from 'rxjs/Subscription';
+import {Operator} from '../../../../services/reporting/domain/query-parameter.model';
+import {createPlaceholder} from '../query-params.helper';
+
+export const REPORTING_BETWEEN_PARAM_CONTROL_VALUE_ACCESSOR: any = {
+  provide: NG_VALUE_ACCESSOR,
+  useExisting: forwardRef(() => ReportingBetweenParamComponent),
+  multi: true,
+};
+
+export const REPORTING_BETWEEN_PARAM_VALIDATOR: any = {
+  provide: NG_VALIDATORS,
+  useExisting: forwardRef(() => ReportingBetweenParamComponent),
+  multi: true,
+};
+
+@Component({
+  providers: [ REPORTING_BETWEEN_PARAM_CONTROL_VALUE_ACCESSOR, REPORTING_BETWEEN_PARAM_VALIDATOR ],
+  selector: 'fims-reporting-between-param',
+  templateUrl: './between.component.html'
+})
+export class ReportingBetweenParamComponent extends AbstractControlValueAccessor
+  implements ControlValueAccessor, Validator, OnInit, OnDestroy {
+
+  changeSubscription: Subscription;
+
+  formGroup: FormGroup;
+
+  @Input() label: string;
+
+  @Input() required: boolean;
+
+  @Input() type: Type;
+
+  @Input() operator: Operator;
+
+  constructor(private formBuilder: FormBuilder) {
+    super();
+  }
+
+  ngOnInit(): void {
+    const validator = this.type === 'DATE' ? FimsValidators.matchRange('start', 'end') : FimsValidators.greaterThan('start', 'end');
+
+    this.formGroup = this.formBuilder.group({
+      start: ['', this.required ? [Validators.required] : []],
+      end: ['', this.required ? [Validators.required] : []]
+    }, { validator });
+
+    this.changeSubscription = Observable.combineLatest(
+      this.formGroup.get('start').valueChanges,
+      this.formGroup.get('end').valueChanges,
+      (start, end) => ({
+        start,
+        end
+      })
+    ).filter(result => result.start && result.end)
+     .subscribe(result => this.value = this.concatValue(result.start, result.end));
+  }
+
+  ngOnDestroy(): void {
+    this.changeSubscription.unsubscribe();
+  }
+
+  writeValue(value: string): void {
+    if (value) {
+      this.formGroup.setValue(this.splitValue(value));
+    } else {
+      this.formGroup.setValue({
+        start: null,
+        end: null
+      });
+    }
+  }
+
+  private splitValue(value: string): any {
+    const values: string[] = value.split('..');
+
+    if (values.length === 2) {
+      return {
+        start: values[0],
+        end: values[1]
+      };
+    }
+  }
+
+  private concatValue(start: string, end: string): string {
+    return `${start}..${end}`;
+  }
+
+  validate(c: AbstractControl): ValidationErrors {
+    return this.formGroup.errors;
+  }
+
+  get placeholder(): string {
+    return createPlaceholder(this.label, this.required);
+  }
+}
diff --git a/src/app/reporting/detail/queryParams/in/in.component.html b/src/app/reporting/detail/queryParams/in/in.component.html
new file mode 100644
index 0000000..edc54c9
--- /dev/null
+++ b/src/app/reporting/detail/queryParams/in/in.component.html
@@ -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.
+-->
+
+<div class="pad-bottom-xs">
+  <div class="mat-body-1">{{placeholder}}</div>
+  <td-chips [items]="[]"
+            [formControl]="formControl"
+            placeholder="{{'Enter value and hit enter' | translate}}">
+    <ng-template td-chip let-chip="chip">
+      <div class="tc-grey-100 bgc-teal-700" td-chip-avatar>{{chip.substring(0, 1).toUpperCase()}}</div> {{chip}}
+    </ng-template>
+  </td-chips>
+</div>
diff --git a/src/app/reporting/detail/queryParams/in/in.component.spec.ts b/src/app/reporting/detail/queryParams/in/in.component.spec.ts
new file mode 100644
index 0000000..5223ed6
--- /dev/null
+++ b/src/app/reporting/detail/queryParams/in/in.component.spec.ts
@@ -0,0 +1,87 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, TestBed} from '@angular/core/testing';
+import {CovalentChipsModule} from '@covalent/core';
+import {ReportingInParamComponent} from './in.component';
+import {Component, ViewChild} from '@angular/core';
+import {FormControl, ReactiveFormsModule} from '@angular/forms';
+import {NoopAnimationsModule} from '@angular/platform-browser/animations';
+import {TranslateModule} from '@ngx-translate/core';
+
+describe('Test in component', () => {
+
+  let fixture: ComponentFixture<TestComponent>;
+
+  let testComponent: TestComponent;
+
+  beforeEach(() => {
+    TestBed.configureTestingModule({
+      imports: [
+        TranslateModule.forRoot(),
+        ReactiveFormsModule,
+        CovalentChipsModule,
+        NoopAnimationsModule
+      ],
+      declarations: [
+        TestComponent,
+        ReportingInParamComponent
+      ]
+    });
+
+    fixture = TestBed.createComponent(TestComponent);
+    testComponent = fixture.componentInstance;
+
+    fixture.detectChanges();
+  });
+
+  it('should set comma separated list', () => {
+    testComponent.paramComponent.formControl.setValue(['test1', 'test2']);
+
+    fixture.detectChanges();
+
+    expect(testComponent.formControl.value).toEqual('test1,test2');
+  });
+
+  it('should mark control as valid when value available', () => {
+    testComponent.paramComponent.formControl.setValue(['test1']);
+
+    fixture.detectChanges();
+
+    expect(testComponent.formControl.valid).toBeTruthy();
+  });
+
+  it('should mark control as invalid when no value available', () => {
+    testComponent.paramComponent.formControl.setValue([]);
+
+    fixture.detectChanges();
+
+    expect(testComponent.formControl.invalid).toBeTruthy();
+  });
+
+});
+
+@Component({
+  template: '<fims-reporting-in-param #paramComponent [formControl]="formControl" [required]="true"></fims-reporting-in-param>'
+})
+class TestComponent {
+
+  @ViewChild('paramComponent') paramComponent: ReportingInParamComponent;
+
+  formControl: FormControl = new FormControl();
+}
diff --git a/src/app/reporting/detail/queryParams/in/in.component.ts b/src/app/reporting/detail/queryParams/in/in.component.ts
new file mode 100644
index 0000000..568a3ae
--- /dev/null
+++ b/src/app/reporting/detail/queryParams/in/in.component.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 {Component, forwardRef, Input, OnDestroy, OnInit} from '@angular/core';
+import {Operator} from '../../../../services/reporting/domain/query-parameter.model';
+import {Type} from '../../../../services/reporting/domain/type.model';
+import {
+  AbstractControl,
+  ControlValueAccessor,
+  FormControl,
+  NG_VALIDATORS,
+  NG_VALUE_ACCESSOR,
+  ValidationErrors,
+  Validator,
+  Validators
+} from '@angular/forms';
+import {AbstractControlValueAccessor} from '../abstract-value-accessor';
+import {Subscription} from 'rxjs/Subscription';
+import {createPlaceholder} from '../query-params.helper';
+
+export const REPORTING_IN_PARAM_CONTROL_VALUE_ACCESSOR: any = {
+  provide: NG_VALUE_ACCESSOR,
+  useExisting: forwardRef(() => ReportingInParamComponent),
+  multi: true,
+};
+
+export const REPORTING_IN_PARAM_VALIDATOR: any = {
+  provide: NG_VALIDATORS,
+  useExisting: forwardRef(() => ReportingInParamComponent),
+  multi: true,
+};
+
+@Component({
+  providers: [ REPORTING_IN_PARAM_CONTROL_VALUE_ACCESSOR, REPORTING_IN_PARAM_VALIDATOR ],
+  selector: 'fims-reporting-in-param',
+  templateUrl: './in.component.html'
+})
+export class ReportingInParamComponent extends AbstractControlValueAccessor implements ControlValueAccessor, Validator, OnInit, OnDestroy {
+
+  private changeSubscription: Subscription;
+
+  formControl: FormControl;
+
+  @Input() label: string;
+
+  @Input() required: boolean;
+
+  @Input() type: Type;
+
+  @Input() operator: Operator;
+
+  constructor() {
+    super();
+  }
+
+  ngOnInit(): void {
+    this.formControl = new FormControl([], this.required ? [Validators.required] : []);
+
+    this.changeSubscription = this.formControl.valueChanges
+      .subscribe((value: string[]) => this.value = value.join(','));
+  }
+
+  ngOnDestroy(): void {
+    this.changeSubscription.unsubscribe();
+  }
+
+  validate(c: AbstractControl): ValidationErrors {
+    return this.formControl.errors;
+  }
+
+  get placeholder(): string {
+    return createPlaceholder(this.label, this.required);
+  }
+}
diff --git a/src/app/reporting/detail/queryParams/input/input.component.html b/src/app/reporting/detail/queryParams/input/input.component.html
new file mode 100644
index 0000000..0e66355
--- /dev/null
+++ b/src/app/reporting/detail/queryParams/input/input.component.html
@@ -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.
+-->
+
+<mat-form-field>
+  <span matPrefix class="pad-right-xs">{{prefix}}</span>
+  <input matInput
+         [formControl]="formControl"
+         [placeholder]="placeholder"
+         [attr.type]="type"
+         flex>
+  <mat-error *ngIf="formControl.hasError('required')" translate>Required</mat-error>
+</mat-form-field>
diff --git a/src/app/reporting/detail/queryParams/input/input.component.ts b/src/app/reporting/detail/queryParams/input/input.component.ts
new file mode 100644
index 0000000..fa4021d
--- /dev/null
+++ b/src/app/reporting/detail/queryParams/input/input.component.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 {Component, forwardRef, Input, OnDestroy, OnInit} from '@angular/core';
+import {Type} from '../../../../services/reporting/domain/type.model';
+import {
+  AbstractControl,
+  ControlValueAccessor,
+  FormControl,
+  NG_VALIDATORS,
+  NG_VALUE_ACCESSOR,
+  ValidationErrors,
+  Validator,
+  Validators
+} from '@angular/forms';
+import {Subscription} from 'rxjs/Subscription';
+import {AbstractControlValueAccessor} from '../abstract-value-accessor';
+import {Operator} from '../../../../services/reporting/domain/query-parameter.model';
+import {createPlaceholder} from '../query-params.helper';
+
+const OperatorPrefixMap = {
+  'EQUALS': 'equals',
+  'LIKE': 'contains',
+  'GREATER': 'is greater than',
+  'LESSER': 'is lesser than'
+};
+
+export const REPORTING_EQUALS_PARAM_CONTROL_VALUE_ACCESSOR: any = {
+  provide: NG_VALUE_ACCESSOR,
+  useExisting: forwardRef(() => ReportingInputParamComponent),
+  multi: true,
+};
+
+export const REPORTING_INPUT_PARAM_VALIDATOR: any = {
+  provide: NG_VALIDATORS,
+  useExisting: forwardRef(() => ReportingInputParamComponent),
+  multi: true,
+};
+
+@Component({
+  providers: [ REPORTING_EQUALS_PARAM_CONTROL_VALUE_ACCESSOR, REPORTING_INPUT_PARAM_VALIDATOR ],
+  templateUrl: './input.component.html'
+})
+export class ReportingInputParamComponent extends AbstractControlValueAccessor
+  implements ControlValueAccessor, Validator, OnInit, OnDestroy {
+
+  private changeSubscription: Subscription;
+
+  formControl: FormControl;
+
+  @Input() label: string;
+
+  @Input() required: boolean;
+
+  @Input() type: Type;
+
+  @Input() operator: Operator;
+
+  constructor() {
+    super();
+  }
+
+  ngOnInit(): void {
+    this.formControl = new FormControl([], this.required ? [Validators.required] : []);
+
+    this.changeSubscription = this.formControl.valueChanges
+      .subscribe(value => this.value = value);
+  }
+
+  ngOnDestroy(): void {
+    this.changeSubscription.unsubscribe();
+  }
+
+  validate(c: AbstractControl): ValidationErrors {
+    return this.formControl.errors;
+  }
+
+  get placeholder(): string {
+    return createPlaceholder(this.label, this.required);
+  }
+
+  get prefix(): string {
+    return OperatorPrefixMap[this.operator];
+  }
+}
diff --git a/src/app/reporting/detail/queryParams/query-param.component.ts b/src/app/reporting/detail/queryParams/query-param.component.ts
new file mode 100644
index 0000000..f6a6425
--- /dev/null
+++ b/src/app/reporting/detail/queryParams/query-param.component.ts
@@ -0,0 +1,119 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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,
+  ComponentFactoryResolver,
+  ComponentRef,
+  Directive,
+  forwardRef,
+  Input,
+  OnInit,
+  ViewChild,
+  ViewContainerRef
+} from '@angular/core';
+import {Operator} from '../../../services/reporting/domain/query-parameter.model';
+import {ControlValueAccessor, NG_VALUE_ACCESSOR} from '@angular/forms';
+import {Type} from '../../../services/reporting/domain/type.model';
+import {AbstractControlValueAccessor} from './abstract-value-accessor';
+import {ReportingInputParamComponent} from './input/input.component';
+import {ReportingInParamComponent} from './in/in.component';
+import {ReportingBetweenParamComponent} from './between/between.component';
+
+export const ELEMENT_INPUT_CONTROL_VALUE_ACCESSOR: any = {
+  provide: NG_VALUE_ACCESSOR,
+  useExisting: forwardRef(() => ReportingQueryParamComponent),
+  multi: true,
+};
+
+@Directive({
+  selector: '[fimsQueryParamContainer]',
+})
+export class FimsQueryParamDirective {
+  constructor(public viewContainer: ViewContainerRef) { }
+}
+
+@Component({
+  providers: [ELEMENT_INPUT_CONTROL_VALUE_ACCESSOR ],
+  selector: 'fims-reporting-query-param',
+  template: '<div fimsQueryParamContainer></div>'
+})
+export class ReportingQueryParamComponent extends AbstractControlValueAccessor implements ControlValueAccessor, OnInit {
+
+  @Input() label: string;
+
+  @Input() required: boolean;
+
+  @Input() operator: Operator;
+
+  @Input() type: Type;
+
+  @ViewChild(FimsQueryParamDirective) childElement: FimsQueryParamDirective;
+
+  set value(v: any) {
+    if (v !== this._value) {
+      this._value = v;
+      this.onChange(v);
+    }
+  }
+
+  constructor(private componentFactoryResolver: ComponentFactoryResolver) {
+    super();
+  }
+
+  ngOnInit(): void {
+    const ref: ComponentRef<any> = this.componentFactoryResolver
+      .resolveComponentFactory(this.getComponentType(this.operator))
+      .create(this.childElement.viewContainer.injector);
+
+    this.childElement.viewContainer.insert(ref.hostView);
+
+    ref.instance.label = this.label;
+    ref.instance.required = this.required;
+    ref.instance.type = this.type;
+    ref.instance.operator = this.operator;
+
+    ref.instance.registerOnChange((value: any) => {
+      this.value = value;
+    });
+  }
+
+  private getComponentType(operator: Operator): any {
+    switch (operator) {
+      case 'GREATER':
+      case 'LESSER':
+      case 'EQUALS':
+      case 'LIKE': {
+        return ReportingInputParamComponent;
+      }
+
+      case 'IN': {
+        return ReportingInParamComponent;
+      }
+
+      case 'BETWEEN': {
+        return ReportingBetweenParamComponent;
+      }
+
+      default:
+        console.error(`Could not find component for operator: ${operator}`);
+        break;
+    }
+  }
+}
diff --git a/src/app/reporting/detail/queryParams/query-params.component.html b/src/app/reporting/detail/queryParams/query-params.component.html
new file mode 100644
index 0000000..a401e01
--- /dev/null
+++ b/src/app/reporting/detail/queryParams/query-params.component.html
@@ -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.
+-->
+
+<form [formGroup]="form">
+  <ng-container *ngFor="let queryParam of queryParams">
+    <fims-reporting-query-param [formControlName]="queryParam.name"
+                                [required]="queryParam.mandatory"
+                                [label]="queryParam.name"
+                                [type]="queryParam.type"
+                                [operator]="queryParam.operator">
+    </fims-reporting-query-param>
+  </ng-container>
+</form>
diff --git a/src/app/reporting/detail/queryParams/query-params.component.spec.ts b/src/app/reporting/detail/queryParams/query-params.component.spec.ts
new file mode 100644
index 0000000..e519550
--- /dev/null
+++ b/src/app/reporting/detail/queryParams/query-params.component.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 {By} from '@angular/platform-browser';
+import {ComponentFixture, TestBed} from '@angular/core/testing';
+import {ReportingQueryParamsComponent} from './query-params.component';
+import {Component, NO_ERRORS_SCHEMA} from '@angular/core';
+import {NoopAnimationsModule} from '@angular/platform-browser/animations';
+import {QueryParameter} from '../../../services/reporting/domain/query-parameter.model';
+import {FimsSharedModule} from '../../../common/common.module';
+
+describe('Test reporting query params component', () => {
+
+  let fixture: ComponentFixture<ReportingQueryParamsComponent>;
+
+  let testComponent: ReportingQueryParamsComponent;
+
+  beforeEach(() => {
+    TestBed.configureTestingModule({
+      declarations: [
+        ReportingQueryParamsComponent,
+        TestComponent
+      ],
+      imports: [
+        FimsSharedModule,
+        NoopAnimationsModule
+      ],
+      schemas: [NO_ERRORS_SCHEMA]
+    });
+
+    fixture = TestBed.createComponent(ReportingQueryParamsComponent);
+    testComponent = fixture.componentInstance;
+
+    fixture.detectChanges();
+  });
+
+  xit('should render fims-reporting-query-param on the page', () => {
+    const listItems = fixture.debugElement.queryAll(By.css('fims-reporting-query-param'));
+
+    expect(listItems.length).toBe(2);
+  });
+
+});
+
+@Component({
+  template: '<fims-reporting-query-params #mandatoryComponent [queryParams]="queryParams"></fims-reporting-query-params>'
+})
+class TestComponent {
+  queryParams: QueryParameter[] = [
+    { name: 'testa', mandatory: true, type: 'TEXT', operator: 'EQUALS' },
+    { name: 'testb', mandatory: true, type: 'TEXT', operator: 'EQUALS' }
+  ];
+}
diff --git a/src/app/reporting/detail/queryParams/query-params.component.ts b/src/app/reporting/detail/queryParams/query-params.component.ts
new file mode 100644
index 0000000..d9f131b
--- /dev/null
+++ b/src/app/reporting/detail/queryParams/query-params.component.ts
@@ -0,0 +1,56 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, Input} from '@angular/core';
+import {QueryParameter} from '../../../services/reporting/domain/query-parameter.model';
+import {FormControl, FormGroup} from '@angular/forms';
+import {FormComponent} from '../../../common/forms/form.component';
+
+@Component({
+  selector: 'fims-reporting-query-params',
+  templateUrl: './query-params.component.html'
+})
+export class ReportingQueryParamsComponent extends FormComponent<QueryParameter[]> {
+
+  private _queryParams: QueryParameter[];
+
+  @Input() set queryParams(queryParams: QueryParameter[]) {
+    if (!queryParams) {
+      return;
+    }
+
+    this._queryParams = queryParams;
+
+    queryParams.forEach(queryParam => this.addFormControl(this.form, queryParam));
+  };
+
+  private addFormControl(form: FormGroup, queryParam: QueryParameter): void {
+    form.addControl(queryParam.name, new FormControl(''));
+  }
+
+  get formData(): QueryParameter[] {
+    return this.queryParams.map(queryParam => Object.assign({}, queryParam, {
+        value: this.form.get(queryParam.name).value
+      })
+    );
+  }
+
+  get queryParams(): QueryParameter[] {
+    return this._queryParams;
+  }
+}
diff --git a/src/app/reporting/detail/queryParams/query-params.helper.ts b/src/app/reporting/detail/queryParams/query-params.helper.ts
new file mode 100644
index 0000000..d1cadce
--- /dev/null
+++ b/src/app/reporting/detail/queryParams/query-params.helper.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 function createPlaceholder(label: string, required: boolean): string {
+  const asterisk = required ? '' : '*';
+  return `${label}${asterisk}`;
+}
diff --git a/src/app/reporting/detail/report-page/report-page.component.html b/src/app/reporting/detail/report-page/report-page.component.html
new file mode 100644
index 0000000..b80d0ff
--- /dev/null
+++ b/src/app/reporting/detail/report-page/report-page.component.html
@@ -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.
+-->
+
+<table td-data-table>
+  <thead>
+    <tr td-data-table-column-row>
+      <th td-data-table-column *ngFor="let columnName of reportPage?.header?.columnNames">
+        <span>{{columnName}}</span>
+      </th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr td-data-table-row *ngFor="let row of reportPage?.rows">
+      <td td-data-table-cell *ngFor="let value of row.values">
+        {{cellValue(value.values)}}
+      </td>
+    </tr>
+    <tr td-data-table-row>
+      <td td-data-table-cell *ngFor="let value of reportPage?.footer?.values">
+        {{cellValue(value.values)}}
+      </td>
+    </tr>
+  </tbody>
+</table>
+
+<div class="mat-padding" *ngIf="!reportPage || reportPage.rows?.length === 0" layout="row" layout-align="center center">
+  <h3 translate>No results to display.</h3>
+</div>
diff --git a/src/app/reporting/detail/report-page/report-page.component.ts b/src/app/reporting/detail/report-page/report-page.component.ts
new file mode 100644
index 0000000..7662f83
--- /dev/null
+++ b/src/app/reporting/detail/report-page/report-page.component.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 {Component, Input} from '@angular/core';
+import {ReportPage} from '../../../services/reporting/domain/report-page.model';
+
+@Component({
+  selector: 'fims-reporting-report-page',
+  templateUrl: './report-page.component.html'
+})
+export class ReportingReportPageComponent {
+
+  @Input() reportPage: ReportPage;
+
+  cellValue(values: string[]): string {
+    return values.join(', ');
+  }
+}
diff --git a/src/app/reporting/detail/reporting-definition.component.html b/src/app/reporting/detail/reporting-definition.component.html
new file mode 100644
index 0000000..ca40524
--- /dev/null
+++ b/src/app/reporting/detail/reporting-definition.component.html
@@ -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.
+-->
+
+<fims-layout-card-over title="{{ 'Report definitions' | translate}}">
+  <fims-two-column-layout>
+    <fims-reporting-criteria #criteria
+                             [queryParameter]="queryParameter$ | async"
+                             [displayableFields]="displayableFields$ | async"
+                             (onGenerateReport)="generateReport($event)"
+                             left>
+    </fims-reporting-criteria>
+    <fims-reporting-report-page [reportPage]="reportPage$ | async" right></fims-reporting-report-page>
+  </fims-two-column-layout>
+</fims-layout-card-over>
+
+
diff --git a/src/app/reporting/detail/reporting-definition.component.ts b/src/app/reporting/detail/reporting-definition.component.ts
new file mode 100644
index 0000000..022afa1
--- /dev/null
+++ b/src/app/reporting/detail/reporting-definition.component.ts
@@ -0,0 +1,73 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, OnInit, ViewChild} from '@angular/core';
+import {Observable} from 'rxjs/Observable';
+import {ReportDefinition} from '../../services/reporting/domain/report-definition.model';
+import {ActivatedRoute} from '@angular/router';
+import {ReportingService} from '../../services/reporting/reporting.service';
+import {ReportRequest} from '../../services/reporting/domain/report-request.model';
+import {QueryParameter} from '../../services/reporting/domain/query-parameter.model';
+import {ReportPage} from '../../services/reporting/domain/report-page.model';
+import {DisplayableField} from '../../services/reporting/domain/displayable-field.model';
+import {GenerateReportEvent, ReportingCriteriaComponent} from './criteria/criteria.component';
+
+@Component({
+  templateUrl: './reporting-definition.component.html'
+})
+export class ReportingDefinitionComponent implements OnInit {
+
+  private category: string;
+
+  private identifier: string;
+
+  @ViewChild('criteria') criteriaComponent: ReportingCriteriaComponent;
+
+  queryParameter$: Observable<QueryParameter[]>;
+
+  displayableFields$: Observable<DisplayableField[]>;
+
+  reportPage$: Observable<ReportPage>;
+
+  constructor(private route: ActivatedRoute, private reportingService: ReportingService) {}
+
+  ngOnInit(): void {
+    const reportDefinition$: Observable<ReportDefinition> = this.route.params
+      .do(params => {
+        this.category = params['category'];
+        this.identifier = params['identifier'];
+      })
+      .switchMap(params => this.reportingService.findReportDefinition(params['category'], params['identifier']));
+
+    this.queryParameter$ = reportDefinition$
+      .map(reportDefinition => reportDefinition.queryParameters);
+
+    this.displayableFields$ = reportDefinition$
+      .map(reportDefinition => reportDefinition.displayableFields);
+  }
+
+  generateReport(event: GenerateReportEvent): void {
+    const reportRequest: ReportRequest = {
+      displayableFields: event.displayableFields,
+      queryParameters: event.queryParameter
+    };
+
+    this.reportPage$ = this.reportingService.generateReport(this.category, this.identifier, reportRequest);
+  }
+}
diff --git a/src/app/reporting/reporting-definitions.component.html b/src/app/reporting/reporting-definitions.component.html
new file mode 100644
index 0000000..7554cc3
--- /dev/null
+++ b/src/app/reporting/reporting-definitions.component.html
@@ -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.
+-->
+
+<mat-nav-list>
+  <h3 mat-subheader>Reports</h3>
+  <mat-list-item *ngFor="let definition of reportDefinitions$ | async">
+    <a matLine (click)="goToReport(definition.identifier)">{{ definition.name }}</a>
+    <mat-icon>chevron_right</mat-icon>
+  </mat-list-item>
+</mat-nav-list>
+
+
diff --git a/src/app/reporting/reporting-definitions.component.spec.ts b/src/app/reporting/reporting-definitions.component.spec.ts
new file mode 100644
index 0000000..e6b7d8a
--- /dev/null
+++ b/src/app/reporting/reporting-definitions.component.spec.ts
@@ -0,0 +1,97 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, inject, TestBed} from '@angular/core/testing';
+import {TranslateModule} from '@ngx-translate/core';
+import {NoopAnimationsModule} from '@angular/platform-browser/animations';
+import {ActivatedRoute, Router} from '@angular/router';
+import {ReportingService} from '../services/reporting/reporting.service';
+import {Observable} from 'rxjs/Observable';
+import {By} from '@angular/platform-browser';
+import {ReportingDefinitionsComponent} from './reporting-definitions.component';
+import {ReportDefinition} from '../services/reporting/domain/report-definition.model';
+import {ActivatedRouteStub} from '../common/testing/router-stubs';
+import {FimsSharedModule} from '../common/common.module';
+import {MatLine, MatListModule, MatToolbarModule} from '@angular/material';
+
+const definitions: ReportDefinition[] = [
+  { identifier: 'reportOne', name: '', description: '', displayableFields: [], queryParameters: [] },
+  { identifier: 'reportTwo', name: '', description: '', displayableFields: [], queryParameters: [] }
+];
+
+describe('Test reporting definitions component', () => {
+
+  let activatedRoute: ActivatedRouteStub;
+
+  let fixture: ComponentFixture<ReportingDefinitionsComponent>;
+
+  let testComponent: ReportingDefinitionsComponent;
+
+  beforeEach(() => {
+    activatedRoute = new ActivatedRouteStub();
+
+    activatedRoute.testParams = { category: 'categoryOne' };
+
+    TestBed.configureTestingModule({
+      declarations: [
+        ReportingDefinitionsComponent
+      ],
+      imports: [
+        TranslateModule.forRoot(),
+        FimsSharedModule,
+        MatToolbarModule,
+        MatListModule,
+        NoopAnimationsModule
+      ],
+      providers: [
+        {provide: Router, useValue: jasmine.createSpyObj('Router', ['navigate'])},
+        {provide: ActivatedRoute, useValue: activatedRoute },
+        {provide: ReportingService, useValue: jasmine.createSpyObj('reportingService', ['fetchReportDefinitions'])}
+      ]
+    });
+
+    fixture = TestBed.createComponent(ReportingDefinitionsComponent);
+    testComponent = fixture.componentInstance;
+
+    const reportingService = TestBed.get(ReportingService);
+    reportingService.fetchReportDefinitions.and.returnValue(Observable.of(definitions));
+
+    fixture.detectChanges();
+  });
+
+  it('should render mat-list-items on the page', () => {
+    const listItems = fixture.debugElement.queryAll(By.directive(MatLine));
+
+    expect(listItems.length).toBe(2);
+  });
+
+  it('should navigate to report definitions page', inject([Router, ActivatedRoute], (router: Router, route: ActivatedRoute) => {
+    const listItems = fixture.debugElement.queryAll(By.directive(MatLine));
+
+    listItems[1].nativeElement.click();
+
+    fixture.detectChanges();
+
+    expect(router.navigate).toHaveBeenCalledWith(['reports', 'reportTwo'], { relativeTo: route });
+  }));
+
+
+  it('should call the service with the right param', inject([ReportingService], (service: ReportingService) => {
+    expect(service.fetchReportDefinitions).toHaveBeenCalledWith('categoryOne');
+  }));
+});
diff --git a/src/app/reporting/reporting-definitions.component.ts b/src/app/reporting/reporting-definitions.component.ts
new file mode 100644
index 0000000..1f6742d
--- /dev/null
+++ b/src/app/reporting/reporting-definitions.component.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 {Component, OnInit} from '@angular/core';
+import {ReportingService} from '../services/reporting/reporting.service';
+import {Observable} from 'rxjs/Observable';
+import {ActivatedRoute, Router} from '@angular/router';
+import {ReportDefinition} from '../services/reporting/domain/report-definition.model';
+
+@Component({
+  templateUrl: './reporting-definitions.component.html'
+})
+export class ReportingDefinitionsComponent implements OnInit {
+
+  reportDefinitions$: Observable<ReportDefinition[]>;
+
+  constructor(private router: Router, private route: ActivatedRoute, private reportingService: ReportingService) {}
+
+  ngOnInit(): void {
+    this.reportDefinitions$ = this.route.params
+      .switchMap(params => this.reportingService.fetchReportDefinitions(params['category']));
+  }
+
+  goToReport(identifier: string): void {
+    this.router.navigate(['reports', identifier], { relativeTo: this.route });
+  }
+}
diff --git a/src/app/reporting/reporting.component.html b/src/app/reporting/reporting.component.html
new file mode 100644
index 0000000..7bb5429
--- /dev/null
+++ b/src/app/reporting/reporting.component.html
@@ -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.
+-->
+
+<fims-layout-card-over title="{{ 'Report categories' | translate}}">
+  <div layout-gt-xs="row" flex layout-align="center center">
+    <div flex-gt-xs="50">
+      <mat-nav-list>
+        <h3 mat-subheader>Categories</h3>
+        <a mat-list-item *ngFor="let category of categories$ | async" [routerLink]="['categories', category]" routerLinkActive="active">
+          <h3 matLine>{{ category }}</h3>
+          <mat-icon>chevron_right</mat-icon>
+        </a>
+      </mat-nav-list>
+    </div>
+    <div flex-gt-xs="50">
+      <router-outlet></router-outlet>
+    </div>
+  </div>
+</fims-layout-card-over>
diff --git a/src/app/reporting/reporting.component.spec.ts b/src/app/reporting/reporting.component.spec.ts
new file mode 100644
index 0000000..dddf418
--- /dev/null
+++ b/src/app/reporting/reporting.component.spec.ts
@@ -0,0 +1,91 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, TestBed} from '@angular/core/testing';
+import {ReportingComponent} from './reporting.component';
+import {TranslateModule} from '@ngx-translate/core';
+import {NoopAnimationsModule} from '@angular/platform-browser/animations';
+import {ActivatedRoute, Router} from '@angular/router';
+import {ReportingService} from '../services/reporting/reporting.service';
+import {Observable} from 'rxjs/Observable';
+import {By} from '@angular/platform-browser';
+import {FimsSharedModule} from '../common/common.module';
+import {RouterLinkStubDirective, RouterOutletStubComponent} from '../common/testing/router-stubs';
+import {MatListModule, MatToolbarModule} from '@angular/material';
+
+describe('Test reporting component', () => {
+
+  const activatedRoute: ActivatedRoute = undefined;
+
+  let fixture: ComponentFixture<ReportingComponent>;
+
+  let testComponent: ReportingComponent;
+
+  beforeEach(() => {
+    TestBed.configureTestingModule({
+      declarations: [
+        ReportingComponent,
+        RouterLinkStubDirective,
+        RouterOutletStubComponent
+      ],
+      imports: [
+        TranslateModule.forRoot(),
+        FimsSharedModule,
+        MatListModule,
+        MatToolbarModule,
+        NoopAnimationsModule
+      ],
+      providers: [
+        {provide: Router, useValue: jasmine.createSpyObj('Router', ['navigate'])},
+        {provide: ActivatedRoute, useValue: activatedRoute},
+        {provide: ReportingService, useValue: jasmine.createSpyObj('reportingService', ['fetchCategories'])}
+      ]
+    });
+
+    fixture = TestBed.createComponent(ReportingComponent);
+    testComponent = fixture.componentInstance;
+
+    const reportingService = TestBed.get(ReportingService);
+    reportingService.fetchCategories.and.returnValue(Observable.of(['categoryOne', 'categoryTwo']));
+
+    fixture.detectChanges();
+  });
+
+  it('should render mat-list-items on the page', () => {
+    const listItems = fixture.debugElement.queryAll(By.css('a[mat-list-item]'));
+
+    expect(listItems.length).toBe(2);
+  });
+
+  it('should navigate to report definitions page', () => {
+    const listItems = fixture.debugElement.queryAll(By.css('a[mat-list-item]'));
+
+    listItems[0].nativeElement.click();
+
+    fixture.detectChanges();
+
+    const linkDebugs = fixture.debugElement
+      .queryAll(By.directive(RouterLinkStubDirective));
+
+    const links = linkDebugs
+      .map(de => de.injector.get(RouterLinkStubDirective) as RouterLinkStubDirective);
+
+    expect(links[0].navigatedTo).toEqual(['categories', 'categoryOne']);
+  });
+
+});
diff --git a/src/app/reporting/reporting.component.ts b/src/app/reporting/reporting.component.ts
new file mode 100644
index 0000000..3080af0
--- /dev/null
+++ b/src/app/reporting/reporting.component.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 {Component, OnInit} from '@angular/core';
+import {ReportingService} from '../services/reporting/reporting.service';
+import {Observable} from 'rxjs/Observable';
+
+@Component({
+  templateUrl: './reporting.component.html'
+})
+export class ReportingComponent implements OnInit {
+
+  categories$: Observable<string[]>;
+
+  constructor(private reportingService: ReportingService) {}
+
+  ngOnInit(): void {
+    this.categories$ = this.reportingService.fetchCategories();
+  }
+
+}
diff --git a/src/app/reporting/reporting.module.ts b/src/app/reporting/reporting.module.ts
new file mode 100644
index 0000000..ad9d428
--- /dev/null
+++ b/src/app/reporting/reporting.module.ts
@@ -0,0 +1,91 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 {CovalentChipsModule, CovalentDataTableModule, CovalentExpansionPanelModule, CovalentStepsModule} from '@covalent/core';
+import {
+  MatButtonModule,
+  MatCardModule,
+  MatIconModule,
+  MatInputModule,
+  MatListModule,
+  MatTabsModule,
+  MatToolbarModule
+} from '@angular/material';
+import {CommonModule} from '@angular/common';
+import {TranslateModule} from '@ngx-translate/core';
+import {FimsSharedModule} from '../common/common.module';
+import {NgModule, Type} from '@angular/core';
+import {RouterModule} from '@angular/router';
+import {FormsModule, ReactiveFormsModule} from '@angular/forms';
+import {ReportingRoutes} from './reporting.routes';
+import {ReportingDefinitionsComponent} from './reporting-definitions.component';
+import {ReportingDefinitionComponent} from './detail/reporting-definition.component';
+import {ReportingQueryParamsComponent} from './detail/queryParams/query-params.component';
+import {FimsQueryParamDirective, ReportingQueryParamComponent} from './detail/queryParams/query-param.component';
+import {ReportingBetweenParamComponent} from './detail/queryParams/between/between.component';
+import {ReportingInputParamComponent} from './detail/queryParams/input/input.component';
+import {ReportingInParamComponent} from './detail/queryParams/in/in.component';
+import {ReportingComponent} from './reporting.component';
+import {ReportingReportPageComponent} from './detail/report-page/report-page.component';
+import {ReportingDisplayableFieldsComponent} from './detail/displayableFields/displayable-fields.component';
+import {ReportingCriteriaComponent} from './detail/criteria/criteria.component';
+
+const QUERY_PARAM_COMPONENTS: Type<any>[] = [
+  ReportingBetweenParamComponent,
+  ReportingInputParamComponent,
+  ReportingInParamComponent
+];
+
+@NgModule({
+  imports: [
+    RouterModule.forChild(ReportingRoutes),
+    FimsSharedModule,
+    TranslateModule,
+    CommonModule,
+    FormsModule,
+    ReactiveFormsModule,
+    MatCardModule,
+    MatIconModule,
+    MatToolbarModule,
+    MatInputModule,
+    MatButtonModule,
+    MatTabsModule,
+    MatListModule,
+    CovalentStepsModule,
+    CovalentDataTableModule,
+    CovalentChipsModule,
+    CovalentExpansionPanelModule
+  ],
+  declarations: [
+    ReportingComponent,
+    ReportingDefinitionsComponent,
+    ReportingDefinitionComponent,
+    ReportingCriteriaComponent,
+    ReportingQueryParamsComponent,
+    ReportingQueryParamComponent,
+    ReportingReportPageComponent,
+    ReportingDisplayableFieldsComponent,
+    QUERY_PARAM_COMPONENTS,
+    FimsQueryParamDirective
+  ],
+  entryComponents: [
+    QUERY_PARAM_COMPONENTS
+  ]
+})
+export class ReportingModule {}
diff --git a/src/app/reporting/reporting.routes.ts b/src/app/reporting/reporting.routes.ts
new file mode 100644
index 0000000..300bd2e
--- /dev/null
+++ b/src/app/reporting/reporting.routes.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 {Routes} from '@angular/router';
+import {ReportingComponent} from './reporting.component';
+import {ReportingDefinitionsComponent} from './reporting-definitions.component';
+import {ReportingDefinitionComponent} from './detail/reporting-definition.component';
+
+export const ReportingRoutes: Routes = [
+  {
+    path: '',
+    component: ReportingComponent,
+    children: [
+      {
+        path: 'categories/:category',
+        component: ReportingDefinitionsComponent
+      }
+    ]
+  },
+  {
+    path: 'categories/:category/reports/:identifier',
+    component: ReportingDefinitionComponent
+  }
+];
diff --git a/src/app/roles/components/permission-list-item.component.html b/src/app/roles/components/permission-list-item.component.html
new file mode 100644
index 0000000..1d311e4
--- /dev/null
+++ b/src/app/roles/components/permission-list-item.component.html
@@ -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.
+-->
+
+<mat-list-item>
+  <mat-icon matListAvatar>lock</mat-icon>
+  <h3 matLine>{{formPermission.label}}</h3>
+  <h4 matLine></h4>
+  <p matLine></p>
+  <mat-checkbox [(ngModel)]="formPermission.read" class="list-checkbox" [disabled]="readOnly" translate>Read</mat-checkbox>
+  <mat-checkbox [(ngModel)]="formPermission.change" class="list-checkbox" [disabled]="readOnly" translate>Change</mat-checkbox>
+  <mat-checkbox [(ngModel)]="formPermission.remove" class="list-checkbox" [disabled]="readOnly" translate>Delete</mat-checkbox>
+</mat-list-item>
diff --git a/src/app/roles/components/permission-list-item.component.scss b/src/app/roles/components/permission-list-item.component.scss
new file mode 100644
index 0000000..db936c4
--- /dev/null
+++ b/src/app/roles/components/permission-list-item.component.scss
@@ -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.
+ */
+
+
+.list-checkbox{
+  margin: 10px
+}
diff --git a/src/app/roles/components/permission-list-item.component.ts b/src/app/roles/components/permission-list-item.component.ts
new file mode 100644
index 0000000..a764fd5
--- /dev/null
+++ b/src/app/roles/components/permission-list-item.component.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 {Component, Input} from '@angular/core';
+import {FormPermission} from '../model/form-permission.model';
+
+@Component({
+  selector: 'fims-permission-list-item',
+  templateUrl: './permission-list-item.component.html',
+  styleUrls: ['./permission-list-item.component.scss']
+})
+export class PermissionListItemComponent {
+
+  private _readOnly: boolean;
+
+  @Input() formPermission: FormPermission;
+
+  @Input() set readOnly(readOnly: boolean) {
+    this._readOnly = readOnly;
+  };
+
+  get readOnly(): boolean {
+    return this._readOnly || this.formPermission.readOnly;
+  }
+}
diff --git a/src/app/roles/detail/role.detail.component.html b/src/app/roles/detail/role.detail.component.html
new file mode 100644
index 0000000..dc81837
--- /dev/null
+++ b/src/app/roles/detail/role.detail.component.html
@@ -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.
+-->
+
+<fims-layout-card-over title="{{ role.identifier }}" [navigateBackTo]="'../../'" *ngIf="(role$ | async) as role">
+  <fims-layout-card-over-header-menu>
+    <button mat-icon-button (click)="deleteRole(role)" title="{{'Delete this role' | translate}}" *hasPermission="{ id: 'identity_roles', accessLevel: 'DELETE' }"><mat-icon>delete</mat-icon></button>
+  </fims-layout-card-over-header-menu>
+  <mat-list>
+    <h3 mat-subheader translate>Permissions</h3>
+    <ng-container *ngFor="let group of permissionGroup$ | async">
+      <h3 mat-subheader translate>{{group.groupId}}</h3>
+      <fims-permission-list-item *ngFor="let permission of group.formPermissions" [formPermission]="permission" [readOnly]="true"></fims-permission-list-item>
+      <mat-divider></mat-divider>
+    </ng-container>
+  </mat-list>
+</fims-layout-card-over>
+<fims-fab-button title="{{'Edit role' | translate}}" icon="mode_edit" [link]="['edit']" [permission]="{ id: 'identity_roles', accessLevel: 'CHANGE'}"></fims-fab-button>
diff --git a/src/app/roles/detail/role.detail.component.ts b/src/app/roles/detail/role.detail.component.ts
new file mode 100644
index 0000000..75267f5
--- /dev/null
+++ b/src/app/roles/detail/role.detail.component.ts
@@ -0,0 +1,85 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, OnDestroy, OnInit} from '@angular/core';
+import {RolesStore} from '../store/index';
+import * as fromRoles from '../store';
+import {Role} from '../../services/identity/domain/role.model';
+import {DELETE, SelectAction} from '../store/role.actions';
+import {Subscription} from 'rxjs/Subscription';
+import {ActivatedRoute} from '@angular/router';
+import {IdentityService} from '../../services/identity/identity.service';
+import {PermittableGroup} from '../../services/anubis/permittable-group.model';
+import {Observable} from 'rxjs/Observable';
+import {FormPermissionService} from '../helper/form-permission.service';
+import {TdDialogService} from '@covalent/core';
+import {FormPermissionGroup} from '../model/form-permission-group.model';
+
+@Component({
+  templateUrl: './role.detail.component.html'
+})
+export class RoleDetailComponent implements OnInit, OnDestroy {
+
+  private actionsSubscription: Subscription;
+
+  role$: Observable<Role>;
+
+  permissionGroup$: Observable<FormPermissionGroup[]>;
+
+  constructor(private route: ActivatedRoute, private identityService: IdentityService, private store: RolesStore,
+              private formPermissionService: FormPermissionService, private dialogService: TdDialogService) {}
+
+  ngOnInit(): void {
+    this.actionsSubscription = this.route.params
+      .map(params => new SelectAction(params['id']))
+      .subscribe(this.store);
+
+    this.role$ = this.store.select(fromRoles.getSelectedRole)
+      .filter(role => !!role);
+
+    this.permissionGroup$ = Observable.combineLatest(
+      this.identityService.getPermittableGroups(),
+      this.role$,
+      (groups: PermittableGroup[], role: Role) => this.formPermissionService.mapToFormPermissions(groups, role.permissions)
+    );
+  }
+
+  confirmDeletion(): Observable<boolean> {
+    return this.dialogService.openConfirm({
+      message: 'Do you want to delete this role?',
+      title: 'Confirm deletion',
+      acceptButton: 'DELETE ROLE',
+    }).afterClosed();
+  }
+
+  deleteRole(role: Role): void {
+    this.confirmDeletion()
+      .filter(accept => accept)
+      .subscribe(() => {
+        this.store.dispatch({ type: DELETE, payload: {
+          role,
+          activatedRoute: this.route
+        } });
+      });
+  }
+
+  ngOnDestroy(): void {
+    this.actionsSubscription.unsubscribe();
+  }
+}
diff --git a/src/app/roles/form/create/create.form.component.html b/src/app/roles/form/create/create.form.component.html
new file mode 100644
index 0000000..50c3189
--- /dev/null
+++ b/src/app/roles/form/create/create.form.component.html
@@ -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.
+-->
+
+<fims-layout-card-over title="Create new role">
+  <fims-role-form-component #form
+                              (onSave)="onSave($event)"
+                              (onCancel)="onCancel()"
+                              [role]="role">
+  </fims-role-form-component>
+</fims-layout-card-over>
diff --git a/src/app/roles/form/create/create.form.component.ts b/src/app/roles/form/create/create.form.component.ts
new file mode 100644
index 0000000..8b1d4d1
--- /dev/null
+++ b/src/app/roles/form/create/create.form.component.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 {Component, OnDestroy, OnInit, ViewChild} from '@angular/core';
+import {ActivatedRoute, Router} from '@angular/router';
+import {Role} from '../../../services/identity/domain/role.model';
+import * as fromRoles from '../../store';
+import {Subscription} from 'rxjs/Subscription';
+import {Error} from '../../../services/domain/error.model';
+import {RolesStore} from '../../store/index';
+import {CREATE, RESET_FORM} from '../../store/role.actions';
+
+@Component({
+  templateUrl: './create.form.component.html'
+})
+export class CreateRoleFormComponent implements OnInit, OnDestroy {
+
+  private formStateSubscription: Subscription;
+
+  role: Role = { identifier: '', permissions: []};
+
+  @ViewChild('form') formComponent;
+
+  constructor(private router: Router, private route: ActivatedRoute, private store: RolesStore) {}
+
+  ngOnInit(): void {
+    this.formStateSubscription = this.store.select(fromRoles.getRoleFormError)
+      .filter((error: Error) => !!error)
+      .subscribe((error: Error) => {
+        const detailForm = this.formComponent.detailForm;
+        const errors = detailForm.get('identifier').errors || {};
+        errors['unique'] = true;
+        detailForm.get('identifier').setErrors(errors);
+      });
+  }
+
+  ngOnDestroy(): void {
+    this.formStateSubscription.unsubscribe();
+
+    this.store.dispatch({ type: RESET_FORM });
+  }
+
+  onSave(role: Role): void {
+    this.store.dispatch({ type: CREATE, payload: {
+      role,
+      activatedRoute: this.route
+    } });
+  }
+
+  onCancel(): void {
+    this.navigateAway();
+  }
+
+  navigateAway(): void {
+    this.router.navigate(['../'], { relativeTo: this.route });
+  }
+}
diff --git a/src/app/roles/form/edit/edit.form.component.html b/src/app/roles/form/edit/edit.form.component.html
new file mode 100644
index 0000000..f669a51
--- /dev/null
+++ b/src/app/roles/form/edit/edit.form.component.html
@@ -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.
+-->
+
+<fims-layout-card-over title="{{'Edit role' | translate}}">
+  <fims-role-form-component #form
+                              [editMode]="true"
+                              (onSave)="onSave($event)"
+                              (onCancel)="onCancel()"
+                              [role]="role">
+  </fims-role-form-component>
+</fims-layout-card-over>
diff --git a/src/app/roles/form/edit/edit.form.component.ts b/src/app/roles/form/edit/edit.form.component.ts
new file mode 100644
index 0000000..6134f1e
--- /dev/null
+++ b/src/app/roles/form/edit/edit.form.component.ts
@@ -0,0 +1,65 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, OnDestroy, OnInit} from '@angular/core';
+import {ActivatedRoute, Router} from '@angular/router';
+import {Role} from '../../../services/identity/domain/role.model';
+import * as fromRoles from '../../store';
+import {Subscription} from 'rxjs/Subscription';
+import {SelectAction, UPDATE} from '../../store/role.actions';
+import {RolesStore} from '../../store/index';
+
+@Component({
+  templateUrl: './edit.form.component.html'
+})
+export class EditRoleFormComponent implements OnInit, OnDestroy {
+
+  private actionsSubscription: Subscription;
+  private roleSubscription: Subscription;
+
+  role: Role;
+
+  constructor(private router: Router, private route: ActivatedRoute, private store: RolesStore) {}
+
+  ngOnInit(): void {
+    this.actionsSubscription = this.route.params
+      .map(params => new SelectAction(params['id']))
+      .subscribe(this.store);
+
+    this.roleSubscription = this.store.select(fromRoles.getSelectedRole).subscribe((role: Role) => {
+      this.role = role;
+    });
+  }
+
+  ngOnDestroy(): void {
+    this.actionsSubscription.unsubscribe();
+    this.roleSubscription.unsubscribe();
+  }
+
+  onSave(role: Role): void {
+    this.store.dispatch({ type: UPDATE, payload: {
+      role,
+      activatedRoute: this.route
+    } });
+  }
+
+  onCancel(): void {
+    this.router.navigate(['../'], { relativeTo: this.route });
+  }
+
+}
diff --git a/src/app/roles/form/form.component.html b/src/app/roles/form/form.component.html
new file mode 100644
index 0000000..0c2c3ac
--- /dev/null
+++ b/src/app/roles/form/form.component.html
@@ -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.
+-->
+
+<div class="inset">
+  <div layout="row">
+    <form [formGroup]="detailForm">
+      <div layout="row">
+        <fims-id-input [form]="detailForm" controlName="identifier" [readonly]="editMode"></fims-id-input>
+      </div>
+    </form>
+  </div>
+  <div layout="row" layout-margin>
+    <mat-list>
+      <mat-list-item>
+        <h3 matLine></h3>
+        <h4 matLine></h4>
+        <p matLine></p>
+        <mat-checkbox [(ngModel)]="allRead" class="list-checkbox" translate>Read</mat-checkbox>
+        <mat-checkbox [(ngModel)]="allChange" class="list-checkbox" translate>Change</mat-checkbox>
+        <mat-checkbox [(ngModel)]="allRemove" class="list-checkbox" translate>Delete</mat-checkbox>
+      </mat-list-item>
+      <mat-divider></mat-divider>
+      <ng-container *ngFor="let group of permissionGroups">
+        <h3 mat-subheader translate>{{group.groupId}}</h3>
+        <fims-permission-list-item *ngFor="let permission of group.formPermissions" [formPermission]="permission" [readOnly]="false"></fims-permission-list-item>
+        <mat-divider></mat-divider>
+      </ng-container>
+    </mat-list>
+  </div>
+  <div layout="row" layout-margin>
+    <button type="submit" mat-raised-button color="primary" [disabled]="detailForm.invalid" (click)="save()">{{'SAVE ROLE' | translate}}</button>
+    <button (click)="cancel()" mat-button>{{'CANCEL' | translate}}</button>
+  </div>
+</div>
diff --git a/src/app/roles/form/form.component.scss b/src/app/roles/form/form.component.scss
new file mode 100644
index 0000000..a84a439
--- /dev/null
+++ b/src/app/roles/form/form.component.scss
@@ -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.
+ */
+
+.list-checkbox{
+  margin: 10px
+}
diff --git a/src/app/roles/form/form.component.spec.ts b/src/app/roles/form/form.component.spec.ts
new file mode 100644
index 0000000..f3a5805
--- /dev/null
+++ b/src/app/roles/form/form.component.spec.ts
@@ -0,0 +1,143 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 {async, ComponentFixture, TestBed} from '@angular/core/testing';
+import {FormsModule, ReactiveFormsModule} from '@angular/forms';
+import {RoleFormComponent} from './form.component';
+import {PermittableGroup} from '../../services/anubis/permittable-group.model';
+import {Observable} from 'rxjs/Observable';
+import {IdentityService} from '../../services/identity/identity.service';
+import {Role} from '../../services/identity/domain/role.model';
+import {TranslateLoader, TranslateModule} from '@ngx-translate/core';
+import {IdInputComponent} from '../../common/id-input/id-input.component';
+import {PermittableGroupIdMapper} from '../../services/security/authz/permittable-group-id-mapper';
+import {NoopAnimationsModule} from '@angular/platform-browser/animations';
+import {FormPermissionService} from '../helper/form-permission.service';
+import {PermissionListItemComponent} from '../components/permission-list-item.component';
+import {MatCheckboxModule, MatIconModule, MatInputModule} from '@angular/material';
+
+class FakeLoader implements TranslateLoader {
+  getTranslation(lang: string): Observable<any> {
+    return Observable.of({});
+  }
+}
+
+describe('Test roles form', () => {
+
+  let fixture: ComponentFixture<RoleFormComponent>;
+  let component: RoleFormComponent;
+
+  const officePermittable: PermittableGroup = {
+    identifier: 'office__v1__offices',
+    permittables: [
+      {path: '/offices', method: 'POST'},
+      {path: '/offices', method: 'PUT'}
+    ]
+  };
+
+  const identityService = {
+    getPermittableGroups(): Observable<PermittableGroup[]> {
+      const permittableGroups: PermittableGroup[] = [];
+      permittableGroups.push(officePermittable);
+      return Observable.of(permittableGroups);
+    }
+  };
+
+  beforeEach(() => {
+    TestBed.configureTestingModule({
+      declarations: [
+        RoleFormComponent,
+        IdInputComponent,
+        PermissionListItemComponent
+      ],
+      imports: [
+        NoopAnimationsModule,
+        TranslateModule.forRoot(),
+        MatInputModule,
+        MatIconModule,
+        MatCheckboxModule,
+        FormsModule,
+        ReactiveFormsModule
+      ],
+      providers: [
+        {provide: IdentityService, useValue: identityService},
+        FormPermissionService,
+        PermittableGroupIdMapper
+      ]
+    });
+
+    fixture = TestBed.createComponent(RoleFormComponent);
+    component = fixture.componentInstance;
+  });
+
+  it('should save same role with given permission groups', async(() => {
+    component.role = {
+      identifier: 'test',
+      permissions: [{
+        permittableEndpointGroupIdentifier: officePermittable.identifier,
+        allowedOperations: ['READ', 'CHANGE']
+      }]
+    };
+
+    fixture.detectChanges();
+
+    // Wait for async service call
+    fixture.whenStable().then(() => {
+      component.onSave.subscribe((role: Role) => {
+        const expected: Role = {
+          identifier: 'test',
+          permissions: [{
+            permittableEndpointGroupIdentifier: officePermittable.identifier,
+            allowedOperations: ['READ', 'CHANGE']
+          }]
+        };
+        expect(JSON.stringify(role)).toBe(JSON.stringify(expected));
+      });
+      component.save();
+    });
+  }));
+
+  it('should save changed role when changed', async(() => {
+    component.role = {identifier: 'test', permissions: [
+      {
+        permittableEndpointGroupIdentifier: officePermittable.identifier,
+        allowedOperations: ['READ', 'CHANGE', 'DELETE']
+      }
+    ]};
+
+    fixture.detectChanges();
+
+    // Wait for async service call
+    fixture.whenStable().then(() => {
+      const formPermission = component.formPermissions[0];
+      formPermission.change = false;
+
+      component.onSave.subscribe((role: Role) => {
+        const expected: Role = {
+          identifier: 'test',
+          permissions: [{
+            permittableEndpointGroupIdentifier: officePermittable.identifier,
+            allowedOperations: ['READ']
+          }]
+        };
+        expect(JSON.stringify(role)).toBe(JSON.stringify(expected));
+      });
+      component.save();
+    });
+  }));
+});
diff --git a/src/app/roles/form/form.component.ts b/src/app/roles/form/form.component.ts
new file mode 100644
index 0000000..720b3a8
--- /dev/null
+++ b/src/app/roles/form/form.component.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 {Component, EventEmitter, Input, OnDestroy, OnInit, Output} from '@angular/core';
+import {FormBuilder, FormGroup, Validators} from '@angular/forms';
+import {Role} from '../../services/identity/domain/role.model';
+import {PermittableGroup} from '../../services/anubis/permittable-group.model';
+import {IdentityService} from '../../services/identity/identity.service';
+import {FormPermission} from '../model/form-permission.model';
+import {FimsValidators} from '../../common/validator/validators';
+import {FormPermissionService} from '../helper/form-permission.service';
+import {FormPermissionGroup} from '../model/form-permission-group.model';
+import {Subscription} from 'rxjs/Subscription';
+
+@Component({
+  selector: 'fims-role-form-component',
+  templateUrl: './form.component.html',
+  styleUrls: ['./form.component.scss']
+})
+export class RoleFormComponent implements OnInit, OnDestroy {
+
+  private permittableGroupSubscription: Subscription;
+
+  private _role: Role;
+
+  permissionGroups: FormPermissionGroup[] = [];
+
+  formPermissions: FormPermission[] = [];
+
+  detailForm: FormGroup;
+
+  @Input() editMode: boolean;
+
+  @Input('role') set role(role: Role) {
+    this._role = role;
+    this.prepareForm(role);
+  }
+
+  @Output('onSave') onSave = new EventEmitter<Role>();
+
+  @Output('onCancel') onCancel = new EventEmitter<void>();
+
+  constructor(private formBuilder: FormBuilder, private identityService: IdentityService,
+              private formPermissionService: FormPermissionService) {}
+
+  ngOnInit(): void {
+    this.permittableGroupSubscription = this.identityService.getPermittableGroups()
+      .subscribe((groups: PermittableGroup[]) => {
+        this.permissionGroups = this.formPermissionService.mapToFormPermissions(groups, this._role.permissions);
+
+        this.permissionGroups.forEach(group => {
+          this.formPermissions.push(...group.formPermissions);
+        });
+      });
+  }
+
+  ngOnDestroy(): void {
+    this.permittableGroupSubscription.unsubscribe();
+  }
+
+  private prepareForm(role: Role): void {
+    this.detailForm = this.formBuilder.group({
+      identifier: [role.identifier, [Validators.required, Validators.minLength(3), Validators.maxLength(32), FimsValidators.urlSafe]]
+    });
+  }
+
+  save(): void {
+    const identifier = this.detailForm.get('identifier').value;
+    this.onSave.emit(this.formPermissionService.mapToRole(identifier, this.formPermissions));
+  }
+
+  cancel(): void {
+    this.onCancel.emit();
+  }
+
+  set allRead(checked: boolean) {
+    for (const formPermission of this.formPermissions) {
+      formPermission.read = checked;
+    }
+  }
+
+  set allChange(checked: boolean) {
+    for (const formPermission of this.formPermissions) {
+      formPermission.change = checked;
+    }
+  }
+
+  set allRemove(checked: boolean) {
+    for (const formPermission of this.formPermissions) {
+      formPermission.remove = checked;
+    }
+  }
+
+  get allRead(): boolean {
+    const found: FormPermission = this.formPermissions.find(formPermission => !formPermission.read);
+    return !found;
+  }
+
+  get allChange(): boolean {
+    const found: FormPermission = this.formPermissions.find(formPermission => !formPermission.change);
+    return !found;
+  }
+
+  get allRemove(): boolean {
+    const found: FormPermission = this.formPermissions.find(formPermission => !formPermission.remove);
+    return !found;
+  }
+
+}
diff --git a/src/app/roles/helper/form-permission.service.ts b/src/app/roles/helper/form-permission.service.ts
new file mode 100644
index 0000000..3e0e496
--- /dev/null
+++ b/src/app/roles/helper/form-permission.service.ts
@@ -0,0 +1,123 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 {FormPermission} from '../model/form-permission.model';
+import {AllowedOperation, Permission} from '../../services/identity/domain/permission.model';
+import {PermittableGroup} from '../../services/anubis/permittable-group.model';
+import {FimsPermissionDescriptor} from '../../services/security/authz/fims-permission-descriptor';
+import {Injectable} from '@angular/core';
+import {PermittableGroupIdMapper} from '../../services/security/authz/permittable-group-id-mapper';
+import {Role} from '../../services/identity/domain/role.model';
+import {FormPermissionGroup} from '../model/form-permission-group.model';
+
+@Injectable()
+export class FormPermissionService {
+
+  constructor(private idMapper: PermittableGroupIdMapper) {}
+
+  mapToFormPermissions(groups: PermittableGroup[], permissions: Permission[]): FormPermissionGroup[] {
+    const formGroups: FormPermissionGroup[] = [];
+
+    for (const permittableGroup of groups) {
+      const groupId = this.groupId(permittableGroup);
+
+      let foundGroup = formGroups.find(group => group.groupId === groupId);
+
+      if (!foundGroup) {
+        foundGroup = {
+          groupId,
+          formPermissions: []
+        };
+        formGroups.push(foundGroup);
+      }
+
+      const foundPermission: Permission = this.findPermission(permittableGroup.identifier, permissions);
+
+      const descriptor: FimsPermissionDescriptor = this.idMapper.map(permittableGroup.identifier);
+
+      if (!descriptor) {
+        continue;
+      }
+
+      const formPermission = new FormPermission(permittableGroup.identifier);
+
+      formPermission.label = descriptor.label;
+      formPermission.readOnly = descriptor.readOnly;
+
+      if (foundPermission) {
+        formPermission.read = this.hasOperation(foundPermission.allowedOperations, 'READ');
+        formPermission.change = this.hasOperation(foundPermission.allowedOperations, 'CHANGE');
+        formPermission.remove = this.hasOperation(foundPermission.allowedOperations, 'DELETE');
+      }
+      foundGroup.formPermissions.push(formPermission);
+    }
+
+    this.sortGroups(formGroups);
+
+    return formGroups;
+  }
+
+  private sortGroups(formGroups: FormPermissionGroup[]) {
+    formGroups.sort((a: FormPermissionGroup, b: FormPermissionGroup) => a.groupId.localeCompare(b.groupId));
+    formGroups.forEach(group => group.formPermissions.sort((a: FormPermission, b: FormPermission) => a.label.localeCompare(b.label)));
+  }
+
+  private groupId(group: PermittableGroup): string {
+    const identifier = group.identifier;
+    return identifier.substring(0, identifier.indexOf('_')).toUpperCase();
+  }
+
+  private hasOperation(allowedOperations: AllowedOperation[], operation: AllowedOperation): boolean {
+    return allowedOperations.indexOf(operation) > -1;
+  }
+
+  private findPermission(identifier: string, permissions: Permission[]): Permission {
+    return permissions.find((permission: Permission) => permission.permittableEndpointGroupIdentifier === identifier);
+  }
+
+  mapToRole(identifier: string, formPermissions: FormPermission[]): Role {
+    const permissions: Permission[] = [];
+
+    for (const formPermission of formPermissions) {
+      const allowedOperations: AllowedOperation[] = [];
+
+      if (formPermission.read) {
+        allowedOperations.push('READ');
+      }
+
+      if (formPermission.change) {
+        allowedOperations.push('CHANGE');
+      }
+
+      if (formPermission.remove) {
+        allowedOperations.push('DELETE');
+      }
+
+      permissions.push({
+        permittableEndpointGroupIdentifier: formPermission.groupIdentifier,
+        allowedOperations: allowedOperations
+      });
+    }
+
+    return {
+      identifier: identifier,
+      permissions: permissions
+    };
+  }
+}
+
diff --git a/src/app/roles/model/form-permission-group.model.ts b/src/app/roles/model/form-permission-group.model.ts
new file mode 100644
index 0000000..913ceac
--- /dev/null
+++ b/src/app/roles/model/form-permission-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 {FormPermission} from './form-permission.model';
+
+export interface FormPermissionGroup {
+  groupId: string;
+  formPermissions: FormPermission[];
+}
diff --git a/src/app/roles/model/form-permission.model.ts b/src/app/roles/model/form-permission.model.ts
new file mode 100644
index 0000000..6eaaacf
--- /dev/null
+++ b/src/app/roles/model/form-permission.model.ts
@@ -0,0 +1,93 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 FormPermission {
+
+  private _groupIdentifier: string;
+  private _read = false;
+  private _change = false;
+  private _remove = false;
+
+  private _label = '';
+  private _readOnly = true;
+
+  constructor(groupIdentifier: string) {
+    this._groupIdentifier = groupIdentifier;
+  }
+
+  get groupIdentifier(): string {
+    return this._groupIdentifier;
+  }
+
+  get label(): string {
+    return this._label;
+  }
+
+  set label(value: string) {
+    this._label = value;
+  }
+
+  get readOnly(): boolean {
+    return this._readOnly;
+  }
+
+  set readOnly(value: boolean) {
+    this._readOnly = value;
+  }
+
+  get read(): boolean {
+    return this._read;
+  }
+
+  set read(value: boolean) {
+    this._read = value;
+
+    if (!value) {
+      this.change = false;
+      this.remove = false;
+    }
+  }
+
+  get change(): boolean {
+    return this._change;
+  }
+
+  set change(value: boolean) {
+    this._change = value;
+
+    if (!value) {
+      this.remove = false;
+    } else {
+      this.read = true;
+    }
+  }
+
+  get remove(): boolean {
+    return this._remove;
+  }
+
+  set remove(value: boolean) {
+    this._remove = value;
+
+    if (value) {
+      this.read = true;
+      this.change = true;
+    }
+  }
+}
diff --git a/src/app/roles/role-exists.guard.ts b/src/app/roles/role-exists.guard.ts
new file mode 100644
index 0000000..ff085ac
--- /dev/null
+++ b/src/app/roles/role-exists.guard.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 {Store} from '@ngrx/store';
+import {ActivatedRouteSnapshot, CanActivate, RouterStateSnapshot} from '@angular/router';
+import {Injectable} from '@angular/core';
+import * as fromRoles from './store';
+import {Observable} from 'rxjs/Observable';
+import {of} from 'rxjs/observable/of';
+import {IdentityService} from '../services/identity/identity.service';
+import {LoadAction} from './store/role.actions';
+import {ExistsGuardService} from '../common/guards/exists-guard';
+
+@Injectable()
+export class RoleExistsGuard implements CanActivate {
+
+  constructor(private store: Store<fromRoles.State>,
+              private identityService: IdentityService,
+              private existsGuardService: ExistsGuardService) {}
+
+  hasRoleInStore(id: string): Observable<boolean> {
+    const timestamp$ = this.store.select(fromRoles.getRolesLoadedAt)
+      .map(loadedAt => loadedAt[id]);
+
+    return this.existsGuardService.isWithinExpiry(timestamp$);
+  }
+
+  hasRoleInApi(id: string): Observable<boolean> {
+    const getRole$ = this.identityService.getRole(id)
+      .map(roleEntity => new LoadAction({
+        resource: roleEntity
+      }))
+      .do((action: LoadAction) => this.store.dispatch(action))
+      .map(office => !!office);
+
+    return this.existsGuardService.routeTo404OnError(getRole$);
+  }
+
+  hasRole(id: string): Observable<boolean> {
+    return this.hasRoleInStore(id)
+      .switchMap(inStore => {
+        if (inStore) {
+          return of(inStore);
+        }
+
+        return this.hasRoleInApi(id);
+      });
+  }
+
+  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
+    return this.hasRole(route.params['id']);
+  }
+}
diff --git a/src/app/roles/role.component.html b/src/app/roles/role.component.html
new file mode 100644
index 0000000..b4acc22
--- /dev/null
+++ b/src/app/roles/role.component.html
@@ -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.
+-->
+
+<fims-layout-card-over title="{{'Manage roles' | translate}}">
+  <fims-data-table flex (onActionCellClick)="rowSelect($event)" [columns]="columns" [data]="rolesData$ | async" [loading]="loading$ | async"></fims-data-table>
+</fims-layout-card-over>
+<fims-fab-button title="{{'Create new role' | translate}}" icon="add" [link]="['create']" [permission]="{ id: 'identity_roles', accessLevel: 'CHANGE'}"></fims-fab-button>
diff --git a/src/app/roles/role.component.ts b/src/app/roles/role.component.ts
new file mode 100644
index 0000000..cfc8433
--- /dev/null
+++ b/src/app/roles/role.component.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 {Component, OnInit} from '@angular/core';
+import {ActivatedRoute, Router} from '@angular/router';
+import {Role} from '../services/identity/domain/role.model';
+import {TableData} from '../common/data-table/data-table.component';
+import * as fromRoot from '../store';
+import {Observable} from 'rxjs/Observable';
+import {SEARCH} from '../store/role/role.actions';
+import {RolesStore} from './store/index';
+
+@Component({
+  templateUrl: './role.component.html'
+})
+export class RoleComponent implements OnInit {
+
+  rolesData$: Observable<TableData>;
+
+  loading$: Observable<boolean>;
+
+  columns: any[] = [
+    { name: 'identifier', label: 'Id' }
+  ];
+
+  constructor(private router: Router, private route: ActivatedRoute, private store: RolesStore) {}
+
+  ngOnInit(): void {
+    this.rolesData$ = this.store.select(fromRoot.getRoleSearchResults)
+      .map(rolePage => ({
+          data: rolePage.roles,
+          totalElements: rolePage.totalElements,
+          totalPages: rolePage.totalPages
+        })
+      );
+
+    this.loading$ = this.store.select(fromRoot.getRoleSearchLoading);
+
+    this.store.dispatch({ type: SEARCH });
+  }
+
+  rowSelect(role: Role): void {
+    this.router.navigate(['detail', role.identifier], { relativeTo: this.route });
+  }
+
+}
diff --git a/src/app/roles/role.module.ts b/src/app/roles/role.module.ts
new file mode 100644
index 0000000..7f97f4c
--- /dev/null
+++ b/src/app/roles/role.module.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 {NgModule} from '@angular/core';
+import {RouterModule} from '@angular/router';
+import {FormsModule, ReactiveFormsModule} from '@angular/forms';
+import {RoleComponent} from './role.component';
+import {RoleRoutes} from './role.routing';
+import {RoleFormComponent} from './form/form.component';
+import {CreateRoleFormComponent} from './form/create/create.form.component';
+import {EditRoleFormComponent} from './form/edit/edit.form.component';
+import {FimsSharedModule} from '../common/common.module';
+import {RoleExistsGuard} from './role-exists.guard';
+import {RolesStore, roleStoreFactory} from './store/index';
+import {Store} from '@ngrx/store';
+import {RoleNotificationEffects} from './store/effects/notification.effects';
+import {EffectsModule} from '@ngrx/effects';
+import {RoleRouteEffects} from './store/effects/route.effects';
+import {RoleApiEffects} from './store/effects/service.effects';
+import {RoleDetailComponent} from './detail/role.detail.component';
+import {FormPermissionService} from './helper/form-permission.service';
+import {PermissionListItemComponent} from './components/permission-list-item.component';
+import {MatButtonModule, MatCheckboxModule, MatIconModule, MatInputModule, MatListModule, MatToolbarModule} from '@angular/material';
+import {CommonModule} from '@angular/common';
+import {TranslateModule} from '@ngx-translate/core';
+
+@NgModule({
+  imports: [
+    RouterModule.forChild(RoleRoutes),
+    FimsSharedModule,
+    TranslateModule,
+    CommonModule,
+    ReactiveFormsModule,
+    FormsModule,
+    MatCheckboxModule,
+    MatIconModule,
+    MatListModule,
+    MatToolbarModule,
+    MatInputModule,
+    MatButtonModule,
+
+    EffectsModule.run(RoleApiEffects),
+    EffectsModule.run(RoleRouteEffects),
+    EffectsModule.run(RoleNotificationEffects)
+  ],
+  declarations: [
+    RoleComponent,
+    RoleFormComponent,
+    CreateRoleFormComponent,
+    EditRoleFormComponent,
+    RoleDetailComponent,
+    PermissionListItemComponent
+  ],
+  providers: [
+    FormPermissionService,
+    RoleExistsGuard,
+    { provide: RolesStore, useFactory: roleStoreFactory, deps: [Store]}
+  ],
+  entryComponents: []
+})
+export class RoleModule {}
diff --git a/src/app/roles/role.routing.ts b/src/app/roles/role.routing.ts
new file mode 100644
index 0000000..9cfb28a
--- /dev/null
+++ b/src/app/roles/role.routing.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 {Routes} from '@angular/router';
+import {RoleComponent} from './role.component';
+import {CreateRoleFormComponent} from './form/create/create.form.component';
+import {EditRoleFormComponent} from './form/edit/edit.form.component';
+import {RoleExistsGuard} from './role-exists.guard';
+import {RoleDetailComponent} from './detail/role.detail.component';
+
+export const RoleRoutes: Routes = [
+  {
+    path: '',
+    component: RoleComponent,
+    data: {title: 'Manage roles and permissions', hasPermission: {id: 'identity_roles', accessLevel: 'READ'}}
+  },
+  {
+    path: 'create',
+    component: CreateRoleFormComponent,
+    data: {title: 'Create new role', hasPermission: {id: 'identity_roles', accessLevel: 'CHANGE'}}
+  },
+  {
+    path: 'detail/:id',
+    component: RoleDetailComponent,
+    canActivate: [RoleExistsGuard],
+    data: {title: 'View role', hasPermission: {id: 'identity_roles', accessLevel: 'READ'}}
+  },
+  {
+    path: 'detail/:id/edit',
+    component: EditRoleFormComponent,
+    canActivate: [RoleExistsGuard],
+    data: {title: 'Edit role', hasPermission: {id: 'identity_roles', accessLevel: 'CHANGE'}}
+  }
+];
diff --git a/src/app/roles/store/effects/notification.effects.ts b/src/app/roles/store/effects/notification.effects.ts
new file mode 100644
index 0000000..3103349
--- /dev/null
+++ b/src/app/roles/store/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 roleActions from '../role.actions';
+import {NotificationService, NotificationType} from '../../../services/notification/notification.service';
+
+@Injectable()
+export class RoleNotificationEffects {
+
+  @Effect({ dispatch: false })
+  createRoleSuccess$: Observable<Action> = this.actions$
+    .ofType(roleActions.CREATE_SUCCESS, roleActions.UPDATE_SUCCESS)
+    .do(() => this.notificationService.send({
+      type: NotificationType.MESSAGE,
+      message: 'Role is going to be saved'
+    }));
+
+  @Effect({ dispatch: false })
+  deleteRoleSuccess$: Observable<Action> = this.actions$
+    .ofType(roleActions.DELETE_SUCCESS)
+    .do(() => this.notificationService.send({
+      type: NotificationType.MESSAGE,
+      message: 'Role is going to be deleted'
+    }));
+
+  constructor(private actions$: Actions, private notificationService: NotificationService) {}
+}
+
diff --git a/src/app/roles/store/effects/route.effects.ts b/src/app/roles/store/effects/route.effects.ts
new file mode 100644
index 0000000..92736df
--- /dev/null
+++ b/src/app/roles/store/effects/route.effects.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 {Actions, Effect} from '@ngrx/effects';
+import {Observable} from 'rxjs/Observable';
+import {Action} from '@ngrx/store';
+import * as roleActions from '../role.actions';
+import {Router} from '@angular/router';
+
+@Injectable()
+export class RoleRouteEffects {
+
+  @Effect({ dispatch: false })
+  createRoleSuccess$: Observable<Action> = this.actions$
+    .ofType(roleActions.CREATE_SUCCESS, roleActions.UPDATE_SUCCESS)
+    .map(action => action.payload)
+    .do(payload => this.router.navigate(['../'], { relativeTo: payload.activatedRoute }));
+
+  @Effect({ dispatch: false })
+  deleteRoleSuccess$: Observable<Action> = this.actions$
+    .ofType(roleActions.DELETE_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/roles/store/effects/service.effects.ts b/src/app/roles/store/effects/service.effects.ts
new file mode 100644
index 0000000..732d4ee
--- /dev/null
+++ b/src/app/roles/store/effects/service.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 {Observable} from 'rxjs/Observable';
+import {Action} from '@ngrx/store';
+import {of} from 'rxjs/observable/of';
+import * as roleActions from '../role.actions';
+import {IdentityService} from '../../../services/identity/identity.service';
+
+@Injectable()
+export class RoleApiEffects {
+
+  @Effect()
+  createRole$: Observable<Action> = this.actions$
+    .ofType(roleActions.CREATE)
+    .map((action: roleActions.CreateRoleAction) => action.payload)
+    .mergeMap(payload =>
+      this.identityService.createRole(payload.role)
+        .map(() => new roleActions.CreateRoleSuccessAction({
+          resource: payload.role,
+          activatedRoute: payload.activatedRoute
+        }))
+        .catch((error) => of(new roleActions.CreateRoleFailAction(error)))
+    );
+
+  @Effect()
+  updateRole$: Observable<Action> = this.actions$
+    .ofType(roleActions.UPDATE)
+    .map((action: roleActions.UpdateRoleAction) => action.payload)
+    .mergeMap(payload =>
+      this.identityService.changeRole(payload.role)
+        .map(() => new roleActions.UpdateRoleSuccessAction({
+          resource: payload.role,
+          activatedRoute: payload.activatedRoute
+        }))
+        .catch((error) => of(new roleActions.UpdateRoleFailAction(error)))
+    );
+
+  @Effect()
+  deleteRole$: Observable<Action> = this.actions$
+    .ofType(roleActions.DELETE)
+    .map((action: roleActions.DeleteRoleAction) => action.payload)
+    .mergeMap(payload =>
+      this.identityService.deleteRole(payload.role.identifier)
+        .map(() => new roleActions.DeleteRoleSuccessAction({
+          resource: payload.role,
+          activatedRoute: payload.activatedRoute
+        }))
+        .catch((error) => of(new roleActions.DeleteRoleFailAction(error)))
+    );
+
+  constructor(private actions$: Actions, private identityService: IdentityService) { }
+
+}
diff --git a/src/app/roles/store/index.ts b/src/app/roles/store/index.ts
new file mode 100644
index 0000000..6e36b8c
--- /dev/null
+++ b/src/app/roles/store/index.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 * as fromRoot from '../../store';
+import {ActionReducer, Store} from '@ngrx/store';
+import {createReducer} from '../../store/index';
+import {createSelector} from 'reselect';
+import {createResourceReducer, getResourceLoadedAt, getResourceSelected, ResourceState} from '../../common/store/resource.reducer';
+import {createFormReducer, FormState, getFormError} from '../../common/store/form.reducer';
+
+export interface State extends fromRoot.State {
+  roles: ResourceState;
+  roleForm: FormState;
+}
+
+const reducers = {
+  roles: createResourceReducer('Role'),
+  roleForm: createFormReducer('Role'),
+};
+
+export const roleModuleReducer: ActionReducer<State> = createReducer(reducers);
+
+export class RolesStore extends Store<State> {}
+
+export function roleStoreFactory(appStore: Store<fromRoot.State>) {
+  appStore.replaceReducer(roleModuleReducer);
+  return appStore;
+}
+
+export const getRolesState = (state: State) => state.roles;
+
+export const getRoleFormState = (state: State) => state.roleForm;
+export const getRoleFormError = createSelector(getRoleFormState, getFormError);
+
+export const getRolesLoadedAt = createSelector(getRolesState, getResourceLoadedAt);
+export const getSelectedRole = createSelector(getRolesState, getResourceSelected);
diff --git a/src/app/roles/store/role.actions.ts b/src/app/roles/store/role.actions.ts
new file mode 100644
index 0000000..ff5d1ed
--- /dev/null
+++ b/src/app/roles/store/role.actions.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 {Action} from '@ngrx/store';
+import {type} from '../../store/util';
+import {Role} from '../../services/identity/domain/role.model';
+import {Error} from '../../services/domain/error.model';
+import {
+  CreateResourceSuccessPayload,
+  DeleteResourceSuccessPayload,
+  LoadResourcePayload,
+  SelectResourcePayload,
+  UpdateResourceSuccessPayload
+} from '../../common/store/resource.reducer';
+import {RoutePayload} from '../../common/store/route-payload';
+
+export const LOAD = type('[Role] Load');
+export const SELECT = type('[Role] Select');
+
+export const CREATE = type('[Role] Create');
+export const CREATE_SUCCESS = type('[Role] Create Success');
+export const CREATE_FAIL = type('[Role] Create Fail');
+
+export const UPDATE = type('[Role] Update');
+export const UPDATE_SUCCESS = type('[Role] Update Success');
+export const UPDATE_FAIL = type('[Role] Update Fail');
+
+export const DELETE = type('[Role] Delete');
+export const DELETE_SUCCESS = type('[Role] Delete Success');
+export const DELETE_FAIL = type('[Role] Delete Fail');
+
+export const RESET_FORM = type('[Role] Reset Form');
+
+export interface RoleRoutePayload extends RoutePayload {
+  role: Role;
+}
+
+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 CreateRoleAction implements Action {
+  readonly type = CREATE;
+
+  constructor(public payload: RoleRoutePayload) { }
+}
+
+export class CreateRoleSuccessAction implements Action {
+  readonly type = CREATE_SUCCESS;
+
+  constructor(public payload: CreateResourceSuccessPayload) { }
+}
+
+export class CreateRoleFailAction implements Action {
+  readonly type = CREATE_FAIL;
+
+  constructor(public payload: Error) { }
+}
+
+export class UpdateRoleAction implements Action {
+  readonly type = UPDATE;
+
+  constructor(public payload: RoleRoutePayload) { }
+}
+
+export class UpdateRoleSuccessAction implements Action {
+  readonly type = UPDATE_SUCCESS;
+
+  constructor(public payload: UpdateResourceSuccessPayload) { }
+}
+
+export class UpdateRoleFailAction implements Action {
+  readonly type = UPDATE_FAIL;
+
+  constructor(public payload: Error) { }
+}
+
+export class DeleteRoleAction implements Action {
+  readonly type = DELETE;
+
+  constructor(public payload: RoleRoutePayload) { }
+}
+
+export class DeleteRoleSuccessAction implements Action {
+  readonly type = DELETE_SUCCESS;
+
+  constructor(public payload: DeleteResourceSuccessPayload) { }
+}
+
+export class DeleteRoleFailAction implements Action {
+  readonly type = DELETE_FAIL;
+
+  constructor(public payload: Error) { }
+}
+
+export class ResetRoleFormAction implements Action {
+  readonly type = RESET_FORM;
+
+  constructor() {}
+}
+
+export type Actions
+  = LoadAction
+  | SelectAction
+  | CreateRoleAction
+  | CreateRoleSuccessAction
+  | CreateRoleFailAction
+  | UpdateRoleAction
+  | UpdateRoleSuccessAction
+  | UpdateRoleFailAction
+  | DeleteRoleAction
+  | DeleteRoleSuccessAction
+  | DeleteRoleFailAction
+  | ResetRoleFormAction;
diff --git a/src/app/services/accounting/accounting.service.ts b/src/app/services/accounting/accounting.service.ts
new file mode 100644
index 0000000..cb404bb
--- /dev/null
+++ b/src/app/services/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/services/accounting/domain/account-command-action.model.ts
similarity index 100%
rename from src/app/sevices/accounting/domain/account-command-action.model.ts
rename to src/app/services/accounting/domain/account-command-action.model.ts
diff --git a/src/app/sevices/accounting/domain/account-command.model.ts b/src/app/services/accounting/domain/account-command.model.ts
similarity index 100%
rename from src/app/sevices/accounting/domain/account-command.model.ts
rename to src/app/services/accounting/domain/account-command.model.ts
diff --git a/src/app/sevices/accounting/domain/account-entry-page.model.ts b/src/app/services/accounting/domain/account-entry-page.model.ts
similarity index 100%
rename from src/app/sevices/accounting/domain/account-entry-page.model.ts
rename to src/app/services/accounting/domain/account-entry-page.model.ts
diff --git a/src/app/sevices/accounting/domain/account-entry-type.model.ts b/src/app/services/accounting/domain/account-entry-type.model.ts
similarity index 100%
rename from src/app/sevices/accounting/domain/account-entry-type.model.ts
rename to src/app/services/accounting/domain/account-entry-type.model.ts
diff --git a/src/app/sevices/accounting/domain/account-entry.model.ts b/src/app/services/accounting/domain/account-entry.model.ts
similarity index 100%
rename from src/app/sevices/accounting/domain/account-entry.model.ts
rename to src/app/services/accounting/domain/account-entry.model.ts
diff --git a/src/app/sevices/accounting/domain/account-page.model.ts b/src/app/services/accounting/domain/account-page.model.ts
similarity index 100%
rename from src/app/sevices/accounting/domain/account-page.model.ts
rename to src/app/services/accounting/domain/account-page.model.ts
diff --git a/src/app/sevices/accounting/domain/account-state.model.ts b/src/app/services/accounting/domain/account-state.model.ts
similarity index 100%
rename from src/app/sevices/accounting/domain/account-state.model.ts
rename to src/app/services/accounting/domain/account-state.model.ts
diff --git a/src/app/sevices/accounting/domain/account-type.model.ts b/src/app/services/accounting/domain/account-type.model.ts
similarity index 100%
rename from src/app/sevices/accounting/domain/account-type.model.ts
rename to src/app/services/accounting/domain/account-type.model.ts
diff --git a/src/app/sevices/accounting/domain/account.model.ts b/src/app/services/accounting/domain/account.model.ts
similarity index 100%
rename from src/app/sevices/accounting/domain/account.model.ts
rename to src/app/services/accounting/domain/account.model.ts
diff --git a/src/app/sevices/accounting/domain/chart-of-account-entry.model.ts b/src/app/services/accounting/domain/chart-of-account-entry.model.ts
similarity index 100%
rename from src/app/sevices/accounting/domain/chart-of-account-entry.model.ts
rename to src/app/services/accounting/domain/chart-of-account-entry.model.ts
diff --git a/src/app/sevices/accounting/domain/creditor.model.ts b/src/app/services/accounting/domain/creditor.model.ts
similarity index 100%
rename from src/app/sevices/accounting/domain/creditor.model.ts
rename to src/app/services/accounting/domain/creditor.model.ts
diff --git a/src/app/sevices/accounting/domain/debtor.model.ts b/src/app/services/accounting/domain/debtor.model.ts
similarity index 100%
rename from src/app/sevices/accounting/domain/debtor.model.ts
rename to src/app/services/accounting/domain/debtor.model.ts
diff --git a/src/app/sevices/accounting/domain/financial-condition-entry.model.ts b/src/app/services/accounting/domain/financial-condition-entry.model.ts
similarity index 100%
rename from src/app/sevices/accounting/domain/financial-condition-entry.model.ts
rename to src/app/services/accounting/domain/financial-condition-entry.model.ts
diff --git a/src/app/sevices/accounting/domain/financial-condition-section.model.ts b/src/app/services/accounting/domain/financial-condition-section.model.ts
similarity index 100%
rename from src/app/sevices/accounting/domain/financial-condition-section.model.ts
rename to src/app/services/accounting/domain/financial-condition-section.model.ts
diff --git a/src/app/sevices/accounting/domain/financial-condition.model.ts b/src/app/services/accounting/domain/financial-condition.model.ts
similarity index 100%
rename from src/app/sevices/accounting/domain/financial-condition.model.ts
rename to src/app/services/accounting/domain/financial-condition.model.ts
diff --git a/src/app/sevices/accounting/domain/income-statement-entry.model.ts b/src/app/services/accounting/domain/income-statement-entry.model.ts
similarity index 100%
rename from src/app/sevices/accounting/domain/income-statement-entry.model.ts
rename to src/app/services/accounting/domain/income-statement-entry.model.ts
diff --git a/src/app/sevices/accounting/domain/income-statement-section.model.ts b/src/app/services/accounting/domain/income-statement-section.model.ts
similarity index 100%
rename from src/app/sevices/accounting/domain/income-statement-section.model.ts
rename to src/app/services/accounting/domain/income-statement-section.model.ts
diff --git a/src/app/sevices/accounting/domain/income-statement.model.ts b/src/app/services/accounting/domain/income-statement.model.ts
similarity index 100%
rename from src/app/sevices/accounting/domain/income-statement.model.ts
rename to src/app/services/accounting/domain/income-statement.model.ts
diff --git a/src/app/sevices/accounting/domain/journal-entry-state.model.ts b/src/app/services/accounting/domain/journal-entry-state.model.ts
similarity index 100%
rename from src/app/sevices/accounting/domain/journal-entry-state.model.ts
rename to src/app/services/accounting/domain/journal-entry-state.model.ts
diff --git a/src/app/sevices/accounting/domain/journal-entry.model.ts b/src/app/services/accounting/domain/journal-entry.model.ts
similarity index 100%
rename from src/app/sevices/accounting/domain/journal-entry.model.ts
rename to src/app/services/accounting/domain/journal-entry.model.ts
diff --git a/src/app/sevices/accounting/domain/ledger-page.model.ts b/src/app/services/accounting/domain/ledger-page.model.ts
similarity index 100%
rename from src/app/sevices/accounting/domain/ledger-page.model.ts
rename to src/app/services/accounting/domain/ledger-page.model.ts
diff --git a/src/app/sevices/accounting/domain/ledger.model.ts b/src/app/services/accounting/domain/ledger.model.ts
similarity index 100%
rename from src/app/sevices/accounting/domain/ledger.model.ts
rename to src/app/services/accounting/domain/ledger.model.ts
diff --git a/src/app/sevices/accounting/domain/permittable-group-ids.ts b/src/app/services/accounting/domain/permittable-group-ids.ts
similarity index 100%
rename from src/app/sevices/accounting/domain/permittable-group-ids.ts
rename to src/app/services/accounting/domain/permittable-group-ids.ts
diff --git a/src/app/sevices/accounting/domain/transaction-type-page.model.ts b/src/app/services/accounting/domain/transaction-type-page.model.ts
similarity index 100%
rename from src/app/sevices/accounting/domain/transaction-type-page.model.ts
rename to src/app/services/accounting/domain/transaction-type-page.model.ts
diff --git a/src/app/sevices/accounting/domain/transaction-type.model.ts b/src/app/services/accounting/domain/transaction-type.model.ts
similarity index 100%
rename from src/app/sevices/accounting/domain/transaction-type.model.ts
rename to src/app/services/accounting/domain/transaction-type.model.ts
diff --git a/src/app/sevices/accounting/domain/trial-balance-entry-type.model.ts b/src/app/services/accounting/domain/trial-balance-entry-type.model.ts
similarity index 100%
rename from src/app/sevices/accounting/domain/trial-balance-entry-type.model.ts
rename to src/app/services/accounting/domain/trial-balance-entry-type.model.ts
diff --git a/src/app/sevices/accounting/domain/trial-balance-entry.model.ts b/src/app/services/accounting/domain/trial-balance-entry.model.ts
similarity index 100%
rename from src/app/sevices/accounting/domain/trial-balance-entry.model.ts
rename to src/app/services/accounting/domain/trial-balance-entry.model.ts
diff --git a/src/app/sevices/accounting/domain/trial-balance.model.ts b/src/app/services/accounting/domain/trial-balance.model.ts
similarity index 100%
rename from src/app/sevices/accounting/domain/trial-balance.model.ts
rename to src/app/services/accounting/domain/trial-balance.model.ts
diff --git a/src/app/sevices/anubis/permittable-endpoint.model.ts b/src/app/services/anubis/permittable-endpoint.model.ts
similarity index 100%
rename from src/app/sevices/anubis/permittable-endpoint.model.ts
rename to src/app/services/anubis/permittable-endpoint.model.ts
diff --git a/src/app/sevices/anubis/permittable-group.model.ts b/src/app/services/anubis/permittable-group.model.ts
similarity index 100%
rename from src/app/sevices/anubis/permittable-group.model.ts
rename to src/app/services/anubis/permittable-group.model.ts
diff --git a/src/app/services/catalog/catalog.service.ts b/src/app/services/catalog/catalog.service.ts
new file mode 100644
index 0000000..43eb77b
--- /dev/null
+++ b/src/app/services/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/services/catalog/domain/catalog.model.ts
similarity index 100%
rename from src/app/sevices/catalog/domain/catalog.model.ts
rename to src/app/services/catalog/domain/catalog.model.ts
diff --git a/src/app/sevices/catalog/domain/field.model.ts b/src/app/services/catalog/domain/field.model.ts
similarity index 100%
rename from src/app/sevices/catalog/domain/field.model.ts
rename to src/app/services/catalog/domain/field.model.ts
diff --git a/src/app/sevices/catalog/domain/option.model.ts b/src/app/services/catalog/domain/option.model.ts
similarity index 100%
rename from src/app/sevices/catalog/domain/option.model.ts
rename to src/app/services/catalog/domain/option.model.ts
diff --git a/src/app/sevices/catalog/domain/value.model.ts b/src/app/services/catalog/domain/value.model.ts
similarity index 100%
rename from src/app/sevices/catalog/domain/value.model.ts
rename to src/app/services/catalog/domain/value.model.ts
diff --git a/src/app/services/cheque/cheque.service.ts b/src/app/services/cheque/cheque.service.ts
new file mode 100644
index 0000000..2d6f650
--- /dev/null
+++ b/src/app/services/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/services/cheque/domain/action.model.ts
similarity index 100%
rename from src/app/sevices/cheque/domain/action.model.ts
rename to src/app/services/cheque/domain/action.model.ts
diff --git a/src/app/sevices/cheque/domain/cheque-processing-command.ts b/src/app/services/cheque/domain/cheque-processing-command.ts
similarity index 100%
rename from src/app/sevices/cheque/domain/cheque-processing-command.ts
rename to src/app/services/cheque/domain/cheque-processing-command.ts
diff --git a/src/app/sevices/cheque/domain/cheque-transaction.ts b/src/app/services/cheque/domain/cheque-transaction.ts
similarity index 100%
rename from src/app/sevices/cheque/domain/cheque-transaction.ts
rename to src/app/services/cheque/domain/cheque-transaction.ts
diff --git a/src/app/sevices/cheque/domain/cheque.model.ts b/src/app/services/cheque/domain/cheque.model.ts
similarity index 100%
rename from src/app/sevices/cheque/domain/cheque.model.ts
rename to src/app/services/cheque/domain/cheque.model.ts
diff --git a/src/app/sevices/cheque/domain/fims-cheque.model.ts b/src/app/services/cheque/domain/fims-cheque.model.ts
similarity index 100%
rename from src/app/sevices/cheque/domain/fims-cheque.model.ts
rename to src/app/services/cheque/domain/fims-cheque.model.ts
diff --git a/src/app/sevices/cheque/domain/issuing-count.model.ts b/src/app/services/cheque/domain/issuing-count.model.ts
similarity index 100%
rename from src/app/sevices/cheque/domain/issuing-count.model.ts
rename to src/app/services/cheque/domain/issuing-count.model.ts
diff --git a/src/app/sevices/cheque/domain/mapper/fims-cheque.mapper.ts b/src/app/services/cheque/domain/mapper/fims-cheque.mapper.ts
similarity index 100%
rename from src/app/sevices/cheque/domain/mapper/fims-cheque.mapper.ts
rename to src/app/services/cheque/domain/mapper/fims-cheque.mapper.ts
diff --git a/src/app/sevices/cheque/domain/micr-resolution.model.ts b/src/app/services/cheque/domain/micr-resolution.model.ts
similarity index 100%
rename from src/app/sevices/cheque/domain/micr-resolution.model.ts
rename to src/app/services/cheque/domain/micr-resolution.model.ts
diff --git a/src/app/sevices/cheque/domain/micr.model.ts b/src/app/services/cheque/domain/micr.model.ts
similarity index 100%
rename from src/app/sevices/cheque/domain/micr.model.ts
rename to src/app/services/cheque/domain/micr.model.ts
diff --git a/src/app/sevices/cheque/domain/permittable-group-ids.ts b/src/app/services/cheque/domain/permittable-group-ids.ts
similarity index 100%
rename from src/app/sevices/cheque/domain/permittable-group-ids.ts
rename to src/app/services/cheque/domain/permittable-group-ids.ts
diff --git a/src/app/sevices/cheque/domain/state.model.ts b/src/app/services/cheque/domain/state.model.ts
similarity index 100%
rename from src/app/sevices/cheque/domain/state.model.ts
rename to src/app/services/cheque/domain/state.model.ts
diff --git a/src/app/sevices/country/country.service.spec.ts b/src/app/services/country/country.service.spec.ts
similarity index 100%
rename from src/app/sevices/country/country.service.spec.ts
rename to src/app/services/country/country.service.spec.ts
diff --git a/src/app/sevices/country/country.service.ts b/src/app/services/country/country.service.ts
similarity index 100%
rename from src/app/sevices/country/country.service.ts
rename to src/app/services/country/country.service.ts
diff --git a/src/app/sevices/country/model/country.model.ts b/src/app/services/country/model/country.model.ts
similarity index 100%
rename from src/app/sevices/country/model/country.model.ts
rename to src/app/services/country/model/country.model.ts
diff --git a/src/app/sevices/currency/currency.service.ts b/src/app/services/currency/currency.service.ts
similarity index 100%
rename from src/app/sevices/currency/currency.service.ts
rename to src/app/services/currency/currency.service.ts
diff --git a/src/app/sevices/currency/domain/currency.model.ts b/src/app/services/currency/domain/currency.model.ts
similarity index 100%
rename from src/app/sevices/currency/domain/currency.model.ts
rename to src/app/services/currency/domain/currency.model.ts
diff --git a/src/app/services/customer/customer.service.ts b/src/app/services/customer/customer.service.ts
new file mode 100644
index 0000000..ac4c69f
--- /dev/null
+++ b/src/app/services/customer/customer.service.ts
@@ -0,0 +1,209 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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/services/customer/domain/command.model.ts
similarity index 100%
rename from src/app/sevices/customer/domain/command.model.ts
rename to src/app/services/customer/domain/command.model.ts
diff --git a/src/app/sevices/customer/domain/customer-document.model.ts b/src/app/services/customer/domain/customer-document.model.ts
similarity index 100%
rename from src/app/sevices/customer/domain/customer-document.model.ts
rename to src/app/services/customer/domain/customer-document.model.ts
diff --git a/src/app/sevices/customer/domain/customer-page.model.ts b/src/app/services/customer/domain/customer-page.model.ts
similarity index 100%
rename from src/app/sevices/customer/domain/customer-page.model.ts
rename to src/app/services/customer/domain/customer-page.model.ts
diff --git a/src/app/sevices/customer/domain/customer-state.model.ts b/src/app/services/customer/domain/customer-state.model.ts
similarity index 100%
rename from src/app/sevices/customer/domain/customer-state.model.ts
rename to src/app/services/customer/domain/customer-state.model.ts
diff --git a/src/app/sevices/customer/domain/customer-type.model.ts b/src/app/services/customer/domain/customer-type.model.ts
similarity index 100%
rename from src/app/sevices/customer/domain/customer-type.model.ts
rename to src/app/services/customer/domain/customer-type.model.ts
diff --git a/src/app/sevices/customer/domain/customer.model.ts b/src/app/services/customer/domain/customer.model.ts
similarity index 100%
rename from src/app/sevices/customer/domain/customer.model.ts
rename to src/app/services/customer/domain/customer.model.ts
diff --git a/src/app/sevices/customer/domain/date-of-birth.model.ts b/src/app/services/customer/domain/date-of-birth.model.ts
similarity index 100%
rename from src/app/sevices/customer/domain/date-of-birth.model.ts
rename to src/app/services/customer/domain/date-of-birth.model.ts
diff --git a/src/app/sevices/customer/domain/expiration-date.model.ts b/src/app/services/customer/domain/expiration-date.model.ts
similarity index 100%
rename from src/app/sevices/customer/domain/expiration-date.model.ts
rename to src/app/services/customer/domain/expiration-date.model.ts
diff --git a/src/app/sevices/customer/domain/identification-card-scan.model.ts b/src/app/services/customer/domain/identification-card-scan.model.ts
similarity index 100%
rename from src/app/sevices/customer/domain/identification-card-scan.model.ts
rename to src/app/services/customer/domain/identification-card-scan.model.ts
diff --git a/src/app/sevices/customer/domain/identification-card.model.ts b/src/app/services/customer/domain/identification-card.model.ts
similarity index 100%
rename from src/app/sevices/customer/domain/identification-card.model.ts
rename to src/app/services/customer/domain/identification-card.model.ts
diff --git a/src/app/sevices/customer/domain/permittable-group-ids.ts b/src/app/services/customer/domain/permittable-group-ids.ts
similarity index 100%
rename from src/app/sevices/customer/domain/permittable-group-ids.ts
rename to src/app/services/customer/domain/permittable-group-ids.ts
diff --git a/src/app/sevices/customer/domain/process-step.model.ts b/src/app/services/customer/domain/process-step.model.ts
similarity index 100%
rename from src/app/sevices/customer/domain/process-step.model.ts
rename to src/app/services/customer/domain/process-step.model.ts
diff --git a/src/app/sevices/customer/domain/task-definition.model.ts b/src/app/services/customer/domain/task-definition.model.ts
similarity index 100%
rename from src/app/sevices/customer/domain/task-definition.model.ts
rename to src/app/services/customer/domain/task-definition.model.ts
diff --git a/src/app/services/depositAccount/deposit-account.service.ts b/src/app/services/depositAccount/deposit-account.service.ts
new file mode 100644
index 0000000..02ef3a0
--- /dev/null
+++ b/src/app/services/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/services/depositAccount/domain/definition/action.model.ts
similarity index 100%
rename from src/app/sevices/depositAccount/domain/definition/action.model.ts
rename to src/app/services/depositAccount/domain/definition/action.model.ts
diff --git a/src/app/sevices/depositAccount/domain/definition/charge.model.ts b/src/app/services/depositAccount/domain/definition/charge.model.ts
similarity index 100%
rename from src/app/sevices/depositAccount/domain/definition/charge.model.ts
rename to src/app/services/depositAccount/domain/definition/charge.model.ts
diff --git a/src/app/sevices/depositAccount/domain/definition/currency.model.ts b/src/app/services/depositAccount/domain/definition/currency.model.ts
similarity index 100%
rename from src/app/sevices/depositAccount/domain/definition/currency.model.ts
rename to src/app/services/depositAccount/domain/definition/currency.model.ts
diff --git a/src/app/sevices/depositAccount/domain/definition/dividend-distribution.model.ts b/src/app/services/depositAccount/domain/definition/dividend-distribution.model.ts
similarity index 100%
rename from src/app/sevices/depositAccount/domain/definition/dividend-distribution.model.ts
rename to src/app/services/depositAccount/domain/definition/dividend-distribution.model.ts
diff --git a/src/app/sevices/depositAccount/domain/definition/product-definition-command.model.ts b/src/app/services/depositAccount/domain/definition/product-definition-command.model.ts
similarity index 100%
rename from src/app/sevices/depositAccount/domain/definition/product-definition-command.model.ts
rename to src/app/services/depositAccount/domain/definition/product-definition-command.model.ts
diff --git a/src/app/sevices/depositAccount/domain/definition/product-definition.model.ts b/src/app/services/depositAccount/domain/definition/product-definition.model.ts
similarity index 100%
rename from src/app/sevices/depositAccount/domain/definition/product-definition.model.ts
rename to src/app/services/depositAccount/domain/definition/product-definition.model.ts
diff --git a/src/app/sevices/depositAccount/domain/definition/term.model.ts b/src/app/services/depositAccount/domain/definition/term.model.ts
similarity index 100%
rename from src/app/sevices/depositAccount/domain/definition/term.model.ts
rename to src/app/services/depositAccount/domain/definition/term.model.ts
diff --git a/src/app/sevices/depositAccount/domain/instance/available-transaction-type.model.ts b/src/app/services/depositAccount/domain/instance/available-transaction-type.model.ts
similarity index 100%
rename from src/app/sevices/depositAccount/domain/instance/available-transaction-type.model.ts
rename to src/app/services/depositAccount/domain/instance/available-transaction-type.model.ts
diff --git a/src/app/sevices/depositAccount/domain/instance/product-instance.model.ts b/src/app/services/depositAccount/domain/instance/product-instance.model.ts
similarity index 100%
rename from src/app/sevices/depositAccount/domain/instance/product-instance.model.ts
rename to src/app/services/depositAccount/domain/instance/product-instance.model.ts
diff --git a/src/app/sevices/depositAccount/domain/interest-payable.model.ts b/src/app/services/depositAccount/domain/interest-payable.model.ts
similarity index 100%
rename from src/app/sevices/depositAccount/domain/interest-payable.model.ts
rename to src/app/services/depositAccount/domain/interest-payable.model.ts
diff --git a/src/app/sevices/depositAccount/domain/permittable-group-ids.ts b/src/app/services/depositAccount/domain/permittable-group-ids.ts
similarity index 100%
rename from src/app/sevices/depositAccount/domain/permittable-group-ids.ts
rename to src/app/services/depositAccount/domain/permittable-group-ids.ts
diff --git a/src/app/sevices/depositAccount/domain/time-unit.model.ts b/src/app/services/depositAccount/domain/time-unit.model.ts
similarity index 100%
rename from src/app/sevices/depositAccount/domain/time-unit.model.ts
rename to src/app/services/depositAccount/domain/time-unit.model.ts
diff --git a/src/app/sevices/depositAccount/domain/type.model.ts b/src/app/services/depositAccount/domain/type.model.ts
similarity index 100%
rename from src/app/sevices/depositAccount/domain/type.model.ts
rename to src/app/services/depositAccount/domain/type.model.ts
diff --git a/src/app/sevices/domain/address/address.model.ts b/src/app/services/domain/address/address.model.ts
similarity index 100%
rename from src/app/sevices/domain/address/address.model.ts
rename to src/app/services/domain/address/address.model.ts
diff --git a/src/app/sevices/domain/contact/contact-detail.model.ts b/src/app/services/domain/contact/contact-detail.model.ts
similarity index 100%
rename from src/app/sevices/domain/contact/contact-detail.model.ts
rename to src/app/services/domain/contact/contact-detail.model.ts
diff --git a/src/app/sevices/domain/date.converter.ts b/src/app/services/domain/date.converter.ts
similarity index 100%
rename from src/app/sevices/domain/date.converter.ts
rename to src/app/services/domain/date.converter.ts
diff --git a/src/app/sevices/domain/error.model.ts b/src/app/services/domain/error.model.ts
similarity index 100%
rename from src/app/sevices/domain/error.model.ts
rename to src/app/services/domain/error.model.ts
diff --git a/src/app/sevices/domain/paging/fetch-request.model.ts b/src/app/services/domain/paging/fetch-request.model.ts
similarity index 100%
rename from src/app/sevices/domain/paging/fetch-request.model.ts
rename to src/app/services/domain/paging/fetch-request.model.ts
diff --git a/src/app/sevices/domain/paging/page.model.ts b/src/app/services/domain/paging/page.model.ts
similarity index 100%
rename from src/app/sevices/domain/paging/page.model.ts
rename to src/app/services/domain/paging/page.model.ts
diff --git a/src/app/sevices/domain/paging/search-param.builder.ts b/src/app/services/domain/paging/search-param.builder.ts
similarity index 100%
rename from src/app/sevices/domain/paging/search-param.builder.ts
rename to src/app/services/domain/paging/search-param.builder.ts
diff --git a/src/app/sevices/domain/paging/sort.model.ts b/src/app/services/domain/paging/sort.model.ts
similarity index 100%
rename from src/app/sevices/domain/paging/sort.model.ts
rename to src/app/services/domain/paging/sort.model.ts
diff --git a/src/app/sevices/http/default-request-options.service.ts b/src/app/services/http/default-request-options.service.ts
similarity index 100%
rename from src/app/sevices/http/default-request-options.service.ts
rename to src/app/services/http/default-request-options.service.ts
diff --git a/src/app/sevices/http/header.service.ts b/src/app/services/http/header.service.ts
similarity index 100%
rename from src/app/sevices/http/header.service.ts
rename to src/app/services/http/header.service.ts
diff --git a/src/app/services/http/http.service.spec.ts b/src/app/services/http/http.service.spec.ts
new file mode 100644
index 0000000..0500f29
--- /dev/null
+++ b/src/app/services/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/services/http/http.service.ts b/src/app/services/http/http.service.ts
new file mode 100644
index 0000000..7a33b35
--- /dev/null
+++ b/src/app/services/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/services/identity/domain/authentication.model.ts
similarity index 100%
rename from src/app/sevices/identity/domain/authentication.model.ts
rename to src/app/services/identity/domain/authentication.model.ts
diff --git a/src/app/sevices/identity/domain/password.model.ts b/src/app/services/identity/domain/password.model.ts
similarity index 100%
rename from src/app/sevices/identity/domain/password.model.ts
rename to src/app/services/identity/domain/password.model.ts
diff --git a/src/app/sevices/identity/domain/permission.model.ts b/src/app/services/identity/domain/permission.model.ts
similarity index 100%
rename from src/app/sevices/identity/domain/permission.model.ts
rename to src/app/services/identity/domain/permission.model.ts
diff --git a/src/app/sevices/identity/domain/permittable-group-ids.model.ts b/src/app/services/identity/domain/permittable-group-ids.model.ts
similarity index 100%
rename from src/app/sevices/identity/domain/permittable-group-ids.model.ts
rename to src/app/services/identity/domain/permittable-group-ids.model.ts
diff --git a/src/app/sevices/identity/domain/role-identifier.model.ts b/src/app/services/identity/domain/role-identifier.model.ts
similarity index 100%
rename from src/app/sevices/identity/domain/role-identifier.model.ts
rename to src/app/services/identity/domain/role-identifier.model.ts
diff --git a/src/app/sevices/identity/domain/role.model.ts b/src/app/services/identity/domain/role.model.ts
similarity index 100%
rename from src/app/sevices/identity/domain/role.model.ts
rename to src/app/services/identity/domain/role.model.ts
diff --git a/src/app/sevices/identity/domain/user-with-password.model.ts b/src/app/services/identity/domain/user-with-password.model.ts
similarity index 100%
rename from src/app/sevices/identity/domain/user-with-password.model.ts
rename to src/app/services/identity/domain/user-with-password.model.ts
diff --git a/src/app/sevices/identity/domain/user.model.ts b/src/app/services/identity/domain/user.model.ts
similarity index 100%
rename from src/app/sevices/identity/domain/user.model.ts
rename to src/app/services/identity/domain/user.model.ts
diff --git a/src/app/services/identity/identity.service.ts b/src/app/services/identity/identity.service.ts
new file mode 100644
index 0000000..63ba3a0
--- /dev/null
+++ b/src/app/services/identity/identity.service.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 {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);
+  }
+
+  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/services/image/image.service.ts b/src/app/services/image/image.service.ts
new file mode 100644
index 0000000..ac08f2b
--- /dev/null
+++ b/src/app/services/image/image.service.ts
@@ -0,0 +1,59 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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/services/notification/notification.service.ts
similarity index 100%
rename from src/app/sevices/notification/notification.service.ts
rename to src/app/services/notification/notification.service.ts
diff --git a/src/app/sevices/office/domain/employee-page.model.ts b/src/app/services/office/domain/employee-page.model.ts
similarity index 100%
rename from src/app/sevices/office/domain/employee-page.model.ts
rename to src/app/services/office/domain/employee-page.model.ts
diff --git a/src/app/services/office/domain/employee.model.ts b/src/app/services/office/domain/employee.model.ts
new file mode 100644
index 0000000..8350439
--- /dev/null
+++ b/src/app/services/office/domain/employee.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 {ContactDetail} from '../../domain/contact/contact-detail.model';
+
+export interface Employee {
+  identifier: string;
+  givenName: string;
+  middleName?: string;
+  surname: string;
+  assignedOffice?: string;
+  contactDetails: ContactDetail[];
+}
+
+export interface Center {
+  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/services/office/domain/office-page.model.ts
similarity index 100%
rename from src/app/sevices/office/domain/office-page.model.ts
rename to src/app/services/office/domain/office-page.model.ts
diff --git a/src/app/sevices/office/domain/office.model.ts b/src/app/services/office/domain/office.model.ts
similarity index 100%
rename from src/app/sevices/office/domain/office.model.ts
rename to src/app/services/office/domain/office.model.ts
diff --git a/src/app/sevices/office/domain/permittable-group-ids.model.ts b/src/app/services/office/domain/permittable-group-ids.model.ts
similarity index 100%
rename from src/app/sevices/office/domain/permittable-group-ids.model.ts
rename to src/app/services/office/domain/permittable-group-ids.model.ts
diff --git a/src/app/services/office/office.service.ts b/src/app/services/office/office.service.ts
new file mode 100644
index 0000000..6496062
--- /dev/null
+++ b/src/app/services/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)
+      .catch(Error.handleError);
+  }
+
+  addBranch(id: string, office: Office): Observable<Office> {
+    return this.http.post(this.baseUrl + '/offices/' + id, office)
+      .catch(Error.handleError);
+  }
+
+  updateOffice(office: Office): Observable<Office> {
+    return this.http.put(this.baseUrl + '/offices/' + office.identifier, office)
+      .catch(Error.handleError);
+  }
+
+  deleteOffice(id: String): Observable<Office> {
+    return this.http.delete(this.baseUrl + '/offices/' + id, {})
+      .catch(Error.handleError);
+  }
+
+  listOffices(fetchRequest?: FetchRequest): Observable<OfficePage> {
+    const params: URLSearchParams = buildSearchParams(fetchRequest);
+
+    const requestOptions: RequestOptionsArgs = {
+      search: params
+    };
+    return this.http.get(this.baseUrl + '/offices', requestOptions)
+      .catch(Error.handleError);
+  }
+
+  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)
+      .catch(Error.handleError);
+  }
+
+  getOffice(id: string): Observable<Office> {
+    return this.http.get(this.baseUrl + '/offices/' + id)
+      .catch(Error.handleError);
+  }
+
+  listEmployees(fetchRequest?: FetchRequest): Observable<EmployeePage> {
+    const params: URLSearchParams = buildSearchParams(fetchRequest);
+
+    const requestOptions: RequestOptionsArgs = {
+      search: params
+    };
+
+    return this.http.get(this.baseUrl + '/employees', requestOptions)
+      .catch(Error.handleError);
+  }
+
+  getEmployee(id: string, silent?: true): Observable<Employee> {
+    return this.http.get(this.baseUrl + '/employees/' + id, {}, silent)
+      .catch(Error.handleError);
+  }
+
+  createEmployee(employee: Employee): Observable<Employee> {
+    return this.http.post(this.baseUrl + '/employees', employee)
+      .catch(Error.handleError);
+  }
+
+  updateEmployee(employee: Employee): Observable<Employee> {
+    return this.http.put(this.baseUrl + '/employees/' + employee.identifier, employee)
+      .catch(Error.handleError);
+  }
+
+  deleteEmployee(id: string): Observable<Employee> {
+    return this.http.delete(this.baseUrl + '/employees/' + id, {})
+      .catch(Error.handleError);
+  }
+
+  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/services/payroll/domain/payroll-allocation.model.ts
similarity index 100%
rename from src/app/sevices/payroll/domain/payroll-allocation.model.ts
rename to src/app/services/payroll/domain/payroll-allocation.model.ts
diff --git a/src/app/sevices/payroll/domain/payroll-collection-history.model.ts b/src/app/services/payroll/domain/payroll-collection-history.model.ts
similarity index 100%
rename from src/app/sevices/payroll/domain/payroll-collection-history.model.ts
rename to src/app/services/payroll/domain/payroll-collection-history.model.ts
diff --git a/src/app/sevices/payroll/domain/payroll-collection-sheet.model.ts b/src/app/services/payroll/domain/payroll-collection-sheet.model.ts
similarity index 100%
rename from src/app/sevices/payroll/domain/payroll-collection-sheet.model.ts
rename to src/app/services/payroll/domain/payroll-collection-sheet.model.ts
diff --git a/src/app/sevices/payroll/domain/payroll-configuration.model.ts b/src/app/services/payroll/domain/payroll-configuration.model.ts
similarity index 100%
rename from src/app/sevices/payroll/domain/payroll-configuration.model.ts
rename to src/app/services/payroll/domain/payroll-configuration.model.ts
diff --git a/src/app/sevices/payroll/domain/payroll-payment-page.model.ts b/src/app/services/payroll/domain/payroll-payment-page.model.ts
similarity index 100%
rename from src/app/sevices/payroll/domain/payroll-payment-page.model.ts
rename to src/app/services/payroll/domain/payroll-payment-page.model.ts
diff --git a/src/app/sevices/payroll/domain/payroll-payment.model.ts b/src/app/services/payroll/domain/payroll-payment.model.ts
similarity index 100%
rename from src/app/sevices/payroll/domain/payroll-payment.model.ts
rename to src/app/services/payroll/domain/payroll-payment.model.ts
diff --git a/src/app/sevices/payroll/domain/permittable-group-ids.ts b/src/app/services/payroll/domain/permittable-group-ids.ts
similarity index 100%
rename from src/app/sevices/payroll/domain/permittable-group-ids.ts
rename to src/app/services/payroll/domain/permittable-group-ids.ts
diff --git a/src/app/services/payroll/payroll.service.ts b/src/app/services/payroll/payroll.service.ts
new file mode 100644
index 0000000..c9236b8
--- /dev/null
+++ b/src/app/services/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/services/portfolio/domain/account-assignment.model.ts
similarity index 100%
rename from src/app/sevices/portfolio/domain/account-assignment.model.ts
rename to src/app/services/portfolio/domain/account-assignment.model.ts
diff --git a/src/app/sevices/portfolio/domain/balance-range.model.ts b/src/app/services/portfolio/domain/balance-range.model.ts
similarity index 100%
rename from src/app/sevices/portfolio/domain/balance-range.model.ts
rename to src/app/services/portfolio/domain/balance-range.model.ts
diff --git a/src/app/sevices/portfolio/domain/balance-segment-set.model.ts b/src/app/services/portfolio/domain/balance-segment-set.model.ts
similarity index 100%
rename from src/app/sevices/portfolio/domain/balance-segment-set.model.ts
rename to src/app/services/portfolio/domain/balance-segment-set.model.ts
diff --git a/src/app/sevices/portfolio/domain/case-command.model.ts b/src/app/services/portfolio/domain/case-command.model.ts
similarity index 100%
rename from src/app/sevices/portfolio/domain/case-command.model.ts
rename to src/app/services/portfolio/domain/case-command.model.ts
diff --git a/src/app/sevices/portfolio/domain/case-customer-documents.model.ts b/src/app/services/portfolio/domain/case-customer-documents.model.ts
similarity index 100%
rename from src/app/sevices/portfolio/domain/case-customer-documents.model.ts
rename to src/app/services/portfolio/domain/case-customer-documents.model.ts
diff --git a/src/app/sevices/portfolio/domain/case-page.model.ts b/src/app/services/portfolio/domain/case-page.model.ts
similarity index 100%
rename from src/app/sevices/portfolio/domain/case-page.model.ts
rename to src/app/services/portfolio/domain/case-page.model.ts
diff --git a/src/app/sevices/portfolio/domain/case-state.model.ts b/src/app/services/portfolio/domain/case-state.model.ts
similarity index 100%
rename from src/app/sevices/portfolio/domain/case-state.model.ts
rename to src/app/services/portfolio/domain/case-state.model.ts
diff --git a/src/app/sevices/portfolio/domain/case.model.ts b/src/app/services/portfolio/domain/case.model.ts
similarity index 100%
rename from src/app/sevices/portfolio/domain/case.model.ts
rename to src/app/services/portfolio/domain/case.model.ts
diff --git a/src/app/sevices/portfolio/domain/charge-definition.model.ts b/src/app/services/portfolio/domain/charge-definition.model.ts
similarity index 100%
rename from src/app/sevices/portfolio/domain/charge-definition.model.ts
rename to src/app/services/portfolio/domain/charge-definition.model.ts
diff --git a/src/app/sevices/portfolio/domain/charge-method.model.ts b/src/app/services/portfolio/domain/charge-method.model.ts
similarity index 100%
rename from src/app/sevices/portfolio/domain/charge-method.model.ts
rename to src/app/services/portfolio/domain/charge-method.model.ts
diff --git a/src/app/sevices/portfolio/domain/chrono-unit.model.ts b/src/app/services/portfolio/domain/chrono-unit.model.ts
similarity index 100%
rename from src/app/sevices/portfolio/domain/chrono-unit.model.ts
rename to src/app/services/portfolio/domain/chrono-unit.model.ts
diff --git a/src/app/sevices/portfolio/domain/cost-component.model.ts b/src/app/services/portfolio/domain/cost-component.model.ts
similarity index 100%
rename from src/app/sevices/portfolio/domain/cost-component.model.ts
rename to src/app/services/portfolio/domain/cost-component.model.ts
diff --git a/src/app/sevices/portfolio/domain/fims-case-page.model.ts b/src/app/services/portfolio/domain/fims-case-page.model.ts
similarity index 100%
rename from src/app/sevices/portfolio/domain/fims-case-page.model.ts
rename to src/app/services/portfolio/domain/fims-case-page.model.ts
diff --git a/src/app/sevices/portfolio/domain/fims-case.model.ts b/src/app/services/portfolio/domain/fims-case.model.ts
similarity index 100%
rename from src/app/sevices/portfolio/domain/fims-case.model.ts
rename to src/app/services/portfolio/domain/fims-case.model.ts
diff --git a/src/app/sevices/portfolio/domain/individuallending/account-designators.model.ts b/src/app/services/portfolio/domain/individuallending/account-designators.model.ts
similarity index 100%
rename from src/app/sevices/portfolio/domain/individuallending/account-designators.model.ts
rename to src/app/services/portfolio/domain/individuallending/account-designators.model.ts
diff --git a/src/app/sevices/portfolio/domain/individuallending/case-parameters.model.ts b/src/app/services/portfolio/domain/individuallending/case-parameters.model.ts
similarity index 100%
rename from src/app/sevices/portfolio/domain/individuallending/case-parameters.model.ts
rename to src/app/services/portfolio/domain/individuallending/case-parameters.model.ts
diff --git a/src/app/sevices/portfolio/domain/individuallending/charge-name.model.ts b/src/app/services/portfolio/domain/individuallending/charge-name.model.ts
similarity index 100%
rename from src/app/sevices/portfolio/domain/individuallending/charge-name.model.ts
rename to src/app/services/portfolio/domain/individuallending/charge-name.model.ts
diff --git a/src/app/sevices/portfolio/domain/individuallending/charge-proportional-designators.model.ts b/src/app/services/portfolio/domain/individuallending/charge-proportional-designators.model.ts
similarity index 100%
rename from src/app/sevices/portfolio/domain/individuallending/charge-proportional-designators.model.ts
rename to src/app/services/portfolio/domain/individuallending/charge-proportional-designators.model.ts
diff --git a/src/app/sevices/portfolio/domain/individuallending/credit-worthiness-factor.model.ts b/src/app/services/portfolio/domain/individuallending/credit-worthiness-factor.model.ts
similarity index 100%
rename from src/app/sevices/portfolio/domain/individuallending/credit-worthiness-factor.model.ts
rename to src/app/services/portfolio/domain/individuallending/credit-worthiness-factor.model.ts
diff --git a/src/app/sevices/portfolio/domain/individuallending/credit-worthiness-snapshot.model.ts b/src/app/services/portfolio/domain/individuallending/credit-worthiness-snapshot.model.ts
similarity index 100%
rename from src/app/sevices/portfolio/domain/individuallending/credit-worthiness-snapshot.model.ts
rename to src/app/services/portfolio/domain/individuallending/credit-worthiness-snapshot.model.ts
diff --git a/src/app/sevices/portfolio/domain/individuallending/document.model.ts b/src/app/services/portfolio/domain/individuallending/document.model.ts
similarity index 100%
rename from src/app/sevices/portfolio/domain/individuallending/document.model.ts
rename to src/app/services/portfolio/domain/individuallending/document.model.ts
diff --git a/src/app/sevices/portfolio/domain/individuallending/moratorium.model.ts b/src/app/services/portfolio/domain/individuallending/moratorium.model.ts
similarity index 100%
rename from src/app/sevices/portfolio/domain/individuallending/moratorium.model.ts
rename to src/app/services/portfolio/domain/individuallending/moratorium.model.ts
diff --git a/src/app/sevices/portfolio/domain/individuallending/planned-payment-page.model.ts b/src/app/services/portfolio/domain/individuallending/planned-payment-page.model.ts
similarity index 100%
rename from src/app/sevices/portfolio/domain/individuallending/planned-payment-page.model.ts
rename to src/app/services/portfolio/domain/individuallending/planned-payment-page.model.ts
diff --git a/src/app/sevices/portfolio/domain/individuallending/planned-payment.model.ts b/src/app/services/portfolio/domain/individuallending/planned-payment.model.ts
similarity index 100%
rename from src/app/sevices/portfolio/domain/individuallending/planned-payment.model.ts
rename to src/app/services/portfolio/domain/individuallending/planned-payment.model.ts
diff --git a/src/app/sevices/portfolio/domain/individuallending/product-parameters.model.ts b/src/app/services/portfolio/domain/individuallending/product-parameters.model.ts
similarity index 100%
rename from src/app/sevices/portfolio/domain/individuallending/product-parameters.model.ts
rename to src/app/services/portfolio/domain/individuallending/product-parameters.model.ts
diff --git a/src/app/sevices/portfolio/domain/individuallending/workflow-action.model.ts b/src/app/services/portfolio/domain/individuallending/workflow-action.model.ts
similarity index 100%
rename from src/app/sevices/portfolio/domain/individuallending/workflow-action.model.ts
rename to src/app/services/portfolio/domain/individuallending/workflow-action.model.ts
diff --git a/src/app/sevices/portfolio/domain/interest-basis.model.ts b/src/app/services/portfolio/domain/interest-basis.model.ts
similarity index 100%
rename from src/app/sevices/portfolio/domain/interest-basis.model.ts
rename to src/app/services/portfolio/domain/interest-basis.model.ts
diff --git a/src/app/sevices/portfolio/domain/interest-range.model.ts b/src/app/services/portfolio/domain/interest-range.model.ts
similarity index 100%
rename from src/app/sevices/portfolio/domain/interest-range.model.ts
rename to src/app/services/portfolio/domain/interest-range.model.ts
diff --git a/src/app/sevices/portfolio/domain/loss-provision-configuration.model.ts b/src/app/services/portfolio/domain/loss-provision-configuration.model.ts
similarity index 100%
rename from src/app/sevices/portfolio/domain/loss-provision-configuration.model.ts
rename to src/app/services/portfolio/domain/loss-provision-configuration.model.ts
diff --git a/src/app/sevices/portfolio/domain/loss-provision-step.model.ts b/src/app/services/portfolio/domain/loss-provision-step.model.ts
similarity index 100%
rename from src/app/sevices/portfolio/domain/loss-provision-step.model.ts
rename to src/app/services/portfolio/domain/loss-provision-step.model.ts
diff --git a/src/app/sevices/portfolio/domain/mapper/fims-case-page.mapper.ts b/src/app/services/portfolio/domain/mapper/fims-case-page.mapper.ts
similarity index 100%
rename from src/app/sevices/portfolio/domain/mapper/fims-case-page.mapper.ts
rename to src/app/services/portfolio/domain/mapper/fims-case-page.mapper.ts
diff --git a/src/app/sevices/portfolio/domain/mapper/fims-case.mapper.ts b/src/app/services/portfolio/domain/mapper/fims-case.mapper.ts
similarity index 100%
rename from src/app/sevices/portfolio/domain/mapper/fims-case.mapper.ts
rename to src/app/services/portfolio/domain/mapper/fims-case.mapper.ts
diff --git a/src/app/sevices/portfolio/domain/mapper/fims-range.mapper.ts b/src/app/services/portfolio/domain/mapper/fims-range.mapper.ts
similarity index 100%
rename from src/app/sevices/portfolio/domain/mapper/fims-range.mapper.ts
rename to src/app/services/portfolio/domain/mapper/fims-range.mapper.ts
diff --git a/src/app/sevices/portfolio/domain/note.model.ts b/src/app/services/portfolio/domain/note.model.ts
similarity index 100%
rename from src/app/sevices/portfolio/domain/note.model.ts
rename to src/app/services/portfolio/domain/note.model.ts
diff --git a/src/app/sevices/portfolio/domain/pattern.model.ts b/src/app/services/portfolio/domain/pattern.model.ts
similarity index 100%
rename from src/app/sevices/portfolio/domain/pattern.model.ts
rename to src/app/services/portfolio/domain/pattern.model.ts
diff --git a/src/app/sevices/portfolio/domain/payment-cycle.model.ts b/src/app/services/portfolio/domain/payment-cycle.model.ts
similarity index 100%
rename from src/app/sevices/portfolio/domain/payment-cycle.model.ts
rename to src/app/services/portfolio/domain/payment-cycle.model.ts
diff --git a/src/app/sevices/portfolio/domain/payment.model.ts b/src/app/services/portfolio/domain/payment.model.ts
similarity index 100%
rename from src/app/sevices/portfolio/domain/payment.model.ts
rename to src/app/services/portfolio/domain/payment.model.ts
diff --git a/src/app/sevices/portfolio/domain/permittable-group-ids.ts b/src/app/services/portfolio/domain/permittable-group-ids.ts
similarity index 100%
rename from src/app/sevices/portfolio/domain/permittable-group-ids.ts
rename to src/app/services/portfolio/domain/permittable-group-ids.ts
diff --git a/src/app/sevices/portfolio/domain/product-page.model.ts b/src/app/services/portfolio/domain/product-page.model.ts
similarity index 100%
rename from src/app/sevices/portfolio/domain/product-page.model.ts
rename to src/app/services/portfolio/domain/product-page.model.ts
diff --git a/src/app/sevices/portfolio/domain/product.model.ts b/src/app/services/portfolio/domain/product.model.ts
similarity index 100%
rename from src/app/sevices/portfolio/domain/product.model.ts
rename to src/app/services/portfolio/domain/product.model.ts
diff --git a/src/app/sevices/portfolio/domain/range-model.ts b/src/app/services/portfolio/domain/range-model.ts
similarity index 100%
rename from src/app/sevices/portfolio/domain/range-model.ts
rename to src/app/services/portfolio/domain/range-model.ts
diff --git a/src/app/sevices/portfolio/domain/range-segment.model.ts b/src/app/services/portfolio/domain/range-segment.model.ts
similarity index 100%
rename from src/app/sevices/portfolio/domain/range-segment.model.ts
rename to src/app/services/portfolio/domain/range-segment.model.ts
diff --git a/src/app/sevices/portfolio/domain/required-account-assignment.model.ts b/src/app/services/portfolio/domain/required-account-assignment.model.ts
similarity index 100%
rename from src/app/sevices/portfolio/domain/required-account-assignment.model.ts
rename to src/app/services/portfolio/domain/required-account-assignment.model.ts
diff --git a/src/app/sevices/portfolio/domain/task-definition.model.ts b/src/app/services/portfolio/domain/task-definition.model.ts
similarity index 100%
rename from src/app/sevices/portfolio/domain/task-definition.model.ts
rename to src/app/services/portfolio/domain/task-definition.model.ts
diff --git a/src/app/sevices/portfolio/domain/task-instance.model.ts b/src/app/services/portfolio/domain/task-instance.model.ts
similarity index 100%
rename from src/app/sevices/portfolio/domain/task-instance.model.ts
rename to src/app/services/portfolio/domain/task-instance.model.ts
diff --git a/src/app/sevices/portfolio/domain/term-range.model.ts b/src/app/services/portfolio/domain/term-range.model.ts
similarity index 100%
rename from src/app/sevices/portfolio/domain/term-range.model.ts
rename to src/app/services/portfolio/domain/term-range.model.ts
diff --git a/src/app/services/portfolio/portfolio.service.ts b/src/app/services/portfolio/portfolio.service.ts
new file mode 100644
index 0000000..63bad61
--- /dev/null
+++ b/src/app/services/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/services/reporting/domain/auto-complete-resource.model.ts
similarity index 100%
rename from src/app/sevices/reporting/domain/auto-complete-resource.model.ts
rename to src/app/services/reporting/domain/auto-complete-resource.model.ts
diff --git a/src/app/sevices/reporting/domain/displayable-field.model.ts b/src/app/services/reporting/domain/displayable-field.model.ts
similarity index 100%
rename from src/app/sevices/reporting/domain/displayable-field.model.ts
rename to src/app/services/reporting/domain/displayable-field.model.ts
diff --git a/src/app/sevices/reporting/domain/footer.model.ts b/src/app/services/reporting/domain/footer.model.ts
similarity index 100%
rename from src/app/sevices/reporting/domain/footer.model.ts
rename to src/app/services/reporting/domain/footer.model.ts
diff --git a/src/app/sevices/reporting/domain/header.model.ts b/src/app/services/reporting/domain/header.model.ts
similarity index 100%
rename from src/app/sevices/reporting/domain/header.model.ts
rename to src/app/services/reporting/domain/header.model.ts
diff --git a/src/app/sevices/reporting/domain/permittable-group-ids.ts b/src/app/services/reporting/domain/permittable-group-ids.ts
similarity index 100%
rename from src/app/sevices/reporting/domain/permittable-group-ids.ts
rename to src/app/services/reporting/domain/permittable-group-ids.ts
diff --git a/src/app/sevices/reporting/domain/query-parameter.model.ts b/src/app/services/reporting/domain/query-parameter.model.ts
similarity index 100%
rename from src/app/sevices/reporting/domain/query-parameter.model.ts
rename to src/app/services/reporting/domain/query-parameter.model.ts
diff --git a/src/app/sevices/reporting/domain/report-definition.model.ts b/src/app/services/reporting/domain/report-definition.model.ts
similarity index 100%
rename from src/app/sevices/reporting/domain/report-definition.model.ts
rename to src/app/services/reporting/domain/report-definition.model.ts
diff --git a/src/app/sevices/reporting/domain/report-page.model.ts b/src/app/services/reporting/domain/report-page.model.ts
similarity index 100%
rename from src/app/sevices/reporting/domain/report-page.model.ts
rename to src/app/services/reporting/domain/report-page.model.ts
diff --git a/src/app/sevices/reporting/domain/report-request.model.ts b/src/app/services/reporting/domain/report-request.model.ts
similarity index 100%
rename from src/app/sevices/reporting/domain/report-request.model.ts
rename to src/app/services/reporting/domain/report-request.model.ts
diff --git a/src/app/sevices/reporting/domain/row.model.ts b/src/app/services/reporting/domain/row.model.ts
similarity index 100%
rename from src/app/sevices/reporting/domain/row.model.ts
rename to src/app/services/reporting/domain/row.model.ts
diff --git a/src/app/sevices/reporting/domain/type.model.ts b/src/app/services/reporting/domain/type.model.ts
similarity index 100%
rename from src/app/sevices/reporting/domain/type.model.ts
rename to src/app/services/reporting/domain/type.model.ts
diff --git a/src/app/sevices/reporting/domain/value.model.ts b/src/app/services/reporting/domain/value.model.ts
similarity index 100%
rename from src/app/sevices/reporting/domain/value.model.ts
rename to src/app/services/reporting/domain/value.model.ts
diff --git a/src/app/services/reporting/reporting.service.ts b/src/app/services/reporting/reporting.service.ts
new file mode 100644
index 0000000..40a12d8
--- /dev/null
+++ b/src/app/services/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/services/security/authn/auth-guard.service.spec.ts
similarity index 100%
rename from src/app/sevices/security/authn/auth-guard.service.spec.ts
rename to src/app/services/security/authn/auth-guard.service.spec.ts
diff --git a/src/app/sevices/security/authn/auth-guard.service.ts b/src/app/services/security/authn/auth-guard.service.ts
similarity index 100%
rename from src/app/sevices/security/authn/auth-guard.service.ts
rename to src/app/services/security/authn/auth-guard.service.ts
diff --git a/src/app/sevices/security/authn/authentication.service.spec.ts b/src/app/services/security/authn/authentication.service.spec.ts
similarity index 100%
rename from src/app/sevices/security/authn/authentication.service.spec.ts
rename to src/app/services/security/authn/authentication.service.spec.ts
diff --git a/src/app/sevices/security/authn/authentication.service.ts b/src/app/services/security/authn/authentication.service.ts
similarity index 100%
rename from src/app/sevices/security/authn/authentication.service.ts
rename to src/app/services/security/authn/authentication.service.ts
diff --git a/src/app/sevices/security/authz/fims-permission-descriptor.ts b/src/app/services/security/authz/fims-permission-descriptor.ts
similarity index 100%
rename from src/app/sevices/security/authz/fims-permission-descriptor.ts
rename to src/app/services/security/authz/fims-permission-descriptor.ts
diff --git a/src/app/sevices/security/authz/fims-permission.model.ts b/src/app/services/security/authz/fims-permission.model.ts
similarity index 100%
rename from src/app/sevices/security/authz/fims-permission.model.ts
rename to src/app/services/security/authz/fims-permission.model.ts
diff --git a/src/app/services/security/authz/permission-id.type.ts b/src/app/services/security/authz/permission-id.type.ts
new file mode 100644
index 0000000..dde53b3
--- /dev/null
+++ b/src/app/services/security/authz/permission-id.type.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.
+ */
+/**
+ * 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/services/security/authz/permission.directive.spec.ts
similarity index 100%
rename from src/app/sevices/security/authz/permission.directive.spec.ts
rename to src/app/services/security/authz/permission.directive.spec.ts
diff --git a/src/app/sevices/security/authz/permission.directive.ts b/src/app/services/security/authz/permission.directive.ts
similarity index 100%
rename from src/app/sevices/security/authz/permission.directive.ts
rename to src/app/services/security/authz/permission.directive.ts
diff --git a/src/app/sevices/security/authz/permission.guard.ts b/src/app/services/security/authz/permission.guard.ts
similarity index 100%
rename from src/app/sevices/security/authz/permission.guard.ts
rename to src/app/services/security/authz/permission.guard.ts
diff --git a/src/app/services/security/authz/permittable-group-id-mapper.ts b/src/app/services/security/authz/permittable-group-id-mapper.ts
new file mode 100644
index 0000000..ac127e5
--- /dev/null
+++ b/src/app/services/security/authz/permittable-group-id-mapper.ts
@@ -0,0 +1,140 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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/services/security/change.password.service.spec.ts
similarity index 100%
rename from src/app/sevices/security/change.password.service.spec.ts
rename to src/app/services/security/change.password.service.spec.ts
diff --git a/src/app/sevices/security/change.password.service.ts b/src/app/services/security/change.password.service.ts
similarity index 100%
rename from src/app/sevices/security/change.password.service.ts
rename to src/app/services/security/change.password.service.ts
diff --git a/src/app/sevices/teller/domain/charge.model.ts b/src/app/services/teller/domain/charge.model.ts
similarity index 100%
rename from src/app/sevices/teller/domain/charge.model.ts
rename to src/app/services/teller/domain/charge.model.ts
diff --git a/src/app/sevices/teller/domain/cheque.model.ts b/src/app/services/teller/domain/cheque.model.ts
similarity index 100%
rename from src/app/sevices/teller/domain/cheque.model.ts
rename to src/app/services/teller/domain/cheque.model.ts
diff --git a/src/app/sevices/teller/domain/micr.model.ts b/src/app/services/teller/domain/micr.model.ts
similarity index 100%
rename from src/app/sevices/teller/domain/micr.model.ts
rename to src/app/services/teller/domain/micr.model.ts
diff --git a/src/app/sevices/teller/domain/permittable-group-ids.ts b/src/app/services/teller/domain/permittable-group-ids.ts
similarity index 100%
rename from src/app/sevices/teller/domain/permittable-group-ids.ts
rename to src/app/services/teller/domain/permittable-group-ids.ts
diff --git a/src/app/sevices/teller/domain/teller-authentication.model.ts b/src/app/services/teller/domain/teller-authentication.model.ts
similarity index 100%
rename from src/app/sevices/teller/domain/teller-authentication.model.ts
rename to src/app/services/teller/domain/teller-authentication.model.ts
diff --git a/src/app/sevices/teller/domain/teller-balance-sheet.model.ts b/src/app/services/teller/domain/teller-balance-sheet.model.ts
similarity index 100%
rename from src/app/sevices/teller/domain/teller-balance-sheet.model.ts
rename to src/app/services/teller/domain/teller-balance-sheet.model.ts
diff --git a/src/app/sevices/teller/domain/teller-denomination.model.ts b/src/app/services/teller/domain/teller-denomination.model.ts
similarity index 100%
rename from src/app/sevices/teller/domain/teller-denomination.model.ts
rename to src/app/services/teller/domain/teller-denomination.model.ts
diff --git a/src/app/sevices/teller/domain/teller-entry.model.ts b/src/app/services/teller/domain/teller-entry.model.ts
similarity index 100%
rename from src/app/sevices/teller/domain/teller-entry.model.ts
rename to src/app/services/teller/domain/teller-entry.model.ts
diff --git a/src/app/sevices/teller/domain/teller-management-command.model.ts b/src/app/services/teller/domain/teller-management-command.model.ts
similarity index 100%
rename from src/app/sevices/teller/domain/teller-management-command.model.ts
rename to src/app/services/teller/domain/teller-management-command.model.ts
diff --git a/src/app/sevices/teller/domain/teller-transaction-costs.model.ts b/src/app/services/teller/domain/teller-transaction-costs.model.ts
similarity index 100%
rename from src/app/sevices/teller/domain/teller-transaction-costs.model.ts
rename to src/app/services/teller/domain/teller-transaction-costs.model.ts
diff --git a/src/app/sevices/teller/domain/teller-transaction.model.ts b/src/app/services/teller/domain/teller-transaction.model.ts
similarity index 100%
rename from src/app/sevices/teller/domain/teller-transaction.model.ts
rename to src/app/services/teller/domain/teller-transaction.model.ts
diff --git a/src/app/sevices/teller/domain/teller.model.ts b/src/app/services/teller/domain/teller.model.ts
similarity index 100%
rename from src/app/sevices/teller/domain/teller.model.ts
rename to src/app/services/teller/domain/teller.model.ts
diff --git a/src/app/services/teller/teller-service.ts b/src/app/services/teller/teller-service.ts
new file mode 100644
index 0000000..1655496
--- /dev/null
+++ b/src/app/services/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/sevices/accounting/accounting.service.ts b/src/app/sevices/accounting/accounting.service.ts
deleted file mode 100644
index d911c43..0000000
--- a/src/app/sevices/accounting/accounting.service.ts
+++ /dev/null
@@ -1,201 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT 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 {HttpClients} 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: HttpClients, @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/catalog/catalog.service.ts b/src/app/sevices/catalog/catalog.service.ts
deleted file mode 100644
index 188fff5..0000000
--- a/src/app/sevices/catalog/catalog.service.ts
+++ /dev/null
@@ -1,58 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT 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 {HttpClients} 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: HttpClients) {
-  }
-
-  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/cheque/cheque.service.ts b/src/app/sevices/cheque/cheque.service.ts
deleted file mode 100644
index 0c2aa64..0000000
--- a/src/app/sevices/cheque/cheque.service.ts
+++ /dev/null
@@ -1,72 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT 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 {HttpClients} 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: HttpClients, @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/customer/customer.service.ts b/src/app/sevices/customer/customer.service.ts
deleted file mode 100644
index a942c66..0000000
--- a/src/app/sevices/customer/customer.service.ts
+++ /dev/null
@@ -1,212 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT 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 {HttpClients} 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: HttpClients, 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/depositAccount/deposit-account.service.ts b/src/app/sevices/depositAccount/deposit-account.service.ts
deleted file mode 100644
index e61071c..0000000
--- a/src/app/sevices/depositAccount/deposit-account.service.ts
+++ /dev/null
@@ -1,110 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT 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 {HttpClients} 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: HttpClients, @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/http/http.service.spec.ts b/src/app/sevices/http/http.service.spec.ts
deleted file mode 100644
index 49148ef..0000000
--- a/src/app/sevices/http/http.service.spec.ts
+++ /dev/null
@@ -1,137 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT 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, HttpClients, 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: HttpClients, 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,
-        HttpClients,
-      ]);
-      this.httpClient = this.injector.get(HttpClients);
-      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
deleted file mode 100644
index 07f239f..0000000
--- a/src/app/sevices/http/http.service.ts
+++ /dev/null
@@ -1,124 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT 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 HttpClients {
-
-  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/identity.service.ts b/src/app/sevices/identity/identity.service.ts
deleted file mode 100644
index 44c1c15..0000000
--- a/src/app/sevices/identity/identity.service.ts
+++ /dev/null
@@ -1,103 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT 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 {HttpClients} 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: HttpClients ) { // , @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
deleted file mode 100644
index 2db3b1e..0000000
--- a/src/app/sevices/image/image.service.ts
+++ /dev/null
@@ -1,61 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT 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/office/domain/employee.model.ts b/src/app/sevices/office/domain/employee.model.ts
deleted file mode 100644
index 485e515..0000000
--- a/src/app/sevices/office/domain/employee.model.ts
+++ /dev/null
@@ -1,28 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT 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/office.service.ts b/src/app/sevices/office/office.service.ts
deleted file mode 100644
index 8309090..0000000
--- a/src/app/sevices/office/office.service.ts
+++ /dev/null
@@ -1,118 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT 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 {HttpClients} 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: HttpClients, @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/payroll.service.ts b/src/app/sevices/payroll/payroll.service.ts
deleted file mode 100644
index d5abd3a..0000000
--- a/src/app/sevices/payroll/payroll.service.ts
+++ /dev/null
@@ -1,60 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT 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 {HttpClients} from '../http/http.service';
-import {PayrollConfiguration} from './domain/payroll-configuration.model';
-
-@Injectable()
-export class PayrollService {
-
-  constructor(private http: HttpClients, @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/portfolio.service.ts b/src/app/sevices/portfolio/portfolio.service.ts
deleted file mode 100644
index d7ddf0a..0000000
--- a/src/app/sevices/portfolio/portfolio.service.ts
+++ /dev/null
@@ -1,285 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT 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 {HttpClients} 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: HttpClients, @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/reporting.service.ts b/src/app/sevices/reporting/reporting.service.ts
deleted file mode 100644
index 8132fe6..0000000
--- a/src/app/sevices/reporting/reporting.service.ts
+++ /dev/null
@@ -1,57 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT 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 {HttpClients} 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: HttpClients, @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/authz/permission-id.type.ts b/src/app/sevices/security/authz/permission-id.type.ts
deleted file mode 100644
index efbe082..0000000
--- a/src/app/sevices/security/authz/permission-id.type.ts
+++ /dev/null
@@ -1,32 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT 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/permittable-group-id-mapper.ts b/src/app/sevices/security/authz/permittable-group-id-mapper.ts
deleted file mode 100644
index c81b30e..0000000
--- a/src/app/sevices/security/authz/permittable-group-id-mapper.ts
+++ /dev/null
@@ -1,139 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT 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/teller/teller-service.ts b/src/app/sevices/teller/teller-service.ts
deleted file mode 100644
index 2302a2d..0000000
--- a/src/app/sevices/teller/teller-service.ts
+++ /dev/null
@@ -1,106 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT 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 {HttpClients} 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: HttpClients, @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
index 2b1c406..9e3f735 100644
--- a/src/app/store/account/account.actions.ts
+++ b/src/app/store/account/account.actions.ts
@@ -18,7 +18,7 @@
  */
 import {Action} from '@ngrx/store';
 import {type} from '../util';
-import {FetchRequest} from '../../sevices/domain/paging/fetch-request.model';
+import {FetchRequest} from '../../services/domain/paging/fetch-request.model';
 import {SearchResult} from '../../common/store/search.reducer';
 
 export const SEARCH = type('[Account] Search');
diff --git a/src/app/store/account/effects/service.effects.spec.ts b/src/app/store/account/effects/service.effects.spec.ts
index 1b0db8a..7129a89 100644
--- a/src/app/store/account/effects/service.effects.spec.ts
+++ b/src/app/store/account/effects/service.effects.spec.ts
@@ -19,8 +19,8 @@
 import {fakeAsync, TestBed, tick} from '@angular/core/testing';
 import {EffectsRunner, EffectsTestingModule} from '@ngrx/effects/testing';
 import {AccountSearchApiEffects} from './service.effects';
-import {AccountingService} from '../../../sevices/accounting/accounting.service';
-import {AccountPage} from '../../../sevices/accounting/domain/account-page.model';
+import {AccountingService} from '../../../services/accounting/accounting.service';
+import {AccountPage} from '../../../services/accounting/domain/account-page.model';
 import {SearchAction, SearchByLedgerAction, SearchCompleteAction} from '../account.actions';
 import {Observable} from 'rxjs/Observable';
 import {emptySearchResult} from '../../../common/store/search.reducer';
diff --git a/src/app/store/account/effects/service.effects.ts b/src/app/store/account/effects/service.effects.ts
index 7d111c2..c53f52d 100644
--- a/src/app/store/account/effects/service.effects.ts
+++ b/src/app/store/account/effects/service.effects.ts
@@ -22,9 +22,9 @@
 import {Action} from '@ngrx/store';
 import {of} from 'rxjs/observable/of';
 import * as accountActions from '../account.actions';
-import {AccountingService} from '../../../sevices/accounting/accounting.service';
+import {AccountingService} from '../../../services/accounting/accounting.service';
 import {emptySearchResult, SearchResult} from '../../../common/store/search.reducer';
-import {AccountPage} from '../../../sevices/accounting/domain/account-page.model';
+import {AccountPage} from '../../../services/accounting/domain/account-page.model';
 
 @Injectable()
 export class AccountSearchApiEffects {
diff --git a/src/app/store/customer/customer.actions.ts b/src/app/store/customer/customer.actions.ts
index c034c34..d04a639 100644
--- a/src/app/store/customer/customer.actions.ts
+++ b/src/app/store/customer/customer.actions.ts
@@ -19,7 +19,7 @@
 
 import {Action} from '@ngrx/store';
 import {type} from '../util';
-import {FetchRequest} from '../../sevices/domain/paging/fetch-request.model';
+import {FetchRequest} from '../../services/domain/paging/fetch-request.model';
 import {SearchResult} from '../../common/store/search.reducer';
 
 export const SEARCH = type('[Customer] Search');
diff --git a/src/app/store/customer/effects/service.effects.spec.ts b/src/app/store/customer/effects/service.effects.spec.ts
index d96b158..43f031e 100644
--- a/src/app/store/customer/effects/service.effects.spec.ts
+++ b/src/app/store/customer/effects/service.effects.spec.ts
@@ -21,9 +21,9 @@
 import {EffectsRunner, EffectsTestingModule} from '@ngrx/effects/testing';
 import {CustomerSearchApiEffects} from './service.effects';
 import {Observable} from 'rxjs/Observable';
-import {CustomerService} from '../../../sevices/customer/customer.service';
+import {CustomerService} from '../../../services/customer/customer.service';
 import {SearchAction, SearchCompleteAction} from '../customer.actions';
-import {CustomerPage} from '../../../sevices/customer/domain/customer-page.model';
+import {CustomerPage} from '../../../services/customer/domain/customer-page.model';
 import {emptySearchResult} from '../../../common/store/search.reducer';
 
 describe('Customer Search Api Effects', () => {
diff --git a/src/app/store/customer/effects/service.effects.ts b/src/app/store/customer/effects/service.effects.ts
index ab9432a..3d95d9e 100644
--- a/src/app/store/customer/effects/service.effects.ts
+++ b/src/app/store/customer/effects/service.effects.ts
@@ -22,7 +22,7 @@
 import {Action} from '@ngrx/store';
 import {of} from 'rxjs/observable/of';
 import * as customerActions from '../customer.actions';
-import {CustomerService} from '../../../sevices/customer/customer.service';
+import {CustomerService} from '../../../services/customer/customer.service';
 import {emptySearchResult} from '../../../common/store/search.reducer';
 
 @Injectable()
diff --git a/src/app/store/employee/effects/service.effects.spec.ts b/src/app/store/employee/effects/service.effects.spec.ts
index f0b5094..7fe718c 100644
--- a/src/app/store/employee/effects/service.effects.spec.ts
+++ b/src/app/store/employee/effects/service.effects.spec.ts
@@ -20,9 +20,9 @@
 import {EffectsRunner, EffectsTestingModule} from '@ngrx/effects/testing';
 import {EmployeeSearchApiEffects} from './service.effects';
 import {Observable} from 'rxjs/Observable';
-import {OfficeService} from '../../../sevices/office/office.service';
+import {OfficeService} from '../../../services/office/office.service';
 import {SearchAction, SearchCompleteAction} from '../employee.actions';
-import {EmployeePage} from '../../../sevices/office/domain/employee-page.model';
+import {EmployeePage} from '../../../services/office/domain/employee-page.model';
 import {emptySearchResult} from '../../../common/store/search.reducer';
 
 describe('Employee Search Api Effects', () => {
diff --git a/src/app/store/employee/effects/service.effects.ts b/src/app/store/employee/effects/service.effects.ts
index ecf7940..4324794 100644
--- a/src/app/store/employee/effects/service.effects.ts
+++ b/src/app/store/employee/effects/service.effects.ts
@@ -17,7 +17,7 @@
  * under the License.
  */
 import {Injectable} from '@angular/core';
-import {OfficeService} from '../../../sevices/office/office.service';
+import {OfficeService} from '../../../services/office/office.service';
 import {Actions, Effect} from '@ngrx/effects';
 import {Observable} from 'rxjs/Observable';
 import {Action} from '@ngrx/store';
diff --git a/src/app/store/employee/employee.actions.ts b/src/app/store/employee/employee.actions.ts
index 2716256..6526245 100644
--- a/src/app/store/employee/employee.actions.ts
+++ b/src/app/store/employee/employee.actions.ts
@@ -18,7 +18,7 @@
  */
 import {Action} from '@ngrx/store';
 import {type} from '../util';
-import {FetchRequest} from '../../sevices/domain/paging/fetch-request.model';
+import {FetchRequest} from '../../services/domain/paging/fetch-request.model';
 import {SearchResult} from '../../common/store/search.reducer';
 
 export const SEARCH = type('[Employee] Search');
diff --git a/src/app/store/index.ts b/src/app/store/index.ts
index 70005b4..f31b4ab 100644
--- a/src/app/store/index.ts
+++ b/src/app/store/index.ts
@@ -23,7 +23,7 @@
 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/store';
+import {compose} from '@ngrx/core/compose';
 import {localStorageSync} from 'ngrx-store-localstorage';
 import {
   createSearchReducer,
diff --git a/src/app/store/ledger/effects/service.effects.ts b/src/app/store/ledger/effects/service.effects.ts
index 422c1fd..28a87ad 100644
--- a/src/app/store/ledger/effects/service.effects.ts
+++ b/src/app/store/ledger/effects/service.effects.ts
@@ -23,7 +23,7 @@
 import {Action} from '@ngrx/store';
 import {of} from 'rxjs/observable/of';
 import * as ledgerActions from '../ledger.actions';
-import {AccountingService} from '../../../sevices/accounting/accounting.service';
+import {AccountingService} from '../../../services/accounting/accounting.service';
 import {emptySearchResult} from '../../../common/store/search.reducer';
 
 @Injectable()
diff --git a/src/app/store/ledger/ledger.actions.ts b/src/app/store/ledger/ledger.actions.ts
index ff522e8..6368bb2 100644
--- a/src/app/store/ledger/ledger.actions.ts
+++ b/src/app/store/ledger/ledger.actions.ts
@@ -19,7 +19,7 @@
 
 import {type} from '../util';
 import {Action} from '@ngrx/store';
-import {FetchRequest} from '../../sevices/domain/paging/fetch-request.model';
+import {FetchRequest} from '../../services/domain/paging/fetch-request.model';
 import {SearchResult} from '../../common/store/search.reducer';
 
 export const SEARCH = type('[Ledger] Search');
diff --git a/src/app/store/office/effects/service.effects.spec.ts b/src/app/store/office/effects/service.effects.spec.ts
index 15ba058..13bf8b4 100644
--- a/src/app/store/office/effects/service.effects.spec.ts
+++ b/src/app/store/office/effects/service.effects.spec.ts
@@ -20,9 +20,9 @@
 import {EffectsRunner, EffectsTestingModule} from '@ngrx/effects/testing';
 import {OfficeSearchApiEffects} from './service.effects';
 import {Observable} from 'rxjs/Observable';
-import {OfficeService} from '../../../sevices/office/office.service';
+import {OfficeService} from '../../../services/office/office.service';
 import {SearchAction, SearchCompleteAction} from '../office.actions';
-import {OfficePage} from '../../../sevices/office/domain/office-page.model';
+import {OfficePage} from '../../../services/office/domain/office-page.model';
 import {emptySearchResult} from '../../../common/store/search.reducer';
 
 describe('Office Search Api Effects', () => {
diff --git a/src/app/store/office/effects/service.effects.ts b/src/app/store/office/effects/service.effects.ts
index e2cae3c..b55c6f2 100644
--- a/src/app/store/office/effects/service.effects.ts
+++ b/src/app/store/office/effects/service.effects.ts
@@ -17,7 +17,7 @@
  * under the License.
  */
 import {Injectable} from '@angular/core';
-import {OfficeService} from '../../../sevices/office/office.service';
+import {OfficeService} from '../../../services/office/office.service';
 import {Actions, Effect} from '@ngrx/effects';
 import {Observable} from 'rxjs/Observable';
 import {Action} from '@ngrx/store';
diff --git a/src/app/store/office/office.actions.ts b/src/app/store/office/office.actions.ts
index c2a8bc4..f03022b 100644
--- a/src/app/store/office/office.actions.ts
+++ b/src/app/store/office/office.actions.ts
@@ -18,7 +18,7 @@
  */
 import {Action} from '@ngrx/store';
 import {type} from '../util';
-import {FetchRequest} from '../../sevices/domain/paging/fetch-request.model';
+import {FetchRequest} from '../../services/domain/paging/fetch-request.model';
 import {SearchResult} from '../../common/store/search.reducer';
 
 export const SEARCH = type('[Office] Search');
diff --git a/src/app/store/role/effects/service.effects.spec.ts b/src/app/store/role/effects/service.effects.spec.ts
index 5d08b61..7cc2359 100644
--- a/src/app/store/role/effects/service.effects.spec.ts
+++ b/src/app/store/role/effects/service.effects.spec.ts
@@ -21,8 +21,8 @@
 import {EffectsRunner, EffectsTestingModule} from '@ngrx/effects/testing';
 import {RoleSearchApiEffects} from './service.effects';
 import {Observable} from 'rxjs/Observable';
-import {IdentityService} from '../../../sevices/identity/identity.service';
-import {Role} from '../../../sevices/identity/domain/role.model';
+import {IdentityService} from '../../../services/identity/identity.service';
+import {Role} from '../../../services/identity/domain/role.model';
 import {SearchAction, SearchCompleteAction} from '../role.actions';
 import {emptySearchResult} from '../../../common/store/search.reducer';
 
diff --git a/src/app/store/role/effects/service.effects.ts b/src/app/store/role/effects/service.effects.ts
index 7173dc3..5945a90 100644
--- a/src/app/store/role/effects/service.effects.ts
+++ b/src/app/store/role/effects/service.effects.ts
@@ -22,9 +22,9 @@
 import {Action} from '@ngrx/store';
 import {of} from 'rxjs/observable/of';
 import * as roleActions from '../role.actions';
-import {IdentityService} from '../../../sevices/identity/identity.service';
+import {IdentityService} from '../../../services/identity/identity.service';
 import {emptySearchResult} from '../../../common/store/search.reducer';
-import {Role} from '../../../sevices/identity/domain/role.model';
+import {Role} from '../../../services/identity/domain/role.model';
 
 const SYSTEM_ROLES: string[] = ['pharaoh', 'scheduler'];
 
diff --git a/src/app/store/security/authentication.reducer.spec.ts b/src/app/store/security/authentication.reducer.spec.ts
index 6b107aa..349d13b 100644
--- a/src/app/store/security/authentication.reducer.spec.ts
+++ b/src/app/store/security/authentication.reducer.spec.ts
@@ -19,7 +19,7 @@
 import {mockAuthentication} from './testing/authentication.mock';
 import {reducer} from './authentication.reducer';
 import {LoginSuccessAction, LoginSuccessPayload, RefreshAccessTokenSuccessAction} from './security.actions';
-import {Authentication} from '../../sevices/identity/domain/authentication.model';
+import {Authentication} from '../../services/identity/domain/authentication.model';
 
 describe('Authentication Reducer', () => {
 
diff --git a/src/app/store/security/authentication.reducer.ts b/src/app/store/security/authentication.reducer.ts
index 91fba56..fcf6c8c 100644
--- a/src/app/store/security/authentication.reducer.ts
+++ b/src/app/store/security/authentication.reducer.ts
@@ -18,7 +18,7 @@
  */
 import * as security from './security.actions';
 import {LoginSuccessPayload} from './security.actions';
-import {Authentication} from '../../sevices/identity/domain/authentication.model';
+import {Authentication} from '../../services/identity/domain/authentication.model';
 
 export interface State {
   username: string;
diff --git a/src/app/store/security/authorization.reducer.ts b/src/app/store/security/authorization.reducer.ts
index 0ad38ca..b2fe184 100644
--- a/src/app/store/security/authorization.reducer.ts
+++ b/src/app/store/security/authorization.reducer.ts
@@ -17,7 +17,7 @@
  * under the License.
  */
 import * as security from './security.actions';
-import {FimsPermission} from '../../sevices/security/authz/fims-permission.model';
+import {FimsPermission} from '../../services/security/authz/fims-permission.model';
 
 export interface State {
   permissions: FimsPermission[];
diff --git a/src/app/store/security/effects/notification.effects.ts b/src/app/store/security/effects/notification.effects.ts
index 3dedf2a..f67ede7 100644
--- a/src/app/store/security/effects/notification.effects.ts
+++ b/src/app/store/security/effects/notification.effects.ts
@@ -22,7 +22,7 @@
 import {Action} from '@ngrx/store';
 import * as securityActions from '../security.actions';
 import {LoginSuccessAction} from '../security.actions';
-import {NotificationService, NotificationType} from '../../../sevices/notification/notification.service';
+import {NotificationService, NotificationType} from '../../../services/notification/notification.service';
 
 @Injectable()
 export class SecurityNotificationEffects {
diff --git a/src/app/store/security/effects/service.effects.spec.ts b/src/app/store/security/effects/service.effects.spec.ts
index 2ce07e2..3f87cc2 100644
--- a/src/app/store/security/effects/service.effects.spec.ts
+++ b/src/app/store/security/effects/service.effects.spec.ts
@@ -20,8 +20,8 @@
 import {EffectsRunner, EffectsTestingModule} from '@ngrx/effects/testing';
 import {SecurityApiEffects} from './service.effects';
 import {Observable} from 'rxjs/Observable';
-import {IdentityService} from '../../../sevices/identity/identity.service';
-import {AuthenticationService} from '../../../sevices/security/authn/authentication.service';
+import {IdentityService} from '../../../services/identity/identity.service';
+import {AuthenticationService} from '../../../services/security/authn/authentication.service';
 import {
   ChangePasswordAction,
   ChangePasswordSuccessAction,
@@ -35,11 +35,11 @@
   RefreshAccessTokenSuccessAction,
   RefreshTokenStartTimerAction
 } from '../security.actions';
-import {PermittableGroupIdMapper} from '../../../sevices/security/authz/permittable-group-id-mapper';
+import {PermittableGroupIdMapper} from '../../../services/security/authz/permittable-group-id-mapper';
 import {Store} from '@ngrx/store';
-import {FimsPermission} from '../../../sevices/security/authz/fims-permission.model';
-import {Permission} from '../../../sevices/identity/domain/permission.model';
-import {IdentityPermittableGroupIds} from '../../../sevices/identity/domain/permittable-group-ids.model';
+import {FimsPermission} from '../../../services/security/authz/fims-permission.model';
+import {Permission} from '../../../services/identity/domain/permission.model';
+import {IdentityPermittableGroupIds} from '../../../services/identity/domain/permittable-group-ids.model';
 import {mockAuthentication} from '../testing/authentication.mock';
 
 describe('Security Api Effects', () => {
diff --git a/src/app/store/security/effects/service.effects.ts b/src/app/store/security/effects/service.effects.ts
index 55845dd..4e0f582 100644
--- a/src/app/store/security/effects/service.effects.ts
+++ b/src/app/store/security/effects/service.effects.ts
@@ -22,14 +22,14 @@
 import {Action, Store} from '@ngrx/store';
 import {of} from 'rxjs/observable/of';
 import * as securityActions from '../security.actions';
-import {AuthenticationService} from '../../../sevices/security/authn/authentication.service';
-import {PermissionId} from '../../../sevices/security/authz/permission-id.type';
-import {FimsPermission} from '../../../sevices/security/authz/fims-permission.model';
-import {Permission} from '../../../sevices/identity/domain/permission.model';
-import {PermittableGroupIdMapper} from '../../../sevices/security/authz/permittable-group-id-mapper';
+import {AuthenticationService} from '../../../services/security/authn/authentication.service';
+import {PermissionId} from '../../../services/security/authz/permission-id.type';
+import {FimsPermission} from '../../../services/security/authz/fims-permission.model';
+import {Permission} from '../../../services/identity/domain/permission.model';
+import {PermittableGroupIdMapper} from '../../../services/security/authz/permittable-group-id-mapper';
 import * as fromRoot from '../../index';
-import {IdentityService} from '../../../sevices/identity/identity.service';
-import {Password} from '../../../sevices/identity/domain/password.model';
+import {IdentityService} from '../../../services/identity/identity.service';
+import {Password} from '../../../services/identity/domain/password.model';
 
 @Injectable()
 export class SecurityApiEffects {
diff --git a/src/app/store/security/security.actions.ts b/src/app/store/security/security.actions.ts
index 0cc5843..8dd520f 100644
--- a/src/app/store/security/security.actions.ts
+++ b/src/app/store/security/security.actions.ts
@@ -18,8 +18,8 @@
  */
 import {Action} from '@ngrx/store';
 import {type} from '../util';
-import {Authentication} from '../../sevices/identity/domain/authentication.model';
-import {FimsPermission} from '../../sevices/security/authz/fims-permission.model';
+import {Authentication} from '../../services/identity/domain/authentication.model';
+import {FimsPermission} from '../../services/security/authz/fims-permission.model';
 
 export const LOGIN = type('[Security] Login');
 export const LOGIN_SUCCESS = type('[Security] Login Success');
diff --git a/src/app/store/security/testing/authentication.mock.ts b/src/app/store/security/testing/authentication.mock.ts
index 9d37fe9..cc248f8 100644
--- a/src/app/store/security/testing/authentication.mock.ts
+++ b/src/app/store/security/testing/authentication.mock.ts
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-import {Authentication} from '../../../sevices/identity/domain/authentication.model';
+import {Authentication} from '../../../services/identity/domain/authentication.model';
 
 export function mockAuthentication(): Authentication {
   return {
diff --git a/src/app/teller/auth/teller-auth.component.html b/src/app/teller/auth/teller-auth.component.html
new file mode 100644
index 0000000..b19b19e
--- /dev/null
+++ b/src/app/teller/auth/teller-auth.component.html
@@ -0,0 +1,47 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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 layout="column" layout-fill>
+  <div class="mat-content" layout-padding flex>
+    <div layout-gt-xs="row" layout-align-gt-xs="center start" class="margin">
+      <div flex-gt-xs="25" layout-align="center center" layout-margin>
+        <mat-card>
+          <mat-card-title>
+            unlock drawer
+          </mat-card-title>
+          <form [formGroup]="form" (ngSubmit)="auth()">
+            <mat-card-content layout="column">
+              <fims-text-input [form]="form" controlName="tellerCode" placeholder="{{'Teller number' | translate}}"></fims-text-input>
+              <div layout="row">
+                <mat-form-field layout-margin flex>
+                  <input matInput placeholder="{{'Password' | translate}}" type="password" formControlName="password" autocomplete="new-password"/>
+                  <mat-error *ngIf="form.get('password').hasError('required')" translate>
+                    Required
+                  </mat-error>
+                </mat-form-field>
+              </div>
+              <p class="mat-caption tc-red-700" *ngIf="error$ | async">{{'Sorry, that login did not work.' | translate}}</p>
+            </mat-card-content>
+            <mat-card-actions>
+              <button type="submit" mat-raised-button color="primary" [disabled]="form.invalid">{{'UNLOCK' | translate}}</button>
+            </mat-card-actions>
+          </form>
+        </mat-card>
+      </div>
+    </div>
+  </div>
+</div>
diff --git a/src/app/teller/auth/teller-auth.component.ts b/src/app/teller/auth/teller-auth.component.ts
new file mode 100644
index 0000000..8ed9904
--- /dev/null
+++ b/src/app/teller/auth/teller-auth.component.ts
@@ -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.
+ */
+import {Component, OnDestroy, OnInit} from '@angular/core';
+import * as fromTeller from '../store/index';
+import {TellerStore} from '../store/index';
+import * as fromRoot from '../../store/index';
+import {UNLOCK_DRAWER} from '../store/teller.actions';
+import {Subscription} from 'rxjs/Subscription';
+import {FormBuilder, FormGroup, Validators} from '@angular/forms';
+import {Observable} from 'rxjs/Observable';
+
+@Component({
+  templateUrl: './teller-auth.component.html'
+})
+export class TellerAuthComponent implements OnInit, OnDestroy {
+
+  private userIdSubscription: Subscription;
+
+  private userId: string;
+
+  form: FormGroup;
+
+  error$: Observable<boolean>;
+
+  constructor(private store: TellerStore, private formBuilder: FormBuilder) {
+    this.userIdSubscription = this.store.select(fromRoot.getUsername)
+      .subscribe(username => this.userId = username);
+  }
+
+  ngOnInit(): void {
+    this.form = this.formBuilder.group({
+      tellerCode: ['', Validators.required],
+      password: ['', Validators.required]
+    });
+
+    this.error$ = this.store.select(fromTeller.getAuthenticationError)
+      .map(error => !!error);
+  }
+
+  ngOnDestroy(): void {
+    this.userIdSubscription.unsubscribe();
+  }
+
+  auth(): void {
+    const tellerCode = this.form.get('tellerCode').value;
+    const password = this.form.get('password').value;
+
+    this.store.dispatch({
+      type: UNLOCK_DRAWER,
+      payload: {
+        employeeId: this.userId,
+        tellerCode,
+        password
+      }
+    });
+  }
+
+}
diff --git a/src/app/teller/customer/customer-detail.component.html b/src/app/teller/customer/customer-detail.component.html
new file mode 100644
index 0000000..105a65b
--- /dev/null
+++ b/src/app/teller/customer/customer-detail.component.html
@@ -0,0 +1,56 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<fims-layout-card-over *ngIf="customer$ | async as customer" title="{{customer.givenName}} {{customer.surname}}" [navigateBackTo]="['../../../../']">
+  <fims-two-column-layout>
+    <ng-container left>
+      <fims-portrait [blob]="portrait$ | async"></fims-portrait>
+      <mat-nav-list>
+        <h3 mat-subheader translate>Transactions</h3>
+        <ng-template let-item let-last="last" ngFor [ngForOf]="availableActions$ | async">
+          <a mat-list-item [routerLink]="[item.relativeLink]" [queryParams]="{ transactionType: item.transactionType }">
+            <mat-icon matListAvatar>{{item.icon}}</mat-icon>
+            <h3 matLine translate>{{item.title}}</h3>
+          </a>
+        </ng-template>
+      </mat-nav-list>
+    </ng-container>
+    <mat-list right>
+      <h3 mat-subheader translate>Address</h3>
+      <mat-list-item>
+        <mat-icon matListAvatar>location_on</mat-icon>
+        <h3 matLine>{{customer.address?.street}}, {{customer.address?.city}}, {{customer.address?.postalCode}},
+          {{customer.address?.country}}</h3>
+      </mat-list-item>
+      <h3 mat-subheader translate>Contact information</h3>
+      <mat-list-item [ngSwitch]="detail.type" *ngFor="let detail of customer.contactDetails">
+        <mat-icon *ngSwitchCase="'EMAIL'" matListAvatar>email</mat-icon>
+        <mat-icon *ngSwitchCase="'PHONE'" matListAvatar>phone</mat-icon>
+        <mat-icon *ngSwitchCase="'MOBILE'" matListAvatar>smartphone</mat-icon>
+        <h3 matLine>{{detail.value}}</h3>
+      </mat-list-item>
+      <mat-list-item *ngIf="!customer.contactDetails?.length">
+        <h3 matLine translate>No contact details available</h3>
+      </mat-list-item>
+      <h3 mat-subheader translate>Birthday</h3>
+      <mat-list-item>
+        <mat-icon matListAvatar>cake</mat-icon>
+        <h3 matLine>{{customer.dateOfBirth | displayFimsDate}}</h3>
+      </mat-list-item>
+    </mat-list>
+  </fims-two-column-layout>
+</fims-layout-card-over>
diff --git a/src/app/teller/customer/customer-detail.component.ts b/src/app/teller/customer/customer-detail.component.ts
new file mode 100644
index 0000000..0588016
--- /dev/null
+++ b/src/app/teller/customer/customer-detail.component.ts
@@ -0,0 +1,75 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, OnDestroy} from '@angular/core';
+import * as fromTeller from '../store/index';
+import {TellerStore} from '../store/index';
+import {Customer} from '../../services/customer/domain/customer.model';
+import {Observable} from 'rxjs/Observable';
+import {Subscription} from 'rxjs/Subscription';
+import {LoadAllDepositProductsAction, LoadAllLoanProductsAction} from '../store/teller.actions';
+import {CustomerService} from '../../services/customer/customer.service';
+import {Action, AvailableActionService} from '../services/available-actions.service';
+
+@Component({
+  templateUrl: './customer-detail.component.html'
+})
+export class TellerCustomerDetailComponent implements OnDestroy {
+
+  private loadDepositProductsSubscription: Subscription;
+
+  private loadLoanProductsSubscription: Subscription;
+
+  portrait$: Observable<Blob>;
+
+  customer$: Observable<Customer>;
+
+  hasDepositProducts$: Observable<boolean>;
+
+  hasLoanProducts$: Observable<boolean>;
+
+  availableActions$: Observable<Action[]>;
+
+  constructor(private store: TellerStore, private customerService: CustomerService, private actionService: AvailableActionService) {
+    this.customer$ = store.select(fromTeller.getTellerSelectedCustomer)
+      .filter(customer => !!customer);
+
+    this.portrait$ = this.customer$
+      .flatMap(customer => this.customerService.getPortrait(customer.identifier));
+
+    this.hasDepositProducts$ = store.select(fromTeller.hasTellerCustomerDepositProducts);
+
+    this.hasLoanProducts$ = store.select(fromTeller.hasTellerCustomerLoanProducts);
+
+    this.loadDepositProductsSubscription = this.customer$
+      .map(customer => new LoadAllDepositProductsAction(customer.identifier))
+      .subscribe(this.store);
+
+    this.loadLoanProductsSubscription = this.customer$
+      .map(customer => new LoadAllLoanProductsAction(customer.identifier))
+      .subscribe(this.store);
+
+    this.availableActions$ = this.customer$
+      .mergeMap(customer => this.actionService.getAvailableActions(customer.identifier));
+  }
+
+  ngOnDestroy(): void {
+    this.loadDepositProductsSubscription.unsubscribe();
+    this.loadLoanProductsSubscription.unsubscribe();
+  }
+}
diff --git a/src/app/teller/customer/customer-index.component.html b/src/app/teller/customer/customer-index.component.html
new file mode 100644
index 0000000..ca721b3
--- /dev/null
+++ b/src/app/teller/customer/customer-index.component.html
@@ -0,0 +1,18 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<router-outlet></router-outlet>
diff --git a/src/app/teller/customer/customer-index.component.ts b/src/app/teller/customer/customer-index.component.ts
new file mode 100644
index 0000000..1b110bb
--- /dev/null
+++ b/src/app/teller/customer/customer-index.component.ts
@@ -0,0 +1,44 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 {SelectCustomerAction} from '../store/teller.actions';
+import {TellerStore} from '../store/index';
+import {Subscription} from 'rxjs/Subscription';
+import {Component, OnDestroy, OnInit} from '@angular/core';
+import {ActivatedRoute} from '@angular/router';
+
+@Component({
+  templateUrl: './customer-index.component.html'
+})
+export class TellerCustomerIndexComponent implements OnInit, OnDestroy {
+
+  private actionsSubscription: Subscription;
+
+  constructor(private route: ActivatedRoute, private store: TellerStore) {}
+
+  ngOnInit(): void {
+    this.actionsSubscription = this.route.params
+      .map(params => new SelectCustomerAction(params['id']))
+      .subscribe(this.store);
+  }
+
+  ngOnDestroy(): void {
+    this.actionsSubscription.unsubscribe();
+  }
+}
diff --git a/src/app/teller/customer/teller-customer-exists.guard.ts b/src/app/teller/customer/teller-customer-exists.guard.ts
new file mode 100644
index 0000000..1eadc70
--- /dev/null
+++ b/src/app/teller/customer/teller-customer-exists.guard.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 {Injectable} from '@angular/core';
+import {ActivatedRouteSnapshot, CanActivate, RouterStateSnapshot} from '@angular/router';
+import * as fromTeller from '../store/index';
+import {TellerStore} from '../store/index';
+import {CustomerService} from '../../services/customer/customer.service';
+import {ExistsGuardService} from '../../common/guards/exists-guard';
+import {Observable} from 'rxjs/Observable';
+import {LoadCustomerAction} from '../store/teller.actions';
+import {of} from 'rxjs/observable/of';
+
+@Injectable()
+export class TellerCustomerExistsGuard implements CanActivate {
+
+  constructor(private store: TellerStore,
+              private customerService: CustomerService,
+              private existsGuardService: ExistsGuardService) {
+  }
+
+  hasCustomerInStore(id: string): Observable<boolean> {
+    const timestamp$: Observable<number> = this.store.select(fromTeller.getTellerCustomerLoadedAt)
+      .map(loadedAt => loadedAt[id]);
+
+    return this.existsGuardService.isWithinExpiry(timestamp$);
+  }
+
+  hasCustomerInApi(id: string): Observable<boolean> {
+    const getCustomer$: Observable<any> = this.customerService.getCustomer(id)
+      .map(customerEntity => new LoadCustomerAction({
+        resource: customerEntity
+      }))
+      .do((action: LoadCustomerAction) => this.store.dispatch(action))
+      .map(customer => !!customer);
+
+    return this.existsGuardService.routeTo404OnError(getCustomer$);
+  }
+
+  hasCustomer(id: string): Observable<boolean> {
+    return this.hasCustomerInStore(id)
+      .switchMap(inStore => {
+        if (inStore) {
+          return of(inStore);
+        }
+        return this.hasCustomerInApi(id);
+      });
+  }
+
+  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
+    return this.hasCustomer(route.params['id']);
+  }
+}
diff --git a/src/app/teller/customer/transaction/cheque/create.component.html b/src/app/teller/customer/transaction/cheque/create.component.html
new file mode 100644
index 0000000..f06a271
--- /dev/null
+++ b/src/app/teller/customer/transaction/cheque/create.component.html
@@ -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.
+-->
+
+<fims-layout-card-over title="Teller transaction">
+  <fims-cheque-transaction-form
+    #form
+    [customerName]="customerName$ | async"
+    [micrResolution]="micrResolution$ | async"
+    [micrResolutionError]="micrResolutionError"
+    [productInstances]="productInstances$ | async"
+    [transactionCosts]="transactionCosts$ | async"
+    [transactionCreated]="transactionCreated"
+    (onExpandMICR)="expandMICR($event)"
+    (onCreateTransaction)="createTransaction($event)"
+    (onConfirmTransaction)="confirmTransaction($event)"
+    (onCancelTransaction)="cancelTransaction()"
+    (onCancel)="cancel()">
+  </fims-cheque-transaction-form>
+</fims-layout-card-over>
diff --git a/src/app/teller/customer/transaction/cheque/create.component.ts b/src/app/teller/customer/transaction/cheque/create.component.ts
new file mode 100644
index 0000000..ecf06d1
--- /dev/null
+++ b/src/app/teller/customer/transaction/cheque/create.component.ts
@@ -0,0 +1,153 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, OnDestroy, OnInit, ViewChild} from '@angular/core';
+import {TellerTransaction, TransactionType} from '../../../../services/teller/domain/teller-transaction.model';
+import {TellerTransactionCosts} from '../../../../services/teller/domain/teller-transaction-costs.model';
+import {CONFIRM_TRANSACTION} from '../../../store/teller.actions';
+import * as fromTeller from '../../../store/index';
+import {TellerStore} from '../../../store/index';
+import * as fromRoot from '../../../../store/index';
+import {DepositAccountService} from '../../../../services/depositAccount/deposit-account.service';
+import {Observable} from 'rxjs/Observable';
+import {ActivatedRoute, Router} from '@angular/router';
+import {Subscription} from 'rxjs/Subscription';
+import {ProductInstance} from '../../../../services/depositAccount/domain/instance/product-instance.model';
+import {Teller} from '../../../../services/teller/domain/teller.model';
+import {TransactionForm} from '../domain/transaction-form.model';
+import {ChequeTransactionFormComponent} from './form.component';
+import {ChequeService} from '../../../../services/cheque/cheque.service';
+import {MICRResolution} from '../../../../services/cheque/domain/micr-resolution.model';
+import {Error} from '../../../../services/domain/error.model';
+import {TellerTransactionService} from '../../../services/transaction.service';
+
+@Component({
+  templateUrl: './create.component.html'
+})
+export class CreateChequeTransactionFormComponent implements OnInit, OnDestroy {
+
+  private authenticatedTellerSubscription: Subscription;
+
+  private usernameSubscription: Subscription;
+
+  private tellerTransactionIdentifier: string;
+
+  private clerk: string;
+
+  @ViewChild('form') form: ChequeTransactionFormComponent;
+
+  transactionType: TransactionType;
+
+  customerName$: Observable<string>;
+
+  micrResolution$: Observable<MICRResolution>;
+
+  micrResolutionError: Error;
+
+  productInstances$: Observable<ProductInstance[]>;
+
+  transactionCosts$: Observable<TellerTransactionCosts>;
+
+  teller: Teller;
+
+  transactionCreated: boolean;
+
+  constructor(private router: Router, private route: ActivatedRoute, private store: TellerStore,
+              private depositService: DepositAccountService, private chequeService: ChequeService,
+              private tellerTransactionService: TellerTransactionService) {}
+
+  ngOnInit(): void {
+    const selectedCustomer$ = this.store.select(fromTeller.getTellerSelectedCustomer)
+      .filter(customer => !!customer);
+
+    this.productInstances$ = selectedCustomer$
+      .switchMap(customer => this.depositService.fetchProductInstances(customer.identifier))
+      .map((instances: ProductInstance[]) => instances.filter(instance => instance.state === 'ACTIVE'));
+
+    this.authenticatedTellerSubscription = this.store.select(fromTeller.getAuthenticatedTeller)
+      .filter(teller => !!teller)
+      .subscribe(teller => { this.teller = teller; } );
+
+    this.usernameSubscription = this.store.select(fromRoot.getUsername)
+      .subscribe(username => this.clerk = username);
+
+    this.customerName$ = selectedCustomer$
+      .map(customer => `${customer.givenName} ${customer.surname}`);
+  }
+
+  ngOnDestroy(): void {
+    this.authenticatedTellerSubscription.unsubscribe();
+    this.usernameSubscription.unsubscribe();
+  }
+
+  expandMICR(identifier: string): void {
+    this.micrResolution$ = this.chequeService.expandMicr(identifier)
+      .do(resolution => this.micrResolutionError = null)
+      .catch(error => {
+        this.micrResolutionError = error;
+        return Observable.empty();
+      });
+  }
+
+  createTransaction(formData: TransactionForm): void {
+    const transaction: TellerTransaction = {
+      customerIdentifier: formData.customerIdentifier,
+      productIdentifier: formData.productIdentifier,
+      customerAccountIdentifier: formData.accountIdentifier,
+      targetAccountIdentifier: formData.targetAccountIdentifier,
+      amount: formData.amount,
+      clerk: this.clerk,
+      transactionDate: new Date().toISOString(),
+      cheque: formData.cheque,
+      transactionType: 'CCHQ'
+    };
+
+    this.transactionCosts$ = this.tellerTransactionService.createTransaction(this.teller.code, transaction)
+      .do(transactionCosts => this.tellerTransactionIdentifier = transactionCosts.tellerTransactionIdentifier)
+      .do(() => this.transactionCreated = true);
+  }
+
+  confirmTransaction(chargesIncluded: boolean): void {
+    this.store.dispatch({
+      type: CONFIRM_TRANSACTION,
+      payload: {
+        tellerCode: this.teller.code,
+        tellerTransactionIdentifier: this.tellerTransactionIdentifier,
+        command: 'CONFIRM',
+        chargesIncluded,
+        activatedRoute: this.route
+      }
+    });
+  }
+
+  cancelTransaction(): void {
+    this.store.dispatch({
+      type: CONFIRM_TRANSACTION,
+      payload: {
+        tellerCode: this.teller.code,
+        tellerTransactionIdentifier: this.tellerTransactionIdentifier,
+        command: 'CANCEL',
+        activatedRoute: this.route
+      }
+    });
+  }
+
+  cancel(): void {
+    this.router.navigate(['../../'], { relativeTo: this.route });
+  }
+}
diff --git a/src/app/teller/customer/transaction/cheque/form.component.html b/src/app/teller/customer/transaction/cheque/form.component.html
new file mode 100644
index 0000000..7a64535
--- /dev/null
+++ b/src/app/teller/customer/transaction/cheque/form.component.html
@@ -0,0 +1,85 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<td-steps mode="'vertical'">
+  <td-step #transactionStep label="{{'Transaction' | translate}}"
+           [state]="!invalid ? 'complete' : 'required'" [disabled]="transactionCreated">
+    <form [formGroup]="chequeForm" layout="row">
+      <fims-text-input [form]="chequeForm" controlName="chequeNumber" placeholder="{{'Cheque number' | translate}}"></fims-text-input>
+      <fims-text-input [form]="chequeForm" controlName="branchSortCode" placeholder="{{'Branch sort code' | translate}}"></fims-text-input>
+      <fims-text-input [form]="chequeForm" controlName="accountNumber" placeholder="{{'Account number' | translate}}"></fims-text-input>
+    </form>
+    <form [formGroup]="amountForm">
+      <div layout="row">
+        <fims-text-input [form]="amountForm" controlName="drawee" placeholder="{{'Issuing Bank' | translate}}"></fims-text-input>
+        <fims-text-input [form]="amountForm" controlName="drawer" placeholder="{{'Issuer' | translate}}"></fims-text-input>
+        <div>
+          <button mat-raised-button color="primary" (click)="validateCheque()" [disabled]="chequeForm.invalid">{{'DETERMINE FROM MICR' | translate}}</button>
+        </div>
+      </div>
+      <td-message *ngIf="!!micrResolutionError"
+                  label="Issuing Bank/Issuer could not be determined in our system from the MICR you entered."
+                  sublabel="Possible reasons: Cheque number has not been issued, Branch sort code is not known in system or account number does not exist in system"
+                  color="accent"
+                  icon="warning">
+      </td-message>
+      <fims-text-input [form]="amountForm" controlName="payee" placeholder="{{'Payee' | translate}}"></fims-text-input>
+      <fims-date-input [form]="amountForm" controlName="dateIssued" placeholder="{{'Date issued' | translate}}"></fims-date-input>
+      <mat-checkbox formControlName="openCheque" layout-margin translate>Is cheque open?</mat-checkbox>
+      <td-message *ngIf="!amountForm.get('openCheque').value" label="Please check identification card of member" color="accent" icon="warning"></td-message>
+      <fims-text-input type="number" [form]="amountForm" controlName="amount" placeholder="{{'Amount' | translate}}"></fims-text-input>
+      <div layout="row">
+        <mat-form-field layout-margin>
+          <mat-select formControlName="productInstance" placeholder="{{ 'Select account to transfer to' | translate }}">
+            <mat-option *ngFor="let instance of productInstances" [value]="instance">
+              {{instance.accountIdentifier}}({{instance.productIdentifier}})
+            </mat-option>
+          </mat-select>
+        </mat-form-field>
+      </div>
+    </form>
+    <ng-template td-step-actions>
+      <button mat-raised-button color="primary" (click)="createTransaction()" [disabled]="createTransactionDisabled">{{'CREATE TRANSACTION' | translate}}</button>
+      <span flex></span>
+      <button mat-button (click)="cancel()" [disabled]="transactionCreated">{{'CANCEL' | translate}}</button>
+    </ng-template>
+  </td-step>
+  <td-step #confirmationStep label="{{'Confirmation' | translate}}">
+    <div layout-gt-xs="row" layout-align="center center">
+      <div layout-gt-xs="row" flex-gt-xs="90" layout-margin>
+        <div flex-gt-xs="25"></div>
+        <div flex-gt-xs="50">
+          <h3 translate>Costs</h3>
+          <fims-teller-transaction-cost
+            [transactionAmount]="amountForm.get('amount').value"
+            [transactionCosts]="transactionCosts">
+          </fims-teller-transaction-cost>
+          <div layout="row" layout-margin>
+            <mat-checkbox [(ngModel)]="chargesIncluded" translate>Fees are paid in cash</mat-checkbox>
+          </div>
+        </div>
+      </div>
+    </div>
+  </td-step>
+  <td-step label="{{'Final step' | translate}}" [state]="'complete'">
+    <ng-template td-step-summary>
+      <button mat-raised-button color="primary" [disabled]="!transactionCreated" (click)="confirmTransaction(chargesIncluded)">{{'CONFIRM TRANSACTION' | translate}}</button>
+      <span flex></span>
+      <button mat-button [disabled]="!transactionCreated" (click)="cancelTransaction()">{{'CANCEL TRANSACTION' | translate}}</button>
+    </ng-template>
+  </td-step>
+</td-steps>
diff --git a/src/app/teller/customer/transaction/cheque/form.component.ts b/src/app/teller/customer/transaction/cheque/form.component.ts
new file mode 100644
index 0000000..4bc5f91
--- /dev/null
+++ b/src/app/teller/customer/transaction/cheque/form.component.ts
@@ -0,0 +1,160 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, OnChanges, OnInit, Output, SimpleChanges, ViewChild} from '@angular/core';
+import {TdStepComponent} from '@covalent/core';
+import {FormBuilder, FormGroup, Validators} from '@angular/forms';
+import {FimsValidators} from '../../../../common/validator/validators';
+import {TellerTransactionCosts} from '../../../../services/teller/domain/teller-transaction-costs.model';
+import {ProductInstance} from '../../../../services/depositAccount/domain/instance/product-instance.model';
+import {TransactionForm} from '../domain/transaction-form.model';
+import {toMICRIdentifier} from '../../../../services/cheque/domain/mapper/fims-cheque.mapper';
+import {Cheque} from '../../../../services/teller/domain/cheque.model';
+import {MICRResolution} from '../../../../services/cheque/domain/micr-resolution.model';
+import {toShortISOString} from '../../../../services/domain/date.converter';
+
+@Component({
+  selector: 'fims-cheque-transaction-form',
+  templateUrl: './form.component.html'
+})
+export class ChequeTransactionFormComponent implements OnInit, OnChanges {
+
+  chequeForm: FormGroup;
+  amountForm: FormGroup;
+
+  chargesIncluded = true;
+
+  numberFormat = '1.2-2';
+
+  @ViewChild('transactionStep') transactionStep: TdStepComponent;
+  @ViewChild('confirmationStep') confirmationStep: TdStepComponent;
+
+  @Input('productInstances') productInstances: ProductInstance[];
+  @Input('transactionCosts') transactionCosts: TellerTransactionCosts;
+  @Input('transactionCreated') transactionCreated: boolean;
+  @Input('micrResolution') micrResolution: MICRResolution;
+  @Input('micrResolutionError') micrResolutionError: Error;
+  @Input('customerName') customerName: string;
+
+  @Output('onExpandMICR') onExpandMICR = new EventEmitter<string>();
+  @Output('onCreateTransaction') onCreateTransaction = new EventEmitter<TransactionForm>();
+  @Output('onConfirmTransaction') onConfirmTransaction = new EventEmitter<boolean>();
+  @Output('onCancelTransaction') onCancelTransaction = new EventEmitter<void>();
+  @Output('onCancel') onCancel = new EventEmitter<void>();
+
+  constructor(private formBuilder: FormBuilder) {
+    this.chequeForm = this.formBuilder.group({
+      chequeNumber: ['', [Validators.required, Validators.minLength(2), Validators.maxLength(8), FimsValidators.isNumber]],
+      branchSortCode: ['', [Validators.required, Validators.minLength(2), Validators.maxLength(11)]],
+      accountNumber: ['', [Validators.required, Validators.minLength(2), Validators.maxLength(34)]]
+    });
+
+    this.amountForm = this.formBuilder.group({
+      productInstance: ['', Validators.required],
+      drawee: ['', Validators.required],
+      drawer: ['', Validators.required],
+      payee: [{value: '', disabled: true}, Validators.required],
+      amount: ['', [Validators.required, FimsValidators.greaterThanValue(0)]],
+      dateIssued: ['', [Validators.required]],
+      openCheque: [false],
+    });
+  }
+
+  ngOnInit(): void {
+    this.transactionStep.open();
+  }
+
+  ngOnChanges(changes: SimpleChanges): void {
+    const draweeControl = this.amountForm.get('drawee');
+    const drawerControl = this.amountForm.get('drawer');
+
+    if (changes.micrResolution && this.micrResolution) {
+      draweeControl.setValue(this.micrResolution.office);
+      drawerControl.setValue(this.micrResolution.customer);
+    }
+
+    if (changes.micrResolutionError && this.micrResolutionError) {
+      draweeControl.setValue('');
+      drawerControl.setValue('');
+    }
+
+    if (changes.transactionCreated && this.transactionCreated) {
+      this.confirmationStep.open();
+    }
+
+    if (changes.customerName) {
+      this.amountForm.get('payee').setValue(this.customerName);
+    }
+  }
+
+  cancel(): void {
+    this.onCancel.emit();
+  }
+
+  validateCheque(): void {
+    const chequeNumber = this.chequeForm.get('chequeNumber').value;
+    const branchSortCode = this.chequeForm.get('branchSortCode').value;
+    const accountNumber = this.chequeForm.get('accountNumber').value;
+    this.onExpandMICR.emit(toMICRIdentifier(chequeNumber, branchSortCode, accountNumber));
+  }
+
+  createTransaction(): void {
+    const productInstance: ProductInstance = this.amountForm.get('productInstance').value;
+
+    const cheque: Cheque = {
+      micr: {
+        chequeNumber: this.chequeForm.get('chequeNumber').value,
+        branchSortCode: this.chequeForm.get('branchSortCode').value,
+        accountNumber: this.chequeForm.get('accountNumber').value
+      },
+      drawee: this.amountForm.get('drawee').value,
+      drawer: this.amountForm.get('drawer').value,
+      payee: this.amountForm.get('payee').value,
+      amount: this.amountForm.get('amount').value,
+      dateIssued: toShortISOString(this.amountForm.get('dateIssued').value),
+      openCheque: this.amountForm.get('openCheque').value
+    };
+
+    const formData: TransactionForm = {
+      productIdentifier: productInstance.productIdentifier,
+      accountIdentifier: productInstance.accountIdentifier,
+      customerIdentifier: productInstance.customerIdentifier,
+      amount: this.amountForm.get('amount').value,
+      cheque
+    };
+
+    this.onCreateTransaction.emit(formData);
+  }
+
+  confirmTransaction(chargesIncluded: boolean): void {
+    this.onConfirmTransaction.emit(chargesIncluded);
+  }
+
+  cancelTransaction(): void {
+    this.onCancelTransaction.emit();
+  }
+
+  get createTransactionDisabled(): boolean {
+    return this.invalid || this.transactionCreated;
+  }
+
+  get invalid(): boolean {
+    return this.chequeForm.invalid || this.amountForm.invalid;
+  }
+
+}
diff --git a/src/app/teller/customer/transaction/components/cost.component.html b/src/app/teller/customer/transaction/components/cost.component.html
new file mode 100644
index 0000000..ed58636
--- /dev/null
+++ b/src/app/teller/customer/transaction/components/cost.component.html
@@ -0,0 +1,55 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<table td-data-table>
+  <thead>
+    <tr td-data-table-column-row>
+      <th td-data-table-column>
+        <span translate>Name</span>
+      </th>
+      <th td-data-table-column>
+        <span translate>Description</span>
+      </th>
+      <th td-data-table-column>
+        <span translate>Amount</span>
+      </th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr td-data-table-row>
+      <td td-data-table-cell translate>Transaction amount</td>
+      <td td-data-table-cell></td>
+      <td td-data-table-cell>{{transactionAmount}}</td>
+    </tr>
+    <tr td-data-table-row *ngFor="let row of transactionCosts?.charges">
+      <td td-data-table-cell>
+        {{row['name']}}
+      </td>
+      <td td-data-table-cell>
+        {{row['description'] }}
+      </td>
+      <td td-data-table-cell>
+        {{row['amount']}}
+      </td>
+    </tr>
+    <tr td-data-table-row>
+      <td td-data-table-cell translate>Total</td>
+      <td td-data-table-cell></td>
+      <td td-data-table-cell>{{transactionCosts?.totalAmount}}</td>
+    </tr>
+  </tbody>
+</table>
diff --git a/src/app/teller/customer/transaction/components/cost.component.ts b/src/app/teller/customer/transaction/components/cost.component.ts
new file mode 100644
index 0000000..32d262c
--- /dev/null
+++ b/src/app/teller/customer/transaction/components/cost.component.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 {Component, Input} from '@angular/core';
+import {TellerTransactionCosts} from '../../../../services/teller/domain/teller-transaction-costs.model';
+
+@Component({
+  templateUrl: './cost.component.html',
+  selector: 'fims-teller-transaction-cost'
+})
+export class TransactionCostComponent {
+
+  @Input('transactionAmount') transactionAmount: number;
+
+  @Input('transactionCosts') transactionCosts: TellerTransactionCosts;
+
+}
diff --git a/src/app/teller/customer/transaction/deposit/create.form.component.html b/src/app/teller/customer/transaction/deposit/create.form.component.html
new file mode 100644
index 0000000..0abfa29
--- /dev/null
+++ b/src/app/teller/customer/transaction/deposit/create.form.component.html
@@ -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.
+-->
+
+<fims-layout-card-over title="Teller transaction">
+  <fims-teller-transaction-form
+    #form
+    [productInstances]="productInstances$ | async"
+    [transactionCosts]="transactionCosts$ | async"
+    [transactionCreated]="transactionCreated"
+    [transactionType]="transactionType"
+    [cashdrawLimit]="teller.cashdrawLimit"
+    (onCreateTransaction)="createTransaction($event)"
+    (onConfirmTransaction)="confirmTransaction($event)"
+    (onCancelTransaction)="cancelTransaction()"
+    (onCancel)="cancel()">
+  </fims-teller-transaction-form>
+</fims-layout-card-over>
diff --git a/src/app/teller/customer/transaction/deposit/create.form.component.ts b/src/app/teller/customer/transaction/deposit/create.form.component.ts
new file mode 100644
index 0000000..6af2363
--- /dev/null
+++ b/src/app/teller/customer/transaction/deposit/create.form.component.ts
@@ -0,0 +1,146 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, OnDestroy, OnInit, ViewChild} from '@angular/core';
+import {TellerTransaction, TransactionType} from '../../../../services/teller/domain/teller-transaction.model';
+import {TellerTransactionCosts} from '../../../../services/teller/domain/teller-transaction-costs.model';
+import {CONFIRM_TRANSACTION} from '../../../store/teller.actions';
+import * as fromTeller from '../../../store/index';
+import {TellerStore} from '../../../store/index';
+import * as fromRoot from '../../../../store/index';
+import {DepositAccountService} from '../../../../services/depositAccount/deposit-account.service';
+import {Observable} from 'rxjs/Observable';
+import {ActivatedRoute, Router} from '@angular/router';
+import {Subscription} from 'rxjs/Subscription';
+import {DepositTransactionFormComponent} from './form.component';
+import {ProductInstance} from '../../../../services/depositAccount/domain/instance/product-instance.model';
+import {Teller} from '../../../../services/teller/domain/teller.model';
+import {TransactionForm} from '../domain/transaction-form.model';
+import {TellerTransactionService} from '../../../services/transaction.service';
+
+@Component({
+  templateUrl: './create.form.component.html'
+})
+export class CreateDepositTransactionFormComponent implements OnInit, OnDestroy {
+
+  private authenticatedTellerSubscription: Subscription;
+
+  private usernameSubscription: Subscription;
+
+  private tellerTransactionIdentifier: string;
+
+  private clerk: string;
+
+  @ViewChild('form') form: DepositTransactionFormComponent;
+
+  transactionType: TransactionType;
+
+  productInstances$: Observable<ProductInstance[]>;
+
+  transactionCosts$: Observable<TellerTransactionCosts>;
+
+  teller: Teller;
+
+  transactionCreated: boolean;
+
+  constructor(private router: Router, private route: ActivatedRoute, private store: TellerStore,
+              private depositService: DepositAccountService, private tellerTransactionService: TellerTransactionService) {
+  }
+
+  ngOnInit(): void {
+    const transactionType$ = this.route.queryParams
+      .map(params => params['transactionType'])
+      .do(transactionType => this.transactionType = transactionType);
+
+    const allProductInstances$ = this.store.select(fromTeller.getTellerSelectedCustomer)
+      .switchMap(customer => this.depositService.fetchProductInstances(customer.identifier));
+
+    this.productInstances$ = Observable.combineLatest(
+      transactionType$,
+      allProductInstances$,
+      (type, productInstances) => this.filterProductInstances(type, productInstances)
+    );
+
+    this.authenticatedTellerSubscription = this.store.select(fromTeller.getAuthenticatedTeller)
+      .filter(teller => !!teller)
+      .subscribe(teller => {
+        this.teller = teller;
+      });
+
+    this.usernameSubscription = this.store.select(fromRoot.getUsername)
+      .subscribe(username => this.clerk = username);
+  }
+
+  filterProductInstances(transactionType: string, productInstances: ProductInstance[]): ProductInstance[] {
+    // If open account only show pending accounts otherwise only active
+    const filterByState = transactionType === 'ACCO' ? 'PENDING' : 'ACTIVE';
+    return productInstances
+      .filter(instance => instance.state === filterByState);
+  }
+
+  ngOnDestroy(): void {
+    this.authenticatedTellerSubscription.unsubscribe();
+    this.usernameSubscription.unsubscribe();
+  }
+
+  createTransaction(formData: TransactionForm): void {
+    const transaction: TellerTransaction = {
+      customerIdentifier: formData.customerIdentifier,
+      productIdentifier: formData.productIdentifier,
+      customerAccountIdentifier: formData.accountIdentifier,
+      targetAccountIdentifier: formData.targetAccountIdentifier,
+      amount: formData.amount,
+      clerk: this.clerk,
+      transactionDate: new Date().toISOString(),
+      transactionType: this.transactionType
+    };
+
+    this.transactionCosts$ = this.tellerTransactionService.createTransaction(this.teller.code, transaction)
+      .do(transactionCosts => this.tellerTransactionIdentifier = transactionCosts.tellerTransactionIdentifier)
+      .do(() => this.transactionCreated = true);
+  }
+
+  confirmTransaction(chargesIncluded: boolean): void {
+    this.store.dispatch({
+      type: CONFIRM_TRANSACTION,
+      payload: {
+        tellerCode: this.teller.code,
+        tellerTransactionIdentifier: this.tellerTransactionIdentifier,
+        command: 'CONFIRM',
+        chargesIncluded,
+        activatedRoute: this.route
+      }
+    });
+  }
+
+  cancelTransaction(): void {
+    this.store.dispatch({
+      type: CONFIRM_TRANSACTION,
+      payload: {
+        tellerCode: this.teller.code,
+        tellerTransactionIdentifier: this.tellerTransactionIdentifier,
+        command: 'CANCEL',
+        activatedRoute: this.route
+      }
+    });
+  }
+
+  cancel(): void {
+    this.router.navigate(['../../'], {relativeTo: this.route});
+  }
+}
diff --git a/src/app/teller/customer/transaction/deposit/form.component.html b/src/app/teller/customer/transaction/deposit/form.component.html
new file mode 100644
index 0000000..f68893f
--- /dev/null
+++ b/src/app/teller/customer/transaction/deposit/form.component.html
@@ -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.
+-->
+
+<td-steps mode="'vertical'">
+  <td-step #transactionStep label="{{'Transaction' | translate}}"
+           [state]="form.valid ? 'complete' : form.pristine ? 'none' : 'required'" [disabled]="transactionCreated">
+    <form [formGroup]="form" layout="column">
+      <mat-form-field layout-margin>
+        <mat-select formControlName="productInstance" placeholder="{{ 'Select account' | translate }}">
+          <mat-option *ngFor="let instance of productInstances" [value]="instance">
+            {{instance.accountIdentifier}}({{instance.productIdentifier}})
+          </mat-option>
+        </mat-select>
+      </mat-form-field>
+      <fims-account-select title="{{'Target account' | translate}}" formControlName="targetAccountIdentifier" *ngIf="enableTargetAccount">
+        <ng-container *ngIf="!form.get('targetAccountIdentifier').pristine && form.get('targetAccountIdentifier').hasError('required')" translate>
+          Required
+        </ng-container>
+        <ng-container *ngIf="form.get('targetAccountIdentifier').hasError('invalidAccount')" translate>
+          Invalid account
+        </ng-container>
+      </fims-account-select>
+      <fims-text-input type="number" [form]="form" controlName="amount" placeholder="{{'Amount' | translate}}"></fims-text-input>
+      <span *ngIf="checkCashdrawLimit" class="text-md" translate>Cashdraw limit is: {{cashdrawLimit | number:numberFormat}}</span>
+      <span *ngIf="checkBalanceLimit" class="text-md" translate>Balance limit is: {{balanceLimit | number:numberFormat}}</span>
+    </form>
+    <ng-template td-step-actions>
+      <button mat-raised-button color="primary" (click)="createTransaction()" [disabled]="createTransactionDisabled">{{'CREATE TRANSACTION' | translate}}</button>
+      <span flex></span>
+      <button mat-button (click)="cancel()" [disabled]="transactionCreated">{{'CANCEL' | translate}}</button>
+    </ng-template>
+  </td-step>
+  <td-step #confirmationStep label="{{'Confirmation' | translate}}">
+    <div layout-gt-xs="row" layout-align="center center">
+      <div layout-gt-xs="row" flex-gt-xs="90" layout-margin>
+        <div flex-gt-xs="25"></div>
+        <div flex-gt-xs="50">
+          <h3 translate>Costs</h3>
+          <fims-teller-transaction-cost
+            [transactionAmount]="form.get('amount').value"
+            [transactionCosts]="transactionCosts">
+          </fims-teller-transaction-cost>
+          <div layout="row" layout-margin>
+            <mat-checkbox [(ngModel)]="chargesIncluded" [disabled]="chargesIncludedDisabled" translate>Fees are paid in cash</mat-checkbox>
+          </div>
+        </div>
+      </div>
+    </div>
+  </td-step>
+  <td-step label="{{'Final step' | translate}}" [state]="'complete'">
+    <ng-template td-step-summary>
+      <button mat-raised-button color="primary" [disabled]="!transactionCreated" (click)="confirmTransaction(chargesIncluded)">{{'CONFIRM TRANSACTION' | translate}}</button>
+      <span flex></span>
+      <button mat-button [disabled]="!transactionCreated" (click)="cancelTransaction()">{{'CANCEL TRANSACTION' | translate}}</button>
+    </ng-template>
+  </td-step>
+</td-steps>
diff --git a/src/app/teller/customer/transaction/deposit/form.component.spec.ts b/src/app/teller/customer/transaction/deposit/form.component.spec.ts
new file mode 100644
index 0000000..e77c702
--- /dev/null
+++ b/src/app/teller/customer/transaction/deposit/form.component.spec.ts
@@ -0,0 +1,217 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, TestBed} from '@angular/core/testing';
+import {FimsSharedModule} from '../../../../common/common.module';
+import {CovalentDataTableModule, CovalentStepsModule} from '@covalent/core';
+import {MatButtonModule, MatCheckboxModule, MatInputModule, MatOptionModule, MatSelectModule} from '@angular/material';
+import {Component, DebugElement, ViewChild} from '@angular/core';
+import {DepositTransactionFormComponent} from './form.component';
+import {By} from '@angular/platform-browser';
+import {ProductInstance} from '../../../../services/depositAccount/domain/instance/product-instance.model';
+import {TranslateModule} from '@ngx-translate/core';
+import {FormsModule, ReactiveFormsModule} from '@angular/forms';
+import {AccountingService} from '../../../../services/accounting/accounting.service';
+import {NoopAnimationsModule} from '@angular/platform-browser/animations';
+import {setValueByCssSelector} from '../../../../common/testing/input-fields';
+import {TransactionCostComponent} from '../components/cost.component';
+import {clickOption} from '../../../../common/testing/select-fields';
+
+describe('Test transaction form', () => {
+
+  const productInstances: ProductInstance[] = [{
+    customerIdentifier: 'test',
+    accountIdentifier: 'test',
+    productIdentifier: '',
+    balance: 500
+  }];
+
+  let fixture: ComponentFixture<TestComponent>;
+  let component: TestComponent;
+
+  beforeEach(() => {
+    TestBed.configureTestingModule({
+      imports: [
+        NoopAnimationsModule,
+        TranslateModule.forRoot(),
+        FimsSharedModule,
+        FormsModule,
+        ReactiveFormsModule,
+        MatInputModule,
+        MatButtonModule,
+        MatSelectModule,
+        MatOptionModule,
+        MatCheckboxModule,
+        CovalentStepsModule,
+        CovalentDataTableModule
+      ],
+      providers: [
+        {
+          provide: AccountingService,
+          useValue: jasmine.createSpyObj('accountingService', ['findAccount'])
+        }
+      ],
+      declarations: [
+        TransactionCostComponent,
+        DepositTransactionFormComponent,
+        TestComponent
+      ]
+    });
+
+  });
+
+  function setup(cashdrawLimit: number, transactionType: string): any {
+    fixture = TestBed.createComponent(TestComponent);
+    component = fixture.componentInstance;
+
+    component.transactionType = transactionType;
+    component.productInstances = productInstances;
+    component.cashdrawLimit = cashdrawLimit;
+
+    fixture.detectChanges();
+  }
+
+  function transactionButton(): DebugElement {
+    const element = fixture.debugElement.query(By.css('td-steps > div:nth-child(1) > td-step-body > div > ' +
+      'div.td-step-body > div > div.td-step-actions > button.mat-raised-button.mat-primary'));
+    return element;
+  }
+
+  function setAmount(value: string): void {
+    setValueByCssSelector(fixture, '#amount', value);
+  }
+
+  describe('test if create transaction is enabled', () => {
+
+    beforeEach(() => {
+      setup(1000, 'ACCC');
+
+      clickOption(fixture, 0);
+    });
+
+    it('when amount matches balance limit', () => {
+      const productInstance = component.form.form.get('productInstance').value;
+
+      expect(productInstance).toEqual(productInstances[0]);
+
+      setAmount('500');
+
+      const button: DebugElement = transactionButton();
+
+      expect(button.properties['disabled']).toBeFalsy('Button should be enabled');
+    });
+
+    it('when amount is 0 and type ACCC', () => {
+      const productInstance = component.form.form.get('productInstance').value;
+
+      setAmount('0');
+
+      expect(productInstance).toEqual(productInstances[0]);
+
+      const button: DebugElement = transactionButton();
+
+      expect(button.properties['disabled']).toBeFalsy('Button should be enabled');
+    });
+  });
+
+  describe('test if create transaction is disabled', () => {
+
+    describe('and type is ACCC', () => {
+      beforeEach(() => {
+        setup(1000, 'ACCC');
+
+        clickOption(fixture, 0);
+      });
+
+      it('when amount exeeds balance', () => {
+        const productInstance = component.form.form.get('productInstance').value;
+
+        expect(productInstance).toEqual(productInstances[0]);
+
+        setAmount('501');
+
+        const button: DebugElement = transactionButton();
+
+        expect(button.properties['disabled']).toBeTruthy('Button should be disabled');
+      });
+
+      it('when amount exeeds withdrawal limit', () => {
+        const productInstance = component.form.form.get('productInstance').value;
+
+        expect(productInstance).toEqual(productInstances[0]);
+
+        setAmount('1001');
+
+        const button: DebugElement = transactionButton();
+
+        expect(button.properties['disabled']).toBeTruthy('Button should be disabled');
+      });
+
+      it('when no amount is given', () => {
+        const productInstance = component.form.form.get('productInstance').value;
+
+        expect(productInstance).toEqual(productInstances[0]);
+
+        const button: DebugElement = transactionButton();
+
+        expect(button.properties['disabled']).toBeTruthy('Button should be disabled');
+      });
+    });
+
+
+    describe('and type is not ACCC', () => {
+      beforeEach(() => {
+        setup(1000, 'ACCO');
+
+        clickOption(fixture, 0);
+      });
+
+      it('when amount is 0', () => {
+        const productInstance = component.form.form.get('productInstance').value;
+
+        setAmount('0');
+
+        expect(productInstance).toEqual(productInstances[0]);
+
+        const button: DebugElement = transactionButton();
+
+        expect(button.properties['disabled']).toBeTruthy('Button should be disabled');
+      });
+    });
+
+  });
+});
+
+@Component({
+  template: `<fims-teller-transaction-form #form
+                                           [productInstances]="productInstances"
+                                           [cashdrawLimit]="cashdrawLimit"
+                                           [transactionType]="transactionType">
+            </fims-teller-transaction-form>`
+})
+class TestComponent {
+
+  productInstances: ProductInstance[];
+
+  cashdrawLimit = 1000;
+
+  transactionType = 'ACCC';
+
+  @ViewChild('form') form: DepositTransactionFormComponent;
+
+}
diff --git a/src/app/teller/customer/transaction/deposit/form.component.ts b/src/app/teller/customer/transaction/deposit/form.component.ts
new file mode 100644
index 0000000..11f7075
--- /dev/null
+++ b/src/app/teller/customer/transaction/deposit/form.component.ts
@@ -0,0 +1,195 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, OnInit, Output, ViewChild} from '@angular/core';
+import {TdStepComponent} from '@covalent/core';
+import {FormBuilder, FormControl, FormGroup, ValidatorFn, Validators} from '@angular/forms';
+import {FimsValidators} from '../../../../common/validator/validators';
+import {TellerTransactionCosts} from '../../../../services/teller/domain/teller-transaction-costs.model';
+import {ProductInstance} from '../../../../services/depositAccount/domain/instance/product-instance.model';
+import {accountExists} from '../../../../common/validator/account-exists.validator';
+import {AccountingService} from '../../../../services/accounting/accounting.service';
+import {TransactionType} from '../../../../services/teller/domain/teller-transaction.model';
+import {TransactionForm} from '../domain/transaction-form.model';
+
+// List of types to check withdrawal limit
+const withdrawalCheckTypes: TransactionType[] = ['ACCC', 'CWDL'];
+
+// List of types to check balance limit
+const balanceCheckTypes: TransactionType[] = ['ACCT', 'ACCC', 'CWDL'];
+
+@Component({
+  selector: 'fims-teller-transaction-form',
+  templateUrl: './form.component.html'
+})
+export class DepositTransactionFormComponent implements OnInit {
+
+  form: FormGroup;
+
+  private _transactionCreated: boolean;
+
+  private _transactionType: TransactionType;
+
+  chargesIncluded = true;
+
+  chargesIncludedDisabled = false;
+
+  enableTargetAccount: boolean;
+
+  numberFormat = '1.2-2';
+
+  checkCashdrawLimit: boolean;
+
+  checkBalanceLimit: boolean;
+
+  balanceLimit: number;
+
+  @ViewChild('transactionStep') transactionStep: TdStepComponent;
+
+  @ViewChild('confirmationStep') confirmationStep: TdStepComponent;
+
+  @Input('productInstances') productInstances: ProductInstance[];
+
+  @Input('transactionCosts') transactionCosts: TellerTransactionCosts;
+
+  @Input('transactionCreated')
+  set transactionCreated(transactionCreated: boolean) {
+    this._transactionCreated = transactionCreated;
+    if (transactionCreated) {
+      this.confirmationStep.open();
+    }
+  };
+
+  @Input('error') error: string;
+
+  @Input('transactionType')
+  set transactionType(transactionType: TransactionType) {
+    this._transactionType = transactionType;
+
+    if (transactionType === 'ACCT') {
+      this.enableTargetAccount = true;
+    }
+
+    if (transactionType === 'ACCO') {
+      this.chargesIncludedDisabled = true;
+    }
+
+    this.checkCashdrawLimit = this.hasType(withdrawalCheckTypes, transactionType);
+    this.checkBalanceLimit = this.hasType(balanceCheckTypes, transactionType);
+  }
+
+  @Input('cashdrawLimit') cashdrawLimit: number;
+
+  @Output('onCreateTransaction') onCreateTransaction = new EventEmitter<TransactionForm>();
+
+  @Output('onConfirmTransaction') onConfirmTransaction = new EventEmitter<boolean>();
+
+  @Output('onCancelTransaction') onCancelTransaction = new EventEmitter<void>();
+
+  @Output('onCancel') onCancel = new EventEmitter<void>();
+
+  constructor(private formBuilder: FormBuilder, private accountingService: AccountingService) {
+  }
+
+  ngOnInit(): void {
+    this.form = this.formBuilder.group({
+      productInstance: ['', Validators.required],
+      amount: ['']
+    });
+
+    if (this.enableTargetAccount) {
+      this.form.addControl('targetAccountIdentifier', new FormControl('', [Validators.required], accountExists(this.accountingService)));
+    }
+
+    this.form.get('productInstance').valueChanges
+      .subscribe(productInstance => this.toggleProductInstance(productInstance));
+
+    this.transactionStep.open();
+  }
+
+  private toggleProductInstance(productInstance: ProductInstance): void {
+    const amountValidators: ValidatorFn[] = [Validators.required];
+
+    const valueValidator: ValidatorFn = this._transactionType === 'ACCC' ? FimsValidators.minValue(0) : FimsValidators.greaterThanValue(0);
+    amountValidators.push(valueValidator);
+
+    this.balanceLimit = productInstance.balance;
+
+    const maxValue = this.getAmountMaxValue(productInstance);
+
+    if (maxValue !== undefined) {
+      amountValidators.push(FimsValidators.maxValue(maxValue));
+    }
+
+    const amountControl = this.form.get('amount') as FormControl;
+
+    amountControl.setValidators(amountValidators);
+
+    amountControl.updateValueAndValidity();
+  }
+
+  private getAmountMaxValue(productInstance: ProductInstance): number {
+    if (this.checkBalanceLimit && this.checkCashdrawLimit) {
+      return Math.min(this.cashdrawLimit, productInstance.balance);
+    }
+
+    if (this.checkBalanceLimit && !this.checkCashdrawLimit) {
+      return productInstance.balance;
+    }
+  }
+
+  private hasType(types: TransactionType[], type: TransactionType): boolean {
+    return types.indexOf(type) > -1;
+  }
+
+  cancel(): void {
+    this.onCancel.emit();
+  }
+
+  createTransaction(): void {
+    const productInstance: ProductInstance = this.form.get('productInstance').value;
+    const targetAccountIdentifierControl: FormControl = this.form.get('targetAccountIdentifier') as FormControl;
+
+    const formData: TransactionForm = {
+      productIdentifier: productInstance.productIdentifier,
+      accountIdentifier: productInstance.accountIdentifier,
+      customerIdentifier: productInstance.customerIdentifier,
+      targetAccountIdentifier: targetAccountIdentifierControl ? targetAccountIdentifierControl.value : undefined,
+      amount: this.form.get('amount').value
+    };
+
+    this.onCreateTransaction.emit(formData);
+  }
+
+  confirmTransaction(chargesIncluded: boolean): void {
+    this.onConfirmTransaction.emit(chargesIncluded);
+  }
+
+  cancelTransaction(): void {
+    this.onCancelTransaction.emit();
+  }
+
+  get transactionCreated(): boolean {
+    return this._transactionCreated;
+  }
+
+  get createTransactionDisabled(): boolean {
+    return this.form.invalid || this.transactionCreated;
+  }
+
+}
diff --git a/src/app/teller/customer/transaction/domain/transaction-form.model.ts b/src/app/teller/customer/transaction/domain/transaction-form.model.ts
new file mode 100644
index 0000000..deffc7b
--- /dev/null
+++ b/src/app/teller/customer/transaction/domain/transaction-form.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 {Cheque} from '../../../../services/teller/domain/cheque.model';
+
+export interface TransactionForm {
+  customerIdentifier: string;
+  productIdentifier: string;
+  productCaseIdentifier?: string;
+  accountIdentifier: string;
+  targetAccountIdentifier?: string;
+  amount: number;
+  cheque?: Cheque;
+}
diff --git a/src/app/teller/customer/transaction/loan/create.form.component.html b/src/app/teller/customer/transaction/loan/create.form.component.html
new file mode 100644
index 0000000..f6bb58f
--- /dev/null
+++ b/src/app/teller/customer/transaction/loan/create.form.component.html
@@ -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.
+-->
+
+<fims-layout-card-over title="{{'Teller transaction' | translate}}">
+  <fims-loan-transaction-form
+    [transactionCreated]="transactionCreated"
+    [caseInstances]="caseInstances$ | async"
+    [transactionCosts]="transactionCosts$ | async"
+    [paymentHint]="paymentHint"
+    (onCreateTransaction)="createTransaction($event)"
+    (onConfirmTransaction)="confirmTransaction($event)"
+    (onCancelTransaction)="cancelTransaction()"
+    (onCaseSelected)="caseSelected($event)"
+    (onCancel)="cancel()">
+  </fims-loan-transaction-form>
+</fims-layout-card-over>
diff --git a/src/app/teller/customer/transaction/loan/create.form.component.ts b/src/app/teller/customer/transaction/loan/create.form.component.ts
new file mode 100644
index 0000000..a1c6886
--- /dev/null
+++ b/src/app/teller/customer/transaction/loan/create.form.component.ts
@@ -0,0 +1,140 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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, OnDestroy, OnInit, ViewChild} from '@angular/core';
+import {TellerTransaction, TransactionType} from '../../../../services/teller/domain/teller-transaction.model';
+import {TellerTransactionCosts} from '../../../../services/teller/domain/teller-transaction-costs.model';
+import {CONFIRM_TRANSACTION} from '../../../store/teller.actions';
+import * as fromTeller from '../../../store/index';
+import {TellerStore} from '../../../store/index';
+import * as fromRoot from '../../../../store/index';
+import {Observable} from 'rxjs/Observable';
+import {ActivatedRoute, Router} from '@angular/router';
+import {Subscription} from 'rxjs/Subscription';
+import {DepositTransactionFormComponent} from '../deposit/form.component';
+import {Teller} from '../../../../services/teller/domain/teller.model';
+import {PortfolioService} from '../../../../services/portfolio/portfolio.service';
+import {TransactionForm} from '../domain/transaction-form.model';
+import {FimsCase} from '../../../../services/portfolio/domain/fims-case.model';
+import {TellerTransactionService} from '../../../services/transaction.service';
+
+@Component({
+  templateUrl: './create.form.component.html'
+})
+export class CreateLoanTransactionFormComponent implements OnInit, OnDestroy {
+
+  private authenticatedTellerSubscription: Subscription;
+
+  private usernameSubscription: Subscription;
+
+  private tellerTransactionIdentifier: string;
+
+  private clerk: string;
+
+  private transactionType: TransactionType;
+
+  @ViewChild('form') form: DepositTransactionFormComponent;
+
+  caseInstances$: Observable<FimsCase[]>;
+
+  transactionCosts$: Observable<TellerTransactionCosts>;
+
+  paymentHint: string;
+
+  teller: Teller;
+
+  transactionCreated: boolean;
+
+  constructor(private router: Router, private route: ActivatedRoute, private store: TellerStore,
+              private portfolioService: PortfolioService, private tellerTransactionService: TellerTransactionService) {
+  }
+
+  ngOnInit(): void {
+    this.route.queryParams.subscribe(params => this.transactionType = params['transactionType']);
+
+    this.caseInstances$ = this.store.select(fromTeller.getTellerSelectedCustomer)
+      .switchMap(customer => this.portfolioService.getAllCasesForCustomer(customer.identifier))
+      .map(casePage => casePage.elements.filter(element => element.currentState === 'ACTIVE'));
+
+    this.authenticatedTellerSubscription = this.store.select(fromTeller.getAuthenticatedTeller)
+      .filter(teller => !!teller)
+      .subscribe(teller => {
+        this.teller = teller;
+      });
+
+    this.usernameSubscription = this.store.select(fromRoot.getUsername)
+      .subscribe(username => this.clerk = username);
+  }
+
+  ngOnDestroy(): void {
+    this.authenticatedTellerSubscription.unsubscribe();
+    this.usernameSubscription.unsubscribe();
+  }
+
+  createTransaction(formData: TransactionForm): void {
+    const transaction: TellerTransaction = {
+      customerIdentifier: formData.customerIdentifier,
+      productIdentifier: formData.productIdentifier,
+      productCaseIdentifier: formData.productCaseIdentifier,
+      customerAccountIdentifier: formData.accountIdentifier,
+      amount: formData.amount,
+      clerk: this.clerk,
+      transactionDate: new Date().toISOString(),
+      transactionType: this.transactionType
+    };
+
+    this.transactionCosts$ = this.tellerTransactionService.createTransaction(this.teller.code, transaction)
+      .do(transactionCosts => this.tellerTransactionIdentifier = transactionCosts.tellerTransactionIdentifier)
+      .do(() => this.transactionCreated = true);
+  }
+
+  confirmTransaction(chargesIncluded: boolean): void {
+    this.store.dispatch({
+      type: CONFIRM_TRANSACTION,
+      payload: {
+        tellerCode: this.teller.code,
+        tellerTransactionIdentifier: this.tellerTransactionIdentifier,
+        command: 'CONFIRM',
+        chargesIncluded,
+        activatedRoute: this.route
+      }
+    });
+  }
+
+  cancelTransaction(): void {
+    this.store.dispatch({
+      type: CONFIRM_TRANSACTION,
+      payload: {
+        tellerCode: this.teller.code,
+        tellerTransactionIdentifier: this.tellerTransactionIdentifier,
+        command: 'CANCEL',
+        activatedRoute: this.route
+      }
+    });
+  }
+
+  caseSelected(caseInstance: FimsCase): void {
+    this.portfolioService.getCostComponentsForAction(caseInstance.productIdentifier, caseInstance.identifier, 'ACCEPT_PAYMENT')
+      .map(payment => payment.balanceAdjustments.ey * -1)
+      .subscribe(paymentHint => this.paymentHint = paymentHint.toString());
+  }
+
+  cancel(): void {
+    this.router.navigate(['../../'], {relativeTo: this.route});
+  }
+}
diff --git a/src/app/teller/customer/transaction/loan/form.component.html b/src/app/teller/customer/transaction/loan/form.component.html
new file mode 100644
index 0000000..c0f0e2e
--- /dev/null
+++ b/src/app/teller/customer/transaction/loan/form.component.html
@@ -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.
+-->
+
+<td-steps mode="'vertical'">
+  <td-step #transactionStep label="{{'Transaction' | translate}}"
+           [state]="form.valid ? 'complete' : form.pristine ? 'none' : 'required'" [disabled]="transactionCreated">
+    <form [formGroup]="form" layout="column">
+      <mat-form-field layout-margin>
+        <mat-select formControlName="caseInstance" placeholder="{{ 'Select account' | translate }}">
+          <mat-option *ngFor="let instance of caseInstances" [value]="instance">
+            {{instance.customerLoanAccountIdentifier}}({{instance.productIdentifier}})
+          </mat-option>
+        </mat-select>
+      </mat-form-field>
+      <fims-text-input type="number" [form]="form" controlName="amount" placeholder="{{'Repayment amount' | translate}}"></fims-text-input>
+      <p>Expected payment: {{paymentHint}}</p>
+    </form>
+    <ng-template td-step-actions>
+      <button mat-raised-button color="primary" (click)="createTransaction()" [disabled]="createTransactionDisabled">{{'CREATE TRANSACTION' | translate}}</button>
+      <span flex></span>
+      <button mat-button (click)="cancel()" [disabled]="transactionCreated">{{'CANCEL' | translate}}</button>
+    </ng-template>
+  </td-step>
+  <td-step #confirmationStep label="{{'Confirmation' | translate}}">
+    <div layout-gt-xs="row" layout-align="center center">
+      <div layout-gt-xs="row" flex-gt-xs="90" layout-margin>
+        <div flex-gt-xs="25"></div>
+        <div flex-gt-xs="50">
+          <h3 translate>Costs</h3>
+          <fims-teller-transaction-cost
+            [transactionAmount]="form.get('amount').value"
+            [transactionCosts]="transactionCosts">
+          </fims-teller-transaction-cost>
+          <div layout="row" layout-margin>
+            <mat-checkbox [(ngModel)]="chargesIncluded" [disabled]="true" translate>Fees are paid in cash</mat-checkbox>
+          </div>
+        </div>
+      </div>
+    </div>
+  </td-step>
+  <td-step label="{{'Final step' | translate}}" [state]="'complete'">
+    <ng-template td-step-summary>
+      <button mat-raised-button color="primary" [disabled]="!transactionCreated" (click)="confirmTransaction(chargesIncluded)">{{'CONFIRM TRANSACTION' | translate}}</button>
+      <span flex></span>
+      <button mat-button [disabled]="!transactionCreated" (click)="cancelTransaction()">{{'CANCEL TRANSACTION' | translate}}</button>
+    </ng-template>
+  </td-step>
+</td-steps>
diff --git a/src/app/teller/customer/transaction/loan/form.component.ts b/src/app/teller/customer/transaction/loan/form.component.ts
new file mode 100644
index 0000000..1418585
--- /dev/null
+++ b/src/app/teller/customer/transaction/loan/form.component.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 {Component, EventEmitter, Input, OnInit, Output, ViewChild} from '@angular/core';
+import {TransactionForm} from '../domain/transaction-form.model';
+import {FormBuilder, FormGroup, Validators} from '@angular/forms';
+import {FimsValidators} from '../../../../common/validator/validators';
+import {FimsCase} from '../../../../services/portfolio/domain/fims-case.model';
+import {TellerTransactionCosts} from '../../../../services/teller/domain/teller-transaction-costs.model';
+import {TdStepComponent} from '@covalent/core';
+
+@Component({
+  selector: 'fims-loan-transaction-form',
+  templateUrl: './form.component.html'
+})
+export class LoanTransactionFormComponent implements OnInit {
+
+  private _transactionCreated: boolean;
+
+  chargesIncluded = true;
+
+  form: FormGroup;
+
+  @Input('caseInstances') caseInstances: FimsCase[];
+  @Input('transactionCosts') transactionCosts: TellerTransactionCosts;
+  @Input('transactionCreated') set transactionCreated(transactionCreated: boolean) {
+    this._transactionCreated = transactionCreated;
+    if (transactionCreated) {
+      this.confirmationStep.open();
+    }
+  };
+
+  @Input('paymentHint') paymentHint: string;
+
+  @Output('onCreateTransaction') onCreateTransaction = new EventEmitter<TransactionForm>();
+  @Output('onConfirmTransaction') onConfirmTransaction = new EventEmitter<boolean>();
+  @Output('onCancelTransaction') onCancelTransaction = new EventEmitter<void>();
+  @Output('onCaseSelected') onCaseSelected = new EventEmitter<FimsCase>();
+  @Output('onCancel') onCancel = new EventEmitter<void>();
+
+  @ViewChild('transactionStep') transactionStep: TdStepComponent;
+  @ViewChild('confirmationStep') confirmationStep: TdStepComponent;
+
+  constructor(private formBuilder: FormBuilder) {}
+
+  ngOnInit(): void {
+    this.form = this.formBuilder.group({
+      caseInstance: ['', Validators.required],
+      amount: ['', [Validators.required, FimsValidators.greaterThanValue(0)]],
+    });
+
+    this.form.get('caseInstance').valueChanges
+      .subscribe(caseInstance => this.onCaseSelected.emit(caseInstance));
+
+    this.transactionStep.open();
+  }
+
+  createTransaction(): void {
+    const fimsCase: FimsCase = this.form.get('caseInstance').value;
+
+    const formData: TransactionForm = {
+      productIdentifier: fimsCase.productIdentifier,
+      productCaseIdentifier: fimsCase.identifier,
+      accountIdentifier: fimsCase.customerLoanAccountIdentifier,
+      customerIdentifier: fimsCase.parameters.customerIdentifier,
+      amount: this.form.get('amount').value
+    };
+
+    this.onCreateTransaction.emit(formData);
+  }
+
+  confirmTransaction(chargesIncluded: boolean): void {
+    this.onConfirmTransaction.emit(chargesIncluded);
+  }
+
+  cancelTransaction(): void {
+    this.onCancelTransaction.emit();
+  }
+
+  cancel(): void {
+    this.onCancel.emit();
+  }
+
+  get transactionCreated(): boolean {
+    return this._transactionCreated;
+  }
+
+  get createTransactionDisabled(): boolean {
+    return this.form.invalid || this.transactionCreated;
+  }
+}
diff --git a/src/app/teller/services/available-actions.service.spec.ts b/src/app/teller/services/available-actions.service.spec.ts
new file mode 100644
index 0000000..ad86e0b
--- /dev/null
+++ b/src/app/teller/services/available-actions.service.spec.ts
@@ -0,0 +1,178 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 {AvailableActionService} from './available-actions.service';
+import {DepositAccountService} from '../../services/depositAccount/deposit-account.service';
+import {Observable} from 'rxjs/Observable';
+import {PortfolioService} from '../../services/portfolio/portfolio.service';
+import {fakeAsync, TestBed, tick} from '@angular/core/testing';
+import {AvailableTransactionType} from '../../services/depositAccount/domain/instance/available-transaction-type.model';
+import {FimsCasePage} from '../../services/portfolio/domain/fims-case-page.model';
+import {FimsCase} from '../../services/portfolio/domain/fims-case.model';
+import {CaseState} from '../../services/portfolio/domain/case-state.model';
+
+describe('AvailableActionService', () => {
+
+  beforeEach(() => {
+    TestBed.configureTestingModule({
+      providers: [
+        AvailableActionService,
+        {
+          provide: DepositAccountService,
+          useValue: jasmine.createSpyObj('depositService', ['fetchPossibleTransactionTypes'])
+        },
+        {
+          provide: PortfolioService,
+          useValue: jasmine.createSpyObj('portfolioService', ['getAllCasesForCustomer'])
+        }
+      ]
+    });
+  });
+
+  function setupDeposit(transactionTypes: AvailableTransactionType[]) {
+    const depositService = TestBed.get(DepositAccountService);
+    depositService.fetchPossibleTransactionTypes.and.returnValue(Observable.of(transactionTypes));
+  }
+
+  function setupPortfolio(cases: FimsCasePage) {
+    const portfolioService = TestBed.get(PortfolioService);
+    portfolioService.getAllCasesForCustomer.and.returnValue(Observable.of(cases));
+  }
+
+  function mockCase(currentState: CaseState): FimsCase {
+    return {
+      identifier: 'test',
+      productIdentifier: 'test',
+      currentState,
+      interest: 1,
+      parameters: null,
+      depositAccountIdentifier: 'test'
+    };
+  }
+
+  it('should merge deposit, loan actions', fakeAsync(() => {
+    setupDeposit([
+      { transactionType: 'ACCC' }
+    ]);
+
+    setupPortfolio({
+      elements: [mockCase('ACTIVE')],
+      totalElements: 1,
+      totalPages: 1
+    });
+
+    const actionService = TestBed.get(AvailableActionService);
+
+    let result = null;
+
+    actionService.getAvailableActions('test').subscribe(_result => result = _result);
+
+    tick();
+
+    // 1 deposit, 1 case
+    expect(result.length).toBe(2);
+  }));
+
+  it('output deposit actions when deposit actions found', fakeAsync(() => {
+    setupDeposit([
+      { transactionType: 'ACCC' },
+      { transactionType: 'CWDL' }
+    ]);
+
+    const actionService = TestBed.get(AvailableActionService);
+
+    let result = null;
+
+    actionService.getAvailableDepositActions('test').subscribe(_result => result = _result);
+
+    tick();
+
+    expect(result.length).toBe(2);
+  }));
+
+  it('not output any deposit actions when no deposit actions found', fakeAsync(() => {
+    setupDeposit([]);
+
+    const actionService = TestBed.get(AvailableActionService);
+
+    let result = null;
+
+    actionService.getAvailableDepositActions('test').subscribe(_result => result = _result);
+
+    tick();
+
+    expect(result).toEqual([]);
+  }));
+
+  it('should output actions when active cases found', fakeAsync(() => {
+    setupPortfolio({
+      elements: [mockCase('ACTIVE')],
+      totalElements: 1,
+      totalPages: 1
+    });
+
+    const actionService = TestBed.get(AvailableActionService);
+
+    let result = null;
+
+    actionService.getAvailableLoanActions('test').subscribe(_result => result = _result);
+
+    tick();
+
+    expect(result.length).toEqual(1);
+  }));
+
+  describe('should not output any loan actions', () => {
+    it('when no cases found', fakeAsync(() => {
+      setupPortfolio({
+        elements: [],
+        totalElements: 0,
+        totalPages: 0
+      });
+
+      const actionService = TestBed.get(AvailableActionService);
+
+      let result = null;
+
+      actionService.getAvailableLoanActions('test').subscribe(_result => result = _result);
+
+      tick();
+
+      expect(result).toEqual([]);
+    }));
+
+    it('when no active cases found', fakeAsync(() => {
+      setupPortfolio({
+        elements: [mockCase('PENDING')],
+        totalElements: 1,
+        totalPages: 1
+      });
+
+      const actionService = TestBed.get(AvailableActionService);
+
+      let result = null;
+
+      actionService.getAvailableLoanActions('test').subscribe(_result => result = _result);
+
+      tick();
+
+      expect(result).toEqual([]);
+    }));
+  });
+
+});
diff --git a/src/app/teller/services/available-actions.service.ts b/src/app/teller/services/available-actions.service.ts
new file mode 100644
index 0000000..087e1ef
--- /dev/null
+++ b/src/app/teller/services/available-actions.service.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 {Injectable} from '@angular/core';
+import {DepositAccountService} from '../../services/depositAccount/deposit-account.service';
+import {Observable} from 'rxjs/Observable';
+import {TransactionType} from '../../services/teller/domain/teller-transaction.model';
+import {PortfolioService} from '../../services/portfolio/portfolio.service';
+import {FetchRequest} from '../../services/domain/paging/fetch-request.model';
+import {FimsCase} from '../../services/portfolio/domain/fims-case.model';
+
+export interface Action {
+  transactionType: TransactionType;
+  icon: string;
+  title: string;
+  relativeLink: string;
+}
+
+const depositActions: Action[] = [
+  { transactionType: 'ACCO', icon: 'create', title: 'Open account', relativeLink: 'transaction/deposit' },
+  { transactionType: 'ACCC', icon: 'close', title: 'Close account', relativeLink: 'transaction/deposit' },
+  { transactionType: 'ACCT', icon: 'swap_horiz', title: 'Account transfer', relativeLink: 'transaction/deposit'},
+  { transactionType: 'CDPT', icon: 'arrow_forward', title: 'Cash deposit', relativeLink: 'transaction/deposit'},
+  { transactionType: 'CWDL', icon: 'arrow_back', title: 'Cash withdrawal', relativeLink: 'transaction/deposit'},
+  { transactionType: 'CCHQ', icon: 'import_contacts', title: 'Cash cheque', relativeLink: 'transaction/cheque'}
+];
+
+const loanActions: Action[] = [
+  { transactionType: 'PPAY', icon: 'arrow_forward', title: 'Repay loan', relativeLink: 'transaction/loan' }
+];
+
+@Injectable()
+export class AvailableActionService {
+
+  constructor(private depositService: DepositAccountService, private portfolioService: PortfolioService) {}
+
+  getAvailableActions(customerIdentifier: string): Observable<Action[]> {
+    const depositActions$ = this.getAvailableDepositActions(customerIdentifier);
+    const loanActions$ = this.getAvailableLoanActions(customerIdentifier);
+    return Observable.combineLatest(
+      depositActions$,
+      loanActions$,
+      (availableDepositActions, availableLoanActions) =>
+        availableDepositActions.concat(availableLoanActions)
+    );
+  }
+
+  getAvailableDepositActions(customerIdentifier: string): Observable<Action[]> {
+    return this.depositService.fetchPossibleTransactionTypes(customerIdentifier)
+      .map(types => depositActions.filter(action =>
+        !!types.find(type => action.transactionType === type.transactionType)
+      ));
+  }
+
+  getAvailableLoanActions(customerIdentifier: string): Observable<Action[]> {
+    return this.hasActivateLoans(customerIdentifier)
+      .map(hasLoanProducts => hasLoanProducts ? loanActions : []);
+  }
+
+  private hasActivateLoans(customerIdentifier: string): Observable<boolean> {
+    const fetchRequest: FetchRequest = {
+      page: {
+        pageIndex: 0,
+        size: 100
+      }
+    };
+    return this.portfolioService.getAllCasesForCustomer(customerIdentifier, fetchRequest)
+      .map(page => page.totalElements > 0 && this.hasActiveLoan(page.elements));
+  }
+
+  private hasActiveLoan(cases: FimsCase[]): boolean {
+    return !!cases.find(element => element.currentState === 'ACTIVE');
+  }
+
+}
diff --git a/src/app/teller/services/transaction.service.ts b/src/app/teller/services/transaction.service.ts
new file mode 100644
index 0000000..aaafe39
--- /dev/null
+++ b/src/app/teller/services/transaction.service.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 {Injectable} from '@angular/core';
+import {NotificationService, NotificationType} from '../../services/notification/notification.service';
+import {TellerService} from '../../services/teller/teller-service';
+import {TellerTransaction} from '../../services/teller/domain/teller-transaction.model';
+import {Observable} from 'rxjs/Observable';
+import {TellerTransactionCosts} from '../../services/teller/domain/teller-transaction-costs.model';
+
+@Injectable()
+export class TellerTransactionService {
+
+  constructor(private tellerService: TellerService, private notificationService: NotificationService) {
+  }
+
+  createTransaction(tellerCode: string, tellerTransaction: TellerTransaction): Observable<TellerTransactionCosts> {
+    return this.tellerService.createTransaction(tellerCode, tellerTransaction)
+      .catch((error: Error) => {
+        this.notificationService.send({
+          type: NotificationType.ALERT,
+          title: 'Invalid transaction',
+          message: error.message
+        });
+        return Observable.empty();
+      });
+  }
+}
diff --git a/src/app/teller/store/authentication.reducer.ts b/src/app/teller/store/authentication.reducer.ts
new file mode 100644
index 0000000..67ebdc3
--- /dev/null
+++ b/src/app/teller/store/authentication.reducer.ts
@@ -0,0 +1,78 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 teller from './teller.actions';
+import {Teller} from '../../services/teller/domain/teller.model';
+
+export interface State {
+  teller: Teller;
+  authenticated: boolean;
+  loading: boolean;
+  error: Error;
+}
+
+const initialState: State = {
+  teller: null,
+  authenticated: false,
+  loading: false,
+  error: null
+};
+
+export function reducer(state = initialState, action: teller.Actions): State {
+  switch (action.type) {
+
+    case teller.UNLOCK_DRAWER: {
+      return Object.assign({}, state, {
+        loading: true,
+        authenticated: false
+      });
+    }
+
+    case teller.UNLOCK_DRAWER_SUCCESS: {
+      const teller: Teller = action.payload;
+      return Object.assign({}, state, {
+        loading: false,
+        teller,
+        authenticated: true
+      });
+    }
+
+    case teller.UNLOCK_DRAWER_FAIL: {
+      const error = action.payload;
+      return Object.assign({}, state, {
+        loading: false,
+        error
+      });
+    }
+
+    case teller.LOCK_DRAWER_SUCCESS: {
+      return initialState;
+    }
+
+    default:
+      return state;
+  }
+}
+
+export const getAuthenticated = (state: State) => state.authenticated;
+
+export const getTeller = (state: State) => state.teller;
+
+export const getError = (state: State) => state.error;
+
+export const getLoading = (state: State) => state.loading;
diff --git a/src/app/teller/store/customer-deposit-products.reducer.ts b/src/app/teller/store/customer-deposit-products.reducer.ts
new file mode 100644
index 0000000..5e7b37d
--- /dev/null
+++ b/src/app/teller/store/customer-deposit-products.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 * as tellers from './teller.actions';
+import {ResourceState} from '../../common/store/resource.reducer';
+import {ProductInstance} from '../../services/depositAccount/domain/instance/product-instance.model';
+import {idsToHashWithCurrentTimestamp, resourcesToHash} from '../../common/store/reducer.helper';
+
+export const initialState: ResourceState = {
+  ids: [],
+  entities: {},
+  loadedAt: {},
+  selectedId: null,
+};
+
+export function reducer(state = initialState, action: tellers.Actions): ResourceState {
+
+  switch (action.type) {
+
+    case tellers.LOAD_ALL_DEPOSIT_PRODUCTS: {
+      return initialState;
+    }
+
+    case tellers.LOAD_ALL_DEPOSIT_PRODUCTS_SUCCESS: {
+      const depositProducts: ProductInstance[] = action.payload;
+
+      const ids = depositProducts.map(depositProduct => depositProduct.accountIdentifier);
+
+      const entities = resourcesToHash(depositProducts, 'accountIdentifier');
+
+      const loadedAt = idsToHashWithCurrentTimestamp(ids);
+
+      return {
+        ids: [ ...ids ],
+        entities: entities,
+        loadedAt: loadedAt,
+        selectedId: state.selectedId
+      };
+    }
+
+    default: {
+      return state;
+    }
+  }
+}
diff --git a/src/app/teller/store/customer-loan-products.reducer.ts b/src/app/teller/store/customer-loan-products.reducer.ts
new file mode 100644
index 0000000..58ff570
--- /dev/null
+++ b/src/app/teller/store/customer-loan-products.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 * as tellers from './teller.actions';
+import {ResourceState} from '../../common/store/resource.reducer';
+import {idsToHashWithCurrentTimestamp, resourcesToHash} from '../../common/store/reducer.helper';
+import {FimsCase} from '../../services/portfolio/domain/fims-case.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_ALL_LOAN_PRODUCTS: {
+      return initialState;
+    }
+
+    case tellers.LOAD_ALL_LOAN_PRODUCTS_SUCCESS: {
+      const caseInstances: FimsCase[] = action.payload;
+
+      const ids = caseInstances.map(caseInstance => caseInstance.identifier);
+
+      const entities = resourcesToHash(caseInstances);
+
+      const loadedAt = idsToHashWithCurrentTimestamp(ids);
+
+      return {
+        ids: [ ...ids ],
+        entities: entities,
+        loadedAt: loadedAt,
+        selectedId: state.selectedId
+      };
+    }
+
+    default: {
+      return state;
+    }
+  }
+}
diff --git a/src/app/teller/store/effects/notification.effects.ts b/src/app/teller/store/effects/notification.effects.ts
new file mode 100644
index 0000000..3b8d557
--- /dev/null
+++ b/src/app/teller/store/effects/notification.effects.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 {Injectable} from '@angular/core';
+import {Actions, Effect} from '@ngrx/effects';
+import {NotificationService, NotificationType} from '../../../services/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 })
+  unlockDrawerSuccess$: Observable<Action> = this.actions$
+    .ofType(tellerActions.UNLOCK_DRAWER_SUCCESS)
+    .do(() => this.notificationService.send({
+      type: NotificationType.MESSAGE,
+      message: 'Teller drawer unlocked'
+    }));
+
+  @Effect({ dispatch: false })
+  lockDrawerSuccess$: Observable<Action> = this.actions$
+    .ofType(tellerActions.LOCK_DRAWER_SUCCESS)
+    .do(() => this.notificationService.send({
+      type: NotificationType.MESSAGE,
+      message: 'Teller drawer is now locked'
+    }));
+
+  @Effect({ dispatch: false })
+  confirmTransactionSuccess: Observable<Action> = this.actions$
+    .ofType(tellerActions.CONFIRM_TRANSACTION_SUCCESS)
+    .map(action => action.payload)
+    .do((payload) => {
+      const action: string = payload.command === 'CONFIRM' ? 'confirmed' : 'canceled';
+      this.notificationService.send({
+        type: NotificationType.MESSAGE,
+        message: `Transaction successfully ${action}`
+      });
+    });
+
+  @Effect({ dispatch: false })
+  confirmTransactionFail: Observable<Action> = this.actions$
+    .ofType(tellerActions.CONFIRM_TRANSACTION_FAIL)
+    .map(action => action.payload)
+    .do((error) => {
+      this.notificationService.send({
+        title: 'Invalid transaction',
+        type: NotificationType.ALERT,
+        message: error.message
+      });
+    });
+
+  constructor(private actions$: Actions, private notificationService: NotificationService) {}
+}
diff --git a/src/app/teller/store/effects/products.service.effects.ts b/src/app/teller/store/effects/products.service.effects.ts
new file mode 100644
index 0000000..5b9967a
--- /dev/null
+++ b/src/app/teller/store/effects/products.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 {DepositAccountService} from '../../../services/depositAccount/deposit-account.service';
+import {PortfolioService} from '../../../services/portfolio/portfolio.service';
+import {Observable} from 'rxjs/Observable';
+import * as tellerActions from '../teller.actions';
+import {Action} from '@ngrx/store';
+import {of} from 'rxjs/observable/of';
+
+@Injectable()
+export class TellerProductsApiEffects {
+
+  @Effect()
+  loadAllDepositProducts$: Observable<Action> = this.actions$
+    .ofType(tellerActions.LOAD_ALL_DEPOSIT_PRODUCTS)
+    .map((action: tellerActions.LoadAllDepositProductsAction) => action.payload)
+    .mergeMap(customerId =>
+      this.depositService.fetchProductInstances(customerId)
+        .map(productInstances => new tellerActions.LoadAllDepositProductsSuccessAction(productInstances))
+        .catch(() => of(new tellerActions.LoadAllDepositProductsSuccessAction([])))
+    );
+
+  @Effect()
+  loadAllLoanProducts$: Observable<Action> = this.actions$
+    .ofType(tellerActions.LOAD_ALL_LOAN_PRODUCTS)
+    .map((action: tellerActions.LoadAllLoanProductsAction) => action.payload)
+    .mergeMap(customerId =>
+      this.portfolioService.getAllCasesForCustomer(customerId)
+        .map(casePage => new tellerActions.LoadAllLoanProductsSuccessAction(casePage.elements))
+        .catch((error) => of(new tellerActions.LoadAllLoanProductsSuccessAction([])))
+    );
+
+  constructor(private actions$: Actions, private depositService: DepositAccountService, private portfolioService: PortfolioService) {}
+}
diff --git a/src/app/teller/store/effects/route.effects.ts b/src/app/teller/store/effects/route.effects.ts
new file mode 100644
index 0000000..6982e2d
--- /dev/null
+++ b/src/app/teller/store/effects/route.effects.ts
@@ -0,0 +1,47 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 {Observable} from 'rxjs/Observable';
+import {Router} from '@angular/router';
+import {Actions, Effect} from '@ngrx/effects';
+import {Injectable} from '@angular/core';
+import * as tellerActions from '../../store/teller.actions';
+
+@Injectable()
+export class TellerRouteEffects {
+
+  @Effect({dispatch: false})
+  unlockDrawerSuccess$: Observable<Action> = this.actions$
+    .ofType(tellerActions.UNLOCK_DRAWER_SUCCESS)
+    .do((payload) => this.router.navigate(['/teller']));
+
+  @Effect({dispatch: false})
+  lockDrawerSuccess$: Observable<Action> = this.actions$
+    .ofType(tellerActions.LOCK_DRAWER_SUCCESS)
+    .do((payload) => this.router.navigate(['/teller/auth']));
+
+  @Effect({dispatch: false})
+  confirmTransactionSuccess$: Observable<Action> = this.actions$
+    .ofType(tellerActions.CONFIRM_TRANSACTION_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/teller/store/effects/service.effects.ts b/src/app/teller/store/effects/service.effects.ts
new file mode 100644
index 0000000..7683219
--- /dev/null
+++ b/src/app/teller/store/effects/service.effects.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 {Injectable} from '@angular/core';
+import {Actions, Effect} from '@ngrx/effects';
+import {TellerService} from '../../../services/teller/teller-service';
+import {Observable} from 'rxjs/Observable';
+import {Action} from '@ngrx/store';
+import * as tellerActions from '../teller.actions';
+import {of} from 'rxjs/observable/of';
+
+@Injectable()
+export class TellerApiEffects {
+
+  @Effect()
+  unlockDrawer$: Observable<Action> = this.actions$
+    .ofType(tellerActions.UNLOCK_DRAWER)
+    .map((action: tellerActions.UnlockDrawerAction) => action.payload)
+    .mergeMap(payload =>
+      this.tellerService.unlockDrawer(payload.tellerCode, {employeeIdentifier: payload.employeeId, password: payload.password})
+        .map(teller => new tellerActions.UnlockDrawerSuccessAction(teller))
+        .catch((error) => of(new tellerActions.UnlockDrawerFailAction(error)))
+    );
+
+  @Effect()
+  lockDrawer$: Observable<Action> = this.actions$
+    .ofType(tellerActions.LOCK_DRAWER)
+    .map((action: tellerActions.LockDrawerAction) => action.payload)
+    .mergeMap(payload =>
+      this.tellerService.executeCommand(payload.tellerCode, 'PAUSE')
+        .map(() => new tellerActions.LockDrawerSuccessAction())
+        .catch((error) => of(new tellerActions.LockDrawerSuccessAction()))
+    );
+
+  @Effect()
+  confirmTransaction$: Observable<Action> = this.actions$
+    .ofType(tellerActions.CONFIRM_TRANSACTION)
+    .map((action: tellerActions.ConfirmTransactionAction) => action.payload)
+    .mergeMap(payload =>
+      this.tellerService.confirmTransaction(payload.tellerCode, payload.tellerTransactionIdentifier, payload.command,
+        payload.chargesIncluded)
+        .map(() => new tellerActions.ConfirmTransactionSuccessAction(payload))
+        .catch((error) => of(new tellerActions.ConfirmTransactionFailAction(error)))
+    );
+
+  constructor(private actions$: Actions, private tellerService: TellerService) {}
+}
diff --git a/src/app/teller/store/index.ts b/src/app/teller/store/index.ts
new file mode 100644
index 0000000..dd59393
--- /dev/null
+++ b/src/app/teller/store/index.ts
@@ -0,0 +1,79 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 {ActionReducer, Store} from '@ngrx/store';
+import {createReducer} from '../../store/index';
+import * as fromAuthentication from './authentication.reducer';
+import * as fromDepositProducts from './customer-deposit-products.reducer';
+import * as fromLoanProducts from './customer-loan-products.reducer';
+import {createSelector} from 'reselect';
+import {
+  createResourceReducer,
+  getResourceAll,
+  getResourceLoadedAt,
+  getResourceSelected,
+  ResourceState
+} from '../../common/store/resource.reducer';
+
+export interface State extends fromRoot.State {
+  tellerAuthentication: fromAuthentication.State;
+  tellerCustomers: ResourceState;
+  tellerCustomerDepositProducts: ResourceState;
+  tellerCustomerLoanProducts: ResourceState;
+}
+
+const reducers = {
+  tellerAuthentication: fromAuthentication.reducer,
+  tellerCustomers: createResourceReducer('Teller Customer'),
+  tellerCustomerDepositProducts: fromDepositProducts.reducer,
+  tellerCustomerLoanProducts: fromLoanProducts.reducer,
+};
+
+export const tellerModuleReducer: ActionReducer<State> = createReducer(reducers);
+
+export class TellerStore extends Store<State> {}
+
+export function tellerStoreFactory(appStore: Store<fromRoot.State>) {
+  appStore.replaceReducer(tellerModuleReducer);
+  return appStore;
+}
+
+export const getAuthenticationState = (state: State) => state.tellerAuthentication;
+
+export const isAuthenticated = createSelector(getAuthenticationState, fromAuthentication.getAuthenticated);
+export const getAuthenticationError = createSelector(getAuthenticationState, fromAuthentication.getError);
+export const getAuthenticationLoading = createSelector(getAuthenticationState, fromAuthentication.getLoading);
+export const getAuthenticatedTeller = createSelector(getAuthenticationState, fromAuthentication.getTeller);
+
+export const getTellerCustomersState = (state: State) => state.tellerCustomers;
+
+export const getTellerCustomerLoadedAt = createSelector(getTellerCustomersState, getResourceLoadedAt);
+export const getTellerSelectedCustomer = createSelector(getTellerCustomersState, getResourceSelected);
+
+export const getTellerCustomerDepositProductsState = (state: State) => state.tellerCustomerDepositProducts;
+export const getAllTellerCustomerDepositProducts = createSelector(getTellerCustomerDepositProductsState, getResourceAll);
+export const hasTellerCustomerDepositProducts = createSelector(getAllTellerCustomerDepositProducts, (products) => {
+  return products.length > 0;
+});
+
+export const getTellerCustomerLoanProductsState = (state: State) => state.tellerCustomerLoanProducts;
+export const getAllTellerCustomerLoanProducts = createSelector(getTellerCustomerLoanProductsState, getResourceAll);
+export const hasTellerCustomerLoanProducts = createSelector(getAllTellerCustomerLoanProducts, (products) => {
+  return products.length > 0;
+});
diff --git a/src/app/teller/store/teller.actions.ts b/src/app/teller/store/teller.actions.ts
new file mode 100644
index 0000000..8f19ff1
--- /dev/null
+++ b/src/app/teller/store/teller.actions.ts
@@ -0,0 +1,160 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 {Action} from '@ngrx/store';
+import {LoadResourcePayload, SelectResourcePayload} from '../../common/store/resource.reducer';
+import {ProductInstance} from '../../services/depositAccount/domain/instance/product-instance.model';
+import {RoutePayload} from '../../common/store/route-payload';
+import {Teller} from '../../services/teller/domain/teller.model';
+import {FimsCase} from '../../services/portfolio/domain/fims-case.model';
+
+export const UNLOCK_DRAWER = type('[Teller] Unlock Drawer');
+export const UNLOCK_DRAWER_SUCCESS = type('[Teller] Unlock Drawer Success');
+export const UNLOCK_DRAWER_FAIL = type('[Teller] Unlock Drawer Fail');
+export const LOCK_DRAWER = type('[Teller] Lock Drawer');
+export const LOCK_DRAWER_SUCCESS = type('[Teller] Lock Drawer Success');
+
+export const LOAD_CUSTOMER = type('[Teller Customer] Load');
+export const SELECT_CUSTOMER = type('[Teller Customer] Select');
+
+export const LOAD_ALL_DEPOSIT_PRODUCTS = type('[Teller Customer] Deposit Product Load All');
+export const LOAD_ALL_DEPOSIT_PRODUCTS_SUCCESS = type('[Teller Customer] Deposit Product Load All Success');
+export const LOAD_ALL_LOAN_PRODUCTS = type('[Teller Customer] Loan Product Load All');
+export const LOAD_ALL_LOAN_PRODUCTS_SUCCESS = type('[Teller Customer] Loan Product Load All Success');
+
+export const CONFIRM_TRANSACTION = type('[Teller Customer] Confirm Transaction');
+export const CONFIRM_TRANSACTION_SUCCESS = type('[Teller Customer] Confirm Transaction Success');
+export const CONFIRM_TRANSACTION_FAIL = type('[Teller Customer] Confirm Transaction Fail');
+
+export interface UnlockDrawerPayload {
+  tellerCode: string;
+  employeeId: string;
+  password: string;
+}
+
+export interface LockDrawerPayload {
+  tellerCode: string;
+}
+
+export interface ConfirmTransactionPayload extends RoutePayload {
+  tellerCode: string;
+  tellerTransactionIdentifier: string;
+  command: string;
+  chargesIncluded: boolean;
+}
+
+export class UnlockDrawerAction implements Action {
+  readonly type = UNLOCK_DRAWER;
+
+  constructor(public payload: UnlockDrawerPayload) { }
+}
+
+export class UnlockDrawerSuccessAction implements Action {
+  readonly type = UNLOCK_DRAWER_SUCCESS;
+
+  constructor(public payload: Teller) { }
+}
+
+export class UnlockDrawerFailAction implements Action {
+  readonly type = UNLOCK_DRAWER_FAIL;
+
+  constructor(public payload: Error) { }
+}
+
+export class LockDrawerAction implements Action {
+  readonly type = LOCK_DRAWER;
+
+  constructor(public payload: LockDrawerPayload) { }
+}
+
+export class LockDrawerSuccessAction implements Action {
+  readonly type = LOCK_DRAWER_SUCCESS;
+
+  constructor() { }
+}
+
+export class LoadCustomerAction implements Action {
+  readonly type = LOAD_CUSTOMER;
+
+  constructor(public payload: LoadResourcePayload) { }
+}
+
+export class SelectCustomerAction implements Action {
+  readonly type = SELECT_CUSTOMER;
+
+  constructor(public payload: SelectResourcePayload) { }
+}
+
+export class LoadAllDepositProductsAction implements Action {
+  readonly type = LOAD_ALL_DEPOSIT_PRODUCTS;
+
+  constructor(public payload: string) { }
+}
+
+export class LoadAllDepositProductsSuccessAction implements Action {
+  readonly type = LOAD_ALL_DEPOSIT_PRODUCTS_SUCCESS;
+
+  constructor(public payload: ProductInstance[]) { }
+}
+
+export class LoadAllLoanProductsAction implements Action {
+  readonly type = LOAD_ALL_LOAN_PRODUCTS;
+
+  constructor(public payload: string) { }
+}
+
+export class LoadAllLoanProductsSuccessAction implements Action {
+  readonly type = LOAD_ALL_LOAN_PRODUCTS_SUCCESS;
+
+  constructor(public payload: FimsCase[]) { }
+}
+
+export class ConfirmTransactionAction implements Action {
+  readonly type = CONFIRM_TRANSACTION;
+
+  constructor(public payload: ConfirmTransactionPayload) {}
+}
+
+export class ConfirmTransactionSuccessAction implements Action {
+  readonly type = CONFIRM_TRANSACTION_SUCCESS;
+
+  constructor(public payload: ConfirmTransactionPayload) {}
+}
+
+export class ConfirmTransactionFailAction implements Action {
+  readonly type = CONFIRM_TRANSACTION_FAIL;
+
+  constructor(public payload: Error) {}
+}
+
+export type Actions
+  = UnlockDrawerAction
+  | UnlockDrawerSuccessAction
+  | UnlockDrawerFailAction
+  | LockDrawerAction
+  | LockDrawerSuccessAction
+  | LoadCustomerAction
+  | SelectCustomerAction
+  | LoadAllDepositProductsAction
+  | LoadAllDepositProductsSuccessAction
+  | LoadAllLoanProductsAction
+  | LoadAllLoanProductsSuccessAction
+  | ConfirmTransactionAction
+  | ConfirmTransactionSuccessAction
+  | ConfirmTransactionFailAction;
diff --git a/src/app/teller/teller-login.guard.ts b/src/app/teller/teller-login.guard.ts
new file mode 100644
index 0000000..c47ece5
--- /dev/null
+++ b/src/app/teller/teller-login.guard.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 {Injectable} from '@angular/core';
+import {ActivatedRoute, ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot} from '@angular/router';
+import {Observable} from 'rxjs/Observable';
+import * as fromTeller from './store/index';
+import {TellerStore} from './store/index';
+
+@Injectable()
+export class TellerLoginGuard implements CanActivate {
+
+  constructor(private store: TellerStore, private router: Router, private route: ActivatedRoute) {}
+
+  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
+    return this.store.select(fromTeller.isAuthenticated)
+      .map(isAuthenticated => {
+        if (!isAuthenticated) {
+          this.router.navigate(['/teller/auth'], { relativeTo: this.route });
+          return false;
+        }
+        return true;
+      });
+  }
+}
diff --git a/src/app/teller/teller.index.component.html b/src/app/teller/teller.index.component.html
new file mode 100644
index 0000000..6fd5727
--- /dev/null
+++ b/src/app/teller/teller.index.component.html
@@ -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.
+-->
+
+<section>
+  <h2 class="mat-display-1 text-upper text-center push-bottom">Search member </h2>
+  <div layout-gt-xs="row" layout-align="center center">
+    <div layout-gt-xs="row" flex-gt-xs="90" layout-margin>
+      <div flex-gt-xs="25"></div>
+      <div flex-gt-xs="50">
+        <mat-card>
+          <mat-card-title>
+            <td-search-input placeholder="Search here" [showUnderline]="false" [debounce]="500" (searchDebounce)="search($event)" (clear)="clearSearch()">
+            </td-search-input>
+          </mat-card-title>
+          <mat-card-subtitle>Search results</mat-card-subtitle>
+          <mat-divider></mat-divider>
+          <mat-list>
+            <ng-template let-item let-last="last" ngFor [ngForOf]="customer$ | async">
+              <mat-list-item>
+                <mat-icon mat-list-icon class="fill-grey-700">face</mat-icon>
+                <h4 matLine>{{item.surname}}, {{item.givenName}}</h4>
+                <button mat-raised-button color="accent" title="{{'SHOW' | translate}}" (click)="showCustomer(item.identifier)">{{'SHOW' | translate}}</button>
+              </mat-list-item>
+              <mat-divider mat-inset *ngIf="!last"></mat-divider>
+            </ng-template>
+          </mat-list>
+          <mat-divider></mat-divider>
+        </mat-card>
+      </div>
+      <div flex-gt-xs="25"></div>
+    </div>
+  </div>
+</section>
+<router-outlet></router-outlet>
+<a mat-fab color="accent" class="mat-fab-position-bottom-right" (click)="logout()" title="{{'Pause' | translate}}">
+  <mat-icon>pause</mat-icon>
+</a>
diff --git a/src/app/teller/teller.index.component.ts b/src/app/teller/teller.index.component.ts
new file mode 100644
index 0000000..ead4c30
--- /dev/null
+++ b/src/app/teller/teller.index.component.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 {Component, OnDestroy} from '@angular/core';
+import * as fromTeller from './store/index';
+import {TellerStore} from './store/index';
+import * as fromRoot from '../store/index';
+import {LOCK_DRAWER} from './store/teller.actions';
+import {Subscription} from 'rxjs/Subscription';
+import {ActivatedRoute, Router} from '@angular/router';
+import {SEARCH} from '../store/customer/customer.actions';
+import {Customer} from '../services/customer/domain/customer.model';
+import {Observable} from 'rxjs/Observable';
+import {Teller} from '../services/teller/domain/teller.model';
+
+@Component({
+  templateUrl: './teller.index.component.html'
+})
+export class TellerIndexComponent implements OnDestroy {
+
+  private tellerCodeSubscription: Subscription;
+
+  private teller: Teller;
+
+  customer$: Observable<Customer[]>;
+
+  constructor(private router: Router, private route: ActivatedRoute, private store: TellerStore) {
+    this.tellerCodeSubscription = store.select(fromTeller.getAuthenticatedTeller)
+      .subscribe(teller => this.teller = teller);
+
+    this.customer$ = store.select(fromRoot.getSearchCustomers);
+  }
+
+  ngOnDestroy(): void {
+    this.tellerCodeSubscription.unsubscribe();
+  }
+
+  logout(): void {
+    this.store.dispatch({
+      type: LOCK_DRAWER,
+      payload: {
+        tellerCode: this.teller.code
+      }
+    });
+  }
+
+  showCustomer(identifier: string): void {
+    this.router.navigate(['customers/detail', identifier], { relativeTo: this.route });
+  }
+
+  search(searchTerm: string): void {
+    this.store.dispatch({
+      type: SEARCH,
+      payload: {
+        searchTerm
+      }
+    });
+  }
+
+  clearSearch(): void {}
+}
diff --git a/src/app/teller/teller.module.ts b/src/app/teller/teller.module.ts
new file mode 100644
index 0000000..ba18111
--- /dev/null
+++ b/src/app/teller/teller.module.ts
@@ -0,0 +1,109 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 {NgModule} from '@angular/core';
+import {CommonModule} from '@angular/common';
+import {TellerRoutes} from './teller.routing';
+import {RouterModule} from '@angular/router';
+import {TellerStore, tellerStoreFactory} from './store/index';
+import {Store} from '@ngrx/store';
+import {TellerIndexComponent} from './teller.index.component';
+import {TellerLoginGuard} from './teller-login.guard';
+import {TellerAuthComponent} from './auth/teller-auth.component';
+import {EffectsModule} from '@ngrx/effects';
+import {TellerApiEffects} from './store/effects/service.effects';
+import {
+  MatAutocompleteModule,
+  MatButtonModule,
+  MatCardModule,
+  MatCheckboxModule,
+  MatIconModule,
+  MatInputModule,
+  MatListModule,
+  MatSelectModule,
+  MatToolbarModule
+} from '@angular/material';
+import {FormsModule, ReactiveFormsModule} from '@angular/forms';
+import {TranslateModule} from '@ngx-translate/core';
+import {TellerRouteEffects} from './store/effects/route.effects';
+import {CovalentDataTableModule, CovalentMessageModule, CovalentSearchModule, CovalentStepsModule} from '@covalent/core';
+import {TellerCustomerExistsGuard} from './customer/teller-customer-exists.guard';
+import {TellerCustomerDetailComponent} from './customer/customer-detail.component';
+import {TellerProductsApiEffects} from './store/effects/products.service.effects';
+import {TellerCustomerIndexComponent} from './customer/customer-index.component';
+import {FimsSharedModule} from '../common/common.module';
+import {DepositTransactionFormComponent} from './customer/transaction/deposit/form.component';
+import {TellerNotificationEffects} from './store/effects/notification.effects';
+import {LoanTransactionFormComponent} from './customer/transaction/loan/form.component';
+import {CreateLoanTransactionFormComponent} from './customer/transaction/loan/create.form.component';
+import {TransactionCostComponent} from './customer/transaction/components/cost.component';
+import {CreateDepositTransactionFormComponent} from './customer/transaction/deposit/create.form.component';
+import {ChequeTransactionFormComponent} from './customer/transaction/cheque/form.component';
+import {CreateChequeTransactionFormComponent} from './customer/transaction/cheque/create.component';
+import {TellerTransactionService} from './services/transaction.service';
+import {AvailableActionService} from './services/available-actions.service';
+
+@NgModule({
+  imports: [
+    RouterModule.forChild(TellerRoutes),
+    TranslateModule,
+    FimsSharedModule,
+    CommonModule,
+    FormsModule,
+    ReactiveFormsModule,
+    MatIconModule,
+    MatButtonModule,
+    MatInputModule,
+    MatCardModule,
+    MatListModule,
+    MatToolbarModule,
+    MatAutocompleteModule,
+    MatSelectModule,
+    MatCheckboxModule,
+    CovalentMessageModule,
+    CovalentStepsModule,
+    CovalentSearchModule,
+    CovalentDataTableModule,
+    EffectsModule.run(TellerApiEffects),
+    EffectsModule.run(TellerRouteEffects),
+    EffectsModule.run(TellerProductsApiEffects),
+    EffectsModule.run(TellerNotificationEffects)
+  ],
+  declarations: [
+    TellerIndexComponent,
+    TellerAuthComponent,
+    TellerCustomerIndexComponent,
+    TellerCustomerDetailComponent,
+    CreateDepositTransactionFormComponent,
+    DepositTransactionFormComponent,
+    LoanTransactionFormComponent,
+    CreateLoanTransactionFormComponent,
+    TransactionCostComponent,
+    CreateChequeTransactionFormComponent,
+    ChequeTransactionFormComponent
+  ],
+  providers: [
+    TellerLoginGuard,
+    TellerCustomerExistsGuard,
+    TellerTransactionService,
+    AvailableActionService,
+    { provide: TellerStore, useFactory: tellerStoreFactory, deps: [Store]}
+  ]
+})
+export class TellerModule { }
diff --git a/src/app/teller/teller.routing.ts b/src/app/teller/teller.routing.ts
new file mode 100644
index 0000000..7357be1
--- /dev/null
+++ b/src/app/teller/teller.routing.ts
@@ -0,0 +1,66 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 {Routes} from '@angular/router';
+import {TellerLoginGuard} from './teller-login.guard';
+import {TellerAuthComponent} from './auth/teller-auth.component';
+import {TellerIndexComponent} from './teller.index.component';
+import {TellerCustomerDetailComponent} from './customer/customer-detail.component';
+import {TellerCustomerExistsGuard} from './customer/teller-customer-exists.guard';
+import {TellerCustomerIndexComponent} from './customer/customer-index.component';
+import {CreateDepositTransactionFormComponent} from './customer/transaction/deposit/create.form.component';
+import {CreateLoanTransactionFormComponent} from './customer/transaction/loan/create.form.component';
+import {CreateChequeTransactionFormComponent} from './customer/transaction/cheque/create.component';
+
+export const TellerRoutes: Routes = [
+  {
+    path: '',
+    canActivate: [TellerLoginGuard],
+    data: { title: 'Teller management', hasPermission: { id: 'teller_operations', accessLevel: 'READ' } },
+    children: [
+      {
+        path: '',
+        component: TellerIndexComponent
+      },
+      {
+        path: 'customers/detail/:id',
+        component: TellerCustomerIndexComponent,
+        data: {
+          hasPermission: { id: 'customer_customers', accessLevel: 'READ' }
+        },
+        canActivate: [ TellerCustomerExistsGuard ],
+        children: [
+          {
+            path: '',
+            component: TellerCustomerDetailComponent,
+            data: {title: 'View Customer'}
+          },
+          {path: 'transaction/deposit', component: CreateDepositTransactionFormComponent, data: { title: 'Create transaction' } },
+          {path: 'transaction/loan', component: CreateLoanTransactionFormComponent, data: { title: 'Create transaction' } },
+          {path: 'transaction/cheque', component: CreateChequeTransactionFormComponent, data: { title: 'Create transaction' }},
+          {path: 'identifications', loadChildren: '../customers/detail/identityCard/identity-card.module#IdentityCardModule'},
+        ]
+      }
+    ]
+  },
+  {
+    path: 'auth',
+    component: TellerAuthComponent,
+    data: { title: 'Teller login' }
+  }
+];
diff --git a/src/app/user/password.component.html b/src/app/user/password.component.html
new file mode 100644
index 0000000..c626dee
--- /dev/null
+++ b/src/app/user/password.component.html
@@ -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.
+-->
+
+<div layout="column" layout-fill>
+  <div class="mat-content" layout-padding flex>
+    <div layout-gt-xs="row" layout-align-gt-xs="center start" class="margin">
+      <div flex-gt-xs="25" layout-align="center center">
+        <mat-card>
+          <mat-card-title translate>Change password</mat-card-title>
+          <mat-card-subtitle *ngIf="forced" translate>please change your password in order to access fims</mat-card-subtitle>
+          <mat-card-content>
+            <form [formGroup]="passwordForm" layout-align="center center" layout-margin>
+              <div layout="row">
+                <mat-form-field layout-margin flex>
+                  <input matInput placeholder="{{'New Password' | translate}}" type="password" formControlName="newPassword" autocomplete="new-password"/>
+                  <mat-error *ngIf="passwordForm.get('newPassword').hasError('required')" translate>
+                    Required
+                  </mat-error>
+                  <mat-error *ngIf="passwordForm.get('newPassword').hasError('minlength')">
+                    {{ 'Must have at least characters.' | translate:{ value: passwordForm.get('newPassword').getError('minlength')['requiredLength']} }}
+                  </mat-error>
+                </mat-form-field>
+              </div>
+              <div layout="row">
+                <mat-form-field layout-margin flex>
+                  <input matInput placeholder="{{'Confirm New Password' | translate}}" type="password" formControlName="confirmNewPassword" autocomplete="new-password"/>
+                  <mat-error *ngIf="passwordForm.get('confirmNewPassword').hasError('required')" translate>
+                    Required
+                  </mat-error>
+                </mat-form-field>
+              </div>
+              <div layout="row">
+                <span *ngIf="passwordForm.hasError('mismatch')" layout-margin class="tc-red-700" translate>
+                  Passwords must match.
+                </span>
+                <span *ngIf="error" layout-margin class="tc-red-700">{{error | translate}}</span>
+              </div>
+            </form>
+          </mat-card-content>
+          <mat-card-actions>
+            <button flex mat-raised-button color="primary" [disabled]="passwordForm.invalid" (click)="changePassword()">{{'Change password' | translate}}</button>
+          </mat-card-actions>
+        </mat-card>
+      </div>
+    </div>
+  </div>
+</div>
diff --git a/src/app/user/password.component.ts b/src/app/user/password.component.ts
new file mode 100644
index 0000000..3f06d25
--- /dev/null
+++ b/src/app/user/password.component.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 {Component, OnDestroy, OnInit} from '@angular/core';
+import {ActivatedRoute} from '@angular/router';
+import {FormBuilder, FormGroup, Validators} from '@angular/forms';
+import {FimsValidators} from '../common/validator/validators';
+import {Subscription} from 'rxjs/Subscription';
+import {Store} from '@ngrx/store';
+import * as fromRoot from '../store';
+import {CHANGE_PASSWORD} from '../store/security/security.actions';
+
+@Component({
+  selector: 'fims-user-password',
+  templateUrl: './password.component.html'
+})
+export class PasswordComponent implements OnInit, OnDestroy {
+
+  private usernameSubscription: Subscription;
+
+  private passwordErrorSubscription: Subscription;
+
+  private currentUser: string;
+
+  passwordForm: FormGroup;
+
+  error: string;
+
+  forced: boolean;
+
+  constructor(private formBuilder: FormBuilder, private route: ActivatedRoute, private store: Store<fromRoot.State>) {}
+
+  ngOnInit() {
+    this.route.queryParams.subscribe((queryParams) => {
+      this.forced = queryParams['forced'] === 'true';
+    });
+
+    this.usernameSubscription = this.store.select(fromRoot.getUsername)
+      .subscribe(username => this.currentUser = username);
+
+    this.passwordErrorSubscription = this.store.select(fromRoot.getPasswordError)
+      .filter(error => !!error)
+      .subscribe(error => this.error = 'There was an error changing your password');
+
+    this.passwordForm = this.createFormGroup();
+  }
+
+  ngOnDestroy(): void {
+    this.usernameSubscription.unsubscribe();
+    this.passwordErrorSubscription.unsubscribe();
+  }
+
+  private createFormGroup(): FormGroup {
+    return this.formBuilder.group({
+      newPassword: ['', [Validators.required, Validators.minLength(8)]],
+      confirmNewPassword: ['', Validators.required]
+    }, { validator: FimsValidators.matchValues('newPassword', 'confirmNewPassword')});
+  }
+
+  changePassword() {
+    const newPassword: string = this.passwordForm.get('newPassword').value;
+
+    this.store.dispatch({ type: CHANGE_PASSWORD, payload: {
+      username: this.currentUser,
+      password: newPassword
+    }});
+  }
+
+}
diff --git a/src/app/user/user.module.ts b/src/app/user/user.module.ts
new file mode 100644
index 0000000..fb6d369
--- /dev/null
+++ b/src/app/user/user.module.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 {NgModule} from '@angular/core';
+import {RouterModule} from '@angular/router';
+import {ReactiveFormsModule} from '@angular/forms';
+import {PasswordComponent} from './password.component';
+import {UserRoutes} from './user.routing';
+import {MatButtonModule, MatCardModule, MatInputModule} from '@angular/material';
+import {CommonModule} from '@angular/common';
+import {TranslateModule} from '@ngx-translate/core';
+
+@NgModule({
+  imports: [
+    RouterModule.forChild(UserRoutes),
+    TranslateModule,
+    CommonModule,
+    ReactiveFormsModule,
+    MatCardModule,
+    MatInputModule,
+    MatButtonModule
+  ],
+  declarations: [
+    PasswordComponent
+  ]
+})
+export class UserModule {}
diff --git a/src/app/user/user.routing.ts b/src/app/user/user.routing.ts
new file mode 100644
index 0000000..e43e38a
--- /dev/null
+++ b/src/app/user/user.routing.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 {Routes} from '@angular/router';
+import {PasswordComponent} from './password.component';
+
+export const UserRoutes: Routes = [
+  { path: '', component: PasswordComponent }
+];
diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json
new file mode 100644
index 0000000..f34fcf0
--- /dev/null
+++ b/src/assets/i18n/en.json
@@ -0,0 +1,24 @@
+{
+  "Account entries for account": "Account entries for account {{value}}",
+  "Account with value": "Account {{value}}",
+  "BEGINNING_BALANCE": "Flat",
+  "Create new loan for member": "Create new loan for member {{value}}",
+  "CURRENT_BALANCE": "Declining",
+  "Edit loan for member": "Edit loan for member {{value}}",
+  "Issued by": "Issued by {{value}}",
+  "Please verify the following tasks before you can action this member": "Please verify the following tasks before you can {{value}} this member",
+  "Only characters allowed.": "Only {{value}} characters allowed.",
+  "Value precision must be smaller or equals": "Value precision must be smaller or equals {{value}}",
+  "Only numbers allowed.": "Only {{value}} numbers allowed.",
+  "Value must be greater than or equal to": "Value must be greater than or equal to {{value}}",
+  "Value must be smaller than or equal to": "Value must be smaller than or equal to {{value}}",
+  "Value must be greater than": "Value must be greater than {{value}}",
+  "Value must be smaller than": "Value must be smaller than {{value}}",
+  "Value scale must be smaller than or equal to": "Value scale must be smaller than or equal to {{value}}",
+  "Max file size": "File can't exceed size of {{value}}KB",
+  "Must have at least characters.": "Must have at least {{value}} characters.",
+  "Must have decimal places": "Must have {{value}} decimal places",
+  "Member is assigned to office:": "Member is assigned to office: {{value}}",
+  "Ledger with name": "Ledger {{value}}",
+  "Subledgers of": "Subledgers of {{value}}"
+}
diff --git a/src/assets/i18n/es.json b/src/assets/i18n/es.json
new file mode 100644
index 0000000..31d75ee
--- /dev/null
+++ b/src/assets/i18n/es.json
@@ -0,0 +1,696 @@
+{
+  "Interests will be calculated on a daily basis": "",
+  "Account": "Cuenta",
+  "Account can't be deleted": "",
+  "Account Closing": "",
+  "Account details": "Detalles de cuenta",
+  "Account entries for account": "Entradas de cuenta {{value}}",
+  "Account entries": "Entrada de cuenta",
+  "Account has account entries": "",
+  "Account is going to be deleted": "",
+  "Account is going to be saved": "",
+  "Account Opening": "",
+  "Account Transfer": "",
+  "Account settings": "Configuración de cuentas",
+  "Account with value": "Account {{value}}",
+  "Accounting": "Contabilidad",
+  "Accounts for ledger": "Cuentas para el libro principal",
+  "Accounts": "Cuentas",
+  "Accrue account": "",
+  "Accrue account(Liability accounts only)": "",
+  "Action": "Acción",
+  "Actions": "Acciones",
+  "ACTIVATE": "ACTIVAR",
+  "Active": "Activo",
+  "ACTIVE": "ACTIVO",
+  "Activities": "Actividades",
+  "Add account": "Agregar cuenta",
+  "Add action": "Añadir acción",
+  "Add detail": "",
+  "ADD DOCUMENT": "",
+  "ADD FEE": "",
+  "Add field": "",
+  "Add identification card scan": "",
+  "Add Journal Entry": "Añadir entrada de diario",
+  "Add moratorium": "Agregar mora",
+  "Add new fee": "Añadir nuevo cargo",
+  "Add new task definition": "Agregar nueva definición de tarea",
+  "Add task for member": "Añadir tarea al cliente",
+  "ADD TASK": "AÑADIR TAREA",
+  "ADD STEP": "",
+  "Address": "Dirección",
+  "Affected Accounts": "Cuentas afectadas",
+  "Allow for write-off": "",
+  "Amount disbursed/Total": "Cantidad desembolsada/Total",
+  "Amount paid": "Cantidad paga",
+  "Amount to pay": "Monto a pagar",
+  "Amount": "Cantidad",
+  "amount": "Cantidad",
+  "Annually": "",
+  "Applied on": "",
+  "Applied when": "Aplicar cuando",
+  "April": "Abril",
+  "Arrears allowance reserve account": "Cuenta de reserva de asignaciones atrasadas",
+  "Arrears allowance(Expense accounts only)": "",
+  "Arrears allowance": "Indemnización por atrasos",
+  "Assign member to employee(optional)": "Asignar cliente a empleado (opcional)",
+  "Assign member to office(optional)": "Asignar cliente a un oficial (opcional)",
+  "Assign employee to office(optional)": "Asignar empleado a oficina (opcional)",
+  "Assign role to employee": "Asignar rol a empleado",
+  "Assign product": "",
+  "Assign": "Designar",
+  "Assigned employee": "",
+  "Assigned Office": "Asignar oficina",
+  "Assigned office": "Desginar oficina",
+  "Assigned role": "Rol asignado",
+  "August": "Agosto",
+  "Balance range": "Rango de balance",
+  "Balance": "Balance",
+  "BEGINNING_BALANCE": "Comienzo del balance",
+  "Birthday": "Fecha de nacimiento",
+  "Branch offices": "Sucursal",
+  "Browse...": "",
+  "CANCEL": "CANCELAR",
+  "Cash account": "",
+  "Cash account(Asset accounts only)": "",
+  "Cash account for loans approved but not yet disbursed.": "",
+  "Cash account holding funds for loans which are approved but not yet disbursed.": "",
+  "Cash Deposit": "",
+  "Cash in": "",
+  "Cash on hand": "",
+  "Cash out": "",
+  "Cash Withdrawal": "",
+  "Change custom field": "",
+  "Change language": "Cambiar idioma",
+  "Change password": "Cambiar contraseña",
+  "Change state": "Cambiar estado",
+  "Change the status of the member": "Cambiar el estado del cliente",
+  "Change the status of this account": "Cambiar estado de cuenta",
+  "Change the status of this deposit product": "",
+  "Change": "Cambiar",
+  "Fee is going to be created": "Cargo a ser creado",
+  "Fee is going to be deleted": "Cargo a ser eliminado",
+  "Fee is going to be updated": "Cargo a ser actualizado",
+  "Fee method": "Método de cargo",
+  "Fee name": "",
+  "Fee task": "Cargar tarea",
+  "Fees": "Cargos",
+  "Fees are paid in cash": "",
+  "Chart of accounts": "",
+  "Checking": "",
+  "Cheques receivable account": "",
+  "Cheques receivable account(Asset accounts only)": "",
+  "Choose a file to upload(max size 512 KB)": "",
+  "Choose one task": "Elegir una tarea",
+  "City": "Ciudad",
+  "Clerk": "Empleado",
+  "Click here to upload": "",
+  "CLOSE": "CERRAR",
+  "CLOSED": "CERRADO",
+  "Code": "",
+  "Confirm action": "",
+  "Confirm deletion": "Confirmar eliminación",
+  "Confirm New Password": "Confirmar nueva contraseña",
+  "Confirmation": "",
+  "Contact information": "Información de contacto",
+  "Contact Information": "Información de contacto",
+  "CONTINUE": "CONTINUAR",
+  "Count": "",
+  "Country short name": "Abreviación de país",
+  "Country": "País",
+  "CREATE ACCOUNT": "CREAR CUENTA",
+  "Create and edit roles to manage access levels within fims.": "Crear y editar roles para administrar niveles de acceso",
+  "Create and edit your employees and assign them to offices.": "Crear y editar sus empleados y asignarlos a oficinas",
+  "Create and edit your offices here.": "Crear y editar sus oficiales ",
+  "Create branch office": "Crear sucursal",
+  "Create custom fields": "",
+  "CREATE CUSTOM FIELDS": "",
+  "CREATE MEMBER LOAN": "CREAR PRÉSTAMO A CLIENTE",
+  "CREATE MEMBER": "CREAR CLIENTE",
+  "CREATE DENOMINATION": "",
+  "CREATE DEPOSIT ACCOUNT": "",
+  "CREATE DOCUMENT": "",
+  "CREATE IDENTIFICATION CARD": "",
+  "CREATE IDENTIFICATION CARD SCAN": "",
+  "CREATE EMPLOYEE": "CREAR EMPLEADO",
+  "Create headquarter": "Crear oficina principal",
+  "CREATE JOURNAL ENTRY": "CREAR ENTRADA DE DIARIO",
+  "CREATE LEDGER": "CREAR LIBRO PRINCIPAL",
+  "Create ledger": "Crear libro principal",
+  "Create loan for member": "Crear préstamo a cliente",
+  "Create new account": "Crear nueva cuenta",
+  "Create new member": "Crear nuevo cliente",
+  "Create denomination": "",
+  "Create new denomination": "",
+  "Create new deposit account for member": "",
+  "Create new deposit product": "",
+  "Create new document": "",
+  "Create new identification card": "",
+  "Create new employee": "Crear nuevo empleado",
+  "Create new journal entry": "Crear nueva entrada de diario",
+  "Create new ledger": "Crear nuevo libro principal",
+  "Create new loan account": "Crear nueva cuenta de préstamo",
+  "Create new loan for member": "Crear nuevo préstamo para el cliente {{value}}",
+  "Create new office": "Crear nueva oficina",
+  "Create new product": "Crear nuevo producto",
+  "Create new role": "Crear nuevo rol",
+  "Create new task for member": "Crear nueva tarea para el cliente",
+  "Create new task": "Crear nueva tarea",
+  "Create new transaction type": "",
+  "CREATE OFFICE": "CREAR OFICINA",
+  "CREATE PAGE": "",
+  "CREATE PRODUCT": "CREAR PRODUCTO",
+  "CREATE TRANSACTION TYPE": "",
+  "Create subledger": "Crear un sub libro principal",
+  "Create/edit members for your offices.": "Crear/editar clientes para sus oficinas",
+  "Create/edit ledgers and accounts for your General Ledger.": "Crear/editar libro principal y cuentas de su Libro Mayor",
+  "CREATED": "CREADO",
+  "Created by": "",
+  "Credit": "Crédito",
+  "Creditor": "Acreedor",
+  "Currency": "",
+  "Currency code": "Código de moneda",
+  "Currency digits": "Dígitos de moneda",
+  "Current password": "Contraseña actual",
+  "Current selection": "Selección actual",
+  "Current status": "Estado actual",
+  "CURRENT_BALANCE": "Balance Actual",
+  "Current balance": "Balance Actual",
+  "Custom field": "",
+  "Custom fields": "Campos personalizados",
+  "Member Address": "Dirección del cliente",
+  "Member contact(optional)": "Contacto del cliente (opcional)",
+  "Member deposit account": "",
+  "Member details": "Detalles del cliente",
+  "Member is assigned to office:": "Oficial es asignado directamente a un cliente",
+  "Member loan is going to be created": "Préstamo del cliente a ser creado",
+  "Member loan is going to be updated": "Préstamo del cliente a ser actualizado",
+  "Member loan": "Préstamo de cliente",
+  "Member loans": "",
+  "Member loan ledger": "",
+  "Member": "Cliente",
+  "Members": "Clientes",
+  "Cycle": "Ciclo",
+  "Dashboard": "Tablero",
+  "Data type": "",
+  "Date": "Fecha",
+  "Date must be before today": "",
+  "Date must be after today": "",
+  "Day of birth": "Fecha de nacimiento",
+  "Days late": "",
+  "Days late can't overlap with other days late.": "",
+  "Debit": "Débito",
+  "Debitor": "Deudor",
+  "Debtor": "Deudor",
+  "December": "Diciembre",
+  "DELETE ACCOUNT": "",
+  "DELETE IDENTIFICATION CARD": "",
+  "DELETE LEDGER": "ELIMINAR LIBRO CONTABLE",
+  "DELETE PORTRAIT": "",
+  "DELETE ROLE": "",
+  "DELETE TASK": "",
+  "Delete document": "",
+  "Delete this account": "",
+  "Delete this catalog": "",
+  "Delete this identification card": "",
+  "Delete this employee": "Eliminar empleado",
+  "Delete this field": "",
+  "Delete this ledger": "Eliminar este libro principal",
+  "Delete this product": "",
+  "Delete this office": "Eliminar ésta oficina",
+  "Delete this role": "",
+  "Delete this task": "",
+  "Delete": "Eliminar",
+  "Denomination required": "",
+  "Deposit account is going to be saved": "",
+  "Deposit account used for fees and disbursal": "",
+  "Deposit product": "",
+  "Deposit products": "",
+  "Description": "Descripción",
+  "Description(Optional)": "Descripción (opcional)",
+  "Description(optional)": "Descripción (opcional)",
+  "Deselect": "Deseleccionar",
+  "Details": "Detalles",
+  "DISABLE PRODUCT": "",
+  "DISBURSE LOAN": "DESEMBOLSAR PRÉSTAMO",
+  "Disbursement fee": "",
+  "Disbursement fee income account(Revenue accounts only)": "",
+  "DISTRIBUTE DIVIDEND": "",
+  "Distribute dividends": "",
+  "Dividend distributions": "",
+  "Dividend is going to be distributed": "",
+  "Dividend rate": "",
+  "Do you want to delete ledger {{value}}": "¿Quiere eliminar el libro {{value}} contable?",
+  "Do you want to delete this account?": "",
+  "Do you want to delete this identification card?": "",
+  "Do you want to delete this ledger?": "",
+  "Do you want to delete this role?": "",
+  "Do you want to delete the portrait?": "",
+  "Do you want to delete this task?": "",
+  "Document could not be locked": "",
+  "Document is going to be deleted": "",
+  "Document is going to be saved": "",
+  "Document locked": "",
+  "Document not locked": "",
+  "Due date": "",
+  "Edit account": "Editar cuenta",
+  "Edit configuration": "",
+  "Edit document": "",
+  "Edit fee": "Editar cargo",
+  "Edit field": "",
+  "Edit member loan": "Editar préstamo del cliente",
+  "Edit member": "Editar clientes",
+  "Edit deposit product": "",
+  "Edit identification card": "",
+  "Edit employee": "Editar empleado",
+  "Edit ledger": "Editar libro principal",
+  "Edit loan for member": "Editar préstamo del cliente {{value}}",
+  "Edit office": "Editar oficina",
+  "Edit product": "Editar producto",
+  "Edit role": "Editar rol",
+  "Edit task definition": "Editar definición de tarea",
+  "Edit task": "Editar tarea",
+  "Edit transaction type": "",
+  "Email(optional)": "Correo electrónico (opcional)",
+  "Employee already assigned": "",
+  "Employees can only be assigned to one teller. Please choose a different employee or unassign the employee first.": "",
+  "Employee contact(optional)": "Contacto del empleado (opcional)",
+  "Employee details": "Detalles de empleado",
+  "Employees": "Empleados",
+  "ENABLE PRODUCT": "",
+  "End date": "Fecha de finalización",
+  "Enter your comment here...": "Escriba su comentario aquí...",
+  "Equity ledger": "",
+  "Equity ledger(Equity ledgers only)": "",
+  "Execute task": "Ejecutar tarea",
+  "Expense account": "",
+  "Expense account(Expense accounts only)": "",
+  "Expenses": "",
+  "Expiration date": "Fecha de finalización",
+  "February": "Febrero",
+  "Fee income accounts": "Cuentas de ingreso de comisiones",
+  "Fetching data for chart of accounts...": "",
+  "Fetching results...": "",
+  "Final step": "",
+  "Financial products": "Productos financieros",
+  "First name": "Primer nombre",
+  "First Name": "Primer nombre",
+  "First": "Primero",
+  "Fixed term?": "",
+  "Flexible interest during the term?": "",
+  "Four eyes": "Cuatro ojos",
+  "Friday": "Viernes",
+  "General Ledger": "Libro mayor",
+  "Go back to member": "Volver a cliente",
+  "Go back to member loan": "",
+  "Go back to deposit product": "",
+  "Go back to identification cards": "",
+  "Go to account": "Ir a cuenta",
+  "Go to accounts": "Ir a cuentas",
+  "Go to member": "Ir a cliente",
+  "Go to members": "Ir a clientes",
+  "Go to general ledger": "Ir al libro principal",
+  "Go to ledger": "Ir al libro principal",
+  "Go to overview": "Ir a resumen",
+  "Go to parent": "Ir a principal",
+  "Go to parent ledger": "",
+  "Go to profile": "Ir al perfil",
+  "GO TO TASKS": "",
+  "Gross Profit": "",
+  "fims login": "Registración Fims",
+  "Hint": "",
+  "Hint(Optional)": "",
+  "Holders": "Titulares",
+  "Id": "Identificación",
+  "Identification card": "",
+  "Identification cards": "",
+  "Identification card available": "Tarjeta de identificación disponible",
+  "Identification": "Identificación",
+  "Identifier": "Identificador",
+  "Identities": "",
+  "Identity card(optional)": "Cédula de identidad (opcional)",
+  "in": "en",
+  "Include empty entries?": "¿Incluir entradas vacías?",
+  "Income": "",
+  "Income statement": "",
+  "Initial balance": "Balance inicial",
+  "Interest accrual account(Asset accounts only)": "",
+  "Interest creditor account": "Cuenta de acreedores de intereses",
+  "Interest income account(Revenue accounts only)": "",
+  "interest is applied": "Interés aplicado",
+  "Interest payable:": "",
+  "Interest range": "Alcance de los intereses",
+  "Interest range?": "¿Alcance de los intereses?",
+  "Interest settings": "Configuración de intereses",
+  "Interest": "Interés",
+  "Invalid account": "",
+  "Invalid country": "",
+  "Invalid date range": "Rango de datos inválido",
+  "Invalid transaction": "",
+  "Issued by": "Emitido por",
+  "Issued ID": "Identificación emitida",
+  "Issuer": "Emisor",
+  "Issuing Bank": "",
+  "It seems the resource you requested is either not available or you don't have the permission to access it.": "El recurso solicitado puede no estar disponible o usted no tiene permisos para acceder a él.",
+  "January": "Enero",
+  "Journal entries": "Diario de entradas",
+  "Journal Entry details": "Detalles de entrada de diario",
+  "Journal": "",
+  "July": "Julio",
+  "June": "Junio",
+  "Label": "",
+  "Last name": "Apellido",
+  "Last Name": "Apellido",
+  "Last modified by": "",
+  "Last transaction": "",
+  "Last": "Último",
+  "Late fee": "",
+  "Late fee accrual account(Asset accounts only)": "",
+  "Late fee income account(Revenue accounts only)": "",
+  "Latest activity": "Última actividad",
+  "Ledger and account settings": "",
+  "Ledger can't be deleted": "Libro contable no puede ser eliminado",
+  "Ledger details": "Detalles del libro principal",
+  "Ledger has accounts or sub ledgers": "Libro contable tiene cuentas o sub libros",
+  "Ledger in which to create individual member loan account.": "",
+  "Ledger used to create member loan account.": "",
+  "Ledger with name": "Libro contable con nombre",
+  "Ledger": "Libro principal",
+  "Length": "",
+  "Loan accounts": "Cuentas de préstamos",
+  "Loan details": "Detalle de préstamo",
+  "Loan funds allocation": "",
+  "loan is approved": "Préstamo aprobado",
+  "loan is closed": "Préstamo cerrado",
+  "loan is denied": "Préstamo denegado",
+  "loan is disbursed": "Préstamo desembolsado",
+  "loan is opened": "Préstamo abierto",
+  "loan is recovered": "Préstamo recuperado",
+  "loan is written off": "Préstamo amortizado",
+  "Loan product id": "Identificador del producto de préstamo",
+  "Loan product operations": "",
+  "Loan products": "Productos de préstamo",
+  "Loan origination fee": "",
+  "Loan origination fee income account(Revenue accounts only)": "",
+  "Loan": "Préstamo",
+  "Loans in process ledger": "",
+  "LOCK": "BLOQUEAR",
+  "LOCKED": "BLOQUEADO",
+  "Loss provision": "",
+  "Loss provision configuration": "",
+  "Manage member loans": "Administrar préstamos de clientes",
+  "Manage member tasks": "Administrar tareas de clientes",
+  "Manage members": "Administrar clientes",
+  "Manage member deposit accounts": "",
+  "Manage deposit products": "",
+  "Manage documents": "",
+  "Manage employees": "Administrar empleados",
+  "Manage fees": "",
+  "Manage identification cards": "",
+  "Manage ledger accounts": "Administrar contabilidad",
+  "Manage loan accounts": "Administrar cuentas de préstamos",
+  "Manage Loan product fees": "Administrar cargos del producto de préstamo",
+  "Manage Loan products": "Administrar productos de préstamo",
+  "Manage loan products": "Administrar productos",
+  "Manage offices": "Administrar oficinas",
+  "Manage payments": "Administrar pagos",
+  "Manage roles and permissions": "Administrar roles y permisos",
+  "Manage roles": "Administrar roles",
+  "Manage task definitions": "Administrar definiciones de tareas",
+  "Manage tasks of this product": "Administrar tareas para este producto",
+  "Manage tasks": "Administrar tareas",
+  "Manage transaction types": "",
+  "Management": "Administración",
+  "Mandatory": "Obligatorio",
+  "Mandatory:": "Obligatorio:",
+  "Mandatory tasks(These tasks must be executed)": "",
+  "March": "Marzo",
+  "Maturity": "",
+  "Maximum dispersal amount": "Cantidad máxima de dispersión",
+  "Maximum dispersal count": "Conteo máximo de dispersión",
+  "Maximum interest rate": "Interés máximo",
+  "Maximum principal amount": "Cantidad máxima principal",
+  "Max digits": "",
+  "Max file size": "{{value}}KB",
+  "Max value": "",
+  "May": "Mayo",
+  "Message": "Mensaje",
+  "Message(Optional)": "Mensaje (opcional)",
+  "Middle name(optional)": "Segundo nombre (opcional)",
+  "Min value": "",
+  "Minimum balance": "",
+  "Minimum interest rate": "Interés mínimo",
+  "Minimum principal amount": "Cantidad mínima principal",
+  "Mobile(optional)": "Celular (opcional)",
+  "Monday": "Lunes",
+  "Month": "",
+  "MONTHS": "MESES",
+  "Monthly": "",
+  "Moratorium": "Mora",
+  "Multiple disbursals?": "¿Múltiples desembolsos?",
+  "Must be greater than minimum": "Debe ser mayor al mínimo",
+  "Must be greater than or equal minimum": "",
+  "Must have at least characters.": "{{value}}",
+  "Must have decimal places": "",
+  "Name": "Nombre",
+  "Navigate to ledger of this account": "Navegar a través del libro principal de ésta cuenta",
+  "Navigate to reference account": "Navegar en la cuenta de referencia",
+  "Navigation": "Navegación",
+  "Net Income (Loss)": "",
+  "New Password": "Nueva contraseña",
+  "Next repayment": "Próximo pagos",
+  "No account selected": "No se seleccionó cuenta",
+  "No account was found.": "No se encontró la cuenta",
+  "No address available": "Dirección no disponible",
+  "No contact details available": "No hay detalles de contacto disponibles",
+  "No member was found.": "Cliente no encontrado",
+  "No data for chart of accounts available.": "",
+  "No employee was found.": "Empleado no encontrado",
+  "No file selected yet.": "",
+  "No Headquarter found": "No se encontró oficina principal",
+  "No holders available": "No hay titulares disponibles",
+  "No identification card available": "No hay tarjeta de identificación disponible",
+  "No office assigned to employee, yet.": "No se ha asignado oficina al empleado aún.",
+  "No office assigned": "Oficina sin asignar",
+  "No office was found.": "No se encontró la oficina",
+  "No product selected": "No se seleccionó producto",
+  "No product was found.": "No se encontró el producto",
+  "No tasks available": "",
+  "No reference account available": "No hay referencia de cuenta disponible",
+  "No results to display.": "",
+  "No role assigned to employee, yet.": "No se ha asignado rol al empleado aún.",
+  "No role was found.": "No fue encontrado ningún rol.",
+  "No selection": "Sin seleccionar",
+  "No signature authorities available": "No hay autoridades de firma disponibles",
+  "None": "",
+  "Note(Optional)": "Nota (opcional)",
+  "Note value": "",
+  "November": "Noviembre",
+  "Number": "Numero",
+  "October": "Octubre",
+  "of": "",
+  "Office Address(optional)": "Dirección de oficina (opcional)",
+  "Office can't be deleted": "",
+  "Office details": "Detalles de oficina",
+  "Office has either branch offices, employees or teller assigned to it.": "",
+  "Offices": "Oficinas",
+  "Oh no, it looks like you don't have a headquarter created yet. No worries you can do it now!": "¡Oh no! No se ha creado una oficina principal aún. No se preocupe, podemos crearla ahora.",
+  "OK": "OK",
+  "on the": "el",
+  "on": "el",
+  "Only 32 characters allowed.": "Únicamente 32 caracteres permitidos",
+  "Only 5 figures allowed": "Sólo se permiten 5 caracteres",
+  "Only characters allowed.": "Sólo caracteres permitidos",
+  "Only numbers allowed.": "Sólo números permitidos",
+  "OPEN": "ABIERTO",
+  "Opened on": "",
+  "Options": "",
+  "Optional tasks": "",
+  "Page is going to be deleted": "",
+  "Page is going to be uploaded": "",
+  "Page number": "",
+  "Pages uploaded": "",
+  "Parent Ledger": "",
+  "Parent office": "Oficina padre",
+  "Passport": "Pasaporte",
+  "Password": "Contraseña",
+  "Passwords must match.": "Las contraseñas deben coincidir",
+  "Payment cycle": "Ciclo de pago",
+  "payment is accepted": "Pago aceptado",
+  "payment is late": "Pago atrasado",
+  "Payment": "Pago",
+  "Payments": "Pagos",
+  "PENDING": "PENDIENTE",
+  "Percent provision": "",
+  "Period": "Período",
+  "Phone(optional)": "Teléfono (opcional)",
+  "Planned payments": "Pagos programados",
+  "Please add minimum one debtor.": "",
+  "Please add minimum one creditor.": "",
+  "please change your password in order to access fims": "Por favor, modifique su contraseña para poder acceder a Fims",
+  "Please choose a different page number": "",
+  "Please enter a different identifier.": "Por favor, introducir un identificador distinto",
+  "Please make sure all pages are uploaded and are in sequence": "",
+  "Please verify the following tasks before you can action this member": "Por favor, verifique las siguientes tareas antes de activar éste cliente",
+  "Portrait can't exceed size of 512KB.": "",
+  "Portrait is going to be uploaded": "",
+  "Postal code(optional)": "Código postal (opcional)",
+  "Press the upload button below to upload the portrait.": "",
+  "Principal Amount": "Cantidad principal",
+  "Processing fee": "",
+  "Processing fee income account(Revenue accounts only)": "",
+  "Product details": "Detalles de producto",
+  "Product is already assigned to a member.": "",
+  "Product is going to be created": "Producto a ser creado",
+  "Product is going to be deleted": "",
+  "Product is going to be disabled": "Producto a ser deshabilitado",
+  "Product is going to be enabled": "Producto a ser habilitado",
+  "Product is going to be updated": "Producto a ser actualizado",
+  "Product not enabled": "",
+  "Product": "Producto",
+  "Proportional?": "",
+  "Quick access": "Acceso rápido",
+  "Read": "Leer",
+  "Recent activities": "Actividades recientes",
+  "Reference account": "Cuenta de referencia",
+  "Region(optional)": "Región (opcional)",
+  "Remaining principal": "Principal remanente",
+  "Remove": "Suprimir",
+  "Remove detail": "",
+  "REMOVE DOCUMENT": "",
+  "REMOVE FEE": "",
+  "Remove field": "",
+  "REMOVE STEP": "",
+  "Repay every": "Pagar cada",
+  "REPAY LOAN NOW": "REPAGAR PRÉSTAMO AHORA",
+  "Required": "Obligatorio",
+  "Required, Only 2 characters allowed": "Obligatorio, sólo 2 caracteres permitidos",
+  "Return disbursement": "",
+  "Resource not available": "Recurso no disponible",
+  "Results": "Resultados",
+  "Role": "",
+  "Roles": "Roles",
+  "Roles/Permissions": "Roles/Permisos",
+  "Role is going to be deleted": "",
+  "Role is going to be saved": "",
+  "Rows per page": "",
+  "Saturday": "Sábado",
+  "SAVE FEE": "GUARDAR CARGO",
+  "SAVE ROLE": "GUARDAR ROL",
+  "SAVE TASK": "GUARDAR TAREA",
+  "SAVE": "GUARDAR",
+  "Savings": "",
+  "Scans uploaded": "",
+  "Search beneficiary": "",
+  "Search results": "Buscar resultados",
+  "Search": "Búsqueda",
+  "Search...": "Búsqueda...",
+  "Second": "Segundo",
+  "Select loan product": "Elegir producto de préstamo",
+  "Select product": "",
+  "Select": "Seleccionar",
+  "Selected file:": "",
+  "September": "Setiembre",
+  "Service not available": "Servicio no disponible",
+  "Settings": "Configuraciones",
+  "Share": "",
+  "SHOW": "VER",
+  "Show accounts when displayed in chart?": "",
+  "Short name": "",
+  "sign in via your current account": "Registrarse a través de su cuenta actual",
+  "Sign In": "Registrarse",
+  "Sign Out": "Desconectarse",
+  "Signature Authorities": "Autoridades de firma",
+  "Signature authorities": "Autoridades de firma\n",
+  "Sorry, but you are not allowed to see this page.": "Lo sentimos, pero no está autorizado a ver ésta página",
+  "Sorry, that login did not work": "",
+  "Sorry, there was a problem executing your command": "",
+  "Sorry, there was a problem executing your task": "",
+  "Start date": "Fecha de inicio",
+  "State": "Estado",
+  "Street": "Calle",
+  "Subledger": "Sub libro principal",
+  "Subtotal": "",
+  "Sum of Debtors and sum of Creditors must match.": "",
+  "Sunday": "Domingo",
+  "Task details": "Detalles de tarea",
+  "Task is going to be created": "Tarea a ser creada",
+  "Task is going to be updated": "Tarea a ser actualizada",
+  "Task is going to be deleted": "",
+  "Task needs to be executed, before loan": "Tareas a ser ejecutadas antes del préstamo",
+  "Tasks": "Tareas",
+  "Teller account": "",
+  "Teller account(Asset accounts only)": "",
+  "Teller is not paused": "",
+  "Teller must be paused to create denominations": "",
+  "Tenant": "",
+  "Term": "Plazo",
+  "Term period": "",
+  "There was an error changing your password": "",
+  "Third": "Tercero",
+  "This product can be assigned to a member": "",
+  "This is a four eyes task and must be executed by somebody else than you.": "",
+  "This teller requires a denomination before it can be closed.": "",
+  "Thursday": "Jueves",
+  "To assign this product to a member it needs to be enabled first": "",
+  "Total": "Total",
+  "Total expenses": "",
+  "Transaction date": "Fecha de transacción",
+  "Transaction type": "",
+  "Trial balance": "Balance de comprobación",
+  "Tuesday": "Martes",
+  "Type": "Tipo",
+  "Unassign": "No designar",
+  "Unique, Required.": "Unico, requerido",
+  "UNLOCK": "DESBLOQUEAR",
+  "UPDATE ACCOUNT": "ACTUALIZAR CUENTA",
+  "Update custom field": "",
+  "UPDATE FEE": "ACTUALIZAR CARGO",
+  "UPDATE MEMBER LOAN": "ACTUALIZAR PRÉSTAMO DE CLIENTE",
+  "UPDATE MEMBER": "ACTUALIZAR CLIENTE",
+  "UPDATE DEPOSIT ACCOUNT": "",
+  "UPDATE DOCUMENT": "",
+  "UPDATE IDENTIFICATION CARD": "",
+  "UPDATE EMPLOYEE": "ACTUALIZAR EMPLEADO",
+  "UPDATE LEDGER": "ACTUALIZAR LIBRO PRINCIPAL",
+  "UPDATE LOSS CONFIGURATION": "",
+  "UPDATE OFFICE": "ACTUALIZAR OFICINA",
+  "UPDATE PRODUCT": "ACTUALIZAR PRODUCTO",
+  "UPDATE TASK": "ACTUALIZAR TAREA",
+  "UPDATE TRANSACTION TYPE": "",
+  "Upload new identification card scan": "",
+  "Upload new page": "",
+  "Upload portrait": "",
+  "Username": "Nombre de usuario",
+  "Valid for status changes to:": "Validar cambios de estado a:",
+  "Vault account": "",
+  "Vault account(Asset accounts only)": "",
+  "Value must be greater than or equal to": "El valor debe ser mayor o igual",
+  "Value must be greater than": "El valor debe ser mayor",
+  "Value must be smaller than or equal to": "El valor debe ser menor o igual",
+  "Value must be smaller than": "El valor debe ser menor",
+  "Value scale must be smaller than or equal to": "{{value}}",
+  "Values can't overlap with other values.": "",
+  "View": "",
+  "View accounts for this ledger": "Ver cuentas para este libro principal",
+  "View chart of accounts": "",
+  "View member": "Ver cliente",
+  "View identification cards": "",
+  "View income statement": "",
+  "View employees": "Ver empleados",
+  "View General Ledger": "Ver libro mayor",
+  "View headquarter office": "Ver oficina principal",
+  "View journal entries": "Ver diario de entradas",
+  "View payments": "Ver pagos",
+  "View roles": "Ver roles",
+  "View transaction types": "",
+  "View trial balance": "Ver balance de comprobación",
+  "View subledgers": "",
+  "We are very sorry, it seems there is a problem with our servers. Please contact your administrator if the problem occurs.": "Lo sentimos, pero hay un problema con nuestros servidores. Por favor contacte a su administrador si el problema vuelve a suceder.",
+  "Wednesday": "Miércoles",
+  "WEEKS": "SEMANAS",
+  "Write off": "Pedir por escrito",
+  "Year": "",
+  "YEARS": "AÑOS",
+  "You can lock this document": ""
+}
diff --git a/src/assets/images/ic_account_circle_black_48dp_2x.png b/src/assets/images/ic_account_circle_black_48dp_2x.png
new file mode 100644
index 0000000..c6b56c3
--- /dev/null
+++ b/src/assets/images/ic_account_circle_black_48dp_2x.png
Binary files differ
diff --git a/src/environments/environment.prod.ts b/src/environments/environment.prod.ts
index 3612073..94eeec3 100644
--- a/src/environments/environment.prod.ts
+++ b/src/environments/environment.prod.ts
@@ -1,3 +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 const environment = {
   production: true
 };
diff --git a/src/environments/environment.ts b/src/environments/environment.ts
index b7f639a..865aeb9 100644
--- a/src/environments/environment.ts
+++ b/src/environments/environment.ts
@@ -1,8 +1,21 @@
-// The file contents for the current environment will overwrite these during build.
-// The build system defaults to the dev environment which uses `environment.ts`, but if you do
-// `ng build --env=prod` then `environment.prod.ts` will be used instead.
-// The list of which env maps to which file can be found in `.angular-cli.json`.
-
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 const environment = {
   production: false
 };
diff --git a/src/favicon.png b/src/favicon.png
new file mode 100644
index 0000000..85e8e66
--- /dev/null
+++ b/src/favicon.png
Binary files differ
diff --git a/src/index.html b/src/index.html
index d85cec1..354e928 100644
--- a/src/index.html
+++ b/src/index.html
@@ -1,16 +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.
+-->
+
 <!doctype html>
-<html lang="en">
+<html>
 <head>
   <meta charset="utf-8">
-  <title>FineractCnWebApp</title>
+  <title>fineractcn-group-finance</title>
   <base href="/">
-
+  <link rel="icon" type="image/png" href="favicon.png">
   <meta name="viewport" content="width=device-width, initial-scale=1">
-  <link rel="icon" type="image/x-icon" href="favicon.ico">
-  <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
 </head>
 <body>
-  <app-root></app-root>
-  
+  <fims-app>
+    <div style="padding: 20%;text-align:center;">
+      Loading fineractcn-group-finance...
+    </div>
+  </fims-app>
 </body>
 </html>
diff --git a/src/main.ts b/src/main.ts
index 7042750..61db9db 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -1,9 +1,28 @@
-import { enableProdMode } from '@angular/core';
-import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
-import 'hammerjs';
-import 'material-design-icons/iconfont/material-icons.css'
-import { AppModule } from './app/app.module';
-import { environment } from './environments/environment';
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 {enableProdMode} from '@angular/core';
+import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
+
+import {environment} from './environments/environment';
+import {AppModule} from './app/';
+// tslint:disable-next-line:no-import-side-effect
 import './rxjs.imports';
 
 if (environment.production) {
@@ -23,4 +42,4 @@
   { 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
+]).bootstrapModule(AppModule);
diff --git a/src/polyfills.ts b/src/polyfills.ts
index eb16e86..733aefe 100644
--- a/src/polyfills.ts
+++ b/src/polyfills.ts
@@ -1,84 +1,38 @@
 /**
- * This file includes polyfills needed by Angular and is loaded before the app.
- * You can add your own extra polyfills to this file.
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
  *
- * This file is divided into 2 sections:
- *   1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
- *   2. Application imports. Files imported after ZoneJS that should be loaded before your main
- *      file.
+ *   http://www.apache.org/licenses/LICENSE-2.0
  *
- * The current setup is for so-called "evergreen" browsers; the last versions of browsers that
- * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
- * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
- *
- * Learn more in https://angular.io/docs/ts/latest/guide/browser-support.html
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
  */
-
-/***************************************************************************************************
- * BROWSER POLYFILLS
- */
-
-/** 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/reflect';
+// This file includes polyfills needed by Angular 2 and is loaded before
+// the app. You can add your own extra polyfills to this file.
+/* tslint:disable:no-import-side-effect */
+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/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`.
-
-/** IE10 and IE11 requires the following for the Reflect API. */
-// import 'core-js/es6/reflect';
-
-
-/** Evergreen browsers require these. **/
-// Used for reflect-metadata in JIT. If you use AOT (and only Angular decorators), you can remove.
-import 'core-js/es7/reflect';
-
-
-/**
- * Required to support Web Animations `@angular/platform-browser/animations`.
- * Needed for: All but Chrome, Firefox and Opera. http://caniuse.com/#feat=web-animation
- **/
-// import 'web-animations-js';  // Run `npm install --save web-animations-js`.
-
-/**
- * By default, zone.js will patch all possible macroTask and DomEvents
- * user can disable parts of macroTask/DomEvents patch by setting following flags
- */
-
- // (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
- // (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
- // (window as any).__zone_symbol__BLACK_LISTED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
-
- /*
- * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
- * with the following flag, it will bypass `zone.js` patch for IE/Edge
- */
-// (window as any).__Zone_enable_cross_context_check = true;
-
-/***************************************************************************************************
- * Zone JS is required by default for Angular itself.
- */
-import 'zone.js/dist/zone';  // Included with Angular CLI.
-
-
-
-/***************************************************************************************************
- * APPLICATION IMPORTS
- */
+import 'zone.js/dist/zone';
diff --git a/src/rxjs.imports.ts b/src/rxjs.imports.ts
index 5992076..3af5609 100644
--- a/src/rxjs.imports.ts
+++ b/src/rxjs.imports.ts
@@ -1,3 +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.
+ */
+/* tslint:disable:no-import-side-effect */
 import 'rxjs/add/operator/debounceTime';
 import 'rxjs/add/operator/map';
 import 'rxjs/add/operator/switchMap';
diff --git a/src/styles.scss b/src/styles.scss
index f14b648..84ece87 100644
--- a/src/styles.scss
+++ b/src/styles.scss
@@ -1,44 +1,37 @@
-/* You can add global styles to this file, and also import other style files */
-@import "~@angular/material/prebuilt-themes/indigo-pink.css";
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
 
-body{
-    margin:0;
+// Custom style for loading elements without height
+.will-load {
+  min-height: 80px;
 }
-.heading{
-    margin-left:2%;
-    padding-top:5px;
-    padding-bottom: 5px;
-    height:15px;
-  }
-
-.fineract-button{
-    margin-top:5px;
-    margin-left:75%;
-    width:150px;
+// Href line height wasn't right for md-icon-button
+a[md-icon-button] {
+  line-height: 36px;
 }
 
-.main-div {
-    display:flex;
-    flex-direction: column;
-    margin-left: 2%;
-    margin-right:2%;
-    border-radius: 5px 5px 5px 5px;
-    background-color: white;
-    min-height: 100%;
+// Stretch tab labels until there is an attribute for it
+.md-tab-label {
+  flex-grow: 1;
+}
 
-  }
-
-  .filter{
-      width: 50%;
-  }
-
-  .page-space{
-    
-    margin-left: 2%;
-    background-color: white;
- 
- }
-
- .fineract-form{
-     margin-left: 2%;
- }
\ No newline at end of file
+// Disable final step in stepper components
+td-steps > div:last-child > td-step-header {
+  pointer-events: none;
+}
diff --git a/src/test.ts b/src/test.ts
index 1631789..9c0c42f 100644
--- a/src/test.ts
+++ b/src/test.ts
@@ -1,13 +1,38 @@
-// This file is required by karma.conf.js and loads recursively all the .spec and framework files
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+/* tslint:disable:no-import-side-effect */
+import 'zone.js/dist/long-stack-trace-zone';
+import 'zone.js/dist/proxy.js';
+import 'zone.js/dist/sync-test';
+import 'zone.js/dist/jasmine-patch';
+import 'zone.js/dist/async-test';
+import 'zone.js/dist/fake-async-test';
+import './rxjs.imports';
+import {getTestBed} from '@angular/core/testing';
+import {BrowserDynamicTestingModule, platformBrowserDynamicTesting} from '@angular/platform-browser-dynamic/testing';
 
-import 'zone.js/dist/zone-testing';
-import { getTestBed } from '@angular/core/testing';
-import {
-  BrowserDynamicTestingModule,
-  platformBrowserDynamicTesting
-} from '@angular/platform-browser-dynamic/testing';
+// Unfortunately there's no typing for the `__karma__` variable. Just declare it as any.
+declare var __karma__: any;
+declare var require: any;
 
-declare const require: any;
+// Prevent Karma from running prematurely.
+__karma__.loaded = function (): void { /* noop */ };
 
 // First, initialize the Angular testing environment.
 getTestBed().initTestEnvironment(
@@ -18,3 +43,5 @@
 const context = require.context('./', true, /\.spec\.ts$/);
 // And load the modules.
 context.keys().map(context);
+// Finally, start Karma to run the tests.
+__karma__.start();
diff --git a/src/theme.scss b/src/theme.scss
new file mode 100644
index 0000000..ac0df48
--- /dev/null
+++ b/src/theme.scss
@@ -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 '~@angular/material/theming';
+@import '~@covalent/core/theming/all-theme';
+
+// Plus imports for other components in your app.
+
+// Include the base styles for Angular Material core. We include this here so that you only
+// have to load a single css file for Angular Material in your app.
+@include mat-core();
+
+// Define the palettes for your theme using the Material Design palettes available in palette.scss
+// (imported above). For each palette, you can optionally specify a default, lighter, and darker
+// hue.
+$primary: mat-palette($mat-green, 700);
+
+$accent: mat-palette($mat-blue, 800, A100, A400);
+
+// The warn palette is optional (defaults to red).
+$warn: mat-palette($mat-red, 600);
+
+// Create the theme object (a Sass map containing all of the palettes).
+$theme: mat-light-theme($primary, $accent, $warn);
+
+// Include theme styles for core and each component used in your app.
+// Alternatively, you can import and @include the theme mixins for each component
+// that you are using.
+@include angular-material-theme($theme);
+@include covalent-theme($theme);
+
+// Active icon color in list nav
+mat-nav-list {
+  [mat-list-item].active {
+    mat-icon[matlistavatar] {
+      background-color: mat-color($accent);
+      color: mat-color($accent, default-contrast)
+    }
+    mat-icon[matlisticon] {
+      color: mat-color($accent);
+    }
+  }
+}
diff --git a/src/tsconfig.app.json b/src/tsconfig.app.json
index 39ba8db..3c80422 100644
--- a/src/tsconfig.app.json
+++ b/src/tsconfig.app.json
@@ -2,12 +2,13 @@
   "extends": "../tsconfig.json",
   "compilerOptions": {
     "outDir": "../out-tsc/app",
-    "baseUrl": "./",
     "module": "es2015",
+    "baseUrl": "",
     "types": []
   },
   "exclude": [
     "test.ts",
-    "**/*.spec.ts"
+    "**/*.spec.ts",
+    "app/common/testing/**"
   ]
 }
diff --git a/src/tsconfig.spec.json b/src/tsconfig.spec.json
index ac22a29..510e3f1 100644
--- a/src/tsconfig.spec.json
+++ b/src/tsconfig.spec.json
@@ -2,8 +2,9 @@
   "extends": "../tsconfig.json",
   "compilerOptions": {
     "outDir": "../out-tsc/spec",
-    "baseUrl": "./",
     "module": "commonjs",
+    "target": "es5",
+    "baseUrl": "",
     "types": [
       "jasmine",
       "node"
diff --git a/src/typings.d.ts b/src/typings.d.ts
index ef5c7bd..c50e59b 100644
--- a/src/typings.d.ts
+++ b/src/typings.d.ts
@@ -1,4 +1,26 @@
-/* SystemJS module definition */
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+// Typings reference file, see links for more information
+// https://github.com/typings/typings
+// https://www.typescriptlang.org/docs/handbook/writing-declaration-files.html
+
 declare var module: NodeModule;
 interface NodeModule {
   id: string;
diff --git a/tsconfig.json b/tsconfig.json
index 2e2f5a9..ba8e68b 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -1,20 +1,21 @@
 {
   "compileOnSave": false,
   "compilerOptions": {
-    "outDir": "./dist/out-tsc",
     "baseUrl": "src",
-    "sourceMap": true,
     "declaration": false,
-    "moduleResolution": "node",
     "emitDecoratorMetadata": true,
     "experimentalDecorators": true,
+    "lib": ["es6", "dom"],
+    "module": "es6",
+    "moduleResolution": "node",
+    "outDir": "../dist/out-tsc",
+    "sourceMap": true,
     "target": "es5",
     "typeRoots": [
-      "node_modules/@types"
+      "./node_modules/@types"
     ],
-    "lib": [
-      "es2017",
-      "dom"
+    "types": [
+      "jasmine", "hammerjs"
     ]
   }
 }
diff --git a/tslint.json b/tslint.json
index 9963d6c..ab27a82 100644
--- a/tslint.json
+++ b/tslint.json
@@ -11,15 +11,11 @@
       "check-space"
     ],
     "curly": true,
-    "deprecation": {
-      "severity": "warn"
-    },
     "eofline": true,
     "forin": true,
     "import-blacklist": [
       true,
-      "rxjs",
-      "rxjs/Rx"
+      "rxjs"
     ],
     "import-spacing": true,
     "indent": [
@@ -35,14 +31,8 @@
     "member-access": false,
     "member-ordering": [
       true,
-      {
-        "order": [
-          "static-field",
-          "instance-field",
-          "static-method",
-          "instance-method"
-        ]
-      }
+      "static-before-instance",
+      "variables-before-functions"
     ],
     "no-arg": true,
     "no-bitwise": true,
@@ -60,6 +50,7 @@
     "no-empty": false,
     "no-empty-interface": true,
     "no-eval": true,
+    "no-import-side-effect": true,
     "no-inferrable-types": [
       true,
       "ignore-params"
@@ -73,6 +64,9 @@
     "no-trailing-whitespace": true,
     "no-unnecessary-initializer": true,
     "no-unused-expression": true,
+    "no-unused-variable": [
+      true
+    ],
     "no-use-before-declare": true,
     "no-var-keyword": true,
     "object-literal-sort-keys": false,
@@ -90,7 +84,6 @@
     ],
     "radix": true,
     "semicolon": [
-      true,
       "always"
     ],
     "triple-equals": [
@@ -107,6 +100,7 @@
         "variable-declaration": "nospace"
       }
     ],
+    "typeof-compare": true,
     "unified-signatures": true,
     "variable-name": false,
     "whitespace": [
@@ -120,16 +114,15 @@
     "directive-selector": [
       true,
       "attribute",
-      "app",
+      "fims",
       "camelCase"
     ],
     "component-selector": [
       true,
       "element",
-      "app",
+      "fims",
       "kebab-case"
     ],
-    "no-output-on-prefix": true,
     "use-input-property-decorator": true,
     "use-output-property-decorator": true,
     "use-host-property-decorator": true,
@@ -138,6 +131,9 @@
     "use-life-cycle-interface": true,
     "use-pipe-transform-interface": true,
     "component-class-suffix": true,
-    "directive-class-suffix": true
+    "directive-class-suffix": true,
+    "no-access-missing-member": true,
+    "templates-use-public": true,
+    "invoke-injectable": true
   }
 }