From 6e552d72eba5fa68cfd442ed565ff8da040a5387 Mon Sep 17 00:00:00 2001 From: Abhay Chawla Date: Sun, 19 Aug 2018 22:49:48 +0530 Subject: [PATCH] refractor: accounting module (#300) * Refractor components. * Add resolvers. * Add relative router links. * Replace mat-icons by font-awesome icons. * Add comments. Fixes: #298 --- .stylelintrc | 7 +- .../accounting/accounting-routing.module.ts | 267 ++++++++++---- .../accounting-rule.resolver.ts | 31 ++ .../accounting-rules-template.resolver.ts | 30 ++ .../accounting-rules.component.html | 12 +- .../accounting-rules.component.scss | 4 + .../accounting-rules.component.ts | 74 ++-- .../accounting-rules.resolver.ts | 30 ++ .../create-rule/create-rule.component.html | 43 ++- .../create-rule/create-rule.component.scss | 5 - .../create-rule/create-rule.component.ts | 87 +++-- .../edit-rule/edit-rule.component.html | 43 ++- .../edit-rule/edit-rule.component.scss | 5 - .../edit-rule/edit-rule.component.ts | 89 +++-- ...one-of-the-fields-is-required.validator.ts | 11 + .../view-rule/view-rule.component.html | 12 +- .../view-rule/view-rule.component.scss | 6 +- .../view-rule/view-rule.component.ts | 22 +- .../view-rule/view-rule.resolver.ts | 19 - src/app/accounting/accounting.component.html | 165 +++++---- src/app/accounting/accounting.component.ts | 4 + src/app/accounting/accounting.module.ts | 35 +- src/app/accounting/accounting.service.ts | 325 ++++++++++++++---- .../chart-of-accounts.component.html | 28 +- .../chart-of-accounts.component.scss | 45 +-- .../chart-of-accounts.component.ts | 64 +++- .../chart-of-accounts.resolver.ts | 14 + .../chart-of-accounts-template.resolver.ts | 30 ++ .../create-gl-account.component.html | 39 ++- .../create-gl-account.component.scss | 4 +- .../create-gl-account.component.ts | 64 +++- .../edit-gl-account.component.html | 41 ++- .../edit-gl-account.component.scss | 10 + .../edit-gl-account.component.ts | 42 ++- ...and-chart-of-accounts-template.resolver.ts | 53 +++ .../gl-account-node.model.ts | 8 + .../gl-account-tree.service.ts | 30 +- .../view-gl-account.component.html | 33 +- .../view-gl-account.component.scss | 16 +- .../view-gl-account.component.ts | 30 +- .../view-gl-account.resolver.ts | 38 -- .../closing-entries.component.html | 12 +- .../closing-entries.component.ts | 93 +++-- .../closing-entries.resolver.ts | 30 ++ .../closing-entries/closing-entry.resolver.ts | 31 ++ .../create-closure.component.html | 19 +- .../create-closure.component.ts | 67 +++- .../edit-closure/edit-closure.component.html | 13 +- .../edit-closure/edit-closure.component.ts | 70 ++-- .../view-closure/view-closure.component.html | 24 +- .../view-closure/view-closure.component.scss | 16 +- .../view-closure/view-closure.component.ts | 40 ++- .../accounting-rules-associations.resolver.ts | 30 ++ .../common-resolvers/currencies.resolver.ts | 30 ++ .../common-resolvers/gl-accounts.resolver.ts | 30 ++ .../loan-products.resolver.ts | 30 ++ .../common-resolvers/offices.resolver.ts | 30 ++ .../payment-types.resolver.ts | 30 ++ .../provisioning-categories.resolver.ts | 30 ++ .../create-journal-entry.component.html | 255 ++++++++------ .../create-journal-entry.component.ts | 128 ++++--- ...-financial-activity-mapping.component.html | 18 +- ...te-financial-activity-mapping.component.ts | 92 +++-- ...ial-activity-mappings-template.resolver.ts | 30 ++ ...-financial-activity-mapping.component.html | 10 +- ...it-financial-activity-mapping.component.ts | 103 ++++-- ...-activity-mapping-and-template.resolver.ts | 31 ++ ...financial-activity-mappings.component.html | 6 +- ...financial-activity-mappings.component.scss | 4 + .../financial-activity-mappings.component.ts | 65 ++-- .../financial-activity-mappings.resolver.ts | 30 ++ .../financial-activity-mapping.resolver.ts | 31 ++ ...-financial-activity-mapping.component.html | 12 +- ...-financial-activity-mapping.component.scss | 16 +- ...ew-financial-activity-mapping.component.ts | 41 ++- .../frequent-postings.component.html | 274 +++++++++------ .../frequent-postings.component.ts | 145 +++++--- .../migrate-opening-balances.component.html | 31 +- .../migrate-opening-balances.component.scss | 6 +- .../migrate-opening-balances.component.ts | 146 +++++--- ...one-of-the-fields-is-required.validator.ts | 9 + .../periodic-accruals.component.html | 13 +- .../periodic-accruals.component.ts | 46 ++- .../create-provisioning-entry.component.html | 12 +- .../create-provisioning-entry.component.ts | 56 ++- .../provisioning-entries.component.html | 21 +- .../provisioning-entries.component.ts | 79 +++-- .../provisioning-entries.resolver.ts | 30 ++ .../provisioning-entry-entries.resolver.ts | 31 ++ .../provisioning-entry.resolver.ts | 31 ++ .../view-provisioning-entry.component.html | 14 +- .../view-provisioning-entry.component.scss | 6 +- .../view-provisioning-entry.component.ts | 237 ++++++++----- .../provisioning-journal-entries.resolver.ts | 31 ++ ...rovisioning-journal-entries.component.html | 3 +- ...-provisioning-journal-entries.component.ts | 67 ++-- .../revert-transaction.component.html | 3 +- .../revert-transaction.component.ts | 9 + .../journal-entry.datasource.ts | 31 +- .../search-journal-entry.component.html | 20 +- .../search-journal-entry.component.scss | 4 + .../search-journal-entry.component.ts | 188 +++++++--- .../view-journal-entry.component.html | 4 + .../view-journal-entry.component.scss | 23 +- .../view-journal-entry.component.ts | 8 + .../view-transaction/transaction.resolver.ts | 31 ++ .../view-transaction.component.html | 11 +- .../view-transaction.component.scss | 12 +- .../view-transaction.component.ts | 93 +++-- .../core/http/error-handler.interceptor.ts | 6 +- .../breadcrumb/breadcrumb.component.scss | 1 + .../users/view-user/view-user.component.html | 16 +- .../users/view-user/view-user.component.scss | 6 +- src/app/shared/icons.module.ts | 26 ++ 114 files changed, 3760 insertions(+), 1432 deletions(-) create mode 100644 src/app/accounting/accounting-rules/accounting-rule.resolver.ts create mode 100644 src/app/accounting/accounting-rules/accounting-rules-template.resolver.ts create mode 100644 src/app/accounting/accounting-rules/accounting-rules.resolver.ts create mode 100644 src/app/accounting/accounting-rules/one-of-the-fields-is-required.validator.ts delete mode 100644 src/app/accounting/accounting-rules/view-rule/view-rule.resolver.ts create mode 100644 src/app/accounting/chart-of-accounts/create-gl-account/chart-of-accounts-template.resolver.ts create mode 100644 src/app/accounting/chart-of-accounts/gl-account-and-chart-of-accounts-template.resolver.ts delete mode 100644 src/app/accounting/chart-of-accounts/view-gl-account/view-gl-account.resolver.ts create mode 100644 src/app/accounting/closing-entries/closing-entries.resolver.ts create mode 100644 src/app/accounting/closing-entries/closing-entry.resolver.ts create mode 100644 src/app/accounting/common-resolvers/accounting-rules-associations.resolver.ts create mode 100644 src/app/accounting/common-resolvers/currencies.resolver.ts create mode 100644 src/app/accounting/common-resolvers/gl-accounts.resolver.ts create mode 100644 src/app/accounting/common-resolvers/loan-products.resolver.ts create mode 100644 src/app/accounting/common-resolvers/offices.resolver.ts create mode 100644 src/app/accounting/common-resolvers/payment-types.resolver.ts create mode 100644 src/app/accounting/common-resolvers/provisioning-categories.resolver.ts create mode 100644 src/app/accounting/financial-activity-mappings/create-financial-activity-mapping/financial-activity-mappings-template.resolver.ts create mode 100644 src/app/accounting/financial-activity-mappings/edit-financial-activity-mapping/financial-activity-mapping-and-template.resolver.ts create mode 100644 src/app/accounting/financial-activity-mappings/financial-activity-mappings.resolver.ts create mode 100644 src/app/accounting/financial-activity-mappings/view-financial-activity-mapping/financial-activity-mapping.resolver.ts create mode 100644 src/app/accounting/migrate-opening-balances/only-one-of-the-fields-is-required.validator.ts create mode 100644 src/app/accounting/provisioning-entries/provisioning-entries.resolver.ts create mode 100644 src/app/accounting/provisioning-entries/view-provisioning-entry/provisioning-entry-entries.resolver.ts create mode 100644 src/app/accounting/provisioning-entries/view-provisioning-entry/provisioning-entry.resolver.ts create mode 100644 src/app/accounting/provisioning-entries/view-provisioning-journal-entries/provisioning-journal-entries.resolver.ts create mode 100644 src/app/accounting/view-transaction/transaction.resolver.ts diff --git a/.stylelintrc b/.stylelintrc index f506c6c67..1c46f7847 100644 --- a/.stylelintrc +++ b/.stylelintrc @@ -23,7 +23,12 @@ "selector-class-pattern": "[a-z-]+", "selector-id-pattern": "[a-z-]+", "selector-max-id": 1, - "selector-no-qualifying-type": true, + "selector-no-qualifying-type": [ + true, + { + "ignore": ["class"] + } + ], "selector-max-universal": 0, "selector-type-no-unknown": [ true, diff --git a/src/app/accounting/accounting-routing.module.ts b/src/app/accounting/accounting-routing.module.ts index 386ae3f96..715ea2d21 100644 --- a/src/app/accounting/accounting-routing.module.ts +++ b/src/app/accounting/accounting-routing.module.ts @@ -1,3 +1,6 @@ +/** TODO: Separate routing into feature modules for cleaner accounting module. */ + +/** Angular Imports */ import { NgModule } from '@angular/core'; import { Routes, RouterModule } from '@angular/router'; @@ -7,24 +10,21 @@ import { Route } from '../core/route/route.service'; /** Translation Imports */ import { extract } from '../core/i18n/i18n.service'; +/** Custom Components */ import { AccountingComponent } from './accounting.component'; import { FrequentPostingsComponent } from './frequent-postings/frequent-postings.component'; -import { ViewTransactionComponent } from './view-transaction/view-transaction.component'; import { CreateJournalEntryComponent } from './create-journal-entry/create-journal-entry.component'; import { SearchJournalEntryComponent } from './search-journal-entry/search-journal-entry.component'; +import { ViewTransactionComponent } from './view-transaction/view-transaction.component'; import { FinancialActivityMappingsComponent } from './financial-activity-mappings/financial-activity-mappings.component'; import { CreateFinancialActivityMappingComponent } from './financial-activity-mappings/create-financial-activity-mapping/create-financial-activity-mapping.component'; import { ViewFinancialActivityMappingComponent } from './financial-activity-mappings/view-financial-activity-mapping/view-financial-activity-mapping.component'; import { EditFinancialActivityMappingComponent } from './financial-activity-mappings/edit-financial-activity-mapping/edit-financial-activity-mapping.component'; +import { MigrateOpeningBalancesComponent } from './migrate-opening-balances/migrate-opening-balances.component'; import { ChartOfAccountsComponent } from './chart-of-accounts/chart-of-accounts.component'; - -import { ChartOfAccountsResolver } from './chart-of-accounts/chart-of-accounts.resolver'; - import { CreateGlAccountComponent } from './chart-of-accounts/create-gl-account/create-gl-account.component'; import { ViewGlAccountComponent } from './chart-of-accounts/view-gl-account/view-gl-account.component'; -import { ViewGlAccountResolver } from './chart-of-accounts/view-gl-account/view-gl-account.resolver'; import { EditGlAccountComponent } from './chart-of-accounts/edit-gl-account/edit-gl-account.component'; - import { ClosingEntriesComponent } from './closing-entries/closing-entries.component'; import { CreateClosureComponent } from './closing-entries/create-closure/create-closure.component'; import { ViewClosureComponent } from './closing-entries/view-closure/view-closure.component'; @@ -32,15 +32,40 @@ import { EditClosureComponent } from './closing-entries/edit-closure/edit-closur import { AccountingRulesComponent } from './accounting-rules/accounting-rules.component'; import { CreateRuleComponent } from './accounting-rules/create-rule/create-rule.component'; import { ViewRuleComponent } from './accounting-rules/view-rule/view-rule.component'; -import { ViewAccountingRuleResolver } from './accounting-rules/view-rule/view-rule.resolver'; import { EditRuleComponent } from './accounting-rules/edit-rule/edit-rule.component'; import { PeriodicAccrualsComponent } from './periodic-accruals/periodic-accruals.component'; import { ProvisioningEntriesComponent } from './provisioning-entries/provisioning-entries.component'; import { CreateProvisioningEntryComponent } from './provisioning-entries/create-provisioning-entry/create-provisioning-entry.component'; import { ViewProvisioningEntryComponent } from './provisioning-entries/view-provisioning-entry/view-provisioning-entry.component'; import { ViewProvisioningJournalEntriesComponent } from './provisioning-entries/view-provisioning-journal-entries/view-provisioning-journal-entries.component'; -import { MigrateOpeningBalancesComponent } from './migrate-opening-balances/migrate-opening-balances.component'; +/** Custom Resolvers */ +import { OfficesResolver } from './common-resolvers/offices.resolver'; +import { AccountingRulesAssociationsResolver } from './common-resolvers/accounting-rules-associations.resolver'; +import { CurrenciesResolver } from './common-resolvers/currencies.resolver'; +import { PaymentTypesResolver } from './common-resolvers/payment-types.resolver'; +import { GlAccountsResolver } from './common-resolvers/gl-accounts.resolver'; +import { TransactionResolver } from './view-transaction/transaction.resolver'; +import { FinancialActivityMappingsResolver } from './financial-activity-mappings/financial-activity-mappings.resolver'; +import { FinancialActivityMappingsTemplateResolver } from './financial-activity-mappings/create-financial-activity-mapping/financial-activity-mappings-template.resolver'; +import { FinancialActivityMappingResolver } from './financial-activity-mappings/view-financial-activity-mapping/financial-activity-mapping.resolver'; +import { FinancialActivityMappingAndTemplateResolver } from './financial-activity-mappings/edit-financial-activity-mapping/financial-activity-mapping-and-template.resolver'; +import { ChartOfAccountsResolver } from './chart-of-accounts/chart-of-accounts.resolver'; +import { ChartOfAccountsTemplateResolver } from './chart-of-accounts/create-gl-account/chart-of-accounts-template.resolver'; +import { GlAccountAndChartOfAccountsTemplateResolver } from './chart-of-accounts/gl-account-and-chart-of-accounts-template.resolver'; +import { ClosingEntriesResolver } from './closing-entries/closing-entries.resolver'; +import { ClosingEntryResolver } from './closing-entries/closing-entry.resolver'; +import { AccountingRulesResolver } from './accounting-rules/accounting-rules.resolver'; +import { AccountingRulesTemplateResolver } from './accounting-rules/accounting-rules-template.resolver'; +import { AccountingRuleResolver } from './accounting-rules/accounting-rule.resolver'; +import { ProvisioningEntriesResolver } from './provisioning-entries/provisioning-entries.resolver'; +import { ProvisioningEntryResolver } from './provisioning-entries/view-provisioning-entry/provisioning-entry.resolver'; +import { ProvisioningEntryEntriesResolver } from './provisioning-entries/view-provisioning-entry/provisioning-entry-entries.resolver'; +import { LoanProductsResolver } from './common-resolvers/loan-products.resolver'; +import { ProvisioningCategoriesResolver } from './common-resolvers/provisioning-categories.resolver'; +import { ProvisioningJournalEntriesResolver } from './provisioning-entries/view-provisioning-journal-entries/provisioning-journal-entries.resolver'; + +/** Accounting Routes */ const routes: Routes = [ Route.withShell([ { @@ -52,43 +77,73 @@ const routes: Routes = [ component: AccountingComponent }, { - path: 'frequent-postings', - component: FrequentPostingsComponent, - data: { title: extract('Frequent Postings'), breadcrumb: 'Frequent Postings' } - }, - { - path: 'transactions', - data: { title: extract('Transactions'), breadcrumb: 'Transactions' }, + path: 'journal-entries', + data: { title: extract('Search Journal Entry'), breadcrumb: 'Journal Entries' }, children: [ { - path: 'view/:id', - component: ViewTransactionComponent, - data: { title: extract('View Transaction'), routeParamBreadcrumb: 'id' } - } + path: '', + component: SearchJournalEntryComponent, + resolve: { + offices: OfficesResolver, + glAccounts: GlAccountsResolver + }, + }, + { + path: 'frequent-postings', + component: FrequentPostingsComponent, + data: { title: extract('Frequent Postings'), breadcrumb: 'Frequent Postings' }, + resolve: { + offices: OfficesResolver, + accountingRules: AccountingRulesAssociationsResolver, + currencies: CurrenciesResolver, + paymentTypes: PaymentTypesResolver + } + }, + { + path: 'create', + component: CreateJournalEntryComponent, + data: { title: extract('Create Journal Entry'), breadcrumb: 'Create' }, + resolve: { + offices: OfficesResolver, + currencies: CurrenciesResolver, + paymentTypes: PaymentTypesResolver, + glAccounts: GlAccountsResolver + } + }, + { + path: 'transactions', + data: { title: extract('Transactions'), breadcrumb: 'Transactions', addBreadcrumbLink: false }, + children: [ + { + path: 'view/:id', + component: ViewTransactionComponent, + data: { title: extract('View Transaction'), routeParamBreadcrumb: 'id' }, + resolve: { + transaction: TransactionResolver + } + } + ] + }, ] }, - { - path: 'journal-entries', - component: SearchJournalEntryComponent, - data: { title: extract('Search Journal Entry'), breadcrumb: 'Search Journal Entry' } - }, - { - path: 'journal-entries/create', - component: CreateJournalEntryComponent, - data: { title: extract('Create Journal Entry'), breadcrumb: 'Create Journal Entry' } - }, { path: 'financial-activity-mappings', data: { title: extract('Financial Activity Mappings'), breadcrumb: 'Financial Activity Mappings' }, children: [ { path: '', - component: FinancialActivityMappingsComponent + component: FinancialActivityMappingsComponent, + resolve: { + financialActivityAccounts: FinancialActivityMappingsResolver + } }, { path: 'create', component: CreateFinancialActivityMappingComponent, data: { title: extract('Create Financial Activity Mapping'), breadcrumb: 'Create' }, + resolve: { + financialActivityAccountsTemplate: FinancialActivityMappingsTemplateResolver + } }, { path: 'view/:id', @@ -96,52 +151,72 @@ const routes: Routes = [ children: [ { path: '', - component: ViewFinancialActivityMappingComponent + component: ViewFinancialActivityMappingComponent, + resolve: { + financialActivityAccount: FinancialActivityMappingResolver + } }, { path: 'edit', component: EditFinancialActivityMappingComponent, - data: { title: extract('Edit Financial Activity Mapping'), breadcrumb: 'Edit', routeParamBreadcrumb: false } + data: { title: extract('Edit Financial Activity Mapping'), breadcrumb: 'Edit', routeParamBreadcrumb: false }, + resolve: { + financialActivityAccountAndTemplate: FinancialActivityMappingAndTemplateResolver + } } ] } ] }, + { + path: 'migrate-opening-balances', + component: MigrateOpeningBalancesComponent, + data: { title: extract('Migrate Opening Balances'), breadcrumb: 'Migrate Opening Balances' }, + resolve: { + offices: OfficesResolver, + currencies: CurrenciesResolver + } + }, { path: 'chart-of-accounts', data: { title: extract('Chart of Accounts'), breadcrumb: 'Chart of Accounts' }, - resolve: { - glAccountData: ChartOfAccountsResolver - }, children: [ { path: '', component: ChartOfAccountsComponent, resolve: { - glAccountData: ChartOfAccountsResolver + chartOfAccounts: ChartOfAccountsResolver } }, { - path: 'gl-accounts/create', - component: CreateGlAccountComponent, - data: { title: extract('Create GL Account'), breadcrumb: 'Create GL Account' }, - }, - { - path: 'gl-accounts/view/:id', - data: { title: extract('View GL Account'), routeResolveBreadcrumb: ['glAccount', 'name'] }, - resolve: { - glAccount: ViewGlAccountResolver - }, - runGuardsAndResolvers: 'always', + path: 'gl-accounts', children: [ { - path: '', - component: ViewGlAccountComponent + path: 'create', + component: CreateGlAccountComponent, + data: { title: extract('Create GL Account'), breadcrumb: 'Create GL Account' }, + resolve: { + chartOfAccountsTemplate: ChartOfAccountsTemplateResolver + } }, { - path: 'edit', - component: EditGlAccountComponent, - data: { title: extract('Edit GL Account'), breadcrumb: 'Edit', routeResolveBreadcrumb: false } + path: 'view/:id', + data: { title: extract('View GL Account'), routeResolveBreadcrumb: ['glAccountAndChartOfAccountsTemplate', 'name'] }, + resolve: { + glAccountAndChartOfAccountsTemplate: GlAccountAndChartOfAccountsTemplateResolver + }, + runGuardsAndResolvers: 'always', + children: [ + { + path: '', + component: ViewGlAccountComponent + }, + { + path: 'edit', + component: EditGlAccountComponent, + data: { title: extract('Edit GL Account'), breadcrumb: 'Edit', routeResolveBreadcrumb: false } + } + ] } ] } @@ -153,16 +228,27 @@ const routes: Routes = [ children: [ { path: '', - component: ClosingEntriesComponent + component: ClosingEntriesComponent, + resolve: { + offices: OfficesResolver, + glAccountClosures: ClosingEntriesResolver + } }, { path: 'create', component: CreateClosureComponent, - data: { title: extract('Create Accounting Closure'), breadcrumb: 'Create' } + data: { title: extract('Create Accounting Closure'), breadcrumb: 'Create' }, + resolve: { + offices: OfficesResolver + } }, { path: 'view/:id', data: { title: extract('View Accounting Closure'), routeParamBreadcrumb: 'id' }, + resolve: { + glAccountClosure: ClosingEntryResolver + }, + runGuardsAndResolvers: 'always', children: [ { path: '', @@ -183,18 +269,24 @@ const routes: Routes = [ children: [ { path: '', - component: AccountingRulesComponent + component: AccountingRulesComponent, + resolve: { + accountingRules: AccountingRulesResolver + } }, { path: 'create', component: CreateRuleComponent, - data: { title: extract('Create Accounting Rule'), breadcrumb: 'Create' } + data: { title: extract('Create Accounting Rule'), breadcrumb: 'Create' }, + resolve: { + accountingRulesTemplate: AccountingRulesTemplateResolver + } }, { path: 'view/:id', data: { title: extract('View Accounting Rule'), routeResolveBreadcrumb: ['accountingRule', 'name'] }, resolve: { - accountingRule: ViewAccountingRuleResolver + accountingRule: AccountingRuleResolver }, runGuardsAndResolvers: 'always', children: [ @@ -205,7 +297,10 @@ const routes: Routes = [ { path: 'edit', component: EditRuleComponent, - data: { title: extract('Edit Accounting Rules'), breadcrumb: 'Edit', routeResolveBreadcrumb: false } + data: { title: extract('Edit Accounting Rules'), breadcrumb: 'Edit', routeResolveBreadcrumb: false }, + resolve: { + accountingRulesTemplate: AccountingRulesTemplateResolver + } } ] } @@ -222,7 +317,10 @@ const routes: Routes = [ children: [ { path: '', - component: ProvisioningEntriesComponent + component: ProvisioningEntriesComponent, + resolve: { + provisioningEntries: ProvisioningEntriesResolver + } }, { path: 'create', @@ -232,38 +330,69 @@ const routes: Routes = [ { path: 'view/:id', component: ViewProvisioningEntryComponent, - data: { title: extract('View Provisioning Entry'), routeParamBreadcrumb: 'id' } + data: { title: extract('View Provisioning Entry'), routeParamBreadcrumb: 'id' }, + resolve: { + provisioningEntry: ProvisioningEntryResolver, + provisioningEntryEntries: ProvisioningEntryEntriesResolver, + office: OfficesResolver, + loanProducts: LoanProductsResolver, + provisioningCategories: ProvisioningCategoriesResolver + } }, { path: 'journal-entries', - data: { title: extract('View Provisioning Journal Entry'), breadcrumb: 'Journal Entries', addBreadcrumbLink: false }, + data: { breadcrumb: 'Journal Entries', addBreadcrumbLink: false }, children: [ { path: 'view/:id', component: ViewProvisioningJournalEntriesComponent, - data: { title: extract('View Provisioning Journal Entry'), routeParamBreadcrumb: 'id' } + data: { title: extract('View Provisioning Journal Entry'), routeParamBreadcrumb: 'id' }, + resolve: { + provisioningJournalEntries: ProvisioningJournalEntriesResolver + } } ] } ] - }, - { - path: 'migrate-opening-balances', - component: MigrateOpeningBalancesComponent, - data: { title: extract('Migrate Opening Balances'), breadcrumb: 'Migrate Opening Balances' } } ] } ]) ]; +/** + * Accounting Routing Module + * + * Configures the accounting routes. + */ @NgModule({ imports: [RouterModule.forChild(routes)], exports: [RouterModule], providers: [ + OfficesResolver, + AccountingRulesAssociationsResolver, + CurrenciesResolver, + PaymentTypesResolver, + GlAccountsResolver, + TransactionResolver, + FinancialActivityMappingsResolver, + FinancialActivityMappingsTemplateResolver, + FinancialActivityMappingResolver, + FinancialActivityMappingAndTemplateResolver, ChartOfAccountsResolver, - ViewGlAccountResolver, - ViewAccountingRuleResolver + ChartOfAccountsTemplateResolver, + GlAccountAndChartOfAccountsTemplateResolver, + ClosingEntriesResolver, + ClosingEntryResolver, + AccountingRulesResolver, + AccountingRulesTemplateResolver, + AccountingRuleResolver, + ProvisioningEntriesResolver, + ProvisioningEntryResolver, + ProvisioningEntryEntriesResolver, + LoanProductsResolver, + ProvisioningCategoriesResolver, + ProvisioningJournalEntriesResolver ] }) export class AccountingRoutingModule { } diff --git a/src/app/accounting/accounting-rules/accounting-rule.resolver.ts b/src/app/accounting/accounting-rules/accounting-rule.resolver.ts new file mode 100644 index 000000000..056b6d060 --- /dev/null +++ b/src/app/accounting/accounting-rules/accounting-rule.resolver.ts @@ -0,0 +1,31 @@ +/** Angular Imports */ +import { Injectable } from '@angular/core'; +import { Resolve, ActivatedRouteSnapshot } from '@angular/router'; + +/** rxjs Imports */ +import { Observable } from 'rxjs'; + +/** Custom Services */ +import { AccountingService } from '../accounting.service'; + +/** + * Accounting rule data resolver. + */ +@Injectable() +export class AccountingRuleResolver implements Resolve { + + /** + * @param {AccountingService} accountingService Accounting service. + */ + constructor(private accountingService: AccountingService) {} + + /** + * Returns the accounting rule data. + * @returns {Observable} + */ + resolve(route: ActivatedRouteSnapshot): Observable { + const id = route.paramMap.get('id'); + return this.accountingService.getAccountingRule(id); + } + +} diff --git a/src/app/accounting/accounting-rules/accounting-rules-template.resolver.ts b/src/app/accounting/accounting-rules/accounting-rules-template.resolver.ts new file mode 100644 index 000000000..967e2fc00 --- /dev/null +++ b/src/app/accounting/accounting-rules/accounting-rules-template.resolver.ts @@ -0,0 +1,30 @@ +/** Angular Imports */ +import { Injectable } from '@angular/core'; +import { Resolve } from '@angular/router'; + +/** rxjs Imports */ +import { Observable } from 'rxjs'; + +/** Custom Services */ +import { AccountingService } from '../accounting.service'; + +/** + * Accounting rules template data resolver. + */ +@Injectable() +export class AccountingRulesTemplateResolver implements Resolve { + + /** + * @param {AccountingService} accountingService Accounting service. + */ + constructor(private accountingService: AccountingService) {} + + /** + * Returns the accounting rules template data. + * @returns {Observable} + */ + resolve(): Observable { + return this.accountingService.getAccountingRulesTemplate(); + } + +} diff --git a/src/app/accounting/accounting-rules/accounting-rules.component.html b/src/app/accounting/accounting-rules/accounting-rules.component.html index 747a9ffb5..36bc2702b 100644 --- a/src/app/accounting/accounting-rules/accounting-rules.component.html +++ b/src/app/accounting/accounting-rules/accounting-rules.component.html @@ -1,14 +1,16 @@
-
-
+
- + Filter +
@@ -47,7 +49,7 @@ - + diff --git a/src/app/accounting/accounting-rules/accounting-rules.component.scss b/src/app/accounting/accounting-rules/accounting-rules.component.scss index 1922e7ffa..24edb63bb 100644 --- a/src/app/accounting/accounting-rules/accounting-rules.component.scss +++ b/src/app/accounting/accounting-rules/accounting-rules.component.scss @@ -1,3 +1,7 @@ table { width: 100%; + + .select-row:hover { + cursor: pointer; + } } diff --git a/src/app/accounting/accounting-rules/accounting-rules.component.ts b/src/app/accounting/accounting-rules/accounting-rules.component.ts index 85b37960f..2b9c6dfd7 100644 --- a/src/app/accounting/accounting-rules/accounting-rules.component.ts +++ b/src/app/accounting/accounting-rules/accounting-rules.component.ts @@ -1,7 +1,11 @@ +/** Angular Imports */ import { Component, OnInit, ViewChild } from '@angular/core'; import { MatPaginator, MatSort, MatTableDataSource } from '@angular/material'; -import { AccountingService } from '../accounting.service'; +import { ActivatedRoute } from '@angular/router'; +/** + * Accounting rules component. + */ @Component({ selector: 'mifosx-accounting-rules', templateUrl: './accounting-rules.component.html', @@ -9,37 +13,59 @@ import { AccountingService } from '../accounting.service'; }) export class AccountingRulesComponent implements OnInit { + /** Accounting rule data. */ + accountingRuleData: any; + /** Columns to be displayed in accounting rules table. */ displayedColumns: string[] = ['name', 'officeName', 'debitTags', 'debitAccount', 'creditTags', 'creditAccount']; - dataSource: any; + /** Data source for accounting rules table. */ + dataSource: MatTableDataSource; + /** Paginator for accounting rules table. */ @ViewChild(MatPaginator) paginator: MatPaginator; + /** Sorter for accounting rules table. */ @ViewChild(MatSort) sort: MatSort; - constructor(private accountingService: AccountingService) { } - - ngOnInit() { - this.getAccountingRules(); - } - - getAccountingRules() { - this.accountingService.getAccountingRules().subscribe(accountingRules => { - accountingRules.forEach((accountingRule: any) => { - accountingRule.debitTags = accountingRule.debitTags ? accountingRule.debitTags.map((debitTag: any) => debitTag.tag.name).join(', ') : ''; - accountingRule.creditTags = accountingRule.creditTags ? accountingRule.creditTags.map((creditTag: any) => creditTag.tag.name).join(', ') : ''; - }); - this.dataSource = new MatTableDataSource(accountingRules); - this.dataSource.paginator = this.paginator; - this.dataSource.sortingDataAccessor = (accountingRule: any, property: any) => { - switch (property) { - case 'debitAccount': return accountingRule.debitAccounts[0].name; - case 'creditAccount': return accountingRule.creditAccounts[0].name; - default: return accountingRule[property]; - } - }; - this.dataSource.sort = this.sort; + /** + * Retrieves the accounting rules data from `resolve`. + * @param {ActivatedRoute} route Activated Route. + */ + constructor(private route: ActivatedRoute) { + this.route.data.subscribe((data: { accountingRules: any }) => { + this.accountingRuleData = data.accountingRules; }); } + /** + * Sets the accounting rules table. + */ + ngOnInit() { + this.setAccountingRules(); + } + + /** + * Initializes the data source, paginator and sorter for accounting rules table. + */ + setAccountingRules() { + this.accountingRuleData.forEach((accountingRule: any) => { + accountingRule.debitTags = accountingRule.debitTags ? accountingRule.debitTags.map((debitTag: any) => debitTag.tag.name).join(', ') : ''; + accountingRule.creditTags = accountingRule.creditTags ? accountingRule.creditTags.map((creditTag: any) => creditTag.tag.name).join(', ') : ''; + }); + this.dataSource = new MatTableDataSource(this.accountingRuleData); + this.dataSource.paginator = this.paginator; + this.dataSource.sortingDataAccessor = (accountingRule: any, property: any) => { + switch (property) { + case 'debitAccount': return accountingRule.debitAccounts[0].name; + case 'creditAccount': return accountingRule.creditAccounts[0].name; + default: return accountingRule[property]; + } + }; + this.dataSource.sort = this.sort; + } + + /** + * Filters data in accounting rules table based on passed value. + * @param {string} filterValue Value to filter data. + */ applyFilter(filterValue: string) { this.dataSource.filter = filterValue.trim().toLowerCase(); } diff --git a/src/app/accounting/accounting-rules/accounting-rules.resolver.ts b/src/app/accounting/accounting-rules/accounting-rules.resolver.ts new file mode 100644 index 000000000..f19b3944c --- /dev/null +++ b/src/app/accounting/accounting-rules/accounting-rules.resolver.ts @@ -0,0 +1,30 @@ +/** Angular Imports */ +import { Injectable } from '@angular/core'; +import { Resolve } from '@angular/router'; + +/** rxjs Imports */ +import { Observable } from 'rxjs'; + +/** Custom Services */ +import { AccountingService } from '../accounting.service'; + +/** + * Accounting rules data resolver. + */ +@Injectable() +export class AccountingRulesResolver implements Resolve { + + /** + * @param {AccountingService} accountingService Accounting service. + */ + constructor(private accountingService: AccountingService) {} + + /** + * Returns the accounting rules data. + * @returns {Observable} + */ + resolve(): Observable { + return this.accountingService.getAccountingRules(); + } + +} diff --git a/src/app/accounting/accounting-rules/create-rule/create-rule.component.html b/src/app/accounting/accounting-rules/create-rule/create-rule.component.html index e92880aeb..63c096cd9 100644 --- a/src/app/accounting/accounting-rules/create-rule/create-rule.component.html +++ b/src/app/accounting/accounting-rules/create-rule/create-rule.component.html @@ -9,19 +9,27 @@
- + Accounting Rule Name + + + Accounting Rule Name is required + - + Office + {{ office.name }} + + Office is required +
- + Affected GL Entry (Debit) Rule Type * Fixed Account List of Accounts @@ -29,7 +37,8 @@
- + Account to Debit + {{ glAccount.name }} @@ -38,22 +47,23 @@
- + Debit Tags + {{ debitTag.name }} -
- +
+ Multiple Debit Entries Allowed
- + Affected GL Entry (Credit) Rule Type * Fixed Account List of Accounts @@ -61,7 +71,8 @@
- + Account to Credit + {{ glAccount.name }} @@ -70,22 +81,24 @@
- + Credit Tags + {{ creditTag.name }} -
- +
+ Multiple Credit Entries Allowed
- + Description +
@@ -93,8 +106,8 @@ - - + + diff --git a/src/app/accounting/accounting-rules/create-rule/create-rule.component.scss b/src/app/accounting/accounting-rules/create-rule/create-rule.component.scss index 47139b9fc..9f423ad35 100644 --- a/src/app/accounting/accounting-rules/create-rule/create-rule.component.scss +++ b/src/app/accounting/accounting-rules/create-rule/create-rule.component.scss @@ -2,11 +2,6 @@ margin: 17.5px 0 0 0; } -.additional-selection { - opacity: 0.75; - font-size: 0.75em; -} - .entries-wrapper { position: relative; diff --git a/src/app/accounting/accounting-rules/create-rule/create-rule.component.ts b/src/app/accounting/accounting-rules/create-rule/create-rule.component.ts index cee1fb9a9..7b0529cd7 100644 --- a/src/app/accounting/accounting-rules/create-rule/create-rule.component.ts +++ b/src/app/accounting/accounting-rules/create-rule/create-rule.component.ts @@ -1,17 +1,17 @@ +/** Angular Imports */ import { Component, OnInit } from '@angular/core'; -import { FormGroup, FormBuilder, Validators, ValidatorFn, ValidationErrors } from '@angular/forms'; -import { Router } from '@angular/router'; +import { FormGroup, FormBuilder, Validators } from '@angular/forms'; +import { Router, ActivatedRoute } from '@angular/router'; +/** Custom Services */ import { AccountingService } from '../../accounting.service'; -const oneOfTheFieldsIsRequiredValidator: ValidatorFn = (accountingRuleForm: FormGroup): ValidationErrors | null => { - const accountToDebit = accountingRuleForm.controls.accountToDebit.value; - const debitTags = accountingRuleForm.controls.debitTags.value; - const accountToCredit = accountingRuleForm.controls.accountToCredit.value; - const creditTags = accountingRuleForm.controls.creditTags.value; - return ((accountToDebit || debitTags) && (accountToCredit || creditTags)) ? null : { 'error': true }; -}; +/** Custom Validators */ +import { oneOfTheFieldsIsRequiredValidator } from '../one-of-the-fields-is-required.validator'; +/** + * Create accounting rule component. + */ @Component({ selector: 'mifosx-create-rule', templateUrl: './create-rule.component.html', @@ -19,23 +19,48 @@ const oneOfTheFieldsIsRequiredValidator: ValidatorFn = (accountingRuleForm: Form }) export class CreateRuleComponent implements OnInit { + /** Accounting rule form. */ accountingRuleForm: FormGroup; + /** Office data. */ officeData: any; + /** GL Account data. */ glAccountData: any; + /** Debit tag data. */ debitTagData: any; + /** Credit tag data. */ creditTagData: any; + /** + * Retrieves the offices, gl accounts, debit tags and credit tags data from `resolve`. + * @param {FormBuilder} formBuilder Form Builder. + * @param {AccountingService} accountingService Accounting Service. + * @param {ActivatedRoute} route Activated Route. + * @param {Router} router Router for navigation. + */ constructor(private formBuilder: FormBuilder, private accountingService: AccountingService, - private router: Router) { } + private route: ActivatedRoute, + private router: Router) { + this.route.data.subscribe((data: { accountingRulesTemplate: any }) => { + this.officeData = data.accountingRulesTemplate.allowedOffices; + this.glAccountData = data.accountingRulesTemplate.allowedAccounts; + this.debitTagData = data.accountingRulesTemplate.allowedDebitTagOptions; + this.creditTagData = data.accountingRulesTemplate.allowedCreditTagOptions; + }); + } + /** + * Creates and sets accounting rules form. + */ ngOnInit() { - this.createAccountingRulesForm(); - this.getAccountingRulesTemplate(); + this.createAccountingRuleForm(); this.setAccountingRulesForm(); } - createAccountingRulesForm() { + /** + * Creates accounting rule form. + */ + createAccountingRuleForm() { this.accountingRuleForm = this.formBuilder.group({ 'name': ['', Validators.required], 'officeId': ['', Validators.required], @@ -51,40 +76,52 @@ export class CreateRuleComponent implements OnInit { }, { validator: oneOfTheFieldsIsRequiredValidator }); } + /** + * Sets accounting rule form for selected accounting rule type. + */ setAccountingRulesForm() { - this.accountingRuleForm.get('debitRuleType').valueChanges.subscribe(debitRuleType => { + this.accountingRuleForm.get('debitRuleType').valueChanges.subscribe((debitRuleType: string) => { if (debitRuleType === 'fixedAccount') { this.accountingRuleForm.get('debitTags').reset(); this.accountingRuleForm.get('allowMultipleDebitEntries').reset(); } else { + this.accountingRuleForm.get('accountToDebit').reset(); this.accountingRuleForm.get('allowMultipleDebitEntries').setValue(false); } }); - this.accountingRuleForm.get('creditRuleType').valueChanges.subscribe(creditRuleType => { + this.accountingRuleForm.get('creditRuleType').valueChanges.subscribe((creditRuleType: string) => { if (creditRuleType === 'fixedAccount') { this.accountingRuleForm.get('creditTags').reset(); this.accountingRuleForm.get('allowMultipleCreditEntries').reset(); } else { + this.accountingRuleForm.get('accountToCredit').reset(); this.accountingRuleForm.get('allowMultipleCreditEntries').setValue(false); } }); } - getAccountingRulesTemplate() { - this.accountingService.getAccountingRulesTemplate().subscribe((accountingRulesData: any) => { - this.officeData = accountingRulesData.allowedOffices; - this.glAccountData = accountingRulesData.allowedAccounts; - this.debitTagData = accountingRulesData.allowedDebitTagOptions; - this.creditTagData = accountingRulesData.allowedCreditTagOptions; - }); - } - + /** + * Submits the accounting rule form and creates accounting rule, + * if successful redirects to view created rule. + */ submit() { const accountingRule = this.accountingRuleForm.value; + if (accountingRule.debitRuleType === 'fixedAccount') { + delete accountingRule.debitTags; + delete accountingRule.allowMultipleDebitEntries; + } else { + delete accountingRule.accountToDebit; + } + if (accountingRule.creditRuleType === 'fixedAccount') { + delete accountingRule.creditTags; + delete accountingRule.allowMultipleCreditEntries; + } else { + delete accountingRule.accountToCredit; + } delete accountingRule.debitRuleType; delete accountingRule.creditRuleType; this.accountingService.createAccountingRule(accountingRule).subscribe((response: any) => { - this.router.navigate(['/accounting/accounting-rules/view', response.resourceId]); + this.router.navigate(['../view', response.resourceId], { relativeTo: this.route }); }); } diff --git a/src/app/accounting/accounting-rules/edit-rule/edit-rule.component.html b/src/app/accounting/accounting-rules/edit-rule/edit-rule.component.html index c13907d9e..db1b9a2e7 100644 --- a/src/app/accounting/accounting-rules/edit-rule/edit-rule.component.html +++ b/src/app/accounting/accounting-rules/edit-rule/edit-rule.component.html @@ -9,19 +9,27 @@
- + Accounting Rule Name + + + Accounting Rule Name is required + - + Office + {{ office.name }} + + Office is required +
- + Affected GL Entry (Debit) Rule Type * Fixed Account List of Accounts @@ -29,7 +37,8 @@
- + Account to Debit + {{ glAccount.name }} @@ -38,22 +47,23 @@
- + Debit Tags + {{ debitTag.name }} -
- +
+ Multiple Debit Entries Allowed
- + Affected GL Entry (Credit) Rule Type * Fixed Account List of Accounts @@ -61,7 +71,8 @@
- + Account to Credit + {{ glAccount.name }} @@ -70,22 +81,24 @@
- + Credit Tags + {{ creditTag.name }} -
- +
+ Multiple Credit Entries Allowed
- + Description +
@@ -93,8 +106,8 @@ - - + + diff --git a/src/app/accounting/accounting-rules/edit-rule/edit-rule.component.scss b/src/app/accounting/accounting-rules/edit-rule/edit-rule.component.scss index 47139b9fc..9f423ad35 100644 --- a/src/app/accounting/accounting-rules/edit-rule/edit-rule.component.scss +++ b/src/app/accounting/accounting-rules/edit-rule/edit-rule.component.scss @@ -2,11 +2,6 @@ margin: 17.5px 0 0 0; } -.additional-selection { - opacity: 0.75; - font-size: 0.75em; -} - .entries-wrapper { position: relative; diff --git a/src/app/accounting/accounting-rules/edit-rule/edit-rule.component.ts b/src/app/accounting/accounting-rules/edit-rule/edit-rule.component.ts index d64f5a4a5..dcf02d968 100644 --- a/src/app/accounting/accounting-rules/edit-rule/edit-rule.component.ts +++ b/src/app/accounting/accounting-rules/edit-rule/edit-rule.component.ts @@ -1,17 +1,17 @@ +/** Angular Imports */ import { Component, OnInit } from '@angular/core'; -import { FormGroup, FormBuilder, Validators, ValidatorFn, ValidationErrors } from '@angular/forms'; +import { FormGroup, FormBuilder, Validators } from '@angular/forms'; import { Router, ActivatedRoute } from '@angular/router'; +/** Custom Services */ import { AccountingService } from '../../accounting.service'; -const oneOfTheFieldsIsRequiredValidator: ValidatorFn = (accountingRuleForm: FormGroup): ValidationErrors | null => { - const accountToDebit = accountingRuleForm.controls.accountToDebit.value; - const debitTags = accountingRuleForm.controls.debitTags.value; - const accountToCredit = accountingRuleForm.controls.accountToCredit.value; - const creditTags = accountingRuleForm.controls.creditTags.value; - return ((accountToDebit || debitTags) && (accountToCredit || creditTags)) ? null : { 'error': true }; -}; +/** Custom Validators */ +import { oneOfTheFieldsIsRequiredValidator } from '../one-of-the-fields-is-required.validator'; +/** + * Edit accounting rule component. + */ @Component({ selector: 'mifosx-edit-rule', templateUrl: './edit-rule.component.html', @@ -19,29 +19,54 @@ const oneOfTheFieldsIsRequiredValidator: ValidatorFn = (accountingRuleForm: Form }) export class EditRuleComponent implements OnInit { + /** Accounting rule form. */ accountingRuleForm: FormGroup; + /** Accounting rule. */ accountingRule: any; + /** Office data. */ officeData: any; + /** GL Account data. */ glAccountData: any; + /** Debit tag data. */ debitTagData: any; + /** Credit tag data. */ creditTagData: any; - constructor(private route: ActivatedRoute, - private formBuilder: FormBuilder, + /** + * Retrieves the offices, gl accounts, debit tags, credit tags and accounting rule data from `resolve`. + * @param {FormBuilder} formBuilder Form Builder. + * @param {AccountingService} accountingService Accounting Service. + * @param {ActivatedRoute} route Activated Route. + * @param {Router} router Router for navigation. + */ + constructor(private formBuilder: FormBuilder, private accountingService: AccountingService, + private route: ActivatedRoute, private router: Router) { - this.route.data.subscribe((data: { accountingRule: any }) => { - this.accountingRule = data.accountingRule; - }); + this.route.data.subscribe((data: { + accountingRulesTemplate: any, + accountingRule: any + }) => { + this.officeData = data.accountingRulesTemplate.allowedOffices; + this.glAccountData = data.accountingRulesTemplate.allowedAccounts; + this.debitTagData = data.accountingRulesTemplate.allowedDebitTagOptions; + this.creditTagData = data.accountingRulesTemplate.allowedCreditTagOptions; + this.accountingRule = data.accountingRule; + }); } + /** + * Creates and sets accounting rule form. + */ ngOnInit() { - this.createAccountingRulesForm(); - this.getAccountingRulesTemplate(); + this.createAccountingRuleForm(); this.setAccountingRulesForm(); } - createAccountingRulesForm() { + /** + * Creates accounting rule form. + */ + createAccountingRuleForm() { this.accountingRuleForm = this.formBuilder.group({ 'name': [this.accountingRule.name, Validators.required], 'officeId': [this.accountingRule.officeId, Validators.required], @@ -57,12 +82,16 @@ export class EditRuleComponent implements OnInit { }, { validator: oneOfTheFieldsIsRequiredValidator }); } + /** + * Sets accounting rule form for selected accounting rule type. + */ setAccountingRulesForm() { this.accountingRuleForm.get('debitRuleType').valueChanges.subscribe(debitRuleType => { if (debitRuleType === 'fixedAccount') { this.accountingRuleForm.get('debitTags').reset(); this.accountingRuleForm.get('allowMultipleDebitEntries').reset(); } else { + this.accountingRuleForm.get('accountToDebit').reset(); this.accountingRuleForm.get('allowMultipleDebitEntries').setValue(false); } }); @@ -71,6 +100,7 @@ export class EditRuleComponent implements OnInit { this.accountingRuleForm.get('creditTags').reset(); this.accountingRuleForm.get('allowMultipleCreditEntries').reset(); } else { + this.accountingRuleForm.get('accountToCredit').reset(); this.accountingRuleForm.get('allowMultipleCreditEntries').setValue(false); } }); @@ -94,21 +124,28 @@ export class EditRuleComponent implements OnInit { } } - getAccountingRulesTemplate() { - this.accountingService.getAccountingRulesTemplate().subscribe((accountingRulesData: any) => { - this.officeData = accountingRulesData.allowedOffices; - this.glAccountData = accountingRulesData.allowedAccounts; - this.debitTagData = accountingRulesData.allowedDebitTagOptions; - this.creditTagData = accountingRulesData.allowedCreditTagOptions; - }); - } - + /** + * Submits the accounting rule form and updates accounting rule, + * if successful redirects to view updated rule. + */ submit() { const accountingRule = this.accountingRuleForm.value; + if (accountingRule.debitRuleType === 'fixedAccount') { + delete accountingRule.debitTags; + delete accountingRule.allowMultipleDebitEntries; + } else { + delete accountingRule.accountToDebit; + } + if (accountingRule.creditRuleType === 'fixedAccount') { + delete accountingRule.creditTags; + delete accountingRule.allowMultipleCreditEntries; + } else { + delete accountingRule.accountToCredit; + } delete accountingRule.debitRuleType; delete accountingRule.creditRuleType; this.accountingService.updateAccountingRule(this.accountingRule.id, accountingRule).subscribe((response: any) => { - this.router.navigate(['/accounting/accounting-rules/view', response.resourceId]); + this.router.navigate(['../../', response.resourceId], { relativeTo: this.route }); }); } diff --git a/src/app/accounting/accounting-rules/one-of-the-fields-is-required.validator.ts b/src/app/accounting/accounting-rules/one-of-the-fields-is-required.validator.ts new file mode 100644 index 000000000..ca176ea4c --- /dev/null +++ b/src/app/accounting/accounting-rules/one-of-the-fields-is-required.validator.ts @@ -0,0 +1,11 @@ +/** Angular Imports */ +import { FormGroup, ValidatorFn, ValidationErrors } from '@angular/forms'; + +/** Validates that one of the fields for debit or credit is filled for a given accounting rule type. */ +export const oneOfTheFieldsIsRequiredValidator: ValidatorFn = (accountingRuleForm: FormGroup): ValidationErrors | null => { + const accountToDebit = accountingRuleForm.controls.accountToDebit.value; + const debitTags = accountingRuleForm.controls.debitTags.value; + const accountToCredit = accountingRuleForm.controls.accountToCredit.value; + const creditTags = accountingRuleForm.controls.creditTags.value; + return ((accountToDebit || debitTags) && (accountToCredit || creditTags)) ? null : { 'error': true }; +}; diff --git a/src/app/accounting/accounting-rules/view-rule/view-rule.component.html b/src/app/accounting/accounting-rules/view-rule/view-rule.component.html index eb1bdfa3a..54fc32775 100644 --- a/src/app/accounting/accounting-rules/view-rule/view-rule.component.html +++ b/src/app/accounting/accounting-rules/view-rule/view-rule.component.html @@ -1,9 +1,11 @@
-
@@ -25,11 +27,11 @@ {{ accountingRule.officeName }}
-
+
Description
-
+
{{ accountingRule.description }}
diff --git a/src/app/accounting/accounting-rules/view-rule/view-rule.component.scss b/src/app/accounting/accounting-rules/view-rule/view-rule.component.scss index e8dff44e8..b78000682 100644 --- a/src/app/accounting/accounting-rules/view-rule/view-rule.component.scss +++ b/src/app/accounting/accounting-rules/view-rule/view-rule.component.scss @@ -2,9 +2,9 @@ div { margin: 1rem 0; word-wrap: break-word; - } - .header { - font-weight: 500; + &.header { + font-weight: 500; + } } } diff --git a/src/app/accounting/accounting-rules/view-rule/view-rule.component.ts b/src/app/accounting/accounting-rules/view-rule/view-rule.component.ts index 2969a141e..dc34813ec 100644 --- a/src/app/accounting/accounting-rules/view-rule/view-rule.component.ts +++ b/src/app/accounting/accounting-rules/view-rule/view-rule.component.ts @@ -1,10 +1,17 @@ +/** Angular Imports */ import { Component, OnInit } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; import { MatDialog } from '@angular/material'; +/** Custom Services */ import { AccountingService } from '../../accounting.service'; + +/** Custom Components */ import { DeleteDialogComponent } from '../../../shared/delete-dialog/delete-dialog.component'; +/** + * View accounting rule component. + */ @Component({ selector: 'mifosx-view-rule', templateUrl: './view-rule.component.html', @@ -12,11 +19,19 @@ import { DeleteDialogComponent } from '../../../shared/delete-dialog/delete-dial }) export class ViewRuleComponent implements OnInit { + /** Accounting rule. */ accountingRule: any; - constructor(private route: ActivatedRoute, + /** + * Retrieves the accounting rule data from `resolve`. + * @param {AccountingService} accountingService Accounting Service. + * @param {ActivatedRoute} route Activated Route. + * @param {Router} router Router for navigation. + * @param {MatDialog} dialog Dialog reference. + */ + constructor(private accountingService: AccountingService, + private route: ActivatedRoute, private router: Router, - private accountingService: AccountingService, public dialog: MatDialog) { this.route.data.subscribe((data: { accountingRule: any }) => { this.accountingRule = data.accountingRule; @@ -26,6 +41,9 @@ export class ViewRuleComponent implements OnInit { ngOnInit() { } + /** + * Deletes the accounting rule and redirects to accounting rules. + */ deleteAccountingRule() { const deleteAccountingRuleDialogRef = this.dialog.open(DeleteDialogComponent, { data: { deleteContext: `accounting rule ${this.accountingRule.id}` } diff --git a/src/app/accounting/accounting-rules/view-rule/view-rule.resolver.ts b/src/app/accounting/accounting-rules/view-rule/view-rule.resolver.ts deleted file mode 100644 index 139ca9771..000000000 --- a/src/app/accounting/accounting-rules/view-rule/view-rule.resolver.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { Injectable } from '@angular/core'; -import { Resolve, ActivatedRouteSnapshot } from '@angular/router'; - -import { Observable } from 'rxjs'; - -import { AccountingService } from '../../accounting.service'; - -@Injectable() -export class ViewAccountingRuleResolver implements Resolve { - - constructor(private accountingService: AccountingService) {} - - resolve(route: ActivatedRouteSnapshot): Observable { - const id = route.paramMap.get('id'); - - return this.accountingService.getAccountingRule(id); - } - -} diff --git a/src/app/accounting/accounting.component.html b/src/app/accounting/accounting.component.html index a2a5ac5c5..9f5ded983 100644 --- a/src/app/accounting/accounting.component.html +++ b/src/app/accounting/accounting.component.html @@ -1,84 +1,107 @@ - +
-
+ -
+
- +
- - repeat -

Frequent Postings

-

These are predefined postings

-
+ - - add -

Create Journal Entries

-

Manual journal entry transactions recorded in a journal

-
+ + + + +

Frequent Postings

+

These are predefined postings

+
- - search -

Search Journal Entries

-

Advanced search option for journal entries

-
+ + + + +

Create Journal Entries

+

Manual journal entry transactions recorded in a journal

+
- - link -

Accounts Linked to Financial Activities

-

List of Financial Activities and GL Account Mappings

-
+ + + + +

Search Journal Entries

+

Advanced search option for journal entries

+
- - monetization_on -

Migrate Opening Balances (Office-wise)

-

Set or update office-level opening balances for GL accounts

-
+ + + + +

Accounts Linked to Financial Activities

+

List of Financial Activities and GL Account Mappings

+
-
+ + + + +

Migrate Opening Balances (Office-wise)

+

Set or update office-level opening balances for GL accounts

+
+ + + +
+ +
+ + + + + + + +

Chart of Accounts

+

List of accounts used by the organisation

+
+ + + + + +

Closing Entries

+

Journal entries made at the end of accounting period

+
+ + + + + +

Accounting Rules

+

List of all accounting rules

+
+ + + + + +

Accruals

+

Accrues income, expenses and liabilities as on the provided date

+
+ + + + + +

Provisioning Entries

+

Create provisioning entries

+
+ +
+ +
-
- - - - - device_hub -

Chart of Accounts

-

List of accounts used by the organisation

-
- - - archive -

Closing Entries

-

Journal entries made at the end of accounting period

-
- - - list -

Accounting Rules

-

List of all accounting rules

-
- - - calendar_today -

Accruals

-

Accrues income, expenses and liabilities as on the provided date

-
- - - settings_backup_restore -

Provisioning Entries

-

Create provisioning entries

-
- -
- -
- -
- -
+ +
diff --git a/src/app/accounting/accounting.component.ts b/src/app/accounting/accounting.component.ts index 7f23eaef4..588ab7ba9 100644 --- a/src/app/accounting/accounting.component.ts +++ b/src/app/accounting/accounting.component.ts @@ -1,5 +1,9 @@ +/** Angular Imports */ import { Component, OnInit } from '@angular/core'; +/** + * Accounting component. + */ @Component({ selector: 'mifosx-accounting', templateUrl: './accounting.component.html', diff --git a/src/app/accounting/accounting.module.ts b/src/app/accounting/accounting.module.ts index 1e0ab29b3..de8e5fd00 100644 --- a/src/app/accounting/accounting.module.ts +++ b/src/app/accounting/accounting.module.ts @@ -1,22 +1,24 @@ +/** Angular Imports */ import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { FormsModule, ReactiveFormsModule } from '@angular/forms'; -import { CoreModule } from '../core/core.module'; -import { AccountingComponent } from './accounting.component'; +/** Custom Modules */ +import { SharedModule } from '../shared/shared.module'; import { AccountingRoutingModule } from './accounting-routing.module'; + +/** Custom Components */ +import { AccountingComponent } from './accounting.component'; import { FrequentPostingsComponent } from './frequent-postings/frequent-postings.component'; +import { CreateJournalEntryComponent } from './create-journal-entry/create-journal-entry.component'; +import { SearchJournalEntryComponent } from './search-journal-entry/search-journal-entry.component'; import { ViewTransactionComponent } from './view-transaction/view-transaction.component'; import { RevertTransactionComponent } from './revert-transaction/revert-transaction.component'; import { ViewJournalEntryComponent } from './view-journal-entry/view-journal-entry.component'; -import { CreateJournalEntryComponent } from './create-journal-entry/create-journal-entry.component'; -import { SearchJournalEntryComponent } from './search-journal-entry/search-journal-entry.component'; import { FinancialActivityMappingsComponent } from './financial-activity-mappings/financial-activity-mappings.component'; import { CreateFinancialActivityMappingComponent } from './financial-activity-mappings/create-financial-activity-mapping/create-financial-activity-mapping.component'; import { ViewFinancialActivityMappingComponent } from './financial-activity-mappings/view-financial-activity-mapping/view-financial-activity-mapping.component'; import { EditFinancialActivityMappingComponent } from './financial-activity-mappings/edit-financial-activity-mapping/edit-financial-activity-mapping.component'; +import { MigrateOpeningBalancesComponent } from './migrate-opening-balances/migrate-opening-balances.component'; import { ChartOfAccountsComponent } from './chart-of-accounts/chart-of-accounts.component'; - import { CreateGlAccountComponent } from './chart-of-accounts/create-gl-account/create-gl-account.component'; import { ViewGlAccountComponent } from './chart-of-accounts/view-gl-account/view-gl-account.component'; import { EditGlAccountComponent } from './chart-of-accounts/edit-gl-account/edit-gl-account.component'; @@ -33,28 +35,30 @@ import { ProvisioningEntriesComponent } from './provisioning-entries/provisionin import { CreateProvisioningEntryComponent } from './provisioning-entries/create-provisioning-entry/create-provisioning-entry.component'; import { ViewProvisioningEntryComponent } from './provisioning-entries/view-provisioning-entry/view-provisioning-entry.component'; import { ViewProvisioningJournalEntriesComponent } from './provisioning-entries/view-provisioning-journal-entries/view-provisioning-journal-entries.component'; -import { MigrateOpeningBalancesComponent } from './migrate-opening-balances/migrate-opening-balances.component'; +/** + * Accounting Module + * + * All components related to accounting functions should be declared here. + */ @NgModule({ imports: [ - CoreModule, - CommonModule, - FormsModule, - ReactiveFormsModule, + SharedModule, AccountingRoutingModule ], declarations: [ AccountingComponent, FrequentPostingsComponent, + CreateJournalEntryComponent, + SearchJournalEntryComponent, ViewTransactionComponent, RevertTransactionComponent, ViewJournalEntryComponent, - CreateJournalEntryComponent, - SearchJournalEntryComponent, FinancialActivityMappingsComponent, CreateFinancialActivityMappingComponent, ViewFinancialActivityMappingComponent, EditFinancialActivityMappingComponent, + MigrateOpeningBalancesComponent, ChartOfAccountsComponent, CreateGlAccountComponent, ViewGlAccountComponent, @@ -71,8 +75,7 @@ import { MigrateOpeningBalancesComponent } from './migrate-opening-balances/migr ProvisioningEntriesComponent, CreateProvisioningEntryComponent, ViewProvisioningEntryComponent, - ViewProvisioningJournalEntriesComponent, - MigrateOpeningBalancesComponent + ViewProvisioningJournalEntriesComponent ], entryComponents: [ RevertTransactionComponent, diff --git a/src/app/accounting/accounting.service.ts b/src/app/accounting/accounting.service.ts index 5fc640420..68e767d88 100644 --- a/src/app/accounting/accounting.service.ts +++ b/src/app/accounting/accounting.service.ts @@ -1,19 +1,38 @@ +/** TODO: Separate services for feature modules for cleaner accounting service. */ + +/** Angular Imports */ import { Injectable } from '@angular/core'; import { HttpClient, HttpParams } from '@angular/common/http'; + +/** rxjs Imports */ import { Observable } from 'rxjs'; +/** + * Accounting service. + */ @Injectable({ providedIn: 'root' }) export class AccountingService { + /** + * @param {HttpClient} http Http Client to send requests. + */ constructor(private http: HttpClient) { } + /** + * @returns {Observable} Offices data ordered by id. + */ getOffices(): Observable { - return this.http.get('/offices?orderBy=id'); + const httpParams = new HttpParams().set('orderBy', 'id'); + return this.http.get('/offices', { params: httpParams }); } - getAccountingRules(useHttpParams?: boolean): Observable { + /** + * @param {boolean} useHttpParams True if http params should be used. + * @returns {Observable} Accounting rules data. + */ + getAccountingRules(useHttpParams: boolean = false): Observable { let httpParams = new HttpParams(); if (useHttpParams) { httpParams = httpParams.set('associations', 'all'); @@ -21,45 +40,80 @@ export class AccountingService { return this.http.get('/accountingrules', { params: httpParams }); } + /** + * @returns {Observable} Currencies data. + */ getCurrencies(): Observable { return this.http.get('/currencies'); } + /** + * @returns {Observable} Payment types data. + */ getPaymentTypes(): Observable { return this.http.get('/paymenttypes'); } + /** + * @param {any} journalEntry Journal entry to be created. + * @returns {Observable} + */ createJournalEntry(journalEntry: any): Observable { return this.http.post('/journalentries', journalEntry); } - getJournalEntry(transactionId: string) { - let httpParams = new HttpParams(); - httpParams = httpParams.set('transactionId', transactionId); - httpParams = httpParams.set('transactionDetails', 'true'); + /** + * @returns {Observable} GL Accounts. + */ + getGlAccounts(): Observable { + const httpParams = new HttpParams() + .set('manualEntriesAllowed', 'true') + .set('usage', '1') + .set('disabled', 'false'); + return this.http.get(`/glaccounts`, { params: httpParams }); + } + + /** + * @param transactionId Transaction ID of journal entries. + * @returns {Observable} Journal Entries. + */ + getJournalEntry(transactionId: string): Observable { + const httpParams = new HttpParams() + .set('transactionId', transactionId) + .set('transactionDetails', 'true'); return this.http.get(`/journalentries`, { params: httpParams }); } - // filterBy: officeId, glAccountId, manualEntriesOnly, fromDate, toDate, transactionId - getJournalEntries(filterBy: any, orderBy: string, sortOrder: string, offset: number, limit: number) { + /** + * @param {any} filterBy Properties by which entries should be filtered. + * @param {string} orderBy Property by which entries should be sorted. + * @param {string} sortOrder Sort order: ascending or descending. + * @param {number} offset Page offset. + * @param {number} limit Number of entries within the page. + * @returns {Observable} Journal Entries. + */ + getJournalEntries(filterBy: any, orderBy: string, sortOrder: string, offset: number, limit: number): Observable { let httpParams = new HttpParams() .set('offset', offset.toString()) .set('limit', limit.toString()) .set('sortOrder', sortOrder) .set('orderBy', orderBy); + // filterBy: officeId, glAccountId, manualEntriesOnly, fromDate, toDate, transactionId filterBy.forEach(function (filter: any) { if (filter.value) { httpParams = httpParams.set(filter.type, filter.value); } }); - return this.http.get('/journalentries', { - params: httpParams - }); + return this.http.get('/journalentries', { params: httpParams }); } - revertTransaction(transactionId: string, comments: string) { - let httpParams = new HttpParams(); - httpParams = httpParams.set('command', 'reverse'); + /** + * @param {string} transactionId Transaction ID to be reverted. + * @param {string} comments Any comments. + * @returns {Observable} + */ + revertTransaction(transactionId: string, comments: string): Observable { + const httpParams = new HttpParams().set('command', 'reverse'); let data = {}; if (comments) { data = { comments: comments }; @@ -67,158 +121,281 @@ export class AccountingService { return this.http.post(`/journalentries/${transactionId}`, data, { params: httpParams }); } - getGlAccounts() { - let httpParams = new HttpParams(); - httpParams = httpParams.set('manualEntriesAllowed', 'true'); - httpParams = httpParams.set('usage', '1'); - httpParams = httpParams.set('disabled', 'false'); - return this.http.get(`/glaccounts`, { params: httpParams }); - } - - getFinancialActivityAccounts() { + /** + * @returns {Observable} Financial activity accounts. + */ + getFinancialActivityAccounts(): Observable { return this.http.get('/financialactivityaccounts'); } - getFinancialActivityAccountsTemplate() { + /** + * @returns {Observable} Financial activity accounts template. + */ + getFinancialActivityAccountsTemplate(): Observable { return this.http.get('/financialactivityaccounts/template'); } - createFinancialActivityAccount(financialActivityAccount: any) { + /** + * @param {any} financialActivityAccount Financial activity account to be created. + * @returns {Observable} + */ + createFinancialActivityAccount(financialActivityAccount: any): Observable { return this.http.post('/financialactivityaccounts', financialActivityAccount); } - getFinancialActivityAccount(financialActivityAccountId: number, template: boolean) { + /** + * @param {string} financialActivityAccountId Financial activity account ID of financial activity account. + * @param {boolean} template True if template is required. + * @returns {Observable} Financial activity account. + */ + getFinancialActivityAccount(financialActivityAccountId: string, template: boolean = false): Observable { const httpParams = new HttpParams().set('template', template.toString()); return this.http.get(`/financialactivityaccounts/${financialActivityAccountId}`, { params: httpParams }); } - updateFinancialActivityAccount(financialActivityAccountId: number, financialActivityAccount: any) { + /** + * @param {string} financialActivityAccountId Financial activity account ID of financial activity account to be updated. + * @param {any} financialActivityAccount Financial activity account to be updated. + * @returns {Observable} + */ + updateFinancialActivityAccount(financialActivityAccountId: string, financialActivityAccount: any): Observable { return this.http.put(`/financialactivityaccounts/${financialActivityAccountId}`, financialActivityAccount); } - deleteFinancialActivityAccount(financialActivityAccountId: number) { + /** + * @param {string} financialActivityAccountId Financial activity account ID of financial activity account to be deleted. + * @returns {Observable} + */ + deleteFinancialActivityAccount(financialActivityAccountId: string): Observable { return this.http.delete(`/financialactivityaccounts/${financialActivityAccountId}`); } - getChartOfAccounts() { + /** + * @param {string} officeId Office ID to retrive opening balances accounts for. + * @returns {Observable} + */ + retrieveOpeningBalances(officeId: string): Observable { + const httpParams = new HttpParams().set('officeId', officeId); + return this.http.get('/journalentries/openingbalance', { params: httpParams }); + } + + /** + * @param {any} openingBalances Opening balances for office accounts to be defined. + * @returns {Observable} + */ + defineOpeningBalances(openingBalances: any): Observable { + const httpParams = new HttpParams().set('command', 'defineOpeningBalance'); + return this.http.post(`/journalentries`, openingBalances, { params: httpParams }); + } + + /** + * @returns {Observable} GL accounts. + */ + getChartOfAccounts(): Observable { return this.http.get('/glaccounts'); } - getChartOfAccountsTemplate() { + /** + * @returns {Observable} GL accounts template. + */ + getChartOfAccountsTemplate(): Observable { return this.http.get('/glaccounts/template'); } - createGlAccount(glAccount: any) { + /** + * @param {any} glAccount GL account to be created. + * @returns {Observable} + */ + createGlAccount(glAccount: any): Observable { return this.http.post('/glaccounts', glAccount); } - getGlAccount(glAccountId: string, template: boolean = false) { + /** + * @param {string} glAccountId GL account ID of gl account. + * @param {boolean} template True if template is required. + * @returns {Observable} GL account. + */ + getGlAccount(glAccountId: string, template: boolean = false): Observable { const httpParams = new HttpParams().set('template', template.toString()); return this.http.get(`/glaccounts/${glAccountId}`, { params: httpParams }); } - updateGlAccount(glAccountId: number, glAccount: any) { + /** + * @param {string} glAccountId GL account ID of gl account. + * @param {any} glAccount GL account to be updated. + * @returns {Observable} + */ + updateGlAccount(glAccountId: string, glAccount: any): Observable { return this.http.put(`/glaccounts/${glAccountId}`, glAccount); } - deleteGlAccount(glAccountId: number) { + /** + * @param glAccountId GL account ID of gl account to be deleted. + * @returns {Observable} + */ + deleteGlAccount(glAccountId: string): Observable { return this.http.delete(`/glaccounts/${glAccountId}`); } - getAccountingClosures() { + /** + * @returns {Observable} GL account closures. + */ + getAccountingClosures(): Observable { return this.http.get('/glclosures'); } - createAccountingClosure(accountingClosure: any) { + /** + * @param {any} accountingClosure GL closure to be created. + * @returns {Observable} + */ + createAccountingClosure(accountingClosure: any): Observable { return this.http.post('/glclosures', accountingClosure); } - getAccountingClosure(accountingClosureId: number) { + /** + * @param {string} accountingClosureId Accounting closure ID of gl closure. + * @returns {Observable} Accounting closure. + */ + getAccountingClosure(accountingClosureId: string): Observable { return this.http.get(`/glclosures/${accountingClosureId}`); } - deleteAccountingClosure(accountingClosureId: number) { - return this.http.delete(`/glclosures/${accountingClosureId}`); - } - - updateAccountingClosure(accountingClosureId: number, accountingClosure: any) { + /** + * @param {string} accountingClosureId Accounting closure ID of gl closure to be updated. + * @param {any} accountingClosure Accounting closure to be updated. + * @returns {Observable} + */ + updateAccountingClosure(accountingClosureId: string, accountingClosure: any): Observable { return this.http.put(`/glclosures/${accountingClosureId}`, accountingClosure); } - getAccountingRulesTemplate() { + /** + * @param {string} accountingClosureId Accounting closure ID of gl closure to be deleted. + * @returns {Observable} + */ + deleteAccountingClosure(accountingClosureId: string): Observable { + return this.http.delete(`/glclosures/${accountingClosureId}`); + } + + /** + * @returns {Observable} Accounting rules template. + */ + getAccountingRulesTemplate(): Observable { return this.http.get('/accountingrules/template'); } - createAccountingRule(accountingRule: any) { + /** + * @param {any} accountingRule Accounting rule to be created. + * @returns {Observable} + */ + createAccountingRule(accountingRule: any): Observable { return this.http.post('/accountingrules', accountingRule); } - getAccountingRule(accountingRuleId: string) { + /** + * @param {string} accountingRuleId Accounting rule ID of accounting rule. + * @returns {Observable} Accounting rule. + */ + getAccountingRule(accountingRuleId: string): Observable { return this.http.get(`/accountingrules/${accountingRuleId}`); } - deleteAccountingRule(accountingRuleId: number) { - return this.http.delete(`/accountingrules/${accountingRuleId}`); - } - - updateAccountingRule(accoutingRuleId: number, accountingRule: any) { + /** + * @param {string} accoutingRuleId Accounting rule ID of accounting rule to be updated. + * @param {any} accountingRule Accounting rule to be updated. + * @returns {Observable} + */ + updateAccountingRule(accoutingRuleId: string, accountingRule: any): Observable { return this.http.put(`/accountingrules/${accoutingRuleId}`, accountingRule); } - executePeriodicAccruals(periodicAccruals: any) { + /** + * @param {string} accountingRuleId Accounting rule ID of accounting rule to be deleted. + * @returns {Observable} + */ + deleteAccountingRule(accountingRuleId: string): Observable { + return this.http.delete(`/accountingrules/${accountingRuleId}`); + } + + /** + * @param {any} periodicAccruals Accruals to be executed. + * @returns {Observable} + */ + executePeriodicAccruals(periodicAccruals: any): Observable { return this.http.post('/runaccruals', periodicAccruals); } - getProvisioningEntries() { + /** + * @returns {Observable} Provisioning entries. + */ + getProvisioningEntries(): Observable { return this.http.get('/provisioningentries'); } - createProvisioningEntry(provisioningEntry: any) { + /** + * @param {any} provisioningEntry Provisioning entry to be created. + * @returns {Observable} + */ + createProvisioningEntry(provisioningEntry: any): Observable { return this.http.post('/provisioningentries', provisioningEntry); } - getProvisioningEntry(provisioningEntryId: string) { + /** + * @param {string} provisioningEntryId Provisioning entry ID of provisioning entry. + * @returns {Observable} Provisioning entry. + */ + getProvisioningEntry(provisioningEntryId: string): Observable { return this.http.get(`/provisioningentries/${provisioningEntryId}`); } - getLoanProducts() { + /** + * @returns {Observable} Loan products data. + */ + getLoanProducts(): Observable { return this.http.get('/loanproducts'); } - getProvisioningCategories() { + /** + * @returns {Observable} Provisioning categories data. + */ + getProvisioningCategories(): Observable { return this.http.get('/provisioningcategory'); } - getProvisioningEntryEntries(entryId: string) { - const httpParams = new HttpParams().set('entryId', entryId); + /** + * @param {string} provisioningEntryId Provisioning entry ID of provisioning entry. + * @returns {Observable} Provisioning entry entries. + */ + getProvisioningEntryEntries(provisioningEntryId: string): Observable { + const httpParams = new HttpParams().set('entryId', provisioningEntryId); return this.http.get('/provisioningentries/entries', { params: httpParams }); } - getProvisioningJournalEntries(entryId: string) { - const httpParams = new HttpParams().set('entryId', entryId); + /** + * @param {string} provisioningEntryId Provisioning entry ID of provisioning entry. + * @returns {Observable} Provisioning journal entries. + */ + getProvisioningJournalEntries(provisioningEntryId: string): Observable { + const httpParams = new HttpParams().set('entryId', provisioningEntryId); return this.http.get('/journalentries/provisioning', { params: httpParams }); } - createProvisioningJournalEntries(provisioningEntryId: string) { + /** + * @param {string} provisioningEntryId Provisioning entry ID for which journal entries need to be created. + * @returns {Observable} + */ + createProvisioningJournalEntries(provisioningEntryId: string): Observable { const httpParams = new HttpParams().set('command', 'createjournalentry'); return this.http.post(`/provisioningentries/${provisioningEntryId}`, {}, { params: httpParams }); } - recreateProvisioningEntries(provisioningEntryId: string) { + /** + * @param {string} provisioningEntryId Provisioning entry ID for which provisioning entry needs to be recreated. + * @returns {Observable} + */ + recreateProvisioningEntries(provisioningEntryId: string): Observable { const httpParams = new HttpParams().set('command', 'recreateprovisioningentry'); return this.http.post(`/provisioningentries/${provisioningEntryId}`, {}, { params: httpParams }); } - retrieveOpeningBalances(officeId: number) { - let httpParams = new HttpParams(); - httpParams = httpParams.set('officeId', officeId.toString()); - return this.http.get('/journalentries/openingbalance', { params: httpParams }); - } - - defineOpeningBalances(openingBalances: any) { - let httpParams = new HttpParams(); - httpParams = httpParams.set('command', 'defineOpeningBalance'); - return this.http.post(`/journalentries`, openingBalances, { params: httpParams }); - } - } 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 index 17e44f1ff..8e95012a5 100644 --- a/src/app/accounting/chart-of-accounts/chart-of-accounts.component.html +++ b/src/app/accounting/chart-of-accounts/chart-of-accounts.component.html @@ -1,25 +1,27 @@
- list + - device_hub + -
-
+
- + Filter +
@@ -46,7 +48,7 @@ Disabled
- lens +
@@ -54,8 +56,8 @@ Manual Entries Allowed - check_circle - cancel + + @@ -65,7 +67,7 @@ - + @@ -78,7 +80,7 @@ -
+
{{ '(' + node.glCode + ')' }}   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 index 1bd30aea2..c1304edf1 100644 --- a/src/app/accounting/chart-of-accounts/chart-of-accounts.component.scss +++ b/src/app/accounting/chart-of-accounts/chart-of-accounts.component.scss @@ -1,49 +1,54 @@ table { width: 100%; -} -.gl-account-tree-invisible { - display: none; -} - -.gl-account-tree ul, -.gl-account-tree li { - margin-top: 0; - margin-bottom: 0; - list-style-type: none; + .select-row:hover { + cursor: pointer; + } } .gl-account-tree { + ul, + li { + margin-top: 0; + margin-bottom: 0; + list-style-type: none; + } + li { span { cursor: pointer; } } + + .gl-account-tree-invisible { + display: none; + } } .content { div { margin: 1rem 0; word-wrap: break-word; - } - .header { - font-weight: 500; + &.header { + font-weight: 500; + } } } +// TODO: CONFIGURE THESE CLASSES GLOBALLY +.enabled { + color: #32cd32; +} + .disabled { - color: red; -} - -.enabled { - color: green; + color: #f44366; } .manual-entries-allowed { - color: green; + color: #32cd32; } .manual-entries-not-allowed { - color: red; + color: #f44366; } 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 index dd84b5b8e..12c6d9c8c 100644 --- a/src/app/accounting/chart-of-accounts/chart-of-accounts.component.ts +++ b/src/app/accounting/chart-of-accounts/chart-of-accounts.component.ts @@ -1,14 +1,22 @@ +/** Angular Imports */ import { Component, ViewChild, OnInit, AfterViewInit } from '@angular/core'; import { MatPaginator, MatSort, MatTableDataSource, MatTreeNestedDataSource } from '@angular/material'; import { NestedTreeControl } from '@angular/cdk/tree'; import { FormControl } from '@angular/forms'; import { ActivatedRoute } from '@angular/router'; +/** rxjs Imports */ import { of } from 'rxjs'; +/** Custom Models */ import { GLAccountNode } from './gl-account-node.model'; + +/** Custom Services */ import { GlAccountTreeService } from './gl-account-tree.service'; +/** + * Chart of accounts component. + */ @Component({ selector: 'mifosx-chart-of-accounts', templateUrl: './chart-of-accounts.component.html', @@ -16,37 +24,55 @@ import { GlAccountTreeService } from './gl-account-tree.service'; }) export class ChartOfAccountsComponent implements AfterViewInit, OnInit { + /** Button toggle group form control for type of view. (list/tree) */ viewGroup = new FormControl('listView'); + /** GL Account data. */ glAccountData: any; - + /** Columns to be displayed in chart of accounts table. */ displayedColumns: string[] = ['name', 'glCode', 'glAccountType', 'disabled', 'manualEntriesAllowed', 'usedAs']; - tableDataSource: any; - - @ViewChild(MatPaginator) paginator: MatPaginator; - @ViewChild(MatSort) sort: MatSort; - + /** Data source for chart of accounts table. */ + tableDataSource: MatTableDataSource; + /** Nested tree control for chart of accounts tree. */ nestedTreeControl: NestedTreeControl; + /** Nested tree data source for chart of accounts tree. */ nestedTreeDataSource: MatTreeNestedDataSource; + /** Selected GL Account. */ glAccount: GLAccountNode; + /** Paginator for chart of accounts table. */ + @ViewChild(MatPaginator) paginator: MatPaginator; + /** Sorter for chart of accounts table. */ + @ViewChild(MatSort) sort: MatSort; + + /** + * Retrieves the gl accounts data from `resolve` and initializes(generates) gl accounts tree. + * @param {GlAccountTreeService} glAccountTreeService GL Account tree service. + * @param {ActivatedRoute} route Activated Route. + */ constructor(private glAccountTreeService: GlAccountTreeService, private route: ActivatedRoute) { - this.route.data.subscribe((data: { glAccountData: any }) => { - this.glAccountData = data.glAccountData; + this.route.data.subscribe((data: { chartOfAccounts: any }) => { + this.glAccountData = data.chartOfAccounts; glAccountTreeService.initialize(this.glAccountData); }); this.nestedTreeControl = new NestedTreeControl(this._getChildren); - this.nestedTreeDataSource = new MatTreeNestedDataSource(); + this.nestedTreeDataSource = new MatTreeNestedDataSource(); } + /** + * Initializes the data source for chart of accounts table and tree. + */ ngOnInit() { this.tableDataSource = new MatTableDataSource(this.glAccountData); - this.glAccountTreeService.treeDataChange.subscribe((glAccountTreeData: any) => { + this.glAccountTreeService.treeDataChange.subscribe((glAccountTreeData: GLAccountNode[]) => { this.nestedTreeDataSource.data = glAccountTreeData; this.nestedTreeControl.expand(this.nestedTreeDataSource.data[0]); }); } + /** + * Initializes the paginator and sorter for chart of accounts table. + */ ngAfterViewInit() { this.tableDataSource.paginator = this.paginator; this.tableDataSource.sortingDataAccessor = (glAccount: any, property: any) => { @@ -59,12 +85,18 @@ export class ChartOfAccountsComponent implements AfterViewInit, OnInit { this.tableDataSource.sort = this.sort; } + /** + * Filters data in chart of accounts table based on passed value. + * @param {string} filterValue Value to filter data. + */ applyFilter(filterValue: string) { this.tableDataSource.filter = filterValue.trim().toLowerCase(); } - hasNestedChild = (_: number, node: GLAccountNode) => node.children.length; - + /** + * View selected gl account. + * @param {GLAccountNode} glAccount GL Account to be viewed. + */ viewGLAccountNode(glAccount: GLAccountNode) { if (glAccount.glCode) { this.glAccount = glAccount; @@ -73,6 +105,14 @@ export class ChartOfAccountsComponent implements AfterViewInit, OnInit { } } + /** + * Checks if selected node in tree has children. + */ + hasNestedChild = (_: number, node: GLAccountNode) => node.children.length; + + /** + * Gets the children of selected node in tree. + */ private _getChildren = (node: GLAccountNode) => of(node.children); } diff --git a/src/app/accounting/chart-of-accounts/chart-of-accounts.resolver.ts b/src/app/accounting/chart-of-accounts/chart-of-accounts.resolver.ts index 34a55cfbd..844226bef 100644 --- a/src/app/accounting/chart-of-accounts/chart-of-accounts.resolver.ts +++ b/src/app/accounting/chart-of-accounts/chart-of-accounts.resolver.ts @@ -1,14 +1,28 @@ +/** Angular Imports */ import { Injectable } from '@angular/core'; import { Resolve } from '@angular/router'; +/** rxjs Imports */ import { Observable } from 'rxjs'; + +/** Custom Services */ import { AccountingService } from '../accounting.service'; +/** + * Chart of acocunts data resolver. + */ @Injectable() export class ChartOfAccountsResolver implements Resolve { + /** + * @param {AccountingService} accountingService Accounting service. + */ constructor(private accountingService: AccountingService) {} + /** + * Returns the chart of accounts data. + * @returns {Observable} + */ resolve(): Observable { return this.accountingService.getChartOfAccounts(); } diff --git a/src/app/accounting/chart-of-accounts/create-gl-account/chart-of-accounts-template.resolver.ts b/src/app/accounting/chart-of-accounts/create-gl-account/chart-of-accounts-template.resolver.ts new file mode 100644 index 000000000..4aa047096 --- /dev/null +++ b/src/app/accounting/chart-of-accounts/create-gl-account/chart-of-accounts-template.resolver.ts @@ -0,0 +1,30 @@ +/** Angular Imports */ +import { Injectable } from '@angular/core'; +import { Resolve } from '@angular/router'; + +/** rxjs Imports */ +import { Observable } from 'rxjs'; + +/** Custom Services */ +import { AccountingService } from '../../accounting.service'; + +/** + * Chart of accounts template data resolver. + */ +@Injectable() +export class ChartOfAccountsTemplateResolver implements Resolve { + + /** + * @param {AccountingService} accountingService Accounting service. + */ + constructor(private accountingService: AccountingService) {} + + /** + * Returns the chart of accounts template data. + * @returns {Observable} + */ + resolve(): Observable { + return this.accountingService.getChartOfAccountsTemplate(); + } + +} diff --git a/src/app/accounting/chart-of-accounts/create-gl-account/create-gl-account.component.html b/src/app/accounting/chart-of-accounts/create-gl-account/create-gl-account.component.html index 04f8355ff..78c0a9681 100644 --- a/src/app/accounting/chart-of-accounts/create-gl-account/create-gl-account.component.html +++ b/src/app/accounting/chart-of-accounts/create-gl-account/create-gl-account.component.html @@ -9,31 +9,48 @@
- + Account Type + {{ accountType.value }} + + Account Type is required + - + Account Name + + + Account Name is required + - + Account Usage + {{ accountUsage.value }} + + Account Usage is required + - + GL Code + + + GL Code is required + - + Parent + {{ parent.name }} @@ -41,21 +58,23 @@ - + Tag + {{ tag.name }} -
- +
+ Manual Entries Allowed
- + Description +
@@ -64,7 +83,7 @@ - + diff --git a/src/app/accounting/chart-of-accounts/create-gl-account/create-gl-account.component.scss b/src/app/accounting/chart-of-accounts/create-gl-account/create-gl-account.component.scss index f6d5d645e..624944f61 100644 --- a/src/app/accounting/chart-of-accounts/create-gl-account/create-gl-account.component.scss +++ b/src/app/accounting/chart-of-accounts/create-gl-account/create-gl-account.component.scss @@ -1,7 +1,7 @@ -.manualEntriesAllowedWrapper { +.manual-entries-allowed-wrapper { position: relative; - .manualEntriesAllowed { + .manual-entries-allowed { padding: 0 0 17.5px 0; position: absolute; bottom: 0; diff --git a/src/app/accounting/chart-of-accounts/create-gl-account/create-gl-account.component.ts b/src/app/accounting/chart-of-accounts/create-gl-account/create-gl-account.component.ts index 91221e11f..b7a72c8e9 100644 --- a/src/app/accounting/chart-of-accounts/create-gl-account/create-gl-account.component.ts +++ b/src/app/accounting/chart-of-accounts/create-gl-account/create-gl-account.component.ts @@ -1,9 +1,14 @@ +/** Angular Imports */ import { Component, OnInit } from '@angular/core'; import { FormGroup, FormBuilder, Validators } from '@angular/forms'; import { Router, ActivatedRoute } from '@angular/router'; +/** Custom Services */ import { AccountingService } from '../../accounting.service'; +/** + * Create gl account component. + */ @Component({ selector: 'mifosx-create-gl-account', templateUrl: './create-gl-account.component.html', @@ -11,18 +16,32 @@ import { AccountingService } from '../../accounting.service'; }) export class CreateGlAccountComponent implements OnInit { + /** GL account form. */ glAccountForm: FormGroup; + /** Chart of accounts data. */ chartOfAccountsData: any; + /** Account type data. */ accountTypeData: any; - parentData: any; + /** Account usage data. */ accountUsageData: any; + /** Parent data. */ + parentData: any; + /** Tag data. */ tagData: any; - + /** Account type id. (for creation of sub-ledger account) */ accountTypeId: number; + /** Parent id. (for creation of sub-ledger account) */ parentId: number; + /** Cancel route. (depending on creation of gl account or sub-ledger account) */ + cancelRoute = '../../'; - cancelRoute = '/accounting/chart-of-accounts'; - + /** + * Retrieves the chart of accounts data from `resolve`. + * @param {FormBuilder} formBuilder Form Builder. + * @param {AccountingService} accountingService Accounting Service. + * @param {ActivatedRoute} route Activated Route. + * @param {Router} router Router for navigation. + */ constructor(private formBuilder: FormBuilder, private accountingService: AccountingService, private route: ActivatedRoute, @@ -31,39 +50,44 @@ export class CreateGlAccountComponent implements OnInit { this.accountTypeId = Number(params.get('accountType')); this.parentId = Number(params.get('parent')); if (this.parentId) { - this.cancelRoute = `/accounting/chart-of-accounts/gl-accounts/view/${this.parentId}`; + this.cancelRoute = `../view/${this.parentId}`; } }); + + this.route.data.subscribe((data: { chartOfAccountsTemplate: any }) => { + this.chartOfAccountsData = data.chartOfAccountsTemplate; + this.accountTypeData = data.chartOfAccountsTemplate.accountTypeOptions; + this.accountUsageData = data.chartOfAccountsTemplate.usageOptions; + }); } + /** + * Creates and sets gl account form. + */ ngOnInit() { this.createGlAccountForm(); - this.getChartOfAccountsTemplate(); this.setGLAccountForm(); } + /** + * Creates gl account form. + */ createGlAccountForm() { this.glAccountForm = this.formBuilder.group({ 'type': ['', Validators.required], 'name': ['', Validators.required], 'usage': ['', Validators.required], 'glCode': ['', Validators.required], - 'parentId': [this.parentId], + 'parentId': [this.parentId || undefined], 'tagId': [''], 'manualEntriesAllowed': [true, Validators.required], 'description': [''] }); } - getChartOfAccountsTemplate() { - this.accountingService.getChartOfAccountsTemplate().subscribe((chartOfAccountsData: any) => { - this.chartOfAccountsData = chartOfAccountsData; - this.accountTypeData = chartOfAccountsData.accountTypeOptions; - this.accountUsageData = chartOfAccountsData.usageOptions; - this.glAccountForm.get('type').setValue(this.accountTypeId); - }); - } - + /** + * Sets gl account form for selected account type. + */ setGLAccountForm() { this.glAccountForm.get('type').valueChanges.subscribe(accountTypeId => { switch (accountTypeId) { @@ -84,11 +108,17 @@ export class CreateGlAccountComponent implements OnInit { break; } }); + + this.glAccountForm.get('type').setValue(this.accountTypeId); } + /** + * Submits the gl account form and creates gl account, + * if successful redirects to view created account. + */ submit() { this.accountingService.createGlAccount(this.glAccountForm.value).subscribe((response: any) => { - this.router.navigate(['/accounting/chart-of-accounts/gl-accounts/view', response.resourceId]); + this.router.navigate(['../view', response.resourceId], { relativeTo: this.route }); }); } diff --git a/src/app/accounting/chart-of-accounts/edit-gl-account/edit-gl-account.component.html b/src/app/accounting/chart-of-accounts/edit-gl-account/edit-gl-account.component.html index c7de4a3ef..1090505e7 100644 --- a/src/app/accounting/chart-of-accounts/edit-gl-account/edit-gl-account.component.html +++ b/src/app/accounting/chart-of-accounts/edit-gl-account/edit-gl-account.component.html @@ -9,31 +9,48 @@
- + Account Type + {{ accountType.value }} + + Account Type is required + - + Account Name + + + Account Name is required + - + Account Usage + {{ accountUsage.value }} + + Account Usage is required + - + GL Code + + + GL Code is required + - + Parent + {{ parent.name }} @@ -41,21 +58,23 @@ - + Tag + {{ tag.name }} -
- +
+ Manual Entries Allowed
- + Description +
@@ -63,8 +82,8 @@ - - + + diff --git a/src/app/accounting/chart-of-accounts/edit-gl-account/edit-gl-account.component.scss b/src/app/accounting/chart-of-accounts/edit-gl-account/edit-gl-account.component.scss index e69de29bb..624944f61 100644 --- a/src/app/accounting/chart-of-accounts/edit-gl-account/edit-gl-account.component.scss +++ b/src/app/accounting/chart-of-accounts/edit-gl-account/edit-gl-account.component.scss @@ -0,0 +1,10 @@ +.manual-entries-allowed-wrapper { + position: relative; + + .manual-entries-allowed { + padding: 0 0 17.5px 0; + position: absolute; + bottom: 0; + right: 0; + } +} diff --git a/src/app/accounting/chart-of-accounts/edit-gl-account/edit-gl-account.component.ts b/src/app/accounting/chart-of-accounts/edit-gl-account/edit-gl-account.component.ts index 52173add0..e67067731 100644 --- a/src/app/accounting/chart-of-accounts/edit-gl-account/edit-gl-account.component.ts +++ b/src/app/accounting/chart-of-accounts/edit-gl-account/edit-gl-account.component.ts @@ -1,9 +1,14 @@ +/** Angular Imports */ import { Component, OnInit } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; import { FormGroup, FormBuilder, Validators } from '@angular/forms'; +/** Custom Services */ import { AccountingService } from '../../accounting.service'; +/** + * Edit gl account component. + */ @Component({ selector: 'mifosx-edit-gl-account', templateUrl: './edit-gl-account.component.html', @@ -11,27 +16,46 @@ import { AccountingService } from '../../accounting.service'; }) export class EditGlAccountComponent implements OnInit { + /** GL account form. */ glAccountForm: FormGroup; + /** GL account and chart of accounts data. */ glAccount: any; + /** Account type data. */ accountTypeData: any; + /** Account usage data. */ accountUsageData: any; + /** Parent data. */ parentData: any; + /** Tag data. */ tagData: any; - constructor(private route: ActivatedRoute, - private formBuilder: FormBuilder, + /** + * Retrieves the chart of accounts data from `resolve`. + * @param {FormBuilder} formBuilder Form Builder. + * @param {AccountingService} accountingService Accounting Service. + * @param {ActivatedRoute} route Activated Route. + * @param {Router} router Router for navigation. + */ + constructor(private formBuilder: FormBuilder, private accountingService: AccountingService, + private route: ActivatedRoute, private router: Router) { - this.route.data.subscribe((data: { glAccount: any }) => { - this.glAccount = data.glAccount; + this.route.data.subscribe((data: { glAccountAndChartOfAccountsTemplate: any }) => { + this.glAccount = data.glAccountAndChartOfAccountsTemplate; }); } + /** + * Creates and sets gl account form. + */ ngOnInit() { this.createGlAccountForm(); this.setGLAccountForm(); } + /** + * Creates gl account form. + */ createGlAccountForm() { this.glAccountForm = this.formBuilder.group({ 'type': ['', Validators.required], @@ -45,6 +69,9 @@ export class EditGlAccountComponent implements OnInit { }); } + /** + * Sets gl account form for selected account type. + */ setGLAccountForm() { this.accountTypeData = this.glAccount.accountTypeOptions; this.accountUsageData = this.glAccount.usageOptions; @@ -67,13 +94,18 @@ export class EditGlAccountComponent implements OnInit { break; } }); + this.glAccountForm.get('type').setValue(this.glAccount.type.id); } + /** + * Submits the gl account form and updates gl account, + * if successful redirects to view updated account. + */ submit() { this.accountingService.updateGlAccount(this.glAccount.id, this.glAccountForm.value) .subscribe((response: any) => { - this.router.navigate(['/accounting/chart-of-accounts/gl-accounts/view', response.resourceId]); + this.router.navigate(['../../', response.resourceId], { relativeTo: this.route }); }); } diff --git a/src/app/accounting/chart-of-accounts/gl-account-and-chart-of-accounts-template.resolver.ts b/src/app/accounting/chart-of-accounts/gl-account-and-chart-of-accounts-template.resolver.ts new file mode 100644 index 000000000..b4c0855e7 --- /dev/null +++ b/src/app/accounting/chart-of-accounts/gl-account-and-chart-of-accounts-template.resolver.ts @@ -0,0 +1,53 @@ +/** Angular Imports */ +import { Injectable } from '@angular/core'; +import { Resolve, ActivatedRouteSnapshot } from '@angular/router'; + +/** rxjs Imports */ +import { Observable } from 'rxjs'; +import { map } from 'rxjs/operators'; + +/** Custom Services */ +import { AccountingService } from '../accounting.service'; + +/** + * GL Account and chart of accounts template data resolver. + */ +@Injectable() +export class GlAccountAndChartOfAccountsTemplateResolver implements Resolve { + + /** + * @param {AccountingService} accountingService Accounting service. + */ + constructor(private accountingService: AccountingService) {} + + /** + * Returns the gl account and chart of accounts template data. + * @returns {Observable} + */ + resolve(route: ActivatedRouteSnapshot): Observable { + const id = route.paramMap.get('id'); + + return this.accountingService.getGlAccount(id, true).pipe( + map((glAccountData: any) => { + let accountOptions = []; + switch (glAccountData.type.value) { + case 'ASSET': accountOptions = glAccountData.assetHeaderAccountOptions; + break; + case 'EQUITY': accountOptions = glAccountData.equityHeaderAccountOptions; + break; + case 'EXPENSE': accountOptions = glAccountData.expenseHeaderAccountOptions; + break; + case 'INCOME': accountOptions = glAccountData.incomeHeaderAccountOptions; + break; + case 'LIABILITY': accountOptions = glAccountData.liabilityHeaderAccountOptions; + break; + } + glAccountData.parent = accountOptions.find((accountOption: any) => { + return accountOption.id === glAccountData.parentId; + }); + return glAccountData; + }) + ); + } + +} diff --git a/src/app/accounting/chart-of-accounts/gl-account-node.model.ts b/src/app/accounting/chart-of-accounts/gl-account-node.model.ts index ff0b4cbda..a519cb2e9 100644 --- a/src/app/accounting/chart-of-accounts/gl-account-node.model.ts +++ b/src/app/accounting/chart-of-accounts/gl-account-node.model.ts @@ -1,11 +1,18 @@ +/** + * GL Account Node model. + */ export class GLAccountNode { + + /** GL Account Node children. */ children: GLAccountNode[]; + name: string; glCode: string; type: string; usage: string; manualEntriesAllowed: boolean; description: string; + constructor(name: string, glCode: string = '', type: string = '', @@ -20,4 +27,5 @@ export class GLAccountNode { this.description = description; this.children = []; } + } diff --git a/src/app/accounting/chart-of-accounts/gl-account-tree.service.ts b/src/app/accounting/chart-of-accounts/gl-account-tree.service.ts index 7a9ef5639..c0f17b84c 100644 --- a/src/app/accounting/chart-of-accounts/gl-account-tree.service.ts +++ b/src/app/accounting/chart-of-accounts/gl-account-tree.service.ts @@ -1,30 +1,50 @@ +/** Angular Imports */ import { Injectable } from '@angular/core'; +/** rxjs Imports */ import { BehaviorSubject } from 'rxjs'; +/** Custom Components */ import { GLAccountNode } from './gl-account-node.model'; +/** + * GL Account tree service. + */ @Injectable({ providedIn: 'root' }) export class GlAccountTreeService { + /** GL Account data. */ glAccountData: any; - + /** Chart of accounts tree data behavior subject to represent chart of accounts tree nodes. */ treeDataChange = new BehaviorSubject([]); + /** + * Gets the chart of accounts tree nodes. + */ get treeData(): GLAccountNode[] { return this.treeDataChange.value; } constructor() { } + /** + * Builds the chart of accounts tree and emits the value. + * @param {any} glAccountData Chart of accounts data. + */ initialize(glAccountData: any) { - const data = this.buildGLAccountTree(glAccountData); - this.treeDataChange.next(data); + const treeData = this.buildGLAccountTree(glAccountData); + this.treeDataChange.next(treeData); } + /** + * Builds and returns the chart of accounts tree. + * @param {any} glAccountData Chart of accounts data. + * @returns {GLAccountNode[]} Chart of accounts tree nodes. + */ buildGLAccountTree(glAccountData: any): GLAccountNode[] { const glAccountTree: GLAccountNode[] = []; + // Header nodes glAccountTree.push(new GLAccountNode('GL ACCOUNTS')); glAccountTree[0].children.push(new GLAccountNode('ASSET')); glAccountTree[0].children.push(new GLAccountNode('EQUITY')); @@ -32,6 +52,7 @@ export class GlAccountTreeService { glAccountTree[0].children.push(new GLAccountNode('INCOME')); glAccountTree[0].children.push(new GLAccountNode('LIABILITY')); + // Sort by parent id (so that child nodes can be added properly) glAccountData.sort((glAccountOne: any, glAccountTwo: any) => { if (!glAccountOne.parentId) { glAccountOne.parentId = 0; @@ -41,11 +62,14 @@ export class GlAccountTreeService { const glAccounts: GLAccountNode[] = []; + // Add gl accounts to any array where index for each is denoted by its id for (const glAccount of glAccountData) { glAccounts[glAccount.id] = new GLAccountNode(glAccount.name, glAccount.glCode, glAccount.type.value, glAccount.usage.value, glAccount.manualEntriesAllowed, glAccount.description); } + // Construct gl account tree by adding all nodes belonging to headers (with parent id = 0) by their type, + // and rest as children to respective parent nodes. for (const glAccount of glAccountData) { if (glAccount.parentId === 0) { if (glAccount.type.value === 'ASSET') { diff --git a/src/app/accounting/chart-of-accounts/view-gl-account/view-gl-account.component.html b/src/app/accounting/chart-of-accounts/view-gl-account/view-gl-account.component.html index 4ac1473ff..5bc2a1edb 100644 --- a/src/app/accounting/chart-of-accounts/view-gl-account/view-gl-account.component.html +++ b/src/app/accounting/chart-of-accounts/view-gl-account/view-gl-account.component.html @@ -1,18 +1,23 @@
-
@@ -29,7 +34,7 @@
- {{ glAccount.type.value }} + {{ glAccount.type?.value }}
@@ -40,19 +45,19 @@ {{ glAccount.glCode }}
-
+
Parent Account Name
-
+
{{ glAccount.parent.name }}
-
+
Tag
-
+
{{ glAccount.tagId.name }}
@@ -61,7 +66,7 @@
- {{ glAccount.usage.value }} + {{ glAccount.usage?.value }}
@@ -72,11 +77,11 @@ {{ glAccount.manualEntriesAllowed }}
-
+
Description
-
+
{{ glAccount.description }}
diff --git a/src/app/accounting/chart-of-accounts/view-gl-account/view-gl-account.component.scss b/src/app/accounting/chart-of-accounts/view-gl-account/view-gl-account.component.scss index 98c6e0c98..a4541ccaf 100644 --- a/src/app/accounting/chart-of-accounts/view-gl-account/view-gl-account.component.scss +++ b/src/app/accounting/chart-of-accounts/view-gl-account/view-gl-account.component.scss @@ -1,14 +1,14 @@ .container { max-width: 37rem; -} -.content { - div { - margin: 1rem 0; - word-wrap: break-word; - } + .content { + div { + margin: 1rem 0; + word-wrap: break-word; - .header { - font-weight: 500; + &.header { + font-weight: 500; + } + } } } diff --git a/src/app/accounting/chart-of-accounts/view-gl-account/view-gl-account.component.ts b/src/app/accounting/chart-of-accounts/view-gl-account/view-gl-account.component.ts index 55968f90e..012f38284 100644 --- a/src/app/accounting/chart-of-accounts/view-gl-account/view-gl-account.component.ts +++ b/src/app/accounting/chart-of-accounts/view-gl-account/view-gl-account.component.ts @@ -1,10 +1,17 @@ +/** Angular Imports */ import { Component, OnInit } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; import { MatDialog } from '@angular/material'; +/** Custom Components */ import { DeleteDialogComponent } from '../../../shared/delete-dialog/delete-dialog.component'; + +/** Custom Services */ import { AccountingService } from '../../accounting.service'; +/** + * View gl account component. + */ @Component({ selector: 'mifosx-view-gl-account', templateUrl: './view-gl-account.component.html', @@ -12,22 +19,32 @@ import { AccountingService } from '../../accounting.service'; }) export class ViewGlAccountComponent implements OnInit { + /** GL Account. */ glAccount: any; - constructor(private route: ActivatedRoute, - private accountingService: AccountingService, + /** + * Retrieves the gl account data from `resolve`. + * @param {AccountingService} accountingService Accounting Service. + * @param {ActivatedRoute} route Activated Route. + * @param {Router} router Router for navigation. + * @param {MatDialog} dialog Dialog reference. + */ + constructor(private accountingService: AccountingService, + private route: ActivatedRoute, private router: Router, public dialog: MatDialog) { - this.route.data.subscribe((data: { glAccount: any }) => { - this.glAccount = data.glAccount; + this.route.data.subscribe((data: { glAccountAndChartOfAccountsTemplate: any }) => { + this.glAccount = data.glAccountAndChartOfAccountsTemplate; }); } ngOnInit() { } + /** + * Deletes the gl account and redirects to chart of accounts. + */ deleteGlAccount() { - // TODO: (Validation) gl account cannot be deleted if it has transactions logged against it const deleteGlAccountDialogRef = this.dialog.open(DeleteDialogComponent, { data: { deleteContext: `gl account ${this.glAccount.id}` } }); @@ -41,6 +58,9 @@ export class ViewGlAccountComponent implements OnInit { }); } + /** + * Changes state of gl account. (enabled/disabled) + */ changeGlAccountState() { this.accountingService.updateGlAccount(this.glAccount.id, { disabled: !this.glAccount.disabled }) .subscribe((response: any) => { diff --git a/src/app/accounting/chart-of-accounts/view-gl-account/view-gl-account.resolver.ts b/src/app/accounting/chart-of-accounts/view-gl-account/view-gl-account.resolver.ts deleted file mode 100644 index 5d8f33fde..000000000 --- a/src/app/accounting/chart-of-accounts/view-gl-account/view-gl-account.resolver.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { Injectable } from '@angular/core'; -import { Resolve, ActivatedRouteSnapshot } from '@angular/router'; - -import { Observable } from 'rxjs'; -import { map } from 'rxjs/operators'; - -import { AccountingService } from '../../accounting.service'; - -@Injectable() -export class ViewGlAccountResolver implements Resolve { - - constructor(private accountingService: AccountingService) {} - - resolve(route: ActivatedRouteSnapshot): Observable { - const id = route.paramMap.get('id'); - - return this.accountingService.getGlAccount(id, true).pipe(map((glAccountData: any) => { - let accountOptions = []; - switch (glAccountData.type.value) { - case 'ASSET': accountOptions = glAccountData.assetHeaderAccountOptions; - break; - case 'EQUITY': accountOptions = glAccountData.equityHeaderAccountOptions; - break; - case 'EXPENSE': accountOptions = glAccountData.expenseHeaderAccountOptions; - break; - case 'INCOME': accountOptions = glAccountData.incomeHeaderAccountOptions; - break; - case 'LIABILITY': accountOptions = glAccountData.liabilityHeaderAccountOptions; - break; - } - glAccountData.parent = accountOptions.find((accountOption: any) => { - return accountOption.id === glAccountData.parentId; - }); - return glAccountData; - })); - } - -} diff --git a/src/app/accounting/closing-entries/closing-entries.component.html b/src/app/accounting/closing-entries/closing-entries.component.html index 2b5040553..1fa5c01dd 100644 --- a/src/app/accounting/closing-entries/closing-entries.component.html +++ b/src/app/accounting/closing-entries/closing-entries.component.html @@ -1,12 +1,14 @@
-
-
+
- + Office Name +
@@ -44,7 +46,7 @@ - + diff --git a/src/app/accounting/closing-entries/closing-entries.component.ts b/src/app/accounting/closing-entries/closing-entries.component.ts index 84ff590d7..d3e7e289e 100644 --- a/src/app/accounting/closing-entries/closing-entries.component.ts +++ b/src/app/accounting/closing-entries/closing-entries.component.ts @@ -1,10 +1,15 @@ +/** Angular Imports */ import { Component, OnInit, ViewChild } from '@angular/core'; import { MatPaginator, MatSort, MatTableDataSource } from '@angular/material'; import { FormControl } from '@angular/forms'; -import { startWith, map, tap } from 'rxjs/operators'; +import { ActivatedRoute } from '@angular/router'; -import { AccountingService } from '../accounting.service'; +/** rxjs Imports */ +import { startWith, map } from 'rxjs/operators'; +/** + * Closing entries component. + */ @Component({ selector: 'mifosx-closing-entries', templateUrl: './closing-entries.component.html', @@ -12,52 +17,84 @@ import { AccountingService } from '../accounting.service'; }) export class ClosingEntriesComponent implements OnInit { + /** Columns to be displayed in closing entries table. */ displayedColumns: string[] = ['officeName', 'closingDate', 'comments', 'createdByUsername']; - dataSource: any; - + /** Data source for closing entries table. */ + dataSource: MatTableDataSource; + /** Office name filter form control. */ officeName = new FormControl(); + /** Office data. */ officeData: any; + /** Filtered office data for autocomplete. */ filteredOfficeData: any; + /** GL Account closure data. */ + glAccountClosureData: any; + /** Paginator for closing entries table. */ @ViewChild(MatPaginator) paginator: MatPaginator; + /** Sorter for closing entries table. */ @ViewChild(MatSort) sort: MatSort; - constructor(private accountingService: AccountingService) { } - - ngOnInit() { - this.getOffices(); - this.applyFilter(); - this.getAccountingClosures(); + /** + * Retrieves the offices and gl account closures data from `resolve`. + * @param {ActivatedRoute} route Activated Route. + */ + constructor(private route: ActivatedRoute) { + this.route.data.subscribe((data: { + offices: any, + glAccountClosures: any + }) => { + this.officeData = data.offices; + this.glAccountClosureData = data.glAccountClosures; + }); } + /** + * Sets the filter and closing entries table. + */ + ngOnInit() { + this.applyFilter(); + this.setFilteredOffices(); + this.setAccountingClosures(); + } + + /** + * Filters data in closing entries table based on office name. + */ applyFilter() { this.officeName.valueChanges.subscribe((filterValue: string) => { this.dataSource.filter = filterValue.trim().toLowerCase(); }); } - getAccountingClosures() { - this.accountingService.getAccountingClosures().subscribe((glAccountClosureData: any) => { - this.dataSource = new MatTableDataSource(glAccountClosureData); - this.dataSource.paginator = this.paginator; - this.dataSource.sort = this.sort; - }); + /** + * Initializes the data source, paginator and sorter for closing entries table. + */ + setAccountingClosures() { + this.dataSource = new MatTableDataSource(this.glAccountClosureData); + this.dataSource.paginator = this.paginator; + this.dataSource.sort = this.sort; } - getOffices() { - this.accountingService.getOffices().subscribe(officeData => { - this.officeData = officeData; - this.filteredOfficeData = this.officeName.valueChanges - .pipe( - startWith(''), - map((office: any) => typeof office === 'string' ? office : office.name), - map((officeName: string) => officeName ? this.filterOfficeAutocompleteData(this.officeData, officeName) : this.officeData) - ); - }); + /** + * Sets filtered offices for autocomplete. + */ + setFilteredOffices() { + this.filteredOfficeData = this.officeName.valueChanges + .pipe( + startWith(''), + map((office: any) => typeof office === 'string' ? office : office.name), + map((officeName: string) => officeName ? this.filterOfficeAutocompleteData(officeName) : this.officeData) + ); } - private filterOfficeAutocompleteData(officeData: any, officeName: string): any { - return officeData.filter((office: any) => office.name.toLowerCase().includes(officeName.toLowerCase())); + /** + * Filters offices. + * @param {string} officeName Office name to filter office by. + * @returns {any} Filtered offices. + */ + private filterOfficeAutocompleteData(officeName: string): any { + return this.officeData.filter((office: any) => office.name.toLowerCase().includes(officeName.toLowerCase())); } } diff --git a/src/app/accounting/closing-entries/closing-entries.resolver.ts b/src/app/accounting/closing-entries/closing-entries.resolver.ts new file mode 100644 index 000000000..7e8b4e8a8 --- /dev/null +++ b/src/app/accounting/closing-entries/closing-entries.resolver.ts @@ -0,0 +1,30 @@ +/** Angular Imports */ +import { Injectable } from '@angular/core'; +import { Resolve } from '@angular/router'; + +/** rxjs Imports */ +import { Observable } from 'rxjs'; + +/** Custom Services */ +import { AccountingService } from '../accounting.service'; + +/** + * Closing entries data resolver. + */ +@Injectable() +export class ClosingEntriesResolver implements Resolve { + + /** + * @param {AccountingService} accountingService Accounting service. + */ + constructor(private accountingService: AccountingService) {} + + /** + * Returns the gl account closures data. + * @returns {Observable} + */ + resolve(): Observable { + return this.accountingService.getAccountingClosures(); + } + +} diff --git a/src/app/accounting/closing-entries/closing-entry.resolver.ts b/src/app/accounting/closing-entries/closing-entry.resolver.ts new file mode 100644 index 000000000..bae65d4aa --- /dev/null +++ b/src/app/accounting/closing-entries/closing-entry.resolver.ts @@ -0,0 +1,31 @@ +/** Angular Imports */ +import { Injectable } from '@angular/core'; +import { Resolve, ActivatedRouteSnapshot } from '@angular/router'; + +/** rxjs Imports */ +import { Observable } from 'rxjs'; + +/** Custom Services */ +import { AccountingService } from '../accounting.service'; + +/** + * Closing entry data resolver. + */ +@Injectable() +export class ClosingEntryResolver implements Resolve { + + /** + * @param {AccountingService} accountingService Accounting service. + */ + constructor(private accountingService: AccountingService) {} + + /** + * Returns the gl account closure data. + * @returns {Observable} + */ + resolve(route: ActivatedRouteSnapshot): Observable { + const glAccountClosureId = route.paramMap.get('id'); + return this.accountingService.getAccountingClosure(glAccountClosureId); + } + +} diff --git a/src/app/accounting/closing-entries/create-closure/create-closure.component.html b/src/app/accounting/closing-entries/create-closure/create-closure.component.html index 2b5d45afb..b69a0717c 100644 --- a/src/app/accounting/closing-entries/create-closure/create-closure.component.html +++ b/src/app/accounting/closing-entries/create-closure/create-closure.component.html @@ -9,21 +9,30 @@
- + Office + {{ office.name }} + + Office is required + - + Closing Date + + + Closing Date is required + - + Comments +
@@ -31,8 +40,8 @@ - - + + diff --git a/src/app/accounting/closing-entries/create-closure/create-closure.component.ts b/src/app/accounting/closing-entries/create-closure/create-closure.component.ts index 334403f55..5ace2acd6 100644 --- a/src/app/accounting/closing-entries/create-closure/create-closure.component.ts +++ b/src/app/accounting/closing-entries/create-closure/create-closure.component.ts @@ -1,9 +1,14 @@ +/** Angular Imports */ import { Component, OnInit } from '@angular/core'; import { FormBuilder, FormGroup, Validators } from '@angular/forms'; -import { Router } from '@angular/router'; +import { Router, ActivatedRoute } from '@angular/router'; +/** Custom Services */ import { AccountingService } from '../../accounting.service'; +/** + * Create closure component. + */ @Component({ selector: 'mifosx-create-closure', templateUrl: './create-closure.component.html', @@ -11,22 +16,41 @@ import { AccountingService } from '../../accounting.service'; }) export class CreateClosureComponent implements OnInit { - // TODO: Validations, Hints - + /** Minimum closing date allowed. */ minDate = new Date(2000, 0, 1); + /** Maximum closing date allowed. */ maxDate = new Date(); + /** Accounting closure form. */ accountingClosureForm: FormGroup; + /** Office data. */ officeData: any; + /** + * Retrieves the offices data from `resolve`. + * @param {FormBuilder} formBuilder Form Builder. + * @param {AccountingService} accountingService Accounting Service. + * @param {ActivatedRoute} route Activated Route. + * @param {Router} router Router for navigation. + */ constructor(private formBuilder: FormBuilder, private accountingService: AccountingService, - private router: Router) { } - - ngOnInit() { - this.createAccountingClosureForm(); - this.getOffices(); + private route: ActivatedRoute, + private router: Router) { + this.route.data.subscribe((data: { offices: any }) => { + this.officeData = data.offices; + }); } + /** + * Creates the accounting closure form. + */ + ngOnInit() { + this.createAccountingClosureForm(); + } + + /** + * Creates the accounting closure form. + */ createAccountingClosureForm() { this.accountingClosureForm = this.formBuilder.group({ 'officeId': ['', Validators.required], @@ -35,20 +59,29 @@ export class CreateClosureComponent implements OnInit { }); } - getOffices() { - this.accountingService.getOffices().subscribe(officeData => { - this.officeData = officeData; - }); - } - + /** + * Submits the accounting closure form and creates accounting closure, + * if successful redirects to view created closure. + */ submit() { const accountingClosure = this.accountingClosureForm.value; // TODO: Update once language and date settings are setup accountingClosure.locale = 'en'; - accountingClosure.dateFormat = 'dd MMMM yyyy'; - accountingClosure.closingDate = '4 August 2018'; + accountingClosure.dateFormat = 'yyyy-MM-dd'; + if (accountingClosure.closingDate instanceof Date) { + let day = accountingClosure.closingDate.getDate(); + let month = accountingClosure.closingDate.getMonth() + 1; + const year = accountingClosure.closingDate.getFullYear(); + if (day < 10) { + day = `0${day}`; + } + if (month < 10) { + month = `0${month}`; + } + accountingClosure.closingDate = `${year}-${month}-${day}`; + } this.accountingService.createAccountingClosure(accountingClosure).subscribe((response: any) => { - this.router.navigate(['/accounting/closing-entries/view', response.resourceId]); + this.router.navigate(['../view', response.resourceId], { relativeTo: this.route }); }); } diff --git a/src/app/accounting/closing-entries/edit-closure/edit-closure.component.html b/src/app/accounting/closing-entries/edit-closure/edit-closure.component.html index 237d8021a..ebff5389e 100644 --- a/src/app/accounting/closing-entries/edit-closure/edit-closure.component.html +++ b/src/app/accounting/closing-entries/edit-closure/edit-closure.component.html @@ -9,7 +9,8 @@
- + Office + {{ office.name }} @@ -17,13 +18,15 @@ - + Closing Date + - + Comments +
@@ -31,8 +34,8 @@ - - + + diff --git a/src/app/accounting/closing-entries/edit-closure/edit-closure.component.ts b/src/app/accounting/closing-entries/edit-closure/edit-closure.component.ts index 7bcdc0ce6..259fa0f77 100644 --- a/src/app/accounting/closing-entries/edit-closure/edit-closure.component.ts +++ b/src/app/accounting/closing-entries/edit-closure/edit-closure.component.ts @@ -1,9 +1,14 @@ +/** Angular Imports */ import { Component, OnInit } from '@angular/core'; import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { Router, ActivatedRoute } from '@angular/router'; +/** Custom Services */ import { AccountingService } from '../../accounting.service'; +/** + * Edit closure component. + */ @Component({ selector: 'mifosx-edit-closure', templateUrl: './edit-closure.component.html', @@ -11,35 +16,40 @@ import { AccountingService } from '../../accounting.service'; }) export class EditClosureComponent implements OnInit { - // TODO: Update once language and date settings are setup - + /** Accounting closure form. */ accountingClosureForm: FormGroup; - accountingClosureId: number; - accountingClosure: any; + /** GL Account closure. */ + glAccountClosure: any; + /** Office data. */ officeData: any; + /** + * Retrieves the gl account closure data from `resolve`. + * @param {FormBuilder} formBuilder Form Builder. + * @param {AccountingService} accountingService Accounting Service. + * @param {ActivatedRoute} route Activated Route. + * @param {Router} router Router for navigation. + */ constructor(private formBuilder: FormBuilder, private accountingService: AccountingService, - private router: Router, - private route: ActivatedRoute) { } + private route: ActivatedRoute, + private router: Router) { + this.route.data.subscribe((data: { glAccountClosure: any }) => { + this.glAccountClosure = data.glAccountClosure; + }); + } + /** + * Creates and sets accounting closure form. + */ ngOnInit() { - this.accountingClosureId = Number(this.route.snapshot.paramMap.get('id')); this.createAccountingClosureForm(); - this.getAccountingClosure(); - this.getOffices(); - } - - getAccountingClosure() { - this.accountingService.getAccountingClosure(this.accountingClosureId) - .subscribe((accountingClosure: any) => { - this.accountingClosure = accountingClosure; - this.accountingClosureForm.get('officeId').setValue(this.accountingClosure.officeId); - this.accountingClosureForm.get('closingDate').setValue(new Date()); - this.accountingClosureForm.get('comments').setValue(this.accountingClosure.comments); - }); + this.setAccountingClosure(); } + /** + * Creates accounting closure form. + */ createAccountingClosureForm() { this.accountingClosureForm = this.formBuilder.group({ 'officeId': [{ value: '', disabled: true }, Validators.required], @@ -48,16 +58,26 @@ export class EditClosureComponent implements OnInit { }); } - getOffices() { - this.accountingService.getOffices().subscribe(officeData => { - this.officeData = officeData; - }); + /** + * Sets accounting closure form. + */ + setAccountingClosure() { + this.officeData = [{ id: this.glAccountClosure.officeId, name: this.glAccountClosure.officeName }]; + this.accountingClosureForm.get('officeId').setValue(this.glAccountClosure.officeId); + this.accountingClosureForm.get('closingDate').setValue(new Date(this.glAccountClosure.closingDate)); + this.accountingClosureForm.get('comments').setValue(this.glAccountClosure.comments); } + + /** + * Submits the accounting closure form and updates accounting closure, + * if successful redirects to view updated closure. + */ submit() { - this.accountingService.updateAccountingClosure(this.accountingClosureId, { comments: this.accountingClosureForm.value.comments }) + this.accountingService.updateAccountingClosure(this.glAccountClosure.id, + { comments: this.accountingClosureForm.value.comments }) .subscribe((response: any) => { - this.router.navigate(['/accounting/closing-entries/view', response.resourceId]); + this.router.navigate(['../../', response.resourceId], { relativeTo: this.route }); }); } diff --git a/src/app/accounting/closing-entries/view-closure/view-closure.component.html b/src/app/accounting/closing-entries/view-closure/view-closure.component.html index a07df34cd..7efafdf3a 100644 --- a/src/app/accounting/closing-entries/view-closure/view-closure.component.html +++ b/src/app/accounting/closing-entries/view-closure/view-closure.component.html @@ -1,13 +1,15 @@
-
-
+
@@ -20,7 +22,7 @@
- {{ accountingClosure.officeName }} + {{ glAccountClosure.officeName }}
@@ -28,7 +30,7 @@
- {{ accountingClosure.closingDate }} + {{ glAccountClosure.closingDate }}
@@ -36,7 +38,7 @@
- {{ accountingClosure.createdByUsername }} + {{ glAccountClosure.createdByUsername }}
@@ -44,7 +46,7 @@
- {{ accountingClosure.lastUpdatedByUsername }} + {{ glAccountClosure.lastUpdatedByUsername }}
@@ -52,7 +54,7 @@
- {{ accountingClosure.lastUpdatedDate }} + {{ glAccountClosure.lastUpdatedDate }}
@@ -60,7 +62,7 @@
- {{ accountingClosure.createdDate }} + {{ glAccountClosure.createdDate }}
@@ -68,7 +70,7 @@
- {{ accountingClosure.comments }} + {{ glAccountClosure.comments }}
diff --git a/src/app/accounting/closing-entries/view-closure/view-closure.component.scss b/src/app/accounting/closing-entries/view-closure/view-closure.component.scss index 98c6e0c98..a4541ccaf 100644 --- a/src/app/accounting/closing-entries/view-closure/view-closure.component.scss +++ b/src/app/accounting/closing-entries/view-closure/view-closure.component.scss @@ -1,14 +1,14 @@ .container { max-width: 37rem; -} -.content { - div { - margin: 1rem 0; - word-wrap: break-word; - } + .content { + div { + margin: 1rem 0; + word-wrap: break-word; - .header { - font-weight: 500; + &.header { + font-weight: 500; + } + } } } diff --git a/src/app/accounting/closing-entries/view-closure/view-closure.component.ts b/src/app/accounting/closing-entries/view-closure/view-closure.component.ts index 6895397bc..17e1a0d7d 100644 --- a/src/app/accounting/closing-entries/view-closure/view-closure.component.ts +++ b/src/app/accounting/closing-entries/view-closure/view-closure.component.ts @@ -1,10 +1,17 @@ +/** Angular Imports */ import { Component, OnInit } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; import { MatDialog } from '@angular/material'; +/** Custom Services */ import { AccountingService } from '../../accounting.service'; + +/** Custom Components */ import { DeleteDialogComponent } from '../../../shared/delete-dialog/delete-dialog.component'; +/** + * View closure component. + */ @Component({ selector: 'mifosx-view-closure', templateUrl: './view-closure.component.html', @@ -12,33 +19,38 @@ import { DeleteDialogComponent } from '../../../shared/delete-dialog/delete-dial }) export class ViewClosureComponent implements OnInit { - accountingClosureId: number; - accountingClosure: any; + /** GL Account closure. */ + glAccountClosure: any; + /** + * Retrieves the gl account closure data from `resolve`. + * @param {AccountingService} accountingService Accounting Service. + * @param {ActivatedRoute} route Activated Route. + * @param {Router} router Router for navigation. + * @param {MatDialog} dialog Dialog reference. + */ constructor(private accountingService: AccountingService, private route: ActivatedRoute, private router: Router, - public dialog: MatDialog) { } + public dialog: MatDialog) { + this.route.data.subscribe((data: { glAccountClosure: any }) => { + this.glAccountClosure = data.glAccountClosure; + }); + } ngOnInit() { - this.accountingClosureId = Number(this.route.snapshot.paramMap.get('id')); - this.getAccountingClosure(); - } - - getAccountingClosure() { - this.accountingService.getAccountingClosure(this.accountingClosureId) - .subscribe((accountingClosure: any) => { - this.accountingClosure = accountingClosure; - }); } + /** + * Deletes the gl account closure and redirects to closing entries. + */ deleteAccountingClosure() { const deleteAccountingClosureDialogRef = this.dialog.open(DeleteDialogComponent, { - data: { deleteContext: `accounting closure ${this.accountingClosureId}` } + data: { deleteContext: `accounting closure ${this.glAccountClosure.id}` } }); deleteAccountingClosureDialogRef.afterClosed().subscribe((response: any) => { if (response.delete) { - this.accountingService.deleteAccountingClosure(this.accountingClosureId) + this.accountingService.deleteAccountingClosure(this.glAccountClosure.id) .subscribe(() => { this.router.navigate(['/accounting/closing-entries']); }); diff --git a/src/app/accounting/common-resolvers/accounting-rules-associations.resolver.ts b/src/app/accounting/common-resolvers/accounting-rules-associations.resolver.ts new file mode 100644 index 000000000..d3b8e2837 --- /dev/null +++ b/src/app/accounting/common-resolvers/accounting-rules-associations.resolver.ts @@ -0,0 +1,30 @@ +/** Angular Imports */ +import { Injectable } from '@angular/core'; +import { Resolve } from '@angular/router'; + +/** rxjs Imports */ +import { Observable } from 'rxjs'; + +/** Custom Services */ +import { AccountingService } from '../accounting.service'; + +/** + * Accounting rules associations data resolver. + */ +@Injectable() +export class AccountingRulesAssociationsResolver implements Resolve { + + /** + * @param {AccountingService} accountingService Accounting service. + */ + constructor(private accountingService: AccountingService) {} + + /** + * Returns the accounting rules associations data. + * @returns {Observable} + */ + resolve(): Observable { + return this.accountingService.getAccountingRules(true); + } + +} diff --git a/src/app/accounting/common-resolvers/currencies.resolver.ts b/src/app/accounting/common-resolvers/currencies.resolver.ts new file mode 100644 index 000000000..b396c2489 --- /dev/null +++ b/src/app/accounting/common-resolvers/currencies.resolver.ts @@ -0,0 +1,30 @@ +/** Angular Imports */ +import { Injectable } from '@angular/core'; +import { Resolve } from '@angular/router'; + +/** rxjs Imports */ +import { Observable } from 'rxjs'; + +/** Custom Services */ +import { AccountingService } from '../accounting.service'; + +/** + * Currencies data resolver. + */ +@Injectable() +export class CurrenciesResolver implements Resolve { + + /** + * @param {AccountingService} accountingService Accounting service. + */ + constructor(private accountingService: AccountingService) {} + + /** + * Returns the currencies data. + * @returns {Observable} + */ + resolve(): Observable { + return this.accountingService.getCurrencies(); + } + +} diff --git a/src/app/accounting/common-resolvers/gl-accounts.resolver.ts b/src/app/accounting/common-resolvers/gl-accounts.resolver.ts new file mode 100644 index 000000000..f62ffca32 --- /dev/null +++ b/src/app/accounting/common-resolvers/gl-accounts.resolver.ts @@ -0,0 +1,30 @@ +/** Angular Imports */ +import { Injectable } from '@angular/core'; +import { Resolve } from '@angular/router'; + +/** rxjs Imports */ +import { Observable } from 'rxjs'; + +/** Custom Services */ +import { AccountingService } from '../accounting.service'; + +/** + * GL accounts data resolver. + */ +@Injectable() +export class GlAccountsResolver implements Resolve { + + /** + * @param {AccountingService} accountingService Accounting service. + */ + constructor(private accountingService: AccountingService) {} + + /** + * Returns the gl accounts data. + * @returns {Observable} + */ + resolve(): Observable { + return this.accountingService.getGlAccounts(); + } + +} diff --git a/src/app/accounting/common-resolvers/loan-products.resolver.ts b/src/app/accounting/common-resolvers/loan-products.resolver.ts new file mode 100644 index 000000000..30857554f --- /dev/null +++ b/src/app/accounting/common-resolvers/loan-products.resolver.ts @@ -0,0 +1,30 @@ +/** Angular Imports */ +import { Injectable } from '@angular/core'; +import { Resolve } from '@angular/router'; + +/** rxjs Imports */ +import { Observable } from 'rxjs'; + +/** Custom Services */ +import { AccountingService } from '../accounting.service'; + +/** + * Loan products data resolver. + */ +@Injectable() +export class LoanProductsResolver implements Resolve { + + /** + * @param {AccountingService} accountingService Accounting service. + */ + constructor(private accountingService: AccountingService) {} + + /** + * Returns the loan products data. + * @returns {Observable} + */ + resolve(): Observable { + return this.accountingService.getLoanProducts(); + } + +} diff --git a/src/app/accounting/common-resolvers/offices.resolver.ts b/src/app/accounting/common-resolvers/offices.resolver.ts new file mode 100644 index 000000000..6327821d1 --- /dev/null +++ b/src/app/accounting/common-resolvers/offices.resolver.ts @@ -0,0 +1,30 @@ +/** Angular Imports */ +import { Injectable } from '@angular/core'; +import { Resolve } from '@angular/router'; + +/** rxjs Imports */ +import { Observable } from 'rxjs'; + +/** Custom Services */ +import { AccountingService } from '../accounting.service'; + +/** + * Offices data resolver. + */ +@Injectable() +export class OfficesResolver implements Resolve { + + /** + * @param {AccountingService} accountingService Accounting service. + */ + constructor(private accountingService: AccountingService) {} + + /** + * Returns the offices data. + * @returns {Observable} + */ + resolve(): Observable { + return this.accountingService.getOffices(); + } + +} diff --git a/src/app/accounting/common-resolvers/payment-types.resolver.ts b/src/app/accounting/common-resolvers/payment-types.resolver.ts new file mode 100644 index 000000000..1a167e5d7 --- /dev/null +++ b/src/app/accounting/common-resolvers/payment-types.resolver.ts @@ -0,0 +1,30 @@ +/** Angular Imports */ +import { Injectable } from '@angular/core'; +import { Resolve } from '@angular/router'; + +/** rxjs Imports */ +import { Observable } from 'rxjs'; + +/** Custom Services */ +import { AccountingService } from '../accounting.service'; + +/** + * Payment types data resolver. + */ +@Injectable() +export class PaymentTypesResolver implements Resolve { + + /** + * @param {AccountingService} accountingService Accounting service. + */ + constructor(private accountingService: AccountingService) {} + + /** + * Returns the payment types data. + * @returns {Observable} + */ + resolve(): Observable { + return this.accountingService.getPaymentTypes(); + } + +} diff --git a/src/app/accounting/common-resolvers/provisioning-categories.resolver.ts b/src/app/accounting/common-resolvers/provisioning-categories.resolver.ts new file mode 100644 index 000000000..b8fc9a948 --- /dev/null +++ b/src/app/accounting/common-resolvers/provisioning-categories.resolver.ts @@ -0,0 +1,30 @@ +/** Angular Imports */ +import { Injectable } from '@angular/core'; +import { Resolve } from '@angular/router'; + +/** rxjs Imports */ +import { Observable } from 'rxjs'; + +/** Custom Services */ +import { AccountingService } from '../accounting.service'; + +/** + * Provisioning categories data resolver. + */ +@Injectable() +export class ProvisioningCategoriesResolver implements Resolve { + + /** + * @param {AccountingService} accountingService Accounting service. + */ + constructor(private accountingService: AccountingService) {} + + /** + * Returns the Provisioning categories data. + * @returns {Observable} + */ + resolve(): Observable { + return this.accountingService.getProvisioningCategories(); + } + +} diff --git a/src/app/accounting/create-journal-entry/create-journal-entry.component.html b/src/app/accounting/create-journal-entry/create-journal-entry.component.html index 08487858f..6db77e98d 100644 --- a/src/app/accounting/create-journal-entry/create-journal-entry.component.html +++ b/src/app/accounting/create-journal-entry/create-journal-entry.component.html @@ -1,131 +1,178 @@ - +
-
+ - + -
+ - - - - {{ office.name }} - - - +
- - - - {{ currency.displayLabel }} - - - + + Office + + + {{ office.name }} + + + + Office is required + + -
+ + Currency + + + {{ currency.displayLabel }} + + + + Currency is required + + -
+
- - - - {{ affectedGLEntryDebit.name + ' (' + affectedGLEntryDebit.glCode + ')' }} - - - +
- - - + + Affected GL Entry (Debit) + + + {{ affectedGLEntryDebit.name + ' (' + affectedGLEntryDebit.glCode + ')' }} + + + + Affected GL Entry (Debit) is required + + - - - - + + Debit Amount + + + Debit Amount is required + + + + + + + + +
-
+
-
+
-
+ + Affected GL Entry (Credit) + + + {{ affectedGLEntryCredit.name + ' (' + affectedGLEntryCredit.glCode + ')' }} + + + + Affected GL Entry (Credit) is required + + - - - - {{ affectedGLEntryCredit.name + ' (' + affectedGLEntryCredit.glCode + ')' }} - - - + + Credit Amount + + + Credit Amount is required + + - - - + + + + - - - - +
+ + Reference Number + + + + + Transaction Date + + + + + Transaction Date is required + + + + + Payment Type + + + {{ paymentType.name }} + + + + + + Account Number + + + + + Cheque Number + + + + + Routing Code + + + + + Receipt Number + + + + + Bank Number + + + + + Comments + + +
- - - + - - - - - + + + + - - - - {{ paymentType.name }} - - - + - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - + +
diff --git a/src/app/accounting/create-journal-entry/create-journal-entry.component.ts b/src/app/accounting/create-journal-entry/create-journal-entry.component.ts index c17a565c0..ffada2289 100644 --- a/src/app/accounting/create-journal-entry/create-journal-entry.component.ts +++ b/src/app/accounting/create-journal-entry/create-journal-entry.component.ts @@ -1,9 +1,14 @@ +/** Angular Imports */ import { Component, OnInit } from '@angular/core'; import { FormGroup, FormBuilder, Validators, FormArray } from '@angular/forms'; -import { Router } from '@angular/router'; +import { Router, ActivatedRoute } from '@angular/router'; +/** Custom Services */ import { AccountingService } from '../accounting.service'; +/** + * Create Journal Entry component. + */ @Component({ selector: 'mifosx-create-journal-entry', templateUrl: './create-journal-entry.component.html', @@ -11,28 +16,55 @@ import { AccountingService } from '../accounting.service'; }) export class CreateJournalEntryComponent implements OnInit { - // TODO: Validations - + /** Minimum transaction date allowed. */ minDate = new Date(2000, 0, 1); + /** Maximum transaction date allowed. */ maxDate = new Date(); + /** Journal entry form. */ journalEntryForm: FormGroup; + /** Office data. */ officeData: any; + /** Currency data. */ currencyData: any; + /** Payment type data. */ paymentTypeData: any; + /** Gl Account data. */ glAccountData: any; + /** + * Retrieves the offices, currencies, payment types and gl accounts data from `resolve`. + * @param {FormBuilder} formBuilder Form Builder. + * @param {AccountingService} accountingService Accounting Service. + * @param {ActivatedRoute} route Activated Route. + * @param {Router} router Router for navigation. + */ constructor(private formBuilder: FormBuilder, private accountingService: AccountingService, - private router: Router) { } - - ngOnInit() { - this.createJournalEntryForm(); - this.getOffices(); - this.getCurrencies(); - this.getPaymentTypes(); - this.getGlAccounts(); + private route: ActivatedRoute, + private router: Router) { + this.route.data.subscribe((data: { + offices: any, + currencies: any, + paymentTypes: any, + glAccounts: any + }) => { + this.officeData = data.offices; + this.currencyData = data.currencies.selectedCurrencyOptions; + this.paymentTypeData = data.paymentTypes; + this.glAccountData = data.glAccounts; + }); } + /** + * Creates the journal entry form. + */ + ngOnInit() { + this.createJournalEntryForm(); + } + + /** + * Creates the journal entry form. + */ createJournalEntryForm() { this.journalEntryForm = this.formBuilder.group({ 'officeId': ['', Validators.required], @@ -51,6 +83,10 @@ export class CreateJournalEntryComponent implements OnInit { }); } + /** + * Creates the affected gl entry form. + * @returns {FormGroup} Affected gl entry form. + */ createAffectedGLEntryForm(): FormGroup { return this.formBuilder.group({ 'glAccountId': ['', Validators.required], @@ -58,50 +94,62 @@ export class CreateJournalEntryComponent implements OnInit { }); } - getFormArrayData(type: string): FormArray { - return this.journalEntryForm.get(type); + /** + * Gets the affected gl entry (debits) form array. + * @returns {FormArray} Debits form array. + */ + get debits(): FormArray { + return this.journalEntryForm.get('debits') as FormArray; } + /** + * Gets the affected gl entry (credits) form array. + * @returns {FormArray} Credits form array. + */ + get credits(): FormArray { + return this.journalEntryForm.get('credits') as FormArray; + } + + /** + * Adds the affected gl entry form to given affected gl entry form array. + * @param {FormArray} affectedGLEntryFormArray Given affected gl entry form array (debit/credit). + */ addAffectedGLEntry(affectedGLEntryFormArray: FormArray) { affectedGLEntryFormArray.push(this.createAffectedGLEntryForm()); } + /** + * Removes the affected gl entry form from given affected gl entry form array at given index. + * @param {FormArray} affectedGLEntryFormArray Given affected gl entry form array (debit/credit). + * @param {number} index Array index from where affected gl entry form needs to be removed. + */ removeAffectedGLEntry(affectedGLEntryFormArray: FormArray, index: number) { affectedGLEntryFormArray.removeAt(index); } - getOffices() { - this.accountingService.getOffices().subscribe(officeData => { - this.officeData = officeData; - }); - } - - getCurrencies() { - this.accountingService.getCurrencies().subscribe(currencyData => { - this.currencyData = currencyData.selectedCurrencyOptions; - }); - } - - getPaymentTypes() { - this.accountingService.getPaymentTypes().subscribe(paymentTypeData => { - this.paymentTypeData = paymentTypeData; - }); - } - - getGlAccounts() { - this.accountingService.getGlAccounts().subscribe(glAccountData => { - this.glAccountData = glAccountData; - }); - } - + /** + * Submits the journal entries form and creates journal entry, + * if successful redirects to view created transaction. + */ submit() { const journalEntry = this.journalEntryForm.value; // TODO: Update once language and date settings are setup journalEntry.locale = 'en'; - journalEntry.dateFormat = 'dd MMMM yyyy'; - journalEntry.transactionDate = '20 July 2018'; + journalEntry.dateFormat = 'yyyy-MM-dd'; + if (journalEntry.transactionDate instanceof Date) { + let day = journalEntry.transactionDate.getDate(); + let month = journalEntry.transactionDate.getMonth() + 1; + const year = journalEntry.transactionDate.getFullYear(); + if (day < 10) { + day = `0${day}`; + } + if (month < 10) { + month = `0${month}`; + } + journalEntry.transactionDate = `${year}-${month}-${day}`; + } this.accountingService.createJournalEntry(journalEntry).subscribe(response => { - this.router.navigate(['/accounting/transactions/view', response.transactionId]); + this.router.navigate(['../transactions/view', response.transactionId], { relativeTo: this.route }); }); } diff --git a/src/app/accounting/financial-activity-mappings/create-financial-activity-mapping/create-financial-activity-mapping.component.html b/src/app/accounting/financial-activity-mappings/create-financial-activity-mapping/create-financial-activity-mapping.component.html index 8914d3222..4c95dcc16 100644 --- a/src/app/accounting/financial-activity-mappings/create-financial-activity-mapping/create-financial-activity-mapping.component.html +++ b/src/app/accounting/financial-activity-mappings/create-financial-activity-mapping/create-financial-activity-mapping.component.html @@ -1,7 +1,7 @@
- +
@@ -9,19 +9,27 @@
- + Financial Activity + {{ financialActivity.name }} + + Financial Activity is required + - + Account + {{ glAccount.name + ' (' + glAccount.glCode + ')' }} + + Account is required +
@@ -29,8 +37,8 @@
- - + +
diff --git a/src/app/accounting/financial-activity-mappings/create-financial-activity-mapping/create-financial-activity-mapping.component.ts b/src/app/accounting/financial-activity-mappings/create-financial-activity-mapping/create-financial-activity-mapping.component.ts index 2b6782a82..5fb7af358 100644 --- a/src/app/accounting/financial-activity-mappings/create-financial-activity-mapping/create-financial-activity-mapping.component.ts +++ b/src/app/accounting/financial-activity-mappings/create-financial-activity-mapping/create-financial-activity-mapping.component.ts @@ -1,9 +1,14 @@ +/** Angular Imports */ import { Component, OnInit } from '@angular/core'; import { FormBuilder, FormGroup, Validators } from '@angular/forms'; -import { Router } from '@angular/router'; +import { Router, ActivatedRoute } from '@angular/router'; +/** Custom Services */ import { AccountingService } from '../../accounting.service'; +/** + * Create financial activity mapping component. + */ @Component({ selector: 'mifosx-create-financial-activity-mapping', templateUrl: './create-financial-activity-mapping.component.html', @@ -11,56 +16,79 @@ import { AccountingService } from '../../accounting.service'; }) export class CreateFinancialActivityMappingComponent implements OnInit { + /** Financial activity mapping form. */ financialActivityMappingForm: FormGroup; + /** GL Account options. */ glAccountOptions: any; + /** GL Account data. */ glAccountData: any; + /** Financial activity data. */ financialActivityData: any; - constructor(private formBuider: FormBuilder, + /** + * Retrieves the gl account options and financial activity data from `resolve`. + * @param {FormBuilder} formBuilder Form Builder. + * @param {AccountingService} accountingService Accounting Service. + * @param {ActivatedRoute} route Activated Route. + * @param {Router} router Router for navigation. + */ + constructor(private formBuilder: FormBuilder, private accountingService: AccountingService, - private router: Router) { } - - ngOnInit() { - this.createFinancialActivityMappingForm(); - this.getFinancialActivityAccountsTemplate(); - this.setGLAccountData(); + private route: ActivatedRoute, + private router: Router) { + this.route.data.subscribe((data: { financialActivityAccountsTemplate: any }) => { + this.glAccountOptions = data.financialActivityAccountsTemplate.glAccountOptions; + this.financialActivityData = data.financialActivityAccountsTemplate.financialActivityOptions; + }); } + /** + * Creates the financial activity mapping form and sets the gl account data. + */ + ngOnInit() { + this.createFinancialActivityMappingForm(); + this.setGlAccountData(); + } + + /** + * Creates the financial activity mapping form. + */ createFinancialActivityMappingForm() { - this.financialActivityMappingForm = this.formBuider.group({ + this.financialActivityMappingForm = this.formBuilder.group({ 'financialActivityId': ['', Validators.required], 'glAccountId': ['', Validators.required] }); } - getFinancialActivityAccountsTemplate() { - this.accountingService.getFinancialActivityAccountsTemplate().subscribe((financialActivityAccountData: any) => { - this.glAccountOptions = financialActivityAccountData.glAccountOptions; - this.financialActivityData = financialActivityAccountData.financialActivityOptions; - }); - } - - setGLAccountData() { - this.financialActivityMappingForm.get('financialActivityId').valueChanges.subscribe(financialActivityId => { - switch (financialActivityId) { - case 100: - case 101: - case 102: - case 103: this.glAccountData = this.glAccountOptions.assetAccountOptions; - break; - case 200: - case 201: this.glAccountData = this.glAccountOptions.liabilityAccountOptions; - break; - case 300: this.glAccountData = this.glAccountOptions.equityAccountOptions; - break; - } - }); + /** + * Sets the gl account data on the basis of selected financial activity. + */ + setGlAccountData() { + this.financialActivityMappingForm.get('financialActivityId').valueChanges + .subscribe(financialActivityId => { + switch (financialActivityId) { + case 100: + case 101: + case 102: + case 103: this.glAccountData = this.glAccountOptions.assetAccountOptions; + break; + case 200: + case 201: this.glAccountData = this.glAccountOptions.liabilityAccountOptions; + break; + case 300: this.glAccountData = this.glAccountOptions.equityAccountOptions; + break; + } + }); } + /** + * Submits the financial activity mapping form and creates financial activity account, + * if successful redirects to view created account. + */ submit() { this.accountingService.createFinancialActivityAccount(this.financialActivityMappingForm.value) .subscribe((response: any) => { - this.router.navigate(['/accounting/financial-activity-mappings/view', response.resourceId]); + this.router.navigate(['../view', response.resourceId], { relativeTo: this.route }); }); } diff --git a/src/app/accounting/financial-activity-mappings/create-financial-activity-mapping/financial-activity-mappings-template.resolver.ts b/src/app/accounting/financial-activity-mappings/create-financial-activity-mapping/financial-activity-mappings-template.resolver.ts new file mode 100644 index 000000000..c5454ee35 --- /dev/null +++ b/src/app/accounting/financial-activity-mappings/create-financial-activity-mapping/financial-activity-mappings-template.resolver.ts @@ -0,0 +1,30 @@ +/** Angular Imports */ +import { Injectable } from '@angular/core'; +import { Resolve } from '@angular/router'; + +/** rxjs Imports */ +import { Observable } from 'rxjs'; + +/** Custom Services */ +import { AccountingService } from '../../accounting.service'; + +/** + * Financial activity mappings template data resolver. + */ +@Injectable() +export class FinancialActivityMappingsTemplateResolver implements Resolve { + + /** + * @param {AccountingService} accountingService Accounting service. + */ + constructor(private accountingService: AccountingService) {} + + /** + * Returns the financial activity mappings template data. + * @returns {Observable} + */ + resolve(): Observable { + return this.accountingService.getFinancialActivityAccountsTemplate(); + } + +} diff --git a/src/app/accounting/financial-activity-mappings/edit-financial-activity-mapping/edit-financial-activity-mapping.component.html b/src/app/accounting/financial-activity-mappings/edit-financial-activity-mapping/edit-financial-activity-mapping.component.html index 204165549..7f2172461 100644 --- a/src/app/accounting/financial-activity-mappings/edit-financial-activity-mapping/edit-financial-activity-mapping.component.html +++ b/src/app/accounting/financial-activity-mappings/edit-financial-activity-mapping/edit-financial-activity-mapping.component.html @@ -9,7 +9,8 @@
- + Financial Activity + {{ financialActivity.name }} @@ -17,7 +18,8 @@ - + Account + {{ glAccount.name + ' (' + glAccount.glCode + ')' }} @@ -29,8 +31,8 @@ - - + + diff --git a/src/app/accounting/financial-activity-mappings/edit-financial-activity-mapping/edit-financial-activity-mapping.component.ts b/src/app/accounting/financial-activity-mappings/edit-financial-activity-mapping/edit-financial-activity-mapping.component.ts index 766312a63..73fd2686d 100644 --- a/src/app/accounting/financial-activity-mappings/edit-financial-activity-mapping/edit-financial-activity-mapping.component.ts +++ b/src/app/accounting/financial-activity-mappings/edit-financial-activity-mapping/edit-financial-activity-mapping.component.ts @@ -1,9 +1,14 @@ +/** Angular Imports */ import { Component, OnInit } from '@angular/core'; import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { Router, ActivatedRoute } from '@angular/router'; +/** Custom Services */ import { AccountingService } from '../../accounting.service'; +/** + * Edit financial activity mapping component. + */ @Component({ selector: 'mifosx-edit-financial-activity-mapping', templateUrl: './edit-financial-activity-mapping.component.html', @@ -11,24 +16,54 @@ import { AccountingService } from '../../accounting.service'; }) export class EditFinancialActivityMappingComponent implements OnInit { + /** Financial activity mapping form. */ financialActivityMappingForm: FormGroup; + /** GL Account options. */ glAccountOptions: any; + /** GL Account data. */ glAccountData: any; - financialActivityAccountId: any; + /** Financial activity data. */ financialActivityData: any; + /** Financial activity account ID. */ + financialActivityAccountId: string; + /** Financial activity ID. */ + financialActivityId: number; + /** GL Account ID. */ + glAccountId: number; - constructor(private route: ActivatedRoute, - private formBuider: FormBuilder, + /** + * Retrieves the gl account options, financial activity and financial activity account data from `resolve`. + * @param {FormBuilder} formBuilder Form Builder. + * @param {AccountingService} accountingService Accounting Service. + * @param {ActivatedRoute} route Activated Route. + * @param {Router} router Router for navigation. + */ + constructor(private formBuider: FormBuilder, private accountingService: AccountingService, - private router: Router) { } - - ngOnInit() { - this.financialActivityAccountId = this.route.snapshot.paramMap.get('id'); - this.createFinancialActivityMappingForm(); - this.setGLAccountData(); - this.getFinancialActivityAccount(); + private route: ActivatedRoute, + private router: Router) { + this.route.data.subscribe((data: { financialActivityAccountAndTemplate: any }) => { + this.financialActivityAccountId = data.financialActivityAccountAndTemplate.id; + this.financialActivityId = data.financialActivityAccountAndTemplate.financialActivityData.id; + this.glAccountId = data.financialActivityAccountAndTemplate.glAccountData.id; + this.glAccountOptions = data.financialActivityAccountAndTemplate.glAccountOptions; + this.financialActivityData = data.financialActivityAccountAndTemplate.financialActivityOptions; + }); } + /** + * Creates and sets the financial activity mapping form and sets the gl account data. + */ + ngOnInit() { + this.createFinancialActivityMappingForm(); + this.setGLAccountData(); + this.financialActivityMappingForm.get('financialActivityId').setValue(this.financialActivityId); + this.financialActivityMappingForm.get('glAccountId').setValue(this.glAccountId); + } + + /** + * Creates the financial activity mapping form. + */ createFinancialActivityMappingForm() { this.financialActivityMappingForm = this.formBuider.group({ 'financialActivityId': ['', Validators.required], @@ -36,37 +71,35 @@ export class EditFinancialActivityMappingComponent implements OnInit { }); } - getFinancialActivityAccount() { - this.accountingService.getFinancialActivityAccount(this.financialActivityAccountId, true) - .subscribe((financialActivityAccountData: any) => { - this.glAccountOptions = financialActivityAccountData.glAccountOptions; - this.financialActivityData = financialActivityAccountData.financialActivityOptions; - this.financialActivityMappingForm.get('financialActivityId').setValue(financialActivityAccountData.financialActivityData.id); - this.financialActivityMappingForm.get('glAccountId').setValue(financialActivityAccountData.glAccountData.id); + /** + * Sets the gl account data on the basis of selected financial activity. + */ + setGLAccountData() { + this.financialActivityMappingForm.get('financialActivityId').valueChanges + .subscribe(financialActivityId => { + switch (financialActivityId) { + case 100: + case 101: + case 102: + case 103: this.glAccountData = this.glAccountOptions.assetAccountOptions; + break; + case 200: + case 201: this.glAccountData = this.glAccountOptions.liabilityAccountOptions; + break; + case 300: this.glAccountData = this.glAccountOptions.equityAccountOptions; + break; + } }); } - setGLAccountData() { - this.financialActivityMappingForm.get('financialActivityId').valueChanges.subscribe(financialActivityId => { - switch (financialActivityId) { - case 100: - case 101: - case 102: - case 103: this.glAccountData = this.glAccountOptions.assetAccountOptions; - break; - case 200: - case 201: this.glAccountData = this.glAccountOptions.liabilityAccountOptions; - break; - case 300: this.glAccountData = this.glAccountOptions.equityAccountOptions; - break; - } - }); - } - + /** + * Submits the financial activity mapping form and updates financial activity account, + * if successful redirects to view updated account. + */ submit() { this.accountingService.updateFinancialActivityAccount(this.financialActivityAccountId, this.financialActivityMappingForm.value) .subscribe((response: any) => { - this.router.navigate(['/accounting/financial-activity-mappings/view', response.resourceId]); + this.router.navigate(['../../', response.resourceId], { relativeTo: this.route }); }); } diff --git a/src/app/accounting/financial-activity-mappings/edit-financial-activity-mapping/financial-activity-mapping-and-template.resolver.ts b/src/app/accounting/financial-activity-mappings/edit-financial-activity-mapping/financial-activity-mapping-and-template.resolver.ts new file mode 100644 index 000000000..c1e61da5c --- /dev/null +++ b/src/app/accounting/financial-activity-mappings/edit-financial-activity-mapping/financial-activity-mapping-and-template.resolver.ts @@ -0,0 +1,31 @@ +/** Angular Imports */ +import { Injectable } from '@angular/core'; +import { Resolve, ActivatedRouteSnapshot } from '@angular/router'; + +/** rxjs Imports */ +import { Observable } from 'rxjs'; + +/** Custom Services */ +import { AccountingService } from '../../accounting.service'; + +/** + * Financial activity mapping and template data resolver. + */ +@Injectable() +export class FinancialActivityMappingAndTemplateResolver implements Resolve { + + /** + * @param {AccountingService} accountingService Accounting service. + */ + constructor(private accountingService: AccountingService) {} + + /** + * Returns the financial activity mapping and template data. + * @returns {Observable} + */ + resolve(route: ActivatedRouteSnapshot): Observable { + const financialActivityAccountId = route.paramMap.get('id'); + return this.accountingService.getFinancialActivityAccount(financialActivityAccountId, true); + } + +} diff --git a/src/app/accounting/financial-activity-mappings/financial-activity-mappings.component.html b/src/app/accounting/financial-activity-mappings/financial-activity-mappings.component.html index 66db0c521..37dcb68da 100644 --- a/src/app/accounting/financial-activity-mappings/financial-activity-mappings.component.html +++ b/src/app/accounting/financial-activity-mappings/financial-activity-mappings.component.html @@ -1,6 +1,6 @@
-
@@ -24,7 +24,7 @@ - + diff --git a/src/app/accounting/financial-activity-mappings/financial-activity-mappings.component.scss b/src/app/accounting/financial-activity-mappings/financial-activity-mappings.component.scss index 1922e7ffa..24edb63bb 100644 --- a/src/app/accounting/financial-activity-mappings/financial-activity-mappings.component.scss +++ b/src/app/accounting/financial-activity-mappings/financial-activity-mappings.component.scss @@ -1,3 +1,7 @@ table { width: 100%; + + .select-row:hover { + cursor: pointer; + } } diff --git a/src/app/accounting/financial-activity-mappings/financial-activity-mappings.component.ts b/src/app/accounting/financial-activity-mappings/financial-activity-mappings.component.ts index c1d0ac5e3..d87028a99 100644 --- a/src/app/accounting/financial-activity-mappings/financial-activity-mappings.component.ts +++ b/src/app/accounting/financial-activity-mappings/financial-activity-mappings.component.ts @@ -1,8 +1,11 @@ +/** Angular Imports */ import { Component, OnInit, ViewChild } from '@angular/core'; import { MatTableDataSource, MatPaginator, MatSort } from '@angular/material'; +import { ActivatedRoute } from '@angular/router'; -import { AccountingService } from '../accounting.service'; - +/** + * Financial activity mappings component. + */ @Component({ selector: 'mifosx-financial-activity-mappings', templateUrl: './financial-activity-mappings.component.html', @@ -10,32 +13,50 @@ import { AccountingService } from '../accounting.service'; }) export class FinancialActivityMappingsComponent implements OnInit { + /** Financial activity account data. */ + financialActivityAccountData: any; + /** Columns to be displayed in financial activity mappings table. */ displayedColumns: string[] = ['financialActivity', 'glAccountName', 'glAccountCode']; - dataSource: any; + /** Data source for financial activity mappings table. */ + dataSource: MatTableDataSource; + /** Paginator for financial activity mappings table. */ @ViewChild(MatPaginator) paginator: MatPaginator; + /** Sorter for financial activity mappings table. */ @ViewChild(MatSort) sort: MatSort; - constructor(private accountingService: AccountingService) { } - - ngOnInit() { - this.getFinancialActivityAccounts(); - } - - getFinancialActivityAccounts() { - this.accountingService.getFinancialActivityAccounts().subscribe((financialActivityAccounts: any) => { - this.dataSource = new MatTableDataSource(financialActivityAccounts); - this.dataSource.paginator = this.paginator; - this.dataSource.sortingDataAccessor = (financialActivityAccount: any, property: any) => { - switch (property) { - case 'financialActivity': return financialActivityAccount.financialActivityData.name; - case 'glAccountName': return financialActivityAccount.glAccountData.name; - case 'glAccountCode': return financialActivityAccount.glAccountData.glCode; - default: return financialActivityAccount[property]; - } - }; - this.dataSource.sort = this.sort; + /** + * Retrieves the financial activity accounts data from `resolve`. + * @param {ActivatedRoute} route Activated Route. + */ + constructor(private route: ActivatedRoute) { + this.route.data.subscribe(( data: { financialActivityAccounts: any }) => { + this.financialActivityAccountData = data.financialActivityAccounts; }); } + /** + * Sets the financial activity mappings table. + */ + ngOnInit() { + this.setFinancialActivityAccounts(); + } + + /** + * Initializes the data source, paginator and sorter for financial activity mappings table. + */ + setFinancialActivityAccounts() { + this.dataSource = new MatTableDataSource(this.financialActivityAccountData); + this.dataSource.paginator = this.paginator; + this.dataSource.sortingDataAccessor = (financialActivityAccount: any, property: any) => { + switch (property) { + case 'financialActivity': return financialActivityAccount.financialActivityData.name; + case 'glAccountName': return financialActivityAccount.glAccountData.name; + case 'glAccountCode': return financialActivityAccount.glAccountData.glCode; + default: return financialActivityAccount[property]; + } + }; + this.dataSource.sort = this.sort; + } + } diff --git a/src/app/accounting/financial-activity-mappings/financial-activity-mappings.resolver.ts b/src/app/accounting/financial-activity-mappings/financial-activity-mappings.resolver.ts new file mode 100644 index 000000000..52f7fce5e --- /dev/null +++ b/src/app/accounting/financial-activity-mappings/financial-activity-mappings.resolver.ts @@ -0,0 +1,30 @@ +/** Angular Imports */ +import { Injectable } from '@angular/core'; +import { Resolve } from '@angular/router'; + +/** rxjs Imports */ +import { Observable } from 'rxjs'; + +/** Custom Services */ +import { AccountingService } from '../accounting.service'; + +/** + * Financial activity mappings data resolver. + */ +@Injectable() +export class FinancialActivityMappingsResolver implements Resolve { + + /** + * @param {AccountingService} accountingService Accounting service. + */ + constructor(private accountingService: AccountingService) {} + + /** + * Returns the financial activity mappings data. + * @returns {Observable} + */ + resolve(): Observable { + return this.accountingService.getFinancialActivityAccounts(); + } + +} diff --git a/src/app/accounting/financial-activity-mappings/view-financial-activity-mapping/financial-activity-mapping.resolver.ts b/src/app/accounting/financial-activity-mappings/view-financial-activity-mapping/financial-activity-mapping.resolver.ts new file mode 100644 index 000000000..eb96001af --- /dev/null +++ b/src/app/accounting/financial-activity-mappings/view-financial-activity-mapping/financial-activity-mapping.resolver.ts @@ -0,0 +1,31 @@ +/** Angular Imports */ +import { Injectable } from '@angular/core'; +import { Resolve, ActivatedRouteSnapshot } from '@angular/router'; + +/** rxjs Imports */ +import { Observable } from 'rxjs'; + +/** Custom Services */ +import { AccountingService } from '../../accounting.service'; + +/** + * Financial activity mapping data resolver. + */ +@Injectable() +export class FinancialActivityMappingResolver implements Resolve { + + /** + * @param {AccountingService} accountingService Accounting service. + */ + constructor(private accountingService: AccountingService) {} + + /** + * Returns the financial activity mapping data. + * @returns {Observable} + */ + resolve(route: ActivatedRouteSnapshot): Observable { + const financialActivityAccountId = route.paramMap.get('id'); + return this.accountingService.getFinancialActivityAccount(financialActivityAccountId, false); + } + +} diff --git a/src/app/accounting/financial-activity-mappings/view-financial-activity-mapping/view-financial-activity-mapping.component.html b/src/app/accounting/financial-activity-mappings/view-financial-activity-mapping/view-financial-activity-mapping.component.html index 8d4634936..3374db06b 100644 --- a/src/app/accounting/financial-activity-mappings/view-financial-activity-mapping/view-financial-activity-mapping.component.html +++ b/src/app/accounting/financial-activity-mappings/view-financial-activity-mapping/view-financial-activity-mapping.component.html @@ -1,9 +1,11 @@
-
@@ -20,7 +22,7 @@
- {{ financialActivityAccountData?.financialActivityData.name }} + {{ financialActivityAccount?.financialActivityData.name }}
@@ -28,7 +30,7 @@
- {{ financialActivityAccountData?.glAccountData.name }} + {{ financialActivityAccount?.glAccountData.name }}
diff --git a/src/app/accounting/financial-activity-mappings/view-financial-activity-mapping/view-financial-activity-mapping.component.scss b/src/app/accounting/financial-activity-mappings/view-financial-activity-mapping/view-financial-activity-mapping.component.scss index 98c6e0c98..a4541ccaf 100644 --- a/src/app/accounting/financial-activity-mappings/view-financial-activity-mapping/view-financial-activity-mapping.component.scss +++ b/src/app/accounting/financial-activity-mappings/view-financial-activity-mapping/view-financial-activity-mapping.component.scss @@ -1,14 +1,14 @@ .container { max-width: 37rem; -} -.content { - div { - margin: 1rem 0; - word-wrap: break-word; - } + .content { + div { + margin: 1rem 0; + word-wrap: break-word; - .header { - font-weight: 500; + &.header { + font-weight: 500; + } + } } } diff --git a/src/app/accounting/financial-activity-mappings/view-financial-activity-mapping/view-financial-activity-mapping.component.ts b/src/app/accounting/financial-activity-mappings/view-financial-activity-mapping/view-financial-activity-mapping.component.ts index b57260bbb..20ec312d6 100644 --- a/src/app/accounting/financial-activity-mappings/view-financial-activity-mapping/view-financial-activity-mapping.component.ts +++ b/src/app/accounting/financial-activity-mappings/view-financial-activity-mapping/view-financial-activity-mapping.component.ts @@ -1,10 +1,17 @@ +/** Angular Imports */ import { Component, OnInit } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; import { MatDialog } from '@angular/material'; +/** Custom Services */ import { AccountingService } from '../../accounting.service'; + +/** Custom Components */ import { DeleteDialogComponent } from '../../../shared/delete-dialog/delete-dialog.component'; +/** + * View financial activity mapping component. + */ @Component({ selector: 'mifosx-view-financial-activity-mapping', templateUrl: './view-financial-activity-mapping.component.html', @@ -12,26 +19,34 @@ import { DeleteDialogComponent } from '../../../shared/delete-dialog/delete-dial }) export class ViewFinancialActivityMappingComponent implements OnInit { + /** Financial activity account ID. */ financialActivityAccountId: any; - financialActivityAccountData: any; + /** Financial activity account data. */ + financialActivityAccount: any; - constructor(private route: ActivatedRoute, + /** + * Retrieves the financial activity account data from `resolve`. + * @param {AccountingService} accountingService Accounting Service. + * @param {ActivatedRoute} route Activated Route. + * @param {Router} router Router for navigation. + * @param {MatDialog} dialog Dialog reference. + */ + constructor(private accountingService: AccountingService, + private route: ActivatedRoute, private router: Router, - private accountingService: AccountingService, - public dialog: MatDialog) { } + public dialog: MatDialog) { + this.route.data.subscribe((data: { financialActivityAccount: any }) => { + this.financialActivityAccount = data.financialActivityAccount; + this.financialActivityAccountId = data.financialActivityAccount.id; + }); + } ngOnInit() { - this.financialActivityAccountId = this.route.snapshot.paramMap.get('id'); - this.getFinancialActivityAccount(); - } - - getFinancialActivityAccount() { - this.accountingService.getFinancialActivityAccount(this.financialActivityAccountId, false) - .subscribe((financialActivityAccountData: any) => { - this.financialActivityAccountData = financialActivityAccountData; - }); } + /** + * Deletes the financial activity account and redirects to financial activity mappings. + */ deleteFinancialActivityAccount() { const deleteFinancialActivityAccountDialogRef = this.dialog.open(DeleteDialogComponent, { data: { deleteContext: `financial activity mapping ${this.financialActivityAccountId}` } diff --git a/src/app/accounting/frequent-postings/frequent-postings.component.html b/src/app/accounting/frequent-postings/frequent-postings.component.html index a6f1ed4fb..cf66c229c 100644 --- a/src/app/accounting/frequent-postings/frequent-postings.component.html +++ b/src/app/accounting/frequent-postings/frequent-postings.component.html @@ -1,140 +1,192 @@ - +
-
+ - + -
+ - - - - {{ office.name }} - - - +
- - - - {{ accountingRule.name }} - - - + + Office + + + {{ office.name }} + + + + Office is required + + - - - - {{ currency.displayLabel }} - - - + + Accounting Rule + + + {{ accountingRule.name }} + + + + Accounting Rule is required + + - + + Currency + + + {{ currency.displayLabel }} + + + + Currency is required + + -
+ -
+
- - - - {{ affectedGLEntryDebit.name }} - - - +
- - - + + Affected GL Entry (Debit) + + + {{ affectedGLEntryDebit.name }} + + + + Affected GL Entry (Debit) is required + + - - - - + + Debit Amount + + + Debit Amount is required + + + + + + + + +
-
+
-
+
-
+ + Affected GL Entry (Credit) + + + {{ affectedGLEntryCredit.name }} + + + + Affected GL Entry (Credit) is required + + - - - - {{ affectedGLEntryCredit.name }} - - - + + Credit Amount + + + Credit Amount is required + + - - - + + + + - - - - +
+ + Reference Number + + + + + Transaction Date + + + + + Transaction Date is required + + + + + Payment Type + + + {{ paymentType.name }} + + + + + + Account Number + + + + + Cheque Number + + + + + Routing Code + + + + + Receipt Number + + + + + Bank Number + + + + + Comments + + +
- - - + - - - - - + + + + - - - - {{ paymentType.name }} - - - + - - - + - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - +
diff --git a/src/app/accounting/frequent-postings/frequent-postings.component.ts b/src/app/accounting/frequent-postings/frequent-postings.component.ts index df2d4519e..d5ffb8696 100644 --- a/src/app/accounting/frequent-postings/frequent-postings.component.ts +++ b/src/app/accounting/frequent-postings/frequent-postings.component.ts @@ -1,9 +1,14 @@ +/** Angular Imports */ import { Component, OnInit } from '@angular/core'; import { FormGroup, FormBuilder, Validators, FormArray } from '@angular/forms'; -import { Router } from '@angular/router'; +import { Router, ActivatedRoute } from '@angular/router'; +/** Custom Services */ import { AccountingService } from '../accounting.service'; +/** + * Frequent Postings component. + */ @Component({ selector: 'mifosx-frequent-postings', templateUrl: './frequent-postings.component.html', @@ -11,35 +16,64 @@ import { AccountingService } from '../accounting.service'; }) export class FrequentPostingsComponent implements OnInit { - // TODO: Validations - // https://github.com/openMF/incubator-fineract/blob/master/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/exception/JournalEntryInvalidException.java - + /** Minimum transaction date allowed. */ minDate = new Date(2000, 0, 1); + /** Maximum transaction date allowed. */ maxDate = new Date(); + /** Frequent postings form. */ frequentPostingsForm: FormGroup; + /** Office data. */ officeData: any; + /** Accounting rule data. */ accountingRuleData: any; + /** Currency data. */ currencyData: any; + /** Payment type data. */ paymentTypeData: any; + /** Debit account data. */ debitAccountData: any; + /** Credit account data. */ creditAccountData: any; - + /** True if multiple credit entries are allowed. */ allowMultipleCreditEntries: boolean; + /** True if multiple debit entries are allowed. */ allowMultipleDebitEntries: boolean; + /** + * Retrieves the offices, accounting rules, currencies and payment types data from `resolve`. + * @param {FormBuilder} formBuilder Form Builder. + * @param {AccountingService} accountingService Accounting Service. + * @param {ActivatedRoute} route Activated Route. + * @param {Router} router Router for navigation. + */ constructor(private formBuilder: FormBuilder, private accountingService: AccountingService, - private router: Router) { } + private route: ActivatedRoute, + private router: Router) { + this.route.data.subscribe((data: { + offices: any, + accountingRules: any, + currencies: any, + paymentTypes: any + }) => { + this.officeData = data.offices; + this.accountingRuleData = data.accountingRules; + this.currencyData = data.currencies.selectedCurrencyOptions; + this.paymentTypeData = data.paymentTypes; + }); + } + /** + * Creates the frequent postings form and sets the affected gl entry form array. + */ ngOnInit() { this.createFrequentPostingsForm(); this.setAffectedGLEntryForm(); - this.getOffices(); - this.getAccountingRules(); - this.getCurrencies(); - this.getPaymentTypes(); } + /** + * Creates the frequent postings form. + */ createFrequentPostingsForm() { this.frequentPostingsForm = this.formBuilder.group({ 'officeId': ['', Validators.required], @@ -59,25 +93,30 @@ export class FrequentPostingsComponent implements OnInit { }); } + /** + * Sets the affected gl entry form array. + */ setAffectedGLEntryForm() { this.frequentPostingsForm.get('accountingRule').valueChanges.subscribe(accountingRule => { - const debits = this.frequentPostingsForm.get('debits') as FormArray; - const credits = this.frequentPostingsForm.get('credits') as FormArray; - while (debits.length) { - debits.removeAt(0); + while (this.debits.length) { + this.debits.removeAt(0); } - while (credits.length) { - credits.removeAt(0); + while (this.credits.length) { + this.credits.removeAt(0); } this.allowMultipleDebitEntries = accountingRule.allowMultipleDebitEntries; this.allowMultipleCreditEntries = accountingRule.allowMultipleCreditEntries; this.debitAccountData = accountingRule.debitAccounts; this.creditAccountData = accountingRule.creditAccounts; - this.addAffectedGLEntry(debits); - this.addAffectedGLEntry(credits); + this.addAffectedGLEntry(this.debits); + this.addAffectedGLEntry(this.credits); }); } + /** + * Creates the affected gl entry form. + * @returns {FormGroup} Affected gl entry form. + */ createAffectedGLEntryForm(): FormGroup { return this.formBuilder.group({ 'glAccountId': ['', Validators.required], @@ -85,51 +124,63 @@ export class FrequentPostingsComponent implements OnInit { }); } - getFormArrayData(type: string): FormArray { - return this.frequentPostingsForm.get(type); + /** + * Gets the affected gl entry (debits) form array. + * @returns {FormArray} Debits form array. + */ + get debits(): FormArray { + return this.frequentPostingsForm.get('debits') as FormArray; } + /** + * Gets the affected gl entry (credits) form array. + * @returns {FormArray} Credits form array. + */ + get credits(): FormArray { + return this.frequentPostingsForm.get('credits') as FormArray; + } + + /** + * Adds the affected gl entry form to given affected gl entry form array. + * @param {FormArray} affectedGLEntryFormArray Given affected gl entry form array (debit/credit). + */ addAffectedGLEntry(affectedGLEntryFormArray: FormArray) { affectedGLEntryFormArray.push(this.createAffectedGLEntryForm()); } + /** + * Removes the affected gl entry form from given affected gl entry form array at given index. + * @param {FormArray} affectedGLEntryFormArray Given affected gl entry form array (debit/credit). + * @param {number} index Array index from where affected gl entry form needs to be removed. + */ removeAffectedGLEntry(affectedGLEntryFormArray: FormArray, index: number) { affectedGLEntryFormArray.removeAt(index); } - getOffices() { - this.accountingService.getOffices().subscribe(officeData => { - this.officeData = officeData; - }); - } - - getAccountingRules() { - this.accountingService.getAccountingRules(true).subscribe(accountingRuleData => { - this.accountingRuleData = accountingRuleData; - }); - } - - getCurrencies() { - this.accountingService.getCurrencies().subscribe(currencyData => { - this.currencyData = currencyData.selectedCurrencyOptions; - }); - } - - getPaymentTypes() { - this.accountingService.getPaymentTypes().subscribe(paymentTypeData => { - this.paymentTypeData = paymentTypeData; - }); - } - + /** + * Submits the frequent postings form and creates journal entry, + * if successful redirects to view created transaction. + */ submit() { const journalEntry = this.frequentPostingsForm.value; journalEntry.accountingRule = journalEntry.accountingRule.id; // TODO: Update once language and date settings are setup journalEntry.locale = 'en'; - journalEntry.dateFormat = 'dd MMMM yyyy'; - journalEntry.transactionDate = '20 July 2018'; + journalEntry.dateFormat = 'yyyy-MM-dd'; + if (journalEntry.transactionDate instanceof Date) { + let day = journalEntry.transactionDate.getDate(); + let month = journalEntry.transactionDate.getMonth() + 1; + const year = journalEntry.transactionDate.getFullYear(); + if (day < 10) { + day = `0${day}`; + } + if (month < 10) { + month = `0${month}`; + } + journalEntry.transactionDate = `${year}-${month}-${day}`; + } this.accountingService.createJournalEntry(journalEntry).subscribe(response => { - this.router.navigate(['/accounting/transactions/view', response.transactionId]); + this.router.navigate(['../transactions/view', response.transactionId], { relativeTo: this.route }); }); } diff --git a/src/app/accounting/migrate-opening-balances/migrate-opening-balances.component.html b/src/app/accounting/migrate-opening-balances/migrate-opening-balances.component.html index f9e76d3d8..1ae7a09c2 100644 --- a/src/app/accounting/migrate-opening-balances/migrate-opening-balances.component.html +++ b/src/app/accounting/migrate-opening-balances/migrate-opening-balances.component.html @@ -1,3 +1,4 @@ +
@@ -9,11 +10,15 @@
- + Office + {{ office.name }} + + Office is required + @@ -25,17 +30,25 @@ - + Currency + {{ currency.displayLabel }} + + Currency is required + - + Opening Balances Date + + + Opening Balances Date is required +
@@ -63,7 +76,7 @@
+ *ngFor="let credit of glAccountEntries.controls; let i = index;">
@@ -81,13 +94,15 @@
- + Debit +
- + Credit +
@@ -100,8 +115,8 @@ - - + + diff --git a/src/app/accounting/migrate-opening-balances/migrate-opening-balances.component.scss b/src/app/accounting/migrate-opening-balances/migrate-opening-balances.component.scss index e8dff44e8..b78000682 100644 --- a/src/app/accounting/migrate-opening-balances/migrate-opening-balances.component.scss +++ b/src/app/accounting/migrate-opening-balances/migrate-opening-balances.component.scss @@ -2,9 +2,9 @@ div { margin: 1rem 0; word-wrap: break-word; - } - .header { - font-weight: 500; + &.header { + font-weight: 500; + } } } diff --git a/src/app/accounting/migrate-opening-balances/migrate-opening-balances.component.ts b/src/app/accounting/migrate-opening-balances/migrate-opening-balances.component.ts index 3338d7b12..b57e962d2 100644 --- a/src/app/accounting/migrate-opening-balances/migrate-opening-balances.component.ts +++ b/src/app/accounting/migrate-opening-balances/migrate-opening-balances.component.ts @@ -1,14 +1,17 @@ +/** Angular Imports */ import { Component, OnInit } from '@angular/core'; -import { FormGroup, FormBuilder, Validators, FormArray, ValidatorFn, ValidationErrors } from '@angular/forms'; +import { FormGroup, FormBuilder, Validators, FormArray } from '@angular/forms'; +import { ActivatedRoute, Router } from '@angular/router'; +/** Custom Services */ import { AccountingService } from '../accounting.service'; -const onlyOneOfTheFieldsIsRequiredValidator: ValidatorFn = (glAccountEntriesForm: FormGroup): ValidationErrors | null => { - const debit = glAccountEntriesForm.controls.debit.value; - const credit = glAccountEntriesForm.controls.credit.value; - return ((debit || credit) && !(debit && credit)) ? null : { 'error': true }; -}; +/** Custom Validators */ +import { onlyOneOfTheFieldsIsRequiredValidator } from './only-one-of-the-fields-is-required.validator'; +/** + * Migrate opening balances component. + */ @Component({ selector: 'mifosx-migrate-opening-balances', templateUrl: './migrate-opening-balances.component.html', @@ -16,37 +19,53 @@ const onlyOneOfTheFieldsIsRequiredValidator: ValidatorFn = (glAccountEntriesForm }) export class MigrateOpeningBalancesComponent implements OnInit { + /** Minimum opening balances date allowed. */ minDate = new Date(2000, 0, 1); + /** Maximum opening balances date allowed. */ maxDate = new Date(); + /** Opening balances form. */ openingBalancesForm: FormGroup; + /** Opening balances data. */ openingBalancesData: any; + /** Office data. */ officeData: any; + /** Currency data. */ currencyData: any; - + /** Sum total of debits. */ debitsSum = 0; + /** Sum total of credits. */ creditsSum = 0; - constructor(private accountingService: AccountingService, - private formBuilder: FormBuilder) { } + /** + * Retrieves the offices and currencies from `resolve`. + * @param {FormBuilder} formBuilder Form Builder. + * @param {AccountingService} accountingService Accounting Service. + * @param {ActivatedRoute} route Activated Route. + * @param {Router} router Router for navigation. + */ + constructor(private formBuilder: FormBuilder, + private accountingService: AccountingService, + private route: ActivatedRoute, + private router: Router) { + this.route.data.subscribe((data: { + offices: any, + currencies: any + }) => { + this.officeData = data.offices; + this.currencyData = data.currencies.selectedCurrencyOptions; + }); + } + /** + * Creates the opening balances form. (initially retrieves gl accounts on the basis of specified office) + */ ngOnInit() { this.createOpeningBalancesForm(); - this.getOffices(); - this.getCurrencies(); - } - - getOffices() { - this.accountingService.getOffices().subscribe(officeData => { - this.officeData = officeData; - }); - } - - getCurrencies() { - this.accountingService.getCurrencies().subscribe(currencyData => { - this.currencyData = currencyData.selectedCurrencyOptions; - }); } + /** + * Creates the opening balances form. + */ createOpeningBalancesForm() { this.openingBalancesForm = this.formBuilder.group({ 'officeId': ['', Validators.required], @@ -56,6 +75,11 @@ export class MigrateOpeningBalancesComponent implements OnInit { }); } + /** + * Creates the gl account entry form. + * @param glAccount GL Account for which form is returned. + * @returns {FormGroup} GL Account entry form. + */ createGLAccountEntryForm(glAccount: any): FormGroup { return this.formBuilder.group({ 'glAccountId': [glAccount.glAccountId], @@ -64,19 +88,32 @@ export class MigrateOpeningBalancesComponent implements OnInit { }, { validator: onlyOneOfTheFieldsIsRequiredValidator }); } - getFormArrayData(type: string): FormArray { - return this.openingBalancesForm.get(type); + /** + * Gets the gl account entries form array. + * @returns {FormArray} GL Account entries form array. + */ + get glAccountEntries(): FormArray { + return this.openingBalancesForm.get('glAccountEntries') as FormArray; } + /** + * Retrieves gl accounts on the basis of specified office. + */ retrieveOpeningBalances() { this.accountingService.retrieveOpeningBalances(this.openingBalancesForm.value.officeId) .subscribe((openingBalancesData: any) => { const entry = this.openingBalancesForm.get('glAccountEntries') as FormArray; + openingBalancesData.glAccounts = openingBalancesData.assetAccountOpeningBalances - .concat(openingBalancesData.liabityAccountOpeningBalances, openingBalancesData.equityAccountOpeningBalances, openingBalancesData.incomeAccountOpeningBalances, openingBalancesData.expenseAccountOpeningBalances); + .concat(openingBalancesData.liabityAccountOpeningBalances, + openingBalancesData.equityAccountOpeningBalances, + openingBalancesData.incomeAccountOpeningBalances, + openingBalancesData.expenseAccountOpeningBalances); + openingBalancesData.glAccounts.forEach((glAccount: any) => { entry.push(this.createGLAccountEntryForm(glAccount)); }); + this.openingBalancesData = openingBalancesData; entry.valueChanges.subscribe(() => { @@ -90,32 +127,41 @@ export class MigrateOpeningBalancesComponent implements OnInit { }); } + /** + * Submits the opening balances form and defines opening balances, + * if successful redirects to view created transaction. + */ submit() { - // TODO: Validate - if (this.openingBalancesForm.status !== 'INVALID') { - const openingBalances = this.openingBalancesForm.value; - // TODO: Update once language and date settings are setup - openingBalances.locale = 'en'; - openingBalances.dateFormat = 'dd MMMM yyyy'; - openingBalances.transactionDate = '07 August 2018'; - openingBalances.debits = []; - openingBalances.credits = []; - this.openingBalancesForm.value.glAccountEntries.forEach((entry: any) => { - if (entry.debit) { - openingBalances.debits.push({ glAccountId: entry.glAccountId, amount: entry.debit }); - } - if (entry.credit) { - openingBalances.credits.push({ glAccountId: entry.glAccountId, amount: entry.credit }); - } - }); - delete openingBalances.glAccountEntries; - this.accountingService.defineOpeningBalances(openingBalances) - .subscribe((response: any) => { - console.log(response); - }); - } else { - console.log('Errors exist in form!'); + const openingBalances = this.openingBalancesForm.value; + // TODO: Update once language and date settings are setup + openingBalances.locale = 'en'; + openingBalances.dateFormat = 'yyyy-MM-dd'; + if (openingBalances.transactionDate instanceof Date) { + let day = openingBalances.transactionDate.getDate(); + let month = openingBalances.transactionDate.getMonth() + 1; + const year = openingBalances.transactionDate.getFullYear(); + if (day < 10) { + day = `0${day}`; + } + if (month < 10) { + month = `0${month}`; + } + openingBalances.transactionDate = `${year}-${month}-${day}`; } + openingBalances.debits = []; + openingBalances.credits = []; + this.openingBalancesForm.value.glAccountEntries.forEach((entry: any) => { + if (entry.debit) { + openingBalances.debits.push({ glAccountId: entry.glAccountId, amount: entry.debit }); + } + if (entry.credit) { + openingBalances.credits.push({ glAccountId: entry.glAccountId, amount: entry.credit }); + } + }); + delete openingBalances.glAccountEntries; + this.accountingService.defineOpeningBalances(openingBalances).subscribe((response: any) => { + this.router.navigate(['/accounting/journal-entries/transactions/view', response.transactionId]); + }); } } diff --git a/src/app/accounting/migrate-opening-balances/only-one-of-the-fields-is-required.validator.ts b/src/app/accounting/migrate-opening-balances/only-one-of-the-fields-is-required.validator.ts new file mode 100644 index 000000000..076747f34 --- /dev/null +++ b/src/app/accounting/migrate-opening-balances/only-one-of-the-fields-is-required.validator.ts @@ -0,0 +1,9 @@ +/** Angular Imports */ +import { FormGroup, ValidatorFn, ValidationErrors } from '@angular/forms'; + +/** Validates that atleast one of the fields debit or credit is filled (but not both) for a given gl account. */ +export const onlyOneOfTheFieldsIsRequiredValidator: ValidatorFn = (glAccountEntriesForm: FormGroup): ValidationErrors | null => { + const debit = glAccountEntriesForm.controls.debit.value; + const credit = glAccountEntriesForm.controls.credit.value; + return ((debit || credit) && !(debit && credit)) ? null : { 'error': true }; +}; diff --git a/src/app/accounting/periodic-accruals/periodic-accruals.component.html b/src/app/accounting/periodic-accruals/periodic-accruals.component.html index 8d32a85ae..05429e957 100644 --- a/src/app/accounting/periodic-accruals/periodic-accruals.component.html +++ b/src/app/accounting/periodic-accruals/periodic-accruals.component.html @@ -9,9 +9,13 @@
- + Accrue Till Date + + + Accrue Date is required +
@@ -19,8 +23,11 @@ - - + + diff --git a/src/app/accounting/periodic-accruals/periodic-accruals.component.ts b/src/app/accounting/periodic-accruals/periodic-accruals.component.ts index e83c52f28..168353e67 100644 --- a/src/app/accounting/periodic-accruals/periodic-accruals.component.ts +++ b/src/app/accounting/periodic-accruals/periodic-accruals.component.ts @@ -1,9 +1,14 @@ +/** Angular Imports */ import { Component, OnInit } from '@angular/core'; import { FormGroup, FormBuilder, Validators } from '@angular/forms'; -import { Router } from '@angular/router'; +import { Router, ActivatedRoute } from '@angular/router'; +/** Custom Services */ import { AccountingService } from '../accounting.service'; +/** + * Periodic accruals component. + */ @Component({ selector: 'mifosx-periodic-accruals', templateUrl: './periodic-accruals.component.html', @@ -11,32 +16,63 @@ import { AccountingService } from '../accounting.service'; }) export class PeriodicAccrualsComponent implements OnInit { + /** Minimum accrue date allowed. */ minDate = new Date(2000, 0, 1); + /** Maximum accrue date allowed. */ maxDate = new Date(); + /** Periodic accruals form. */ periodicAccrualsForm: FormGroup; + /** + * @param {FormBuilder} formBuilder Form Builder. + * @param {AccountingService} accountingService Accounting Service. + * @param {ActivatedRoute} route Activated Route. + * @param {Router} router Router for navigation. + */ constructor(private formBuilder: FormBuilder, private accountingService: AccountingService, + private route: ActivatedRoute, private router: Router) { } + /** + * Creates periodic accruals form. + */ ngOnInit() { this.createPeriodicAccrualsForm(); } + /** + * Creates periodic accruals form. + */ createPeriodicAccrualsForm() { this.periodicAccrualsForm = this.formBuilder.group({ 'tillDate': ['', Validators.required] }); } + /** + * Submits the periodic accruals form and executes periodic accruals, + * if successful redirects to accounting. + */ submit() { const periodicAccruals = this.periodicAccrualsForm.value; // TODO: Update once language and date settings are setup periodicAccruals.locale = 'en'; - periodicAccruals.dateFormat = 'dd MMMM yyyy'; - periodicAccruals.tillDate = '06 August 2018'; - this.accountingService.executePeriodicAccruals(periodicAccruals).subscribe(response => { - this.router.navigate(['/accounting']); + periodicAccruals.dateFormat = 'yyyy-MM-dd'; + if (periodicAccruals.tillDate instanceof Date) { + let day = periodicAccruals.tillDate.getDate(); + let month = periodicAccruals.tillDate.getMonth() + 1; + const year = periodicAccruals.tillDate.getFullYear(); + if (day < 10) { + day = `0${day}`; + } + if (month < 10) { + month = `0${month}`; + } + periodicAccruals.tillDate = `${year}-${month}-${day}`; + } + this.accountingService.executePeriodicAccruals(periodicAccruals).subscribe(() => { + this.router.navigate(['../'], { relativeTo: this.route }); }); } diff --git a/src/app/accounting/provisioning-entries/create-provisioning-entry/create-provisioning-entry.component.html b/src/app/accounting/provisioning-entries/create-provisioning-entry/create-provisioning-entry.component.html index 1bdd95a10..1a0e35074 100644 --- a/src/app/accounting/provisioning-entries/create-provisioning-entry/create-provisioning-entry.component.html +++ b/src/app/accounting/provisioning-entries/create-provisioning-entry/create-provisioning-entry.component.html @@ -2,16 +2,20 @@ -
+
- + Date + + + Date is required +
@@ -25,8 +29,8 @@ - - + + diff --git a/src/app/accounting/provisioning-entries/create-provisioning-entry/create-provisioning-entry.component.ts b/src/app/accounting/provisioning-entries/create-provisioning-entry/create-provisioning-entry.component.ts index bc6ae30f7..a6a3eebf9 100644 --- a/src/app/accounting/provisioning-entries/create-provisioning-entry/create-provisioning-entry.component.ts +++ b/src/app/accounting/provisioning-entries/create-provisioning-entry/create-provisioning-entry.component.ts @@ -1,8 +1,14 @@ +/** Angular Imports */ import { Component, OnInit } from '@angular/core'; import { FormGroup, FormBuilder, Validators } from '@angular/forms'; +import { ActivatedRoute, Router } from '@angular/router'; +/** Custom Services */ import { AccountingService } from '../../accounting.service'; +/** + * Create provisioning entry component. + */ @Component({ selector: 'mifosx-create-provisioning-entry', templateUrl: './create-provisioning-entry.component.html', @@ -10,33 +16,65 @@ import { AccountingService } from '../../accounting.service'; }) export class CreateProvisioningEntryComponent implements OnInit { + /** Minimum provisioning date allowed. */ minDate = new Date(2000, 0, 1); + /** Maximum provisioning date allowed. */ maxDate = new Date(); - provisioningEntriesForm: FormGroup; + /** Provisioning entry form. */ + provisioningEntryForm: FormGroup; + /** + * @param {FormBuilder} formBuilder Form Builder. + * @param {AccountingService} accountingService Accounting Service. + * @param {ActivatedRoute} route Activated Route. + * @param {Router} router Router for navigation. + */ constructor(private formBuilder: FormBuilder, - private accountingService: AccountingService) { } + private accountingService: AccountingService, + private route: ActivatedRoute, + private router: Router) { } + /** + * Creates the provisioning entry form. + */ ngOnInit() { - this.createProvisioningEntriesForm(); + this.createProvisioningEntryForm(); } - createProvisioningEntriesForm() { - this.provisioningEntriesForm = this.formBuilder.group({ + /** + * Creates the provisioning entry form. + */ + createProvisioningEntryForm() { + this.provisioningEntryForm = this.formBuilder.group({ 'date': ['', Validators.required], 'createjournalentries': [false] }); } + /** + * Submits the provisioning entry form and creates provisioning entry, + * if successful redirects to view created entry. + */ submit() { - const provisioningEntry = this.provisioningEntriesForm.value; + const provisioningEntry = this.provisioningEntryForm.value; // TODO: Update once language and date settings are setup provisioningEntry.locale = 'en'; - provisioningEntry.dateFormat = 'dd MMMM yyyy'; - provisioningEntry.date = '06 August 2018'; + provisioningEntry.dateFormat = 'yyyy-MM-dd'; + if (provisioningEntry.date instanceof Date) { + let day = provisioningEntry.date.getDate(); + let month = provisioningEntry.date.getMonth() + 1; + const year = provisioningEntry.date.getFullYear(); + if (day < 10) { + day = `0${day}`; + } + if (month < 10) { + month = `0${month}`; + } + provisioningEntry.date = `${year}-${month}-${day}`; + } this.accountingService.createProvisioningEntry(provisioningEntry) .subscribe((response: any) => { - console.log(response); + this.router.navigate(['../view', response.resourceId], { relativeTo: this.route }); }); } diff --git a/src/app/accounting/provisioning-entries/provisioning-entries.component.html b/src/app/accounting/provisioning-entries/provisioning-entries.component.html index 5c76edaad..2260fa61e 100644 --- a/src/app/accounting/provisioning-entries/provisioning-entries.component.html +++ b/src/app/accounting/provisioning-entries/provisioning-entries.component.html @@ -1,14 +1,16 @@
-
-
+
- + Filter +
@@ -36,9 +38,8 @@ View Report - @@ -47,7 +48,7 @@ Recreate Provisioning @@ -56,13 +57,13 @@ View Journal Entry - + diff --git a/src/app/accounting/provisioning-entries/provisioning-entries.component.ts b/src/app/accounting/provisioning-entries/provisioning-entries.component.ts index 642299dbd..217b79566 100644 --- a/src/app/accounting/provisioning-entries/provisioning-entries.component.ts +++ b/src/app/accounting/provisioning-entries/provisioning-entries.component.ts @@ -1,9 +1,14 @@ +/** Angular Imports */ import { Component, OnInit, ViewChild } from '@angular/core'; import { MatSort, MatPaginator, MatTableDataSource } from '@angular/material'; -import { Router } from '@angular/router'; +import { Router, ActivatedRoute } from '@angular/router'; +/** Custom Services */ import { AccountingService } from '../accounting.service'; +/** + * Provisioning entries component. + */ @Component({ selector: 'mifosx-provisioning-entries', templateUrl: './provisioning-entries.component.html', @@ -11,46 +16,76 @@ import { AccountingService } from '../accounting.service'; }) export class ProvisioningEntriesComponent implements OnInit { + /** Provisioning entry data. */ + provisioningEntryData: any; + /** Columns to be displayed in provisioning entries table. */ displayedColumns: string[] = ['createdUser', 'createdDate', 'journalEntry', 'viewReport', 'recreateProvisioning', 'viewJournalEntry']; - dataSource: any; + /** Data source for provisioning entries table. */ + dataSource: MatTableDataSource; + /** Paginator for provisioning entries table. */ @ViewChild(MatPaginator) paginator: MatPaginator; + /** Sorter for provisioning entries table. */ @ViewChild(MatSort) sort: MatSort; + /** + * Retrieves the provisioning entries data from `resolve`. + * @param {AccountingService} accountingService Accounting Service. + * @param {ActivatedRoute} route Activated Route. + * @param {Router} router Router for navigation. + */ constructor(private accountingService: AccountingService, - private router: Router) { } - - ngOnInit() { - this.getProvisioningEntries(); - } - - getProvisioningEntries() { - this.accountingService.getProvisioningEntries().subscribe((provisioningEntryData: any) => { - this.dataSource = new MatTableDataSource(provisioningEntryData.pageItems); - this.dataSource.paginator = this.paginator; - this.dataSource.sort = this.sort; + private route: ActivatedRoute, + private router: Router) { + this.route.data.subscribe((data: { provisioningEntries: any }) => { + this.provisioningEntryData = data.provisioningEntries.pageItems; }); } + /** + * Sets the provisioning entries table. + */ + ngOnInit() { + this.setProvisioningEntries(); + } + + /** + * Initializes the data source, paginator and sorter for provisioning entries table. + */ + setProvisioningEntries() { + this.dataSource = new MatTableDataSource(this.provisioningEntryData); + this.dataSource.paginator = this.paginator; + this.dataSource.sort = this.sort; + } + + /** + * Filters data in provisioning entries table based on passed value. + * @param {string} filterValue Value to filter data. + */ applyFilter(filterValue: string) { this.dataSource.filter = filterValue.trim().toLowerCase(); } - viewReport($event: any, provisioningEntryId: number) { - // Tenant Customizable - $event.stopPropagation(); - } - - recreateProvisioning($event: any, provisioningEntryId: string) { + /** + * Recreates provisioning entry. + * @param {Event} $event Event to stop propagation. + * @param {string} provisioningEntryId Provisioning entry id. + */ + recreateProvisioning($event: Event, provisioningEntryId: string) { this.accountingService.recreateProvisioningEntries(provisioningEntryId) .subscribe((response: any) => { - this.router.navigate(['/accounting/provisioning-entries/view', response.resourceId]); + this.router.navigate(['view', response.resourceId], { relativeTo: this.route }); }); $event.stopPropagation(); } - viewJournalEntry($event: any, provisioningEntryId: number) { - this.router.navigate(['/accounting/provisioning-entries/journal-entries/view', provisioningEntryId]); + /** + * View journal entries for the provisioning entry. + * @param {Event} $event Event to stop propagation. + * @param {number} provisioningEntryId Provisioning entry id. + */ + viewJournalEntry($event: Event, provisioningEntryId: number) { + this.router.navigate(['journal-entries/view', provisioningEntryId], { relativeTo: this.route }); $event.stopPropagation(); } diff --git a/src/app/accounting/provisioning-entries/provisioning-entries.resolver.ts b/src/app/accounting/provisioning-entries/provisioning-entries.resolver.ts new file mode 100644 index 000000000..1bac26b49 --- /dev/null +++ b/src/app/accounting/provisioning-entries/provisioning-entries.resolver.ts @@ -0,0 +1,30 @@ +/** Angular Imports */ +import { Injectable } from '@angular/core'; +import { Resolve } from '@angular/router'; + +/** rxjs Imports */ +import { Observable } from 'rxjs'; + +/** Custom Services */ +import { AccountingService } from '../accounting.service'; + +/** + * Provisioning entries data resolver. + */ +@Injectable() +export class ProvisioningEntriesResolver implements Resolve { + + /** + * @param {AccountingService} accountingService Accounting service. + */ + constructor(private accountingService: AccountingService) {} + + /** + * Returns the provisioning entries data. + * @returns {Observable} + */ + resolve(): Observable { + return this.accountingService.getProvisioningEntries(); + } + +} diff --git a/src/app/accounting/provisioning-entries/view-provisioning-entry/provisioning-entry-entries.resolver.ts b/src/app/accounting/provisioning-entries/view-provisioning-entry/provisioning-entry-entries.resolver.ts new file mode 100644 index 000000000..061cc2e8d --- /dev/null +++ b/src/app/accounting/provisioning-entries/view-provisioning-entry/provisioning-entry-entries.resolver.ts @@ -0,0 +1,31 @@ +/** Angular Imports */ +import { Injectable } from '@angular/core'; +import { Resolve, ActivatedRouteSnapshot } from '@angular/router'; + +/** rxjs Imports */ +import { Observable } from 'rxjs'; + +/** Custom Services */ +import { AccountingService } from '../../accounting.service'; + +/** + * Provisioning entry entries data resolver. + */ +@Injectable() +export class ProvisioningEntryEntriesResolver implements Resolve { + + /** + * @param {AccountingService} accountingService Accounting service. + */ + constructor(private accountingService: AccountingService) {} + + /** + * Returns the provisioning entry entries data. + * @returns {Observable} + */ + resolve(route: ActivatedRouteSnapshot): Observable { + const provisioningEntryId = route.paramMap.get('id'); + return this.accountingService.getProvisioningEntryEntries(provisioningEntryId); + } + +} diff --git a/src/app/accounting/provisioning-entries/view-provisioning-entry/provisioning-entry.resolver.ts b/src/app/accounting/provisioning-entries/view-provisioning-entry/provisioning-entry.resolver.ts new file mode 100644 index 000000000..f0efdc047 --- /dev/null +++ b/src/app/accounting/provisioning-entries/view-provisioning-entry/provisioning-entry.resolver.ts @@ -0,0 +1,31 @@ +/** Angular Imports */ +import { Injectable } from '@angular/core'; +import { Resolve, ActivatedRouteSnapshot } from '@angular/router'; + +/** rxjs Imports */ +import { Observable } from 'rxjs'; + +/** Custom Services */ +import { AccountingService } from '../../accounting.service'; + +/** + * Provisioning entry data resolver. + */ +@Injectable() +export class ProvisioningEntryResolver implements Resolve { + + /** + * @param {AccountingService} accountingService Accounting service. + */ + constructor(private accountingService: AccountingService) {} + + /** + * Returns the provisioning entry data. + * @returns {Observable} + */ + resolve(route: ActivatedRouteSnapshot): Observable { + const provisioningEntryId = route.paramMap.get('id'); + return this.accountingService.getProvisioningEntry(provisioningEntryId); + } + +} diff --git a/src/app/accounting/provisioning-entries/view-provisioning-entry/view-provisioning-entry.component.html b/src/app/accounting/provisioning-entries/view-provisioning-entry/view-provisioning-entry.component.html index 5df7b031a..738cd3409 100644 --- a/src/app/accounting/provisioning-entries/view-provisioning-entry/view-provisioning-entry.component.html +++ b/src/app/accounting/provisioning-entries/view-provisioning-entry/view-provisioning-entry.component.html @@ -2,7 +2,8 @@
@@ -48,15 +49,18 @@
- + Office Name + - + Loan Product + - + Provisioning Category +
@@ -122,7 +126,7 @@ - + diff --git a/src/app/accounting/provisioning-entries/view-provisioning-entry/view-provisioning-entry.component.scss b/src/app/accounting/provisioning-entries/view-provisioning-entry/view-provisioning-entry.component.scss index 3d435a17d..e3260ae64 100644 --- a/src/app/accounting/provisioning-entries/view-provisioning-entry/view-provisioning-entry.component.scss +++ b/src/app/accounting/provisioning-entries/view-provisioning-entry/view-provisioning-entry.component.scss @@ -6,9 +6,9 @@ table { div { margin: 1rem 0; word-wrap: break-word; - } - .header { - font-weight: 500; + &.header { + font-weight: 500; + } } } diff --git a/src/app/accounting/provisioning-entries/view-provisioning-entry/view-provisioning-entry.component.ts b/src/app/accounting/provisioning-entries/view-provisioning-entry/view-provisioning-entry.component.ts index ab88e9f6c..433ef8fbe 100644 --- a/src/app/accounting/provisioning-entries/view-provisioning-entry/view-provisioning-entry.component.ts +++ b/src/app/accounting/provisioning-entries/view-provisioning-entry/view-provisioning-entry.component.ts @@ -1,12 +1,18 @@ +/** Angular Imports */ import { Component, OnInit, ViewChild, AfterViewInit } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; import { FormControl } from '@angular/forms'; import { MatPaginator, MatSort, MatTableDataSource } from '@angular/material'; +/** rxjs Imports */ import { startWith, map, debounceTime, distinctUntilChanged, tap } from 'rxjs/operators'; +/** Custom Services */ import { AccountingService } from '../../accounting.service'; +/** + * View provisioning entry component. + */ @Component({ selector: 'mifosx-view-provisioning-entry', templateUrl: './view-provisioning-entry.component.html', @@ -14,41 +20,87 @@ import { AccountingService } from '../../accounting.service'; }) export class ViewProvisioningEntryComponent implements OnInit, AfterViewInit { + /** Provisioning entry id. */ provisioningEntryId: string; + /** Provisioning entry. */ provisioningEntry: any; - + /** Provisioning entry entries. */ + provisioningEntryEntries: any; + /** Office name filter form control. */ + officeName = new FormControl(); + /** Office data. */ + officeData: any; + /** Filtered office data for autocomplete. */ + filteredOfficeData: any; + /** Loan product filter form control. */ + loanProduct = new FormControl(); + /** Loan product data. */ + loanProductData: any; + /** Filtered loan product data for autocomplete. */ + filteredLoanProductData: any; + /** Provisioning category filter form control. */ + provisioningCategory = new FormControl(); + /** Provisioning category data. */ + provisioningCategoryData: any; + /** Filtered provisioning category data for autocomplete. */ + filteredProvisioningCategoryData: any; + /** Columns to be displayed in provisioning entry entries table. */ displayedColumns: string[] = ['officeName', 'productName', 'currencyCode', 'categoryName', 'amountreserved', 'liabilityAccountName', 'expenseAccountName']; - dataSource: any; - filterValue: any = { officeName: '', productName: '', categoryName: '' }; + /** Data source for provisioning entry entries table. */ + dataSource: MatTableDataSource; + /** Provisioning entry entries filter. */ + filterValue: any = { + officeName: '', + productName: '', + categoryName: '' + }; + /** Paginator for provisioning entry entries table. */ @ViewChild(MatPaginator) paginator: MatPaginator; + /** Sorter for provisioning entry entries table. */ @ViewChild(MatSort) sort: MatSort; - officeName = new FormControl(); - officeData: any; - filteredOfficeData: any; - - loanProduct = new FormControl(); - loanProductData: any; - filteredLoanProductData: any; - - provisioningCategory = new FormControl(); - provisioningCategoryData: any; - filteredProvisioningCategoryData: any; - - constructor(private route: ActivatedRoute, - private router: Router, - private accountingService: AccountingService) { } - - ngOnInit() { - this.provisioningEntryId = this.route.snapshot.paramMap.get('id'); - this.getProvisioningEntry(); - this.getProvisioningEntryEntries(); - this.getOffices(); - this.getLoanProducts(); - this.getProvisioningCategories(); + /** + * Retrieves the provisioning entry, provisioning entry entries, offices, + * loan products, provisioning categories data from `resolve`. + * @param {AccountingService} accountingService Accounting Service. + * @param {ActivatedRoute} route Activated Route. + * @param {Router} router Router for navigation. + */ + constructor(private accountingService: AccountingService, + private route: ActivatedRoute, + private router: Router) { + this.route.data.subscribe((data: { + provisioningEntry: any, + provisioningEntryEntries: any, + offices: any, + loanProducts: any, + provisiningCategories: any + }) => { + this.provisioningEntryId = data.provisioningEntry.id; + this.provisioningEntry = data.provisioningEntry; + this.provisioningEntryEntries = data.provisioningEntryEntries; + this.officeData = data.offices; + this.loanProductData = data.loanProducts; + this.provisioningCategoryData = data.provisiningCategories; + }); } + /** + * Sets filtered offices, loan products and provisioning categories for autocomplete + * and provisioning entry entries table. + */ + ngOnInit() { + this.setProvisioningEntryEntries(); + this.setFilteredOffices(); + this.setFilteredLoanProducts(); + this.setFilteredProvisioningCategories(); + } + + /** + * Subscribes to all search filters: + * Office Name, Loan Product, Provisioning categories. + */ ngAfterViewInit() { this.officeName.valueChanges .pipe( @@ -84,86 +136,109 @@ export class ViewProvisioningEntryComponent implements OnInit, AfterViewInit { .subscribe(); } + /** + * Filter predicate for provisioning entry entries table data. + * @param {any} data Table data. + * @param {any} filterValue Values to filter data by. + */ filterPredicate(data: any, filterValue: any) { return data.officeName.toLowerCase().indexOf(filterValue['officeName']) !== -1 && data.productName.toLowerCase().indexOf(filterValue['productName']) !== -1 && data.categoryName.toLowerCase().indexOf(filterValue['categoryName']) !== -1; } - applyFilter(filterValue: string, property: string) { + /** + * Initializes the data source, paginator, sorter and filter predicate for + * provisioning entry entries table. + */ + setProvisioningEntryEntries() { + this.dataSource = new MatTableDataSource(this.provisioningEntryEntries.pageItems); + this.dataSource.paginator = this.paginator; + this.dataSource.sort = this.sort; + this.dataSource.filterPredicate = this.filterPredicate; + } + + /** + * Filters data in provisioning entry entries table based on passed value. + * @param {string} filterValue Value to filter data. + * @param {string} property Property to filter data. + */ + applyFilter(filterValue: string, property: string) { this.filterValue[property] = filterValue; this.dataSource.filter = this.filterValue; } - getProvisioningEntry() { - this.accountingService.getProvisioningEntry(this.provisioningEntryId) - .subscribe((provisioningEntry: any) => { - this.provisioningEntry = provisioningEntry; - }); + /** + * Sets filtered offices for autocomplete. + */ + setFilteredOffices() { + this.filteredOfficeData = this.officeName.valueChanges + .pipe( + startWith(''), + map((office: any) => typeof office === 'string' ? office : office.name), + map((officeName: string) => officeName ? this.filterOfficeAutocompleteData(officeName) : this.officeData) + ); } - getProvisioningEntryEntries() { - this.accountingService.getProvisioningEntryEntries(this.provisioningEntryId) - .subscribe((provisioningEntryEntries: any) => { - this.dataSource = new MatTableDataSource(provisioningEntryEntries.pageItems); - this.dataSource.paginator = this.paginator; - this.dataSource.sort = this.sort; - this.dataSource.filterPredicate = this.filterPredicate; - }); + /** + * Sets filtered loan products for autocomplete. + */ + setFilteredLoanProducts() { + this.filteredLoanProductData = this.loanProduct.valueChanges + .pipe( + startWith(''), + map((loanProduct: any) => typeof loanProduct === 'string' ? loanProduct : loanProduct.name), + map((loanProductName: string) => loanProductName ? this.filterLoanProductAutocompleteData(loanProductName) : this.loanProductData) + ); } - getOffices() { - this.accountingService.getOffices().subscribe(officeData => { - this.officeData = officeData; - this.filteredOfficeData = this.officeName.valueChanges - .pipe( - startWith(''), - map((office: any) => typeof office === 'string' ? office : office.name), - map((officeName: string) => officeName ? this.filterOfficeAutocompleteData(this.officeData, officeName) : this.officeData) - ); - }); + /** + * Sets filtered provisioning categories for autocomplete. + */ + setFilteredProvisioningCategories() { + this.filteredProvisioningCategoryData = this.provisioningCategory.valueChanges + .pipe( + startWith(''), + map((provisioningCategory: any) => typeof provisioningCategory === 'string' ? provisioningCategory : provisioningCategory.categoryName), + map((provisioningCategoryName: string) => provisioningCategoryName ? this.filterProvisioningCategoryAutocompleteData(provisioningCategoryName) : this.provisioningCategoryData) + ); } - getLoanProducts() { - this.accountingService.getLoanProducts().subscribe(loanProductData => { - this.loanProductData = loanProductData; - this.filteredLoanProductData = this.loanProduct.valueChanges - .pipe( - startWith(''), - map((loanProduct: any) => typeof loanProduct === 'string' ? loanProduct : loanProduct.name), - map((loanProductName: string) => loanProductName ? this.filterLoanProductAutocompleteData(this.loanProductData, loanProductName) : this.loanProductData) - ); - }); + /** + * Filters offices. + * @param {string} officeName Office name to filter office by. + * @returns {any} Filtered offices. + */ + private filterOfficeAutocompleteData(officeName: string): any { + return this.officeData.filter((office: any) => office.name.toLowerCase().includes(officeName.toLowerCase())); } - getProvisioningCategories() { - this.accountingService.getProvisioningCategories().subscribe(provisioningCategoryData => { - this.provisioningCategoryData = provisioningCategoryData; - this.filteredProvisioningCategoryData = this.provisioningCategory.valueChanges - .pipe( - startWith(''), - map((provisioningCategory: any) => typeof provisioningCategory === 'string' ? provisioningCategory : provisioningCategory.categoryName), - map((provisioningCategoryName: string) => provisioningCategoryName ? this.filterProvisioningCategoryAutocompleteData(this.provisioningCategoryData, provisioningCategoryName) : this.provisioningCategoryData) - ); - }); + /** + * Filters loan products. + * @param {string} loanProductName Loan product name to filter loan products by. + * @returns {any} Filtered loan products. + */ + private filterLoanProductAutocompleteData(loanProductName: string): any { + return this.loanProductData.filter((loanProduct: any) => loanProduct.name.toLowerCase().includes(loanProductName.toLocaleLowerCase())); } - private filterOfficeAutocompleteData(officeData: any, officeName: string): any { - return officeData.filter((office: any) => office.name.toLowerCase().includes(officeName.toLowerCase())); - } - - private filterLoanProductAutocompleteData(loanProductData: any, loanProductName: string): any { - return loanProductData.filter((loanProduct: any) => loanProduct.name.toLowerCase().includes(loanProductName.toLocaleLowerCase())); - } - - private filterProvisioningCategoryAutocompleteData(provisioningCategoryData: any, provisioningCategoryName: string): any { - return provisioningCategoryData.filter((provisioningCategory: any) => provisioningCategory.categoryName.toLowerCase().includes(provisioningCategoryName.toLocaleLowerCase())); + /** + * Filters provisioning categories. + * @param {string} provisioningCategoryName Provisioning category name to filter provisioning categories by. + * @returns {any} Filtered provisioning categories. + */ + private filterProvisioningCategoryAutocompleteData(provisioningCategoryName: string): any { + return this.provisioningCategoryData.filter((provisioningCategory: any) => provisioningCategory.categoryName.toLowerCase().includes(provisioningCategoryName.toLocaleLowerCase())); } + /** + * Creates provisioning journal entries + * and redirects to created entries. + */ createProvisioningJournalEntries() { this.accountingService.createProvisioningJournalEntries(this.provisioningEntryId) .subscribe((response: any) => { - this.router.navigate(['/accounting/provisioning-entries/journal-entries/view', response.resourceId]); + this.router.navigate(['../../journal-entries/view', response.resourceId], { relativeTo: this.route }); }); } diff --git a/src/app/accounting/provisioning-entries/view-provisioning-journal-entries/provisioning-journal-entries.resolver.ts b/src/app/accounting/provisioning-entries/view-provisioning-journal-entries/provisioning-journal-entries.resolver.ts new file mode 100644 index 000000000..21e901fe8 --- /dev/null +++ b/src/app/accounting/provisioning-entries/view-provisioning-journal-entries/provisioning-journal-entries.resolver.ts @@ -0,0 +1,31 @@ +/** Angular Imports */ +import { Injectable } from '@angular/core'; +import { Resolve, ActivatedRouteSnapshot } from '@angular/router'; + +/** rxjs Imports */ +import { Observable } from 'rxjs'; + +/** Custom Services */ +import { AccountingService } from '../../accounting.service'; + +/** + * Provisioning journal entries data resolver. + */ +@Injectable() +export class ProvisioningJournalEntriesResolver implements Resolve { + + /** + * @param {AccountingService} accountingService Accounting service. + */ + constructor(private accountingService: AccountingService) {} + + /** + * Returns the provisioning journal entries data. + * @returns {Observable} + */ + resolve(route: ActivatedRouteSnapshot): Observable { + const id = route.paramMap.get('id'); + return this.accountingService.getProvisioningJournalEntries(id); + } + +} diff --git a/src/app/accounting/provisioning-entries/view-provisioning-journal-entries/view-provisioning-journal-entries.component.html b/src/app/accounting/provisioning-entries/view-provisioning-journal-entries/view-provisioning-journal-entries.component.html index 3c3fa137e..7299a9729 100644 --- a/src/app/accounting/provisioning-entries/view-provisioning-journal-entries/view-provisioning-journal-entries.component.html +++ b/src/app/accounting/provisioning-entries/view-provisioning-journal-entries/view-provisioning-journal-entries.component.html @@ -2,7 +2,8 @@
- + Filter +
diff --git a/src/app/accounting/provisioning-entries/view-provisioning-journal-entries/view-provisioning-journal-entries.component.ts b/src/app/accounting/provisioning-entries/view-provisioning-journal-entries/view-provisioning-journal-entries.component.ts index af35f3e28..e28c2d854 100644 --- a/src/app/accounting/provisioning-entries/view-provisioning-journal-entries/view-provisioning-journal-entries.component.ts +++ b/src/app/accounting/provisioning-entries/view-provisioning-journal-entries/view-provisioning-journal-entries.component.ts @@ -1,9 +1,11 @@ +/** Angular Imports */ import { Component, OnInit, ViewChild } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { MatPaginator, MatSort, MatTableDataSource } from '@angular/material'; -import { AccountingService } from '../../accounting.service'; - +/** + * View provisioning journal entries component. + */ @Component({ selector: 'mifosx-view-provisioning-journal-entries', templateUrl: './view-provisioning-journal-entries.component.html', @@ -11,39 +13,56 @@ import { AccountingService } from '../../accounting.service'; }) export class ViewProvisioningJournalEntriesComponent implements OnInit { - provisioningEntryId: string; - + /** Provisioning journal entry data. */ + provisioningJournalEntryData: any; + /** Columns to be displayed in provisioning journal entries table. */ displayedColumns: string[] = ['id', 'officeName', 'transactionDate', 'transactionId', 'glAccountType', 'createdByUserName', 'glAccountCode', 'glAccountName', 'debit', 'credit']; - dataSource: any; + /** Data source for provisioning journal entries table. */ + dataSource: MatTableDataSource; + /** Paginator for provisioning journal entries table. */ @ViewChild(MatPaginator) paginator: MatPaginator; + /** Sorter for provisioning journal entries table. */ @ViewChild(MatSort) sort: MatSort; - constructor(private route: ActivatedRoute, - private accountingService: AccountingService) { } + /** + * Retrieves the provisioning journal entries data from `resolve`. + * @param {ActivatedRoute} route Activated Route. + */ + constructor(private route: ActivatedRoute) { + this.route.data.subscribe((data: { provisioningJournalEntry: any }) => { + this.provisioningJournalEntryData = data.provisioningJournalEntry.pageItems; + }); + } + /** + * Sets the provisioning journal entries table. + */ ngOnInit() { - this.provisioningEntryId = this.route.snapshot.paramMap.get('id'); - this.getProvisioningJournalEntry(); + this.setProvisioningJournalEntry(); } - getProvisioningJournalEntry() { - this.accountingService.getProvisioningJournalEntries(this.provisioningEntryId) - .subscribe((provisioningJournalEntryData: any) => { - this.dataSource = new MatTableDataSource(provisioningJournalEntryData.pageItems); - this.dataSource.paginator = this.paginator; - this.dataSource.sortingDataAccessor = (transaction: any, property: any) => { - switch (property) { - case 'glAccountType': return transaction.glAccountType.value; - case 'debit': return transaction.amount; - case 'credit': return transaction.amount; - default: return transaction[property]; - } - }; - this.dataSource.sort = this.sort; - }); + /** + * Initializes the data source, paginator and sorter for provisioning journal entries table. + */ + setProvisioningJournalEntry() { + this.dataSource = new MatTableDataSource(this.provisioningJournalEntryData); + this.dataSource.paginator = this.paginator; + this.dataSource.sortingDataAccessor = (transaction: any, property: any) => { + switch (property) { + case 'glAccountType': return transaction.glAccountType.value; + case 'debit': return transaction.amount; + case 'credit': return transaction.amount; + default: return transaction[property]; + } + }; + this.dataSource.sort = this.sort; } + /** + * Filters data in provisioning journal entries table based on passed value. + * @param {string} filterValue Value to filter data. + */ applyFilter(filterValue: string) { this.dataSource.filter = filterValue.trim().toLowerCase(); } diff --git a/src/app/accounting/revert-transaction/revert-transaction.component.html b/src/app/accounting/revert-transaction/revert-transaction.component.html index 6a73b91d7..aee7eba5f 100644 --- a/src/app/accounting/revert-transaction/revert-transaction.component.html +++ b/src/app/accounting/revert-transaction/revert-transaction.component.html @@ -4,7 +4,8 @@

Are you sure you want to revert this transaction?

- + Comments +
diff --git a/src/app/accounting/revert-transaction/revert-transaction.component.ts b/src/app/accounting/revert-transaction/revert-transaction.component.ts index 79579f74d..ae0fdfe62 100644 --- a/src/app/accounting/revert-transaction/revert-transaction.component.ts +++ b/src/app/accounting/revert-transaction/revert-transaction.component.ts @@ -1,7 +1,11 @@ +/** Angular Imports */ import { Component, OnInit, Inject } from '@angular/core'; import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material'; import { FormControl } from '@angular/forms'; +/** + * Revert transaction dialog component. + */ @Component({ selector: 'mifosx-revert-transaction', templateUrl: './revert-transaction.component.html', @@ -9,8 +13,13 @@ import { FormControl } from '@angular/forms'; }) export class RevertTransactionComponent implements OnInit { + /** Comments input form control. */ comments = new FormControl(''); + /** + * @param {MatDialogRef} dialogRef Component reference to dialog. + * @param {any} data Provides comments or reverted transaction ID. + */ constructor(public dialogRef: MatDialogRef, @Inject(MAT_DIALOG_DATA) public data: any) { } diff --git a/src/app/accounting/search-journal-entry/journal-entry.datasource.ts b/src/app/accounting/search-journal-entry/journal-entry.datasource.ts index 49c26db8e..523a8c43b 100644 --- a/src/app/accounting/search-journal-entry/journal-entry.datasource.ts +++ b/src/app/accounting/search-journal-entry/journal-entry.datasource.ts @@ -1,32 +1,57 @@ +/** Angular Imports */ import { CollectionViewer, DataSource } from '@angular/cdk/collections'; +/** rxjs Imports */ import { Observable, BehaviorSubject } from 'rxjs'; +/** Custom Services */ import { AccountingService } from '../accounting.service'; +/** + * Journal entries custom data source to implement server side filtering, pagination and sorting. + */ export class JournalEntriesDataSource implements DataSource { + /** Journal entries behavior subject to represent loaded journal entries page. */ private journalEntriesSubject = new BehaviorSubject([]); + /** Records subject to represent total number of filtered journal entry records. */ private recordsSubject = new BehaviorSubject(0); - + /** Records observable which can be subscribed to get the value of total number of filtered journal entry records. */ public records$ = this.recordsSubject.asObservable(); + /** + * @param {AccountingService} accountingService Accounting Service + */ constructor(private accountingService: AccountingService) { } - getJournalEntries(filterBy: any, orderBy: string = '', sortOrder: string = '', offset: number = 0, limit: number = 10) { + /** + * Gets journal entries on the basis of provided parameters and emits the value. + * @param {any} filterBy Properties by which entries should be filtered. + * @param {string} orderBy Property by which entries should be sorted. + * @param {string} sortOrder Sort order: ascending or descending. + * @param {number} pageIndex Page number. + * @param {number} limit Number of entries within the page. + */ + getJournalEntries(filterBy: any, orderBy: string = '', sortOrder: string = '', pageIndex: number = 0, limit: number = 10) { this.journalEntriesSubject.next([]); orderBy = (orderBy === 'debit' || orderBy === 'credit') ? 'amount' : orderBy; - this.accountingService.getJournalEntries(filterBy, orderBy, sortOrder, offset, limit) + this.accountingService.getJournalEntries(filterBy, orderBy, sortOrder, pageIndex * limit, limit) .subscribe((journalEntries: any) => { this.recordsSubject.next(journalEntries.totalFilteredRecords); this.journalEntriesSubject.next(journalEntries.pageItems); }); } + /** + * @param {CollectionViewer} collectionViewer + */ connect(collectionViewer: CollectionViewer): Observable { return this.journalEntriesSubject.asObservable(); } + /** + * @param {CollectionViewer} collectionViewer + */ disconnect(collectionViewer: CollectionViewer): void { this.journalEntriesSubject.complete(); this.recordsSubject.complete(); diff --git a/src/app/accounting/search-journal-entry/search-journal-entry.component.html b/src/app/accounting/search-journal-entry/search-journal-entry.component.html index 582747885..21863d8a1 100644 --- a/src/app/accounting/search-journal-entry/search-journal-entry.component.html +++ b/src/app/accounting/search-journal-entry/search-journal-entry.component.html @@ -1,15 +1,18 @@
- + Office Name + - + GL Account Name or Code + - + Filter + {{ filter.option }} @@ -17,19 +20,22 @@ - + Transaction Date From + - + Transaction Date To + - + Transaction ID +
@@ -104,7 +110,7 @@ - + diff --git a/src/app/accounting/search-journal-entry/search-journal-entry.component.scss b/src/app/accounting/search-journal-entry/search-journal-entry.component.scss index 1922e7ffa..24edb63bb 100644 --- a/src/app/accounting/search-journal-entry/search-journal-entry.component.scss +++ b/src/app/accounting/search-journal-entry/search-journal-entry.component.scss @@ -1,3 +1,7 @@ table { width: 100%; + + .select-row:hover { + cursor: pointer; + } } diff --git a/src/app/accounting/search-journal-entry/search-journal-entry.component.ts b/src/app/accounting/search-journal-entry/search-journal-entry.component.ts index 2e48f1463..809b1723c 100644 --- a/src/app/accounting/search-journal-entry/search-journal-entry.component.ts +++ b/src/app/accounting/search-journal-entry/search-journal-entry.component.ts @@ -1,14 +1,22 @@ +/** Angular Imports */ import { Component, OnInit, ViewChild, AfterViewInit } from '@angular/core'; import { MatPaginator, MatSort } from '@angular/material'; import { FormControl } from '@angular/forms'; -import { Router } from '@angular/router'; +import { Router, ActivatedRoute } from '@angular/router'; +/** rxjs Imports */ import { merge } from 'rxjs'; import { tap, startWith, map, distinctUntilChanged, debounceTime} from 'rxjs/operators'; +/** Custom Services */ import { AccountingService } from '../accounting.service'; + +/** Custom Data Source */ import { JournalEntriesDataSource } from './journal-entry.datasource'; +/** + * Search journal entry component. + */ @Component({ selector: 'mifosx-search-journal-entry', templateUrl: './search-journal-entry.component.html', @@ -16,19 +24,25 @@ import { JournalEntriesDataSource } from './journal-entry.datasource'; }) export class SearchJournalEntryComponent implements OnInit, AfterViewInit { - // TODO: Update once language and date settings are setup + /** Minimum transaction date allowed. */ minDate = new Date(2000, 0, 1); + /** Maximum transaction date allowed. */ maxDate = new Date(); - + /** Office name filter form control. */ officeName = new FormControl(); + /** Office data. */ officeData: any; + /** Filtered office data for autocomplete. */ filteredOfficeData: any; - + /** Gl Account filter form control. */ glAccount = new FormControl(); + /** Gl Account data. */ glAccountData: any; + /** Filtered gl account data. */ filteredGLAccountData: any; - - filter = new FormControl(''); + /** Entry type filter form control. */ + entryTypeFilter = new FormControl(''); + /** Entry type filter data. */ entryTypeFilterData = [ { option: 'All', @@ -43,15 +57,17 @@ export class SearchJournalEntryComponent implements OnInit, AfterViewInit { value: false // Bug: unable to implement from server side } ]; - - transactionDateFrom = new FormControl(new Date(2000, 0, 1)); + /** Transaction date from form control. */ + transactionDateFrom = new FormControl(new Date(new Date().setMonth(new Date().getMonth() - 1))); + /** Transaction date to form control. */ transactionDateTo = new FormControl(new Date()); - + /** Transaction ID form control. */ transactionId = new FormControl(); - + /** Columns to be displayed in journal entries table. */ displayedColumns: string[] = ['id', 'officeName', 'transactionId', 'transactionDate', 'glAccountType', 'createdByUserName', 'glAccountCode', 'glAccountName', 'debit', 'credit']; - dataSource: any; - + /** Data source for journal entries table. */ + dataSource: JournalEntriesDataSource; + /** Journal entries filter. */ filterJournalEntriesBy = [ { type: 'officeId', @@ -71,15 +87,15 @@ export class SearchJournalEntryComponent implements OnInit, AfterViewInit { }, { type: 'fromDate', - value: '1 July 2018' + value: this.getDate(new Date(new Date().setMonth(new Date().getMonth() - 1))) }, { type: 'toDate', - value: '31 July 2018' + value: this.getDate(new Date()) }, { type: 'dateFormat', - value: 'dd MMMM yyyy' + value: 'yyyy-MM-dd' }, { type: 'locale', @@ -87,18 +103,41 @@ export class SearchJournalEntryComponent implements OnInit, AfterViewInit { } ]; + /** Paginator for journal entries table. */ @ViewChild(MatPaginator) paginator: MatPaginator; - @ViewChild(MatSort) sort: MatSort; // Bug: Unable to implement desc order sorting + /** Sorter for journal entries table. */ + @ViewChild(MatSort) sort: MatSort; + /** + * Retrieves the offices and gl accounts data from `resolve`. + * @param {AccountingService} accountingService Accounting Service. + * @param {ActivatedRoute} route Activated Route. + */ constructor(private accountingService: AccountingService, - private router: Router) { } + private route: ActivatedRoute) { + this.route.data.subscribe((data: { + offices: any, + glAccounts: any + }) => { + this.officeData = data.offices; + this.glAccountData = data.glAccounts; + }); + } + /** + * Sets filtered offices and gl accounts for autocomplete and journal entries table. + */ ngOnInit() { - this.getOffices(); - this.getGlAccounts(); + this.setFilteredOffices(); + this.setFilteredGlAccounts(); this.getJournalEntries(); } + /** + * Subscribes to all search filters: + * Office Name, GL Account, Transaction ID, Transaction Date From, Transaction Date To, + * sort change and page change. + */ ngAfterViewInit() { this.officeName.valueChanges .pipe( @@ -132,6 +171,26 @@ export class SearchJournalEntryComponent implements OnInit, AfterViewInit { ) .subscribe(); + this.transactionDateFrom.valueChanges + .pipe( + debounceTime(500), + distinctUntilChanged(), + tap((filterValue) => { + this.applyFilter(this.getDate(filterValue), 'fromDate'); + }) + ) + .subscribe(); + + this.transactionDateTo.valueChanges + .pipe( + debounceTime(500), + distinctUntilChanged(), + tap((filterValue) => { + this.applyFilter(this.getDate(filterValue), 'toDate'); + }) + ) + .subscribe(); + this.sort.sortChange.subscribe(() => this.paginator.pageIndex = 0); merge(this.sort.sortChange, this.paginator.page) @@ -141,13 +200,21 @@ export class SearchJournalEntryComponent implements OnInit, AfterViewInit { .subscribe(); } + /** + * Loads a page of journal entries. + */ loadJournalEntriesPage() { if (!this.sort.direction) { delete this.sort.active; } - this.dataSource.getJournalEntries(this.filterJournalEntriesBy, this.sort.active, this.sort.direction, this.paginator.pageIndex * this.paginator.pageSize, this.paginator.pageSize); + this.dataSource.getJournalEntries(this.filterJournalEntriesBy, this.sort.active, this.sort.direction, this.paginator.pageIndex, this.paginator.pageSize); } + /** + * Filters data in journal entries table based on passed value and poperty. + * @param {string} filterValue Value to filter data. + * @param {string} property Property to filter data by. + */ applyFilter(filterValue: string, property: string) { this.paginator.pageIndex = 0; const findIndex = this.filterJournalEntriesBy.findIndex(filter => filter.type === property); @@ -155,53 +222,92 @@ export class SearchJournalEntryComponent implements OnInit, AfterViewInit { this.loadJournalEntriesPage(); } - viewTransaction(journalEntry: any) { - this.router.navigate(['/accounting/transactions/view', journalEntry.transactionId]); - } - + /** + * Displays office name in form control input. + * @param {any} office Office data. + * @returns {string} Office name if valid otherwise undefined. + */ displayOfficeName(office?: any): string | undefined { return office ? office.name : undefined; } + /** + * Displays gl account name in form control input. + * @param {any} glAccount Gl Account data. + * @returns {string} Gl Account name if valid otherwise undefined. + */ displayGLAccount(glAccount?: any): string | undefined { return glAccount ? glAccount.name + ' (' + glAccount.glCode + ')' : undefined; } - getOffices() { - this.accountingService.getOffices().subscribe(officeData => { - this.officeData = officeData; - this.filteredOfficeData = this.officeName.valueChanges + /** + * Sets filtered offices for autocomplete. + */ + setFilteredOffices() { + this.filteredOfficeData = this.officeName.valueChanges .pipe( startWith(''), map((office: any) => typeof office === 'string' ? office : office.name), - map((officeName: string) => officeName ? this.filterOfficeAutocompleteData(this.officeData, officeName) : this.officeData) + map((officeName: string) => officeName ? this.filterOfficeAutocompleteData(officeName) : this.officeData) ); - }); } - getGlAccounts() { - this.accountingService.getGlAccounts().subscribe(glAccountData => { - this.glAccountData = glAccountData; - this.filteredGLAccountData = this.glAccount.valueChanges + /** + * Sets filtered gl accounts for autocomplete. + */ + setFilteredGlAccounts() { + this.filteredGLAccountData = this.glAccount.valueChanges .pipe( startWith(''), map((glAccount: any) => typeof glAccount === 'string' ? glAccount : glAccount.name + ' (' + glAccount.glCode + ')'), - map((glAccount: string) => glAccount ? this.filterGLAccountAutocompleteData(this.glAccountData, glAccount) : this.glAccountData) + map((glAccount: string) => glAccount ? this.filterGLAccountAutocompleteData(glAccount) : this.glAccountData) ); - }); } - private filterOfficeAutocompleteData(officeData: any, officeName: string): any { - return officeData.filter((office: any) => office.name.toLowerCase().includes(officeName.toLowerCase())); + /** + * Filters offices. + * @param {string} officeName Office name to filter office by. + * @returns {any} Filtered offices. + */ + private filterOfficeAutocompleteData(officeName: string): any { + return this.officeData.filter((office: any) => office.name.toLowerCase().includes(officeName.toLowerCase())); } - private filterGLAccountAutocompleteData(glAccountData: any, glAccount: string): any { - return glAccountData.filter((option: any) => (option.name + ' (' + option.glCode + ')').toLowerCase().includes(glAccount.toLowerCase())); + /** + * Filters gl accounts. + * @param {string} glAccount Gl Account name to filter gl account by. + * @returns {any} Filtered gl accounts. + */ + private filterGLAccountAutocompleteData(glAccount: string): any { + return this.glAccountData.filter((option: any) => (option.name + ' (' + option.glCode + ')').toLowerCase().includes(glAccount.toLowerCase())); } + /** + * Initializes the data source for journal entries table and loads the first page. + */ getJournalEntries() { this.dataSource = new JournalEntriesDataSource(this.accountingService); - this.dataSource.getJournalEntries(this.filterJournalEntriesBy, this.sort.active, this.sort.direction, this.paginator.pageIndex * this.paginator.pageSize, this.paginator.pageSize); + this.dataSource.getJournalEntries(this.filterJournalEntriesBy, this.sort.active, this.sort.direction, this.paginator.pageIndex, this.paginator.pageSize); + } + + /** + * Gets the date from the passed timestamp. + * + * TODO: Update once language and date settings are setup. + * + * @param {any} timestamp Timestam from which date is to be extracted. + */ + private getDate(timestamp: any) { + let day = timestamp.getDate(); + let month = timestamp.getMonth() + 1; + const year = timestamp.getFullYear(); + if (day < 10) { + day = `0${day}`; + } + if (month < 10) { + month = `0${month}`; + } + return `${year}-${month}-${day}`; } } diff --git a/src/app/accounting/view-journal-entry/view-journal-entry.component.html b/src/app/accounting/view-journal-entry/view-journal-entry.component.html index ecc8d79ca..05a9e7245 100644 --- a/src/app/accounting/view-journal-entry/view-journal-entry.component.html +++ b/src/app/accounting/view-journal-entry/view-journal-entry.component.html @@ -1,5 +1,7 @@

View Journal Entry

+
+ @@ -102,7 +104,9 @@
+
+ diff --git a/src/app/accounting/view-journal-entry/view-journal-entry.component.scss b/src/app/accounting/view-journal-entry/view-journal-entry.component.scss index d8a0b3c67..459bcd51b 100644 --- a/src/app/accounting/view-journal-entry/view-journal-entry.component.scss +++ b/src/app/accounting/view-journal-entry/view-journal-entry.component.scss @@ -1,13 +1,16 @@ -th { - text-align: left; - font-weight: 500; -} +table { + th { + text-align: left; + font-weight: 500; + padding: 0 0.4rem 0 0; -th, td { - padding: 0 0.4rem 0 0; -} + &.header { + text-align: center; + padding: 0.4rem 0.4rem 0 0; + } + } -.header { - text-align: center; - padding: 0.4rem 0.4rem 0 0; + td { + padding: 0 0.4rem 0 0; + } } diff --git a/src/app/accounting/view-journal-entry/view-journal-entry.component.ts b/src/app/accounting/view-journal-entry/view-journal-entry.component.ts index 949b7bfe9..3c46218ee 100644 --- a/src/app/accounting/view-journal-entry/view-journal-entry.component.ts +++ b/src/app/accounting/view-journal-entry/view-journal-entry.component.ts @@ -1,6 +1,10 @@ +/** Angular Imports */ import { Component, OnInit, Inject } from '@angular/core'; import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material'; +/** + * View journal entry dialog component. + */ @Component({ selector: 'mifosx-view-journal-entry', templateUrl: './view-journal-entry.component.html', @@ -8,6 +12,10 @@ import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material'; }) export class ViewJournalEntryComponent implements OnInit { + /** + * @param {MatDialogRef} dialogRef Component reference to dialog. + * @param {any} data Provides journal entry. + */ constructor(public dialogRef: MatDialogRef, @Inject(MAT_DIALOG_DATA) public data: any) { } diff --git a/src/app/accounting/view-transaction/transaction.resolver.ts b/src/app/accounting/view-transaction/transaction.resolver.ts new file mode 100644 index 000000000..a2c67ea17 --- /dev/null +++ b/src/app/accounting/view-transaction/transaction.resolver.ts @@ -0,0 +1,31 @@ +/** Angular Imports */ +import { Injectable } from '@angular/core'; +import { Resolve, ActivatedRouteSnapshot } from '@angular/router'; + +/** rxjs Imports */ +import { Observable } from 'rxjs'; + +/** Custom Services */ +import { AccountingService } from '../accounting.service'; + +/** + * Transaction data resolver. + */ +@Injectable() +export class TransactionResolver implements Resolve { + + /** + * @param {AccountingService} accountingService Accounting service. + */ + constructor(private accountingService: AccountingService) {} + + /** + * Returns the transaction data. + * @returns {Observable} + */ + resolve(route: ActivatedRouteSnapshot): Observable { + const transactionId = route.paramMap.get('id'); + return this.accountingService.getJournalEntry(transactionId); + } + +} diff --git a/src/app/accounting/view-transaction/view-transaction.component.html b/src/app/accounting/view-transaction/view-transaction.component.html index 4a00272eb..0403cea49 100644 --- a/src/app/accounting/view-transaction/view-transaction.component.html +++ b/src/app/accounting/view-transaction/view-transaction.component.html @@ -1,7 +1,10 @@ -
+
@@ -88,7 +91,7 @@ - + diff --git a/src/app/accounting/view-transaction/view-transaction.component.scss b/src/app/accounting/view-transaction/view-transaction.component.scss index fff2afccc..2a3da1ac0 100644 --- a/src/app/accounting/view-transaction/view-transaction.component.scss +++ b/src/app/accounting/view-transaction/view-transaction.component.scss @@ -1,17 +1,17 @@ .content { div { line-height: 3rem; - } - .header { - font-weight: 500; + &.header { + font-weight: 500; + } } } table { width: 100%; -} -.select:hover { - cursor: pointer; + .select-row:hover { + cursor: pointer; + } } diff --git a/src/app/accounting/view-transaction/view-transaction.component.ts b/src/app/accounting/view-transaction/view-transaction.component.ts index 4520a31c8..ac910f558 100644 --- a/src/app/accounting/view-transaction/view-transaction.component.ts +++ b/src/app/accounting/view-transaction/view-transaction.component.ts @@ -1,61 +1,94 @@ -import { Component, OnInit, ViewChild, OnDestroy } from '@angular/core'; -import { ActivatedRoute, Router, ParamMap } from '@angular/router'; +/** Angular Imports */ +import { Component, OnInit, ViewChild } from '@angular/core'; +import { ActivatedRoute, Router } from '@angular/router'; import { MatDialog, MatSort, MatPaginator, MatTableDataSource } from '@angular/material'; +/** Custom Services */ import { AccountingService } from '../accounting.service'; + +/** Custom Components */ import { RevertTransactionComponent } from '../revert-transaction/revert-transaction.component'; import { ViewJournalEntryComponent } from '../view-journal-entry/view-journal-entry.component'; +/** + * View transaction component. + */ @Component({ selector: 'mifosx-view-transaction', templateUrl: './view-transaction.component.html', styleUrls: ['./view-transaction.component.scss'] }) -export class ViewTransactionComponent implements OnInit, OnDestroy { +export class ViewTransactionComponent implements OnInit { // TODO: Update once language and date settings are setup - transactionId: string; - transactionId$: any; - displayedColumns: string[] = ['id', 'glAccountType', 'glAccountCode', 'glAccountName', 'debit', 'credit']; - dataSource: any; + /** Transaction data. */ + transaction: any; + /** Transaction ID. */ + transactionId: string; + /** Columns to be displayed in transaction table. */ + displayedColumns: string[] = ['id', 'glAccountType', 'glAccountCode', 'glAccountName', 'debit', 'credit']; + /** Data source for transaction table. */ + dataSource: MatTableDataSource; + + /** Paginator for transaction table. */ @ViewChild(MatPaginator) paginator: MatPaginator; + /** Sorter for transaction table. */ @ViewChild(MatSort) sort: MatSort; - constructor(private route: ActivatedRoute, + /** + * @param {AccountingService} accountingService Accounting Service. + * @param {ActivatedRoute} route Activated Route. + * @param {Router} router Router for navigation. + * @param {MatDialog} dialog Dialog reference. + */ + constructor(private accountingService: AccountingService, + private route: ActivatedRoute, private router: Router, - private accountingService: AccountingService, - public dialog: MatDialog) { } + public dialog: MatDialog) { } + /** + * Retrieves the transaction data from `resolve` and sets the transaction table. + */ ngOnInit() { - this.transactionId$ = this.route.paramMap.subscribe((params: ParamMap) => { - this.transactionId = params.get('id'); - this.getJournalEntry(); + this.route.data.subscribe((data: { transaction: any }) => { + this.transaction = data.transaction; + this.transactionId = data.transaction.pageItems[0].transactionId; + this.setTransaction(); }); } - getJournalEntry() { - this.accountingService.getJournalEntry(this.transactionId).subscribe((journalEntry: any) => { - this.dataSource = new MatTableDataSource(journalEntry.pageItems); - this.dataSource.paginator = this.paginator; - this.dataSource.sortingDataAccessor = (transaction: any, property: any) => { - switch (property) { - case 'glAccountType': return transaction.glAccountType.value; - case 'debit': return transaction.amount; - case 'credit': return transaction.amount; - default: return transaction[property]; - } - }; - this.dataSource.sort = this.sort; - }); + /** + * Initializes the data source for transaction table with journal entries, paginator and sorter. + */ + setTransaction() { + this.dataSource = new MatTableDataSource(this.transaction.pageItems); + this.dataSource.paginator = this.paginator; + this.dataSource.sortingDataAccessor = (transaction: any, property: any) => { + switch (property) { + case 'glAccountType': return transaction.glAccountType.value; + case 'debit': return transaction.amount; + case 'credit': return transaction.amount; + default: return transaction[property]; + } + }; + this.dataSource.sort = this.sort; } + /** + * View details of selected journal entry. + * @param {any} journalEntry Selected journal entry. + */ viewJournalEntry(journalEntry: any) { this.dialog.open(ViewJournalEntryComponent, { data: { journalEntry: journalEntry } }); } + /** + * Reverts the given transaction and redirects to reverted transaction. + * @param {transactionId} transactionId Transaction ID of transaction to be reverted. + */ revertTransaction(transactionId?: string) { const revertTransactionDialogRef = this.dialog.open(RevertTransactionComponent, { data: { reverted: this.dataSource.data[0].reversed, transactionId: transactionId } @@ -67,13 +100,9 @@ export class ViewTransactionComponent implements OnInit, OnDestroy { this.revertTransaction(reversedTransaction.transactionId); }); } else if (response.redirect) { - this.router.navigate(['/accounting/transactions/view', transactionId]); + this.router.navigate(['../', transactionId], { relativeTo: this.route }); } }); } - ngOnDestroy() { - this.transactionId$.unsubscribe(); - } - } diff --git a/src/app/core/http/error-handler.interceptor.ts b/src/app/core/http/error-handler.interceptor.ts index afa8502ce..86c911c20 100644 --- a/src/app/core/http/error-handler.interceptor.ts +++ b/src/app/core/http/error-handler.interceptor.ts @@ -42,7 +42,7 @@ export class ErrorHandlerInterceptor implements HttpInterceptor { let errorMessage = (response.error.developerMessage || response.message); if (response.error.errors) { if (response.error.errors[0]) { - errorMessage = response.error.errors[0].developerMessage; + errorMessage = response.error.errors[0].defaultUserMessage || response.error.errors[0].developerMessage; } } @@ -57,9 +57,9 @@ export class ErrorHandlerInterceptor implements HttpInterceptor { } else if (status === 400) { this.alertService.alert({ type: 'Bad Request', message: 'Invalid parameters were passed in the request!' }); } else if (status === 403) { - this.alertService.alert({ type: 'Unauthorized Request', message: 'You are not authorized for this request!' }); + this.alertService.alert({ type: 'Unauthorized Request', message: errorMessage || 'You are not authorized for this request!' }); } else if (status === 404) { - this.alertService.alert({ type: 'Resource does not exist', message: 'Resource does not exist!' }); + this.alertService.alert({ type: 'Resource does not exist', message: errorMessage || 'Resource does not exist!' }); } else if (status === 500) { this.alertService.alert({ type: 'Internal Server Error', message: 'Internal Server Error. Please try again later.' }); } else { diff --git a/src/app/core/shell/breadcrumb/breadcrumb.component.scss b/src/app/core/shell/breadcrumb/breadcrumb.component.scss index a9a23f3e4..e38a7db3e 100644 --- a/src/app/core/shell/breadcrumb/breadcrumb.component.scss +++ b/src/app/core/shell/breadcrumb/breadcrumb.component.scss @@ -2,6 +2,7 @@ margin: 0 auto; max-width: 74rem; width: 90%; + word-wrap: break-word; .breadcrumb { list-style: none; diff --git a/src/app/self-service/users/view-user/view-user.component.html b/src/app/self-service/users/view-user/view-user.component.html index cd27ff75a..31761cc95 100644 --- a/src/app/self-service/users/view-user/view-user.component.html +++ b/src/app/self-service/users/view-user/view-user.component.html @@ -1,5 +1,13 @@
+ + - -
diff --git a/src/app/self-service/users/view-user/view-user.component.scss b/src/app/self-service/users/view-user/view-user.component.scss index 70d152196..a4541ccaf 100644 --- a/src/app/self-service/users/view-user/view-user.component.scss +++ b/src/app/self-service/users/view-user/view-user.component.scss @@ -5,10 +5,10 @@ div { margin: 1rem 0; word-wrap: break-word; - } - .header { - font-weight: 500; + &.header { + font-weight: 500; + } } } } diff --git a/src/app/shared/icons.module.ts b/src/app/shared/icons.module.ts index fc49765c3..9756c64d7 100644 --- a/src/app/shared/icons.module.ts +++ b/src/app/shared/icons.module.ts @@ -5,31 +5,44 @@ import { NgModule } from '@angular/core'; import { FontAwesomeModule } from '@fortawesome/angular-fontawesome'; import { library } from '@fortawesome/fontawesome-svg-core'; import { + faArchive, faBars, faBell, + faCalendar, faChartBar, faCheckCircle, + faChevronDown, faChevronLeft, faChevronRight, faCircle, faCog, + faCogs, faEdit, faEye, faEyeSlash, faFileUpload, faFillDrip, + faHandHoldingUsd, faHome, + faLink, + faList, faLocationArrow, faLock, faLockOpen, + faMinusCircle, faMoneyBillAlt, + faPlay, faPlus, + faPlusCircle, faSearch, faShieldAlt, faSignOutAlt, faSitemap, faSync, faTachometerAlt, + faTimesCircle, + faTrash, + faUndo, faUniversity, faUserCircle, faUsers, @@ -38,31 +51,44 @@ import { /** Add icons to the library for convenient access in other components. */ library.add( + faArchive, faBars, faBell, + faCalendar, faChartBar, faCheckCircle, + faChevronDown, faChevronLeft, faChevronRight, faCircle, faCog, + faCogs, faEdit, faEye, faEyeSlash, faFileUpload, faFillDrip, + faHandHoldingUsd, faHome, + faLink, + faList, faLocationArrow, faLock, faLockOpen, + faMinusCircle, faMoneyBillAlt, + faPlay, faPlus, + faPlusCircle, faSearch, faShieldAlt, faSignOutAlt, faSitemap, faSync, faTachometerAlt, + faTimesCircle, + faTrash, + faUndo, faUniversity, faUserCircle, faUsers,