refractor: accounting module (#300)
* Refractor components. * Add resolvers. * Add relative router links. * Replace mat-icons by font-awesome icons. * Add comments. Fixes: #298
This commit is contained in:
+6
-1
@@ -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,
|
||||
|
||||
@@ -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 { }
|
||||
|
||||
@@ -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<Object> {
|
||||
|
||||
/**
|
||||
* @param {AccountingService} accountingService Accounting service.
|
||||
*/
|
||||
constructor(private accountingService: AccountingService) {}
|
||||
|
||||
/**
|
||||
* Returns the accounting rule data.
|
||||
* @returns {Observable<any>}
|
||||
*/
|
||||
resolve(route: ActivatedRouteSnapshot): Observable<any> {
|
||||
const id = route.paramMap.get('id');
|
||||
return this.accountingService.getAccountingRule(id);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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<Object> {
|
||||
|
||||
/**
|
||||
* @param {AccountingService} accountingService Accounting service.
|
||||
*/
|
||||
constructor(private accountingService: AccountingService) {}
|
||||
|
||||
/**
|
||||
* Returns the accounting rules template data.
|
||||
* @returns {Observable<any>}
|
||||
*/
|
||||
resolve(): Observable<any> {
|
||||
return this.accountingService.getAccountingRulesTemplate();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,14 +1,16 @@
|
||||
<div class="container m-b-20" fxLayout="row" fxLayoutAlign="end" fxLayoutGap="20px">
|
||||
<button mat-raised-button color="primary" [routerLink]="['/accounting/accounting-rules/create']">
|
||||
<mat-icon>add</mat-icon> Add Rule
|
||||
<button mat-raised-button color="primary" [routerLink]="['create']">
|
||||
<fa-icon icon="plus"></fa-icon>
|
||||
Add Rule
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
|
||||
<div fxLayout="row" fxLayoutGap="20px">
|
||||
<div fxLayout="row">
|
||||
<mat-form-field fxFlex>
|
||||
<input matInput (keyup)="applyFilter($event.target.value)" placeholder="Filter">
|
||||
<mat-label>Filter</mat-label>
|
||||
<input matInput (keyup)="applyFilter($event.target.value)">
|
||||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
@@ -47,7 +49,7 @@
|
||||
</ng-container>
|
||||
|
||||
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: displayedColumns;" [routerLink]="['/accounting/accounting-rules/view', row.id]"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: displayedColumns;" [routerLink]="['view', row.id]" class="select-row"></tr>
|
||||
|
||||
</table>
|
||||
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
table {
|
||||
width: 100%;
|
||||
|
||||
.select-row:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<any>;
|
||||
|
||||
/** 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();
|
||||
}
|
||||
|
||||
@@ -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<Object> {
|
||||
|
||||
/**
|
||||
* @param {AccountingService} accountingService Accounting service.
|
||||
*/
|
||||
constructor(private accountingService: AccountingService) {}
|
||||
|
||||
/**
|
||||
* Returns the accounting rules data.
|
||||
* @returns {Observable<any>}
|
||||
*/
|
||||
resolve(): Observable<any> {
|
||||
return this.accountingService.getAccountingRules();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -9,19 +9,27 @@
|
||||
<div fxLayout="row wrap" fxLayoutGap="2%" fxLayout.lt-md="column">
|
||||
|
||||
<mat-form-field fxFlex="48%">
|
||||
<input matInput placeholder="Accounting Rule Name" required formControlName="name">
|
||||
<mat-label>Accounting Rule Name</mat-label>
|
||||
<input matInput required formControlName="name">
|
||||
<mat-error *ngIf="accountingRuleForm.controls.name.hasError('required')">
|
||||
Accounting Rule Name is <strong>required</strong>
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field fxFlex="48%">
|
||||
<mat-select placeholder="Office" required formControlName="officeId">
|
||||
<mat-label>Office</mat-label>
|
||||
<mat-select required formControlName="officeId">
|
||||
<mat-option *ngFor="let office of officeData" [value]="office.id">
|
||||
{{ office.name }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
<mat-error *ngIf="accountingRuleForm.controls.officeId.hasError('required')">
|
||||
Office is <strong>required</strong>
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
<div fxFlex="48%" fxLayout="row" fxLayout.lt-md="column" class="rule-wrapper">
|
||||
<label fxFlex="50%">Affected GL Entry (Debit) Rule Type *</label>
|
||||
<mat-label fxFlex="50%">Affected GL Entry (Debit) Rule Type *</mat-label>
|
||||
<mat-radio-group fxFlex="50%" fxLayout="row" fxLayoutGap="5%" formControlName="debitRuleType">
|
||||
<mat-radio-button value="fixedAccount">Fixed Account</mat-radio-button>
|
||||
<mat-radio-button value="listOfAccounts">List of Accounts</mat-radio-button>
|
||||
@@ -29,7 +37,8 @@
|
||||
</div>
|
||||
|
||||
<mat-form-field fxFlex="48%" *ngIf="accountingRuleForm.controls.debitRuleType.value === 'fixedAccount'">
|
||||
<mat-select placeholder="Account to Debit" required formControlName="accountToDebit">
|
||||
<mat-label>Account to Debit</mat-label>
|
||||
<mat-select formControlName="accountToDebit">
|
||||
<mat-option *ngFor="let glAccount of glAccountData" [value]="glAccount.id">
|
||||
{{ glAccount.name }}
|
||||
</mat-option>
|
||||
@@ -38,22 +47,23 @@
|
||||
|
||||
<div fxFlex="48%" fxLayout="row" fxLayoutGap="2%" fxLayout.lt-md="column" *ngIf="accountingRuleForm.controls.debitRuleType.value === 'listOfAccounts'">
|
||||
<mat-form-field fxFlex="50%">
|
||||
<mat-select placeholder="Debit Tags" required formControlName="debitTags" multiple>
|
||||
<mat-label>Debit Tags</mat-label>
|
||||
<mat-select formControlName="debitTags" multiple>
|
||||
<mat-option *ngFor="let debitTag of debitTagData" [value]="debitTag.id">
|
||||
{{ debitTag.name }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
|
||||
<div fxFlex="50%" class="entries-wrapper">
|
||||
<mat-checkbox labelPosition="before" required formControlName="allowMultipleDebitEntries" class="entries">
|
||||
<div fxFlex="50%" ngClass.gt-md="entries-wrapper">
|
||||
<mat-checkbox labelPosition="before" formControlName="allowMultipleDebitEntries" class="entries">
|
||||
Multiple Debit Entries Allowed
|
||||
</mat-checkbox>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div fxFlex="48%" fxLayout="row" fxLayout.lt-md="column" class="rule-wrapper">
|
||||
<label fxFlex="50%">Affected GL Entry (Credit) Rule Type *</label>
|
||||
<mat-label fxFlex="50%">Affected GL Entry (Credit) Rule Type *</mat-label>
|
||||
<mat-radio-group fxFlex="50%" fxLayout="row" fxLayoutGap="5%" formControlName="creditRuleType">
|
||||
<mat-radio-button value="fixedAccount">Fixed Account</mat-radio-button>
|
||||
<mat-radio-button value="listOfAccounts">List of Accounts</mat-radio-button>
|
||||
@@ -61,7 +71,8 @@
|
||||
</div>
|
||||
|
||||
<mat-form-field fxFlex="48%" *ngIf="accountingRuleForm.controls.creditRuleType.value === 'fixedAccount'">
|
||||
<mat-select placeholder="Account to Credit" required formControlName="accountToCredit">
|
||||
<mat-label>Account to Credit</mat-label>
|
||||
<mat-select formControlName="accountToCredit">
|
||||
<mat-option *ngFor="let glAccount of glAccountData" [value]="glAccount.id">
|
||||
{{ glAccount.name }}
|
||||
</mat-option>
|
||||
@@ -70,22 +81,24 @@
|
||||
|
||||
<div fxFlex="48%" fxLayout="row" fxLayoutGap="2%" fxLayout.lt-md="column" *ngIf="accountingRuleForm.controls.creditRuleType.value === 'listOfAccounts'">
|
||||
<mat-form-field fxFlex="50%">
|
||||
<mat-select placeholder="Credit Tags" required formControlName="creditTags" multiple>
|
||||
<mat-label>Credit Tags</mat-label>
|
||||
<mat-select formControlName="creditTags" multiple>
|
||||
<mat-option *ngFor="let creditTag of creditTagData" [value]="creditTag.id">
|
||||
{{ creditTag.name }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
|
||||
<div fxFlex="50%" class="entries-wrapper">
|
||||
<mat-checkbox labelPosition="before" required formControlName="allowMultipleCreditEntries" class="entries">
|
||||
<div fxFlex="50%" ngClass.gt-md="entries-wrapper">
|
||||
<mat-checkbox labelPosition="before" formControlName="allowMultipleCreditEntries" class="entries">
|
||||
Multiple Credit Entries Allowed
|
||||
</mat-checkbox>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<mat-form-field fxFlex="98%">
|
||||
<textarea matInput placeholder="Description" formControlName="description"></textarea>
|
||||
<mat-label>Description</mat-label>
|
||||
<textarea matInput formControlName="description"></textarea>
|
||||
</mat-form-field>
|
||||
|
||||
</div>
|
||||
@@ -93,8 +106,8 @@
|
||||
</mat-card-content>
|
||||
|
||||
<mat-card-actions fxLayout="row" fxLayout.xs="column" fxLayoutAlign="center" fxLayoutGap="5px">
|
||||
<button type="button" mat-raised-button [routerLink]="['/accounting/accounting-rules']">Cancel</button>
|
||||
<button mat-raised-button color="primary">Submit</button>
|
||||
<button type="button" mat-raised-button [routerLink]="['../']">Cancel</button>
|
||||
<button mat-raised-button color="primary" [disabled]="!accountingRuleForm.valid">Submit</button>
|
||||
</mat-card-actions>
|
||||
|
||||
</form>
|
||||
|
||||
@@ -2,11 +2,6 @@
|
||||
margin: 17.5px 0 0 0;
|
||||
}
|
||||
|
||||
.additional-selection {
|
||||
opacity: 0.75;
|
||||
font-size: 0.75em;
|
||||
}
|
||||
|
||||
.entries-wrapper {
|
||||
position: relative;
|
||||
|
||||
|
||||
@@ -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 });
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -9,19 +9,27 @@
|
||||
<div fxLayout="row wrap" fxLayoutGap="2%" fxLayout.lt-md="column">
|
||||
|
||||
<mat-form-field fxFlex="48%">
|
||||
<input matInput placeholder="Accounting Rule Name" required formControlName="name">
|
||||
<mat-label>Accounting Rule Name</mat-label>
|
||||
<input matInput required formControlName="name">
|
||||
<mat-error *ngIf="accountingRuleForm.controls.name.hasError('required')">
|
||||
Accounting Rule Name is <strong>required</strong>
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field fxFlex="48%">
|
||||
<mat-select placeholder="Office" required formControlName="officeId">
|
||||
<mat-label>Office</mat-label>
|
||||
<mat-select required formControlName="officeId">
|
||||
<mat-option *ngFor="let office of officeData" [value]="office.id">
|
||||
{{ office.name }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
<mat-error *ngIf="accountingRuleForm.controls.officeId.hasError('required')">
|
||||
Office is <strong>required</strong>
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
<div fxFlex="48%" fxLayout="row" fxLayout.lt-md="column" class="rule-wrapper">
|
||||
<label fxFlex="50%">Affected GL Entry (Debit) Rule Type *</label>
|
||||
<mat-label fxFlex="50%">Affected GL Entry (Debit) Rule Type *</mat-label>
|
||||
<mat-radio-group fxFlex="50%" fxLayout="row" fxLayoutGap="5%" formControlName="debitRuleType">
|
||||
<mat-radio-button value="fixedAccount">Fixed Account</mat-radio-button>
|
||||
<mat-radio-button value="listOfAccounts">List of Accounts</mat-radio-button>
|
||||
@@ -29,7 +37,8 @@
|
||||
</div>
|
||||
|
||||
<mat-form-field fxFlex="48%" *ngIf="accountingRuleForm.controls.debitRuleType.value === 'fixedAccount'">
|
||||
<mat-select placeholder="Account to Debit" required formControlName="accountToDebit">
|
||||
<mat-label>Account to Debit</mat-label>
|
||||
<mat-select formControlName="accountToDebit">
|
||||
<mat-option *ngFor="let glAccount of glAccountData" [value]="glAccount.id">
|
||||
{{ glAccount.name }}
|
||||
</mat-option>
|
||||
@@ -38,22 +47,23 @@
|
||||
|
||||
<div fxFlex="48%" fxLayout="row" fxLayoutGap="2%" fxLayout.lt-md="column" *ngIf="accountingRuleForm.controls.debitRuleType.value === 'listOfAccounts'">
|
||||
<mat-form-field fxFlex="50%">
|
||||
<mat-select placeholder="Debit Tags" required formControlName="debitTags" multiple>
|
||||
<mat-label>Debit Tags</mat-label>
|
||||
<mat-select formControlName="debitTags" multiple>
|
||||
<mat-option *ngFor="let debitTag of debitTagData" [value]="debitTag.id">
|
||||
{{ debitTag.name }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
|
||||
<div fxFlex="50%" class="entries-wrapper">
|
||||
<mat-checkbox labelPosition="before" required formControlName="allowMultipleDebitEntries" class="entries">
|
||||
<div fxFlex="50%" ngClass.gt-md="entries-wrapper">
|
||||
<mat-checkbox labelPosition="before" formControlName="allowMultipleDebitEntries" class="entries">
|
||||
Multiple Debit Entries Allowed
|
||||
</mat-checkbox>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div fxFlex="48%" fxLayout="row" fxLayout.lt-md="column" class="rule-wrapper">
|
||||
<label fxFlex="50%">Affected GL Entry (Credit) Rule Type *</label>
|
||||
<mat-label fxFlex="50%">Affected GL Entry (Credit) Rule Type *</mat-label>
|
||||
<mat-radio-group fxFlex="50%" fxLayout="row" fxLayoutGap="5%" formControlName="creditRuleType">
|
||||
<mat-radio-button value="fixedAccount">Fixed Account</mat-radio-button>
|
||||
<mat-radio-button value="listOfAccounts">List of Accounts</mat-radio-button>
|
||||
@@ -61,7 +71,8 @@
|
||||
</div>
|
||||
|
||||
<mat-form-field fxFlex="48%" *ngIf="accountingRuleForm.controls.creditRuleType.value === 'fixedAccount'">
|
||||
<mat-select placeholder="Account to Credit" required formControlName="accountToCredit">
|
||||
<mat-label>Account to Credit</mat-label>
|
||||
<mat-select formControlName="accountToCredit">
|
||||
<mat-option *ngFor="let glAccount of glAccountData" [value]="glAccount.id">
|
||||
{{ glAccount.name }}
|
||||
</mat-option>
|
||||
@@ -70,22 +81,24 @@
|
||||
|
||||
<div fxFlex="48%" fxLayout="row" fxLayoutGap="2%" fxLayout.lt-md="column" *ngIf="accountingRuleForm.controls.creditRuleType.value === 'listOfAccounts'">
|
||||
<mat-form-field fxFlex="50%">
|
||||
<mat-select placeholder="Credit Tags" required formControlName="creditTags" multiple>
|
||||
<mat-label>Credit Tags</mat-label>
|
||||
<mat-select formControlName="creditTags" multiple>
|
||||
<mat-option *ngFor="let creditTag of creditTagData" [value]="creditTag.id">
|
||||
{{ creditTag.name }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
|
||||
<div fxFlex="50%" class="entries-wrapper">
|
||||
<mat-checkbox labelPosition="before" required formControlName="allowMultipleCreditEntries" class="entries">
|
||||
<div fxFlex="50%" ngClass.gt-md="entries-wrapper">
|
||||
<mat-checkbox labelPosition="before" formControlName="allowMultipleCreditEntries" class="entries">
|
||||
Multiple Credit Entries Allowed
|
||||
</mat-checkbox>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<mat-form-field fxFlex="98%">
|
||||
<textarea matInput placeholder="Description" formControlName="description"></textarea>
|
||||
<mat-label>Description</mat-label>
|
||||
<textarea matInput formControlName="description"></textarea>
|
||||
</mat-form-field>
|
||||
|
||||
</div>
|
||||
@@ -93,8 +106,8 @@
|
||||
</mat-card-content>
|
||||
|
||||
<mat-card-actions fxLayout="row" fxLayout.xs="column" fxLayoutAlign="center" fxLayoutGap="5px">
|
||||
<button type="button" mat-raised-button [routerLink]="['/accounting/accounting-rules/view', accountingRule?.id]">Cancel</button>
|
||||
<button mat-raised-button color="primary">Submit</button>
|
||||
<button type="button" mat-raised-button [routerLink]="['../']">Cancel</button>
|
||||
<button mat-raised-button color="primary" [disabled]="accountingRuleForm.pristine || !accountingRuleForm.valid">Submit</button>
|
||||
</mat-card-actions>
|
||||
|
||||
</form>
|
||||
|
||||
@@ -2,11 +2,6 @@
|
||||
margin: 17.5px 0 0 0;
|
||||
}
|
||||
|
||||
.additional-selection {
|
||||
opacity: 0.75;
|
||||
font-size: 0.75em;
|
||||
}
|
||||
|
||||
.entries-wrapper {
|
||||
position: relative;
|
||||
|
||||
|
||||
@@ -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 });
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -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 };
|
||||
};
|
||||
@@ -1,9 +1,11 @@
|
||||
<div fxLayout="row" fxLayoutAlign="end" fxLayoutGap="2%" fxLayout.lt-md="column" class="container m-b-20">
|
||||
<button mat-raised-button color="primary" [routerLink]="['/accounting/accounting-rules/view', accountingRule?.id, 'edit']">
|
||||
<mat-icon>edit</mat-icon> Edit
|
||||
<button mat-raised-button color="primary" [routerLink]="['edit']">
|
||||
<fa-icon icon="edit"></fa-icon>
|
||||
Edit
|
||||
</button>
|
||||
<button mat-raised-button color="warn" (click)="deleteAccountingRule()">
|
||||
<mat-icon>delete</mat-icon> Delete
|
||||
<fa-icon icon="trash"></fa-icon>
|
||||
Delete
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -25,11 +27,11 @@
|
||||
{{ accountingRule.officeName }}
|
||||
</div>
|
||||
|
||||
<div fxFlex="50%" class="header">
|
||||
<div fxFlex="50%" class="header" *ngIf="accountingRule.description">
|
||||
Description
|
||||
</div>
|
||||
|
||||
<div fxFlex="50%">
|
||||
<div fxFlex="50%" *ngIf="accountingRule.description">
|
||||
{{ accountingRule.description }}
|
||||
</div>
|
||||
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
div {
|
||||
margin: 1rem 0;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
.header {
|
||||
font-weight: 500;
|
||||
&.header {
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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}` }
|
||||
|
||||
@@ -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<Object> {
|
||||
|
||||
constructor(private accountingService: AccountingService) {}
|
||||
|
||||
resolve(route: ActivatedRouteSnapshot): Observable<any> {
|
||||
const id = route.paramMap.get('id');
|
||||
|
||||
return this.accountingService.getAccountingRule(id);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,84 +1,107 @@
|
||||
<mat-card class="container">
|
||||
<div class="container">
|
||||
|
||||
<div fxLayout="row" fxLayout.lt-md="column">
|
||||
<mat-card>
|
||||
|
||||
<div fxFlex="50%">
|
||||
<div fxLayout="row" fxLayout.lt-md="column">
|
||||
|
||||
<mat-nav-list>
|
||||
<div fxFlex="50%">
|
||||
|
||||
<mat-list-item [routerLink]="['/accounting/frequent-postings']">
|
||||
<mat-icon matListIcon>repeat</mat-icon>
|
||||
<h4 matLine>Frequent Postings</h4>
|
||||
<p matLine>These are predefined postings</p>
|
||||
</mat-list-item>
|
||||
<mat-nav-list>
|
||||
|
||||
<mat-list-item [routerLink]="['/accounting/journal-entries/create']">
|
||||
<mat-icon matListIcon>add</mat-icon>
|
||||
<h4 matLine>Create Journal Entries</h4>
|
||||
<p matLine>Manual journal entry transactions recorded in a journal</p>
|
||||
</mat-list-item>
|
||||
<mat-list-item [routerLink]="['journal-entries/frequent-postings']">
|
||||
<mat-icon matListIcon>
|
||||
<fa-icon icon="sync" size="sm"></fa-icon>
|
||||
</mat-icon>
|
||||
<h4 matLine>Frequent Postings</h4>
|
||||
<p matLine>These are predefined postings</p>
|
||||
</mat-list-item>
|
||||
|
||||
<mat-list-item [routerLink]="['/accounting/journal-entries']">
|
||||
<mat-icon matListIcon>search</mat-icon>
|
||||
<h4 matLine>Search Journal Entries</h4>
|
||||
<p matLine>Advanced search option for journal entries</p>
|
||||
</mat-list-item>
|
||||
<mat-list-item [routerLink]="['journal-entries/create']">
|
||||
<mat-icon matListIcon>
|
||||
<fa-icon icon="plus" size="sm"></fa-icon>
|
||||
</mat-icon>
|
||||
<h4 matLine>Create Journal Entries</h4>
|
||||
<p matLine>Manual journal entry transactions recorded in a journal</p>
|
||||
</mat-list-item>
|
||||
|
||||
<mat-list-item [routerLink]="['/accounting/financial-activity-mappings']">
|
||||
<mat-icon matListIcon>link</mat-icon>
|
||||
<h4 matLine>Accounts Linked to Financial Activities</h4>
|
||||
<p matLine>List of Financial Activities and GL Account Mappings</p>
|
||||
</mat-list-item>
|
||||
<mat-list-item [routerLink]="['journal-entries']">
|
||||
<mat-icon matListIcon>
|
||||
<fa-icon icon="search" size="sm"></fa-icon>
|
||||
</mat-icon>
|
||||
<h4 matLine>Search Journal Entries</h4>
|
||||
<p matLine>Advanced search option for journal entries</p>
|
||||
</mat-list-item>
|
||||
|
||||
<mat-list-item [routerLink]="['/accounting/migrate-opening-balances']">
|
||||
<mat-icon matListIcon>monetization_on</mat-icon>
|
||||
<h4 matLine>Migrate Opening Balances (Office-wise)</h4>
|
||||
<p matLine>Set or update office-level opening balances for GL accounts</p>
|
||||
</mat-list-item>
|
||||
<mat-list-item [routerLink]="['financial-activity-mappings']">
|
||||
<mat-icon matListIcon>
|
||||
<fa-icon icon="link" size="sm"></fa-icon>
|
||||
</mat-icon>
|
||||
<h4 matLine>Accounts Linked to Financial Activities</h4>
|
||||
<p matLine>List of Financial Activities and GL Account Mappings</p>
|
||||
</mat-list-item>
|
||||
|
||||
</mat-nav-list>
|
||||
<mat-list-item [routerLink]="['migrate-opening-balances']">
|
||||
<mat-icon matListIcon>
|
||||
<fa-icon icon="hand-holding-usd" size="sm"></fa-icon>
|
||||
</mat-icon>
|
||||
<h4 matLine>Migrate Opening Balances (Office-wise)</h4>
|
||||
<p matLine>Set or update office-level opening balances for GL accounts</p>
|
||||
</mat-list-item>
|
||||
|
||||
</mat-nav-list>
|
||||
|
||||
</div>
|
||||
|
||||
<div fxFlex="50%">
|
||||
|
||||
<mat-nav-list>
|
||||
|
||||
<mat-list-item [routerLink]="['chart-of-accounts']">
|
||||
<mat-icon matListIcon>
|
||||
<fa-icon icon="sitemap" size="sm"></fa-icon>
|
||||
</mat-icon>
|
||||
<h4 matLine>Chart of Accounts</h4>
|
||||
<p matLine>List of accounts used by the organisation</p>
|
||||
</mat-list-item>
|
||||
|
||||
<mat-list-item [routerLink]="['closing-entries']">
|
||||
<mat-icon matListIcon>
|
||||
<fa-icon icon="archive" size="sm"></fa-icon>
|
||||
</mat-icon>
|
||||
<h4 matLine>Closing Entries</h4>
|
||||
<p matLine>Journal entries made at the end of accounting period</p>
|
||||
</mat-list-item>
|
||||
|
||||
<mat-list-item [routerLink]="['accounting-rules']">
|
||||
<mat-icon matListIcon>
|
||||
<fa-icon icon="list" size="sm"></fa-icon>
|
||||
</mat-icon>
|
||||
<h4 matLine>Accounting Rules</h4>
|
||||
<p matLine>List of all accounting rules</p>
|
||||
</mat-list-item>
|
||||
|
||||
<mat-list-item [routerLink]="['periodic-accruals']">
|
||||
<mat-icon matListIcon>
|
||||
<fa-icon icon="calendar" size="sm"></fa-icon>
|
||||
</mat-icon>
|
||||
<h4 matLine>Accruals</h4>
|
||||
<p matLine>Accrues income, expenses and liabilities as on the provided date</p>
|
||||
</mat-list-item>
|
||||
|
||||
<mat-list-item [routerLink]="['provisioning-entries']">
|
||||
<mat-icon matListIcon>
|
||||
<fa-icon icon="cogs" size="sm"></fa-icon>
|
||||
</mat-icon>
|
||||
<h4 matLine>Provisioning Entries</h4>
|
||||
<p matLine>Create provisioning entries</p>
|
||||
</mat-list-item>
|
||||
|
||||
</mat-nav-list>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div fxFlex="50%">
|
||||
|
||||
<mat-nav-list>
|
||||
|
||||
<mat-list-item [routerLink]="['/accounting/chart-of-accounts']">
|
||||
<mat-icon matListIcon>device_hub</mat-icon>
|
||||
<h4 matLine>Chart of Accounts</h4>
|
||||
<p matLine>List of accounts used by the organisation</p>
|
||||
</mat-list-item>
|
||||
|
||||
<mat-list-item [routerLink]="['/accounting/closing-entries']">
|
||||
<mat-icon matListIcon>archive</mat-icon>
|
||||
<h4 matLine>Closing Entries</h4>
|
||||
<p matLine>Journal entries made at the end of accounting period</p>
|
||||
</mat-list-item>
|
||||
|
||||
<mat-list-item [routerLink]="['/accounting/accounting-rules']">
|
||||
<mat-icon matListIcon>list</mat-icon>
|
||||
<h4 matLine>Accounting Rules</h4>
|
||||
<p matLine>List of all accounting rules</p>
|
||||
</mat-list-item>
|
||||
|
||||
<mat-list-item [routerLink]="['/accounting/periodic-accruals']">
|
||||
<mat-icon matListIcon>calendar_today</mat-icon>
|
||||
<h4 matLine>Accruals</h4>
|
||||
<p matLine>Accrues income, expenses and liabilities as on the provided date</p>
|
||||
</mat-list-item>
|
||||
|
||||
<mat-list-item [routerLink]="['/accounting/provisioning-entries']">
|
||||
<mat-icon matListIcon>settings_backup_restore</mat-icon>
|
||||
<h4 matLine>Provisioning Entries</h4>
|
||||
<p matLine>Create provisioning entries</p>
|
||||
</mat-list-item>
|
||||
|
||||
</mat-nav-list>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</mat-card>
|
||||
</mat-card>
|
||||
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
/** Angular Imports */
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
|
||||
/**
|
||||
* Accounting component.
|
||||
*/
|
||||
@Component({
|
||||
selector: 'mifosx-accounting',
|
||||
templateUrl: './accounting.component.html',
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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<any>} Offices data ordered by id.
|
||||
*/
|
||||
getOffices(): Observable<any> {
|
||||
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<any> {
|
||||
/**
|
||||
* @param {boolean} useHttpParams True if http params should be used.
|
||||
* @returns {Observable<any>} Accounting rules data.
|
||||
*/
|
||||
getAccountingRules(useHttpParams: boolean = false): Observable<any> {
|
||||
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<any>} Currencies data.
|
||||
*/
|
||||
getCurrencies(): Observable<any> {
|
||||
return this.http.get('/currencies');
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {Observable<any>} Payment types data.
|
||||
*/
|
||||
getPaymentTypes(): Observable<any> {
|
||||
return this.http.get('/paymenttypes');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {any} journalEntry Journal entry to be created.
|
||||
* @returns {Observable<any>}
|
||||
*/
|
||||
createJournalEntry(journalEntry: any): Observable<any> {
|
||||
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<any>} GL Accounts.
|
||||
*/
|
||||
getGlAccounts(): Observable<any> {
|
||||
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<any>} Journal Entries.
|
||||
*/
|
||||
getJournalEntry(transactionId: string): Observable<any> {
|
||||
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<any>} Journal Entries.
|
||||
*/
|
||||
getJournalEntries(filterBy: any, orderBy: string, sortOrder: string, offset: number, limit: number): Observable<any> {
|
||||
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<any>}
|
||||
*/
|
||||
revertTransaction(transactionId: string, comments: string): Observable<any> {
|
||||
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<any>} Financial activity accounts.
|
||||
*/
|
||||
getFinancialActivityAccounts(): Observable<any> {
|
||||
return this.http.get('/financialactivityaccounts');
|
||||
}
|
||||
|
||||
getFinancialActivityAccountsTemplate() {
|
||||
/**
|
||||
* @returns {Observable<any>} Financial activity accounts template.
|
||||
*/
|
||||
getFinancialActivityAccountsTemplate(): Observable<any> {
|
||||
return this.http.get('/financialactivityaccounts/template');
|
||||
}
|
||||
|
||||
createFinancialActivityAccount(financialActivityAccount: any) {
|
||||
/**
|
||||
* @param {any} financialActivityAccount Financial activity account to be created.
|
||||
* @returns {Observable<any>}
|
||||
*/
|
||||
createFinancialActivityAccount(financialActivityAccount: any): Observable<any> {
|
||||
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<any>} Financial activity account.
|
||||
*/
|
||||
getFinancialActivityAccount(financialActivityAccountId: string, template: boolean = false): Observable<any> {
|
||||
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<any>}
|
||||
*/
|
||||
updateFinancialActivityAccount(financialActivityAccountId: string, financialActivityAccount: any): Observable<any> {
|
||||
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<any>}
|
||||
*/
|
||||
deleteFinancialActivityAccount(financialActivityAccountId: string): Observable<any> {
|
||||
return this.http.delete(`/financialactivityaccounts/${financialActivityAccountId}`);
|
||||
}
|
||||
|
||||
getChartOfAccounts() {
|
||||
/**
|
||||
* @param {string} officeId Office ID to retrive opening balances accounts for.
|
||||
* @returns {Observable<any>}
|
||||
*/
|
||||
retrieveOpeningBalances(officeId: string): Observable<any> {
|
||||
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<any>}
|
||||
*/
|
||||
defineOpeningBalances(openingBalances: any): Observable<any> {
|
||||
const httpParams = new HttpParams().set('command', 'defineOpeningBalance');
|
||||
return this.http.post(`/journalentries`, openingBalances, { params: httpParams });
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {Observable<any>} GL accounts.
|
||||
*/
|
||||
getChartOfAccounts(): Observable<any> {
|
||||
return this.http.get('/glaccounts');
|
||||
}
|
||||
|
||||
getChartOfAccountsTemplate() {
|
||||
/**
|
||||
* @returns {Observable<any>} GL accounts template.
|
||||
*/
|
||||
getChartOfAccountsTemplate(): Observable<any> {
|
||||
return this.http.get('/glaccounts/template');
|
||||
}
|
||||
|
||||
createGlAccount(glAccount: any) {
|
||||
/**
|
||||
* @param {any} glAccount GL account to be created.
|
||||
* @returns {Observable<any>}
|
||||
*/
|
||||
createGlAccount(glAccount: any): Observable<any> {
|
||||
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<any>} GL account.
|
||||
*/
|
||||
getGlAccount(glAccountId: string, template: boolean = false): Observable<any> {
|
||||
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<any>}
|
||||
*/
|
||||
updateGlAccount(glAccountId: string, glAccount: any): Observable<any> {
|
||||
return this.http.put(`/glaccounts/${glAccountId}`, glAccount);
|
||||
}
|
||||
|
||||
deleteGlAccount(glAccountId: number) {
|
||||
/**
|
||||
* @param glAccountId GL account ID of gl account to be deleted.
|
||||
* @returns {Observable<any>}
|
||||
*/
|
||||
deleteGlAccount(glAccountId: string): Observable<any> {
|
||||
return this.http.delete(`/glaccounts/${glAccountId}`);
|
||||
}
|
||||
|
||||
getAccountingClosures() {
|
||||
/**
|
||||
* @returns {Observable<any>} GL account closures.
|
||||
*/
|
||||
getAccountingClosures(): Observable<any> {
|
||||
return this.http.get('/glclosures');
|
||||
}
|
||||
|
||||
createAccountingClosure(accountingClosure: any) {
|
||||
/**
|
||||
* @param {any} accountingClosure GL closure to be created.
|
||||
* @returns {Observable<any>}
|
||||
*/
|
||||
createAccountingClosure(accountingClosure: any): Observable<any> {
|
||||
return this.http.post('/glclosures', accountingClosure);
|
||||
}
|
||||
|
||||
getAccountingClosure(accountingClosureId: number) {
|
||||
/**
|
||||
* @param {string} accountingClosureId Accounting closure ID of gl closure.
|
||||
* @returns {Observable<any>} Accounting closure.
|
||||
*/
|
||||
getAccountingClosure(accountingClosureId: string): Observable<any> {
|
||||
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<any>}
|
||||
*/
|
||||
updateAccountingClosure(accountingClosureId: string, accountingClosure: any): Observable<any> {
|
||||
return this.http.put(`/glclosures/${accountingClosureId}`, accountingClosure);
|
||||
}
|
||||
|
||||
getAccountingRulesTemplate() {
|
||||
/**
|
||||
* @param {string} accountingClosureId Accounting closure ID of gl closure to be deleted.
|
||||
* @returns {Observable<any>}
|
||||
*/
|
||||
deleteAccountingClosure(accountingClosureId: string): Observable<any> {
|
||||
return this.http.delete(`/glclosures/${accountingClosureId}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {Observable<any>} Accounting rules template.
|
||||
*/
|
||||
getAccountingRulesTemplate(): Observable<any> {
|
||||
return this.http.get('/accountingrules/template');
|
||||
}
|
||||
|
||||
createAccountingRule(accountingRule: any) {
|
||||
/**
|
||||
* @param {any} accountingRule Accounting rule to be created.
|
||||
* @returns {Observable<any>}
|
||||
*/
|
||||
createAccountingRule(accountingRule: any): Observable<any> {
|
||||
return this.http.post('/accountingrules', accountingRule);
|
||||
}
|
||||
|
||||
getAccountingRule(accountingRuleId: string) {
|
||||
/**
|
||||
* @param {string} accountingRuleId Accounting rule ID of accounting rule.
|
||||
* @returns {Observable<any>} Accounting rule.
|
||||
*/
|
||||
getAccountingRule(accountingRuleId: string): Observable<any> {
|
||||
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<any>}
|
||||
*/
|
||||
updateAccountingRule(accoutingRuleId: string, accountingRule: any): Observable<any> {
|
||||
return this.http.put(`/accountingrules/${accoutingRuleId}`, accountingRule);
|
||||
}
|
||||
|
||||
executePeriodicAccruals(periodicAccruals: any) {
|
||||
/**
|
||||
* @param {string} accountingRuleId Accounting rule ID of accounting rule to be deleted.
|
||||
* @returns {Observable<any>}
|
||||
*/
|
||||
deleteAccountingRule(accountingRuleId: string): Observable<any> {
|
||||
return this.http.delete(`/accountingrules/${accountingRuleId}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {any} periodicAccruals Accruals to be executed.
|
||||
* @returns {Observable<any>}
|
||||
*/
|
||||
executePeriodicAccruals(periodicAccruals: any): Observable<any> {
|
||||
return this.http.post('/runaccruals', periodicAccruals);
|
||||
}
|
||||
|
||||
getProvisioningEntries() {
|
||||
/**
|
||||
* @returns {Observable<any>} Provisioning entries.
|
||||
*/
|
||||
getProvisioningEntries(): Observable<any> {
|
||||
return this.http.get('/provisioningentries');
|
||||
}
|
||||
|
||||
createProvisioningEntry(provisioningEntry: any) {
|
||||
/**
|
||||
* @param {any} provisioningEntry Provisioning entry to be created.
|
||||
* @returns {Observable<any>}
|
||||
*/
|
||||
createProvisioningEntry(provisioningEntry: any): Observable<any> {
|
||||
return this.http.post('/provisioningentries', provisioningEntry);
|
||||
}
|
||||
|
||||
getProvisioningEntry(provisioningEntryId: string) {
|
||||
/**
|
||||
* @param {string} provisioningEntryId Provisioning entry ID of provisioning entry.
|
||||
* @returns {Observable<any>} Provisioning entry.
|
||||
*/
|
||||
getProvisioningEntry(provisioningEntryId: string): Observable<any> {
|
||||
return this.http.get(`/provisioningentries/${provisioningEntryId}`);
|
||||
}
|
||||
|
||||
getLoanProducts() {
|
||||
/**
|
||||
* @returns {Observable<any>} Loan products data.
|
||||
*/
|
||||
getLoanProducts(): Observable<any> {
|
||||
return this.http.get('/loanproducts');
|
||||
}
|
||||
|
||||
getProvisioningCategories() {
|
||||
/**
|
||||
* @returns {Observable<any>} Provisioning categories data.
|
||||
*/
|
||||
getProvisioningCategories(): Observable<any> {
|
||||
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<any>} Provisioning entry entries.
|
||||
*/
|
||||
getProvisioningEntryEntries(provisioningEntryId: string): Observable<any> {
|
||||
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<any>} Provisioning journal entries.
|
||||
*/
|
||||
getProvisioningJournalEntries(provisioningEntryId: string): Observable<any> {
|
||||
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<any>}
|
||||
*/
|
||||
createProvisioningJournalEntries(provisioningEntryId: string): Observable<any> {
|
||||
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<any>}
|
||||
*/
|
||||
recreateProvisioningEntries(provisioningEntryId: string): Observable<any> {
|
||||
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 });
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,25 +1,27 @@
|
||||
<div class="container m-b-20" fxLayout="row" fxLayoutAlign="end" fxLayoutGap="20px">
|
||||
<mat-button-toggle-group [formControl]="viewGroup">
|
||||
<mat-button-toggle value="listView">
|
||||
<mat-icon>list</mat-icon>
|
||||
<fa-icon icon="list"></fa-icon>
|
||||
</mat-button-toggle>
|
||||
<mat-button-toggle value="treeView">
|
||||
<mat-icon>device_hub</mat-icon>
|
||||
<fa-icon icon="sitemap"></fa-icon>
|
||||
</mat-button-toggle>
|
||||
</mat-button-toggle-group>
|
||||
<button mat-raised-button color="primary" [routerLink]="['/accounting/chart-of-accounts/gl-accounts/create']">
|
||||
<mat-icon>add</mat-icon> Add Account
|
||||
<button mat-raised-button color="primary" [routerLink]="['gl-accounts/create']">
|
||||
<fa-icon icon="plus"></fa-icon>
|
||||
Add Account
|
||||
</button>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- List View -->
|
||||
|
||||
<div class="container" *ngIf="viewGroup.value === 'listView'">
|
||||
<div class="container" [hidden]="viewGroup.value !== 'listView'">
|
||||
|
||||
<div fxLayout="row" fxLayoutGap="20px">
|
||||
<mat-form-field fxFlex>
|
||||
<input matInput (keyup)="applyFilter($event.target.value)" placeholder="Filter">
|
||||
<mat-label>Filter</mat-label>
|
||||
<input matInput (keyup)="applyFilter($event.target.value)">
|
||||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
@@ -46,7 +48,7 @@
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header> Disabled </th>
|
||||
<td mat-cell *matCellDef="let glAccount">
|
||||
<div [className]="glAccount.disabled === true ? 'disabled' : 'enabled'">
|
||||
<mat-icon matTooltip="{{ glAccount.disabled === true ? 'Disabled' : 'Enabled' }}" matTooltipPosition="right">lens</mat-icon>
|
||||
<fa-icon matTooltip="{{ glAccount.disabled === true ? 'Disabled' : 'Enabled' }}" matTooltipPosition="right" icon="circle" size="lg"></fa-icon>
|
||||
</div>
|
||||
</td>
|
||||
</ng-container>
|
||||
@@ -54,8 +56,8 @@
|
||||
<ng-container matColumnDef="manualEntriesAllowed">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header> Manual Entries Allowed </th>
|
||||
<td mat-cell *matCellDef="let glAccount">
|
||||
<mat-icon *ngIf="glAccount.manualEntriesAllowed" class="manual-entries-allowed" matTooltip="Allowed" matTooltipPosition="right">check_circle</mat-icon>
|
||||
<mat-icon *ngIf="!glAccount.manualEntriesAllowed" class="manual-entries-not-allowed" matTooltip="Not Allowed" matTooltipPosition="right">cancel</mat-icon>
|
||||
<fa-icon *ngIf="glAccount.manualEntriesAllowed" class="manual-entries-allowed" matTooltip="Allowed" matTooltipPosition="right" icon="check-circle" size="lg"></fa-icon>
|
||||
<fa-icon *ngIf="!glAccount.manualEntriesAllowed" class="manual-entries-not-allowed" matTooltip="Not Allowed" matTooltipPosition="right" icon="times-circle" size="lg"></fa-icon>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
@@ -65,7 +67,7 @@
|
||||
</ng-container>
|
||||
|
||||
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: displayedColumns;" [routerLink]="['/accounting/chart-of-accounts/gl-accounts/view', row.id]"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: displayedColumns;" [routerLink]="['gl-accounts/view', row.id]" class="select-row"></tr>
|
||||
|
||||
</table>
|
||||
|
||||
@@ -78,7 +80,7 @@
|
||||
|
||||
<!-- Tree View -->
|
||||
|
||||
<div class="container" *ngIf="viewGroup.value === 'treeView'">
|
||||
<div class="container" [hidden]="viewGroup.value !== 'treeView'">
|
||||
|
||||
<div class="m-b-20" fxLayout="row" fxLayoutAlign="end" fxLayoutGap="20px">
|
||||
<button mat-raised-button (click)="nestedTreeControl.expandAll()"> <!-- Bug(angular-material): https://github.com/angular/material2/issues/12170 -->
|
||||
@@ -173,9 +175,7 @@
|
||||
<li>
|
||||
<div class="mat-tree-node">
|
||||
<button mat-icon-button matTreeNodeToggle [attr.aria-label]="'toggle ' + node.name">
|
||||
<mat-icon class="mat-icon-rtl-mirror">
|
||||
{{ nestedTreeControl.isExpanded(node) ? 'expand_more' : 'chevron_right' }}
|
||||
</mat-icon>
|
||||
<fa-icon class="mat-icon-rtl-mirror" icon="{{ nestedTreeControl.isExpanded(node) ? 'chevron-down' : 'chevron-right' }}"></fa-icon>
|
||||
</button>
|
||||
<span (click)="viewGLAccountNode(node)">
|
||||
<span *ngIf="node.glCode">{{ '(' + node.glCode + ')' }}</span>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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<any>;
|
||||
/** Nested tree control for chart of accounts tree. */
|
||||
nestedTreeControl: NestedTreeControl<GLAccountNode>;
|
||||
/** Nested tree data source for chart of accounts tree. */
|
||||
nestedTreeDataSource: MatTreeNestedDataSource<GLAccountNode>;
|
||||
/** 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<GLAccountNode>(this._getChildren);
|
||||
this.nestedTreeDataSource = new MatTreeNestedDataSource();
|
||||
this.nestedTreeDataSource = new MatTreeNestedDataSource<GLAccountNode>();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
}
|
||||
|
||||
@@ -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<Object> {
|
||||
|
||||
/**
|
||||
* @param {AccountingService} accountingService Accounting service.
|
||||
*/
|
||||
constructor(private accountingService: AccountingService) {}
|
||||
|
||||
/**
|
||||
* Returns the chart of accounts data.
|
||||
* @returns {Observable<any>}
|
||||
*/
|
||||
resolve(): Observable<any> {
|
||||
return this.accountingService.getChartOfAccounts();
|
||||
}
|
||||
|
||||
+30
@@ -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<Object> {
|
||||
|
||||
/**
|
||||
* @param {AccountingService} accountingService Accounting service.
|
||||
*/
|
||||
constructor(private accountingService: AccountingService) {}
|
||||
|
||||
/**
|
||||
* Returns the chart of accounts template data.
|
||||
* @returns {Observable<any>}
|
||||
*/
|
||||
resolve(): Observable<any> {
|
||||
return this.accountingService.getChartOfAccountsTemplate();
|
||||
}
|
||||
|
||||
}
|
||||
+29
-10
@@ -9,31 +9,48 @@
|
||||
<div fxLayout="row wrap" fxLayoutGap="2%" fxLayout.lt-md="column">
|
||||
|
||||
<mat-form-field fxFlex="48%">
|
||||
<mat-select placeholder="Account Type" required formControlName="type">
|
||||
<mat-label>Account Type</mat-label>
|
||||
<mat-select required formControlName="type">
|
||||
<mat-option *ngFor="let accountType of accountTypeData" [value]="accountType.id">
|
||||
{{ accountType.value }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
<mat-error *ngIf="glAccountForm.controls.type.hasError('required')">
|
||||
Account Type is <strong>required</strong>
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field fxFlex="48%">
|
||||
<input matInput placeholder="Account Name" required formControlName="name">
|
||||
<mat-label>Account Name</mat-label>
|
||||
<input matInput required formControlName="name">
|
||||
<mat-error *ngIf="glAccountForm.controls.name.hasError('required')">
|
||||
Account Name is <strong>required</strong>
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field fxFlex="48%">
|
||||
<mat-select placeholder="Account Usage" required formControlName="usage">
|
||||
<mat-label>Account Usage</mat-label>
|
||||
<mat-select required formControlName="usage">
|
||||
<mat-option *ngFor="let accountUsage of accountUsageData" [value]="accountUsage.id">
|
||||
{{ accountUsage.value }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
<mat-error *ngIf="glAccountForm.controls.usage.hasError('required')">
|
||||
Account Usage is <strong>required</strong>
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field fxFlex="48%">
|
||||
<input matInput placeholder="GL Code" required formControlName="glCode">
|
||||
<mat-label>GL Code</mat-label>
|
||||
<input matInput required formControlName="glCode">
|
||||
<mat-error *ngIf="glAccountForm.controls.glCode.hasError('required')">
|
||||
GL Code is <strong>required</strong>
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field fxFlex="48%">
|
||||
<mat-select placeholder="Parent" formControlName="parentId">
|
||||
<mat-label>Parent</mat-label>
|
||||
<mat-select formControlName="parentId">
|
||||
<mat-option *ngFor="let parent of parentData" [value]="parent.id">
|
||||
{{ parent.name }}
|
||||
</mat-option>
|
||||
@@ -41,21 +58,23 @@
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field fxFlex="28%">
|
||||
<mat-select placeholder="Tag" formControlName="tagId">
|
||||
<mat-label>Tag</mat-label>
|
||||
<mat-select formControlName="tagId">
|
||||
<mat-option *ngFor="let tag of tagData" [value]="tag.id">
|
||||
{{ tag.name }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
|
||||
<div fxFlex="18%" class="manualEntriesAllowedWrapper">
|
||||
<mat-checkbox labelPosition="before" required formControlName="manualEntriesAllowed" class="manualEntriesAllowed">
|
||||
<div fxFlex="18%" class="manual-entries-allowed-wrapper">
|
||||
<mat-checkbox labelPosition="before" required formControlName="manualEntriesAllowed" class="manual-entries-allowed">
|
||||
Manual Entries Allowed
|
||||
</mat-checkbox>
|
||||
</div>
|
||||
|
||||
<mat-form-field fxFlex="98%">
|
||||
<textarea matInput placeholder="Description" formControlName="description"></textarea>
|
||||
<mat-label>Description</mat-label>
|
||||
<textarea matInput formControlName="description"></textarea>
|
||||
</mat-form-field>
|
||||
|
||||
</div>
|
||||
@@ -64,7 +83,7 @@
|
||||
|
||||
<mat-card-actions fxLayout="row" fxLayout.xs="column" fxLayoutAlign="center" fxLayoutGap="5px">
|
||||
<button type="button" mat-raised-button [routerLink]="[cancelRoute]">Cancel</button>
|
||||
<button mat-raised-button color="primary">Submit</button>
|
||||
<button mat-raised-button color="primary" [disabled]="!glAccountForm.valid">Submit</button>
|
||||
</mat-card-actions>
|
||||
|
||||
</form>
|
||||
|
||||
+2
-2
@@ -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;
|
||||
|
||||
+47
-17
@@ -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 });
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
+30
-11
@@ -9,31 +9,48 @@
|
||||
<div fxLayout="row wrap" fxLayoutGap="2%" fxLayout.lt-md="column">
|
||||
|
||||
<mat-form-field fxFlex="48%">
|
||||
<mat-select placeholder="Account Type" required formControlName="type">
|
||||
<mat-label>Account Type</mat-label>
|
||||
<mat-select required formControlName="type">
|
||||
<mat-option *ngFor="let accountType of accountTypeData" [value]="accountType.id">
|
||||
{{ accountType.value }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
<mat-error *ngIf="glAccountForm.controls.type.hasError('required')">
|
||||
Account Type is <strong>required</strong>
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field fxFlex="48%">
|
||||
<input matInput placeholder="Account Name" required formControlName="name">
|
||||
<mat-label>Account Name</mat-label>
|
||||
<input matInput required formControlName="name">
|
||||
<mat-error *ngIf="glAccountForm.controls.name.hasError('required')">
|
||||
Account Name is <strong>required</strong>
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field fxFlex="48%">
|
||||
<mat-select placeholder="Account Usage" required formControlName="usage">
|
||||
<mat-label>Account Usage</mat-label>
|
||||
<mat-select required formControlName="usage">
|
||||
<mat-option *ngFor="let accountUsage of accountUsageData" [value]="accountUsage.id">
|
||||
{{ accountUsage.value }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
<mat-error *ngIf="glAccountForm.controls.usage.hasError('required')">
|
||||
Account Usage is <strong>required</strong>
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field fxFlex="48%">
|
||||
<input matInput placeholder="GL Code" required formControlName="glCode">
|
||||
<mat-label>GL Code</mat-label>
|
||||
<input matInput required formControlName="glCode">
|
||||
<mat-error *ngIf="glAccountForm.controls.glCode.hasError('required')">
|
||||
GL Code is <strong>required</strong>
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field fxFlex="48%">
|
||||
<mat-select placeholder="Parent" formControlName="parentId">
|
||||
<mat-label>Parent</mat-label>
|
||||
<mat-select formControlName="parentId">
|
||||
<mat-option *ngFor="let parent of parentData" [value]="parent.id">
|
||||
{{ parent.name }}
|
||||
</mat-option>
|
||||
@@ -41,21 +58,23 @@
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field fxFlex="28%">
|
||||
<mat-select placeholder="Tag" formControlName="tagId">
|
||||
<mat-label>Tag</mat-label>
|
||||
<mat-select formControlName="tagId">
|
||||
<mat-option *ngFor="let tag of tagData" [value]="tag.id">
|
||||
{{ tag.name }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
|
||||
<div fxFlex="18%" class="manualEntriesAllowedWrapper">
|
||||
<mat-checkbox labelPosition="before" required formControlName="manualEntriesAllowed" class="manualEntriesAllowed">
|
||||
<div fxFlex="18%" class="manual-entries-allowed-wrapper">
|
||||
<mat-checkbox labelPosition="before" required formControlName="manualEntriesAllowed" class="manual-entries-allowed">
|
||||
Manual Entries Allowed
|
||||
</mat-checkbox>
|
||||
</div>
|
||||
|
||||
<mat-form-field fxFlex="98%">
|
||||
<textarea matInput placeholder="Description" formControlName="description"></textarea>
|
||||
<mat-label>Description</mat-label>
|
||||
<textarea matInput formControlName="description"></textarea>
|
||||
</mat-form-field>
|
||||
|
||||
</div>
|
||||
@@ -63,8 +82,8 @@
|
||||
</mat-card-content>
|
||||
|
||||
<mat-card-actions fxLayout="row" fxLayout.xs="column" fxLayoutAlign="center" fxLayoutGap="5px">
|
||||
<button type="button" mat-raised-button [routerLink]="['/accounting/chart-of-accounts']">Cancel</button>
|
||||
<button mat-raised-button color="primary">Submit</button>
|
||||
<button type="button" mat-raised-button [routerLink]="['../']">Cancel</button>
|
||||
<button mat-raised-button color="primary" [disabled]="glAccountForm.pristine">Submit</button>
|
||||
</mat-card-actions>
|
||||
|
||||
</form>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 });
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
+53
@@ -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<Object> {
|
||||
|
||||
/**
|
||||
* @param {AccountingService} accountingService Accounting service.
|
||||
*/
|
||||
constructor(private accountingService: AccountingService) {}
|
||||
|
||||
/**
|
||||
* Returns the gl account and chart of accounts template data.
|
||||
* @returns {Observable<any>}
|
||||
*/
|
||||
resolve(route: ActivatedRouteSnapshot): Observable<any> {
|
||||
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;
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 = [];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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<GLAccountNode[]>([]);
|
||||
|
||||
/**
|
||||
* 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') {
|
||||
|
||||
+19
-14
@@ -1,18 +1,23 @@
|
||||
<div fxLayout="row" fxLayoutAlign="end" fxLayoutGap="2%" fxLayout.lt-md="column" class="container m-b-20">
|
||||
<button mat-raised-button color="primary" *ngIf="glAccount.usage.value === 'HEADER'" [routerLink]="['/accounting/chart-of-accounts/gl-accounts/create']" [queryParams]="{ parent: glAccount.id, accountType: glAccount.type.id }">
|
||||
<mat-icon>add</mat-icon> Subledger Account
|
||||
<fa-icon icon="plus"></fa-icon>
|
||||
Subledger Account
|
||||
</button>
|
||||
<button mat-raised-button color="primary" [routerLink]="['/accounting/chart-of-accounts/gl-accounts/view', glAccount.id, 'edit']">
|
||||
<mat-icon>edit</mat-icon> Edit
|
||||
<button mat-raised-button color="primary" [routerLink]="['edit']">
|
||||
<fa-icon icon="edit"></fa-icon>
|
||||
Edit
|
||||
</button>
|
||||
<button mat-raised-button color="warn" *ngIf="!glAccount.disabled" (click)="changeGlAccountState()">
|
||||
<mat-icon>lock</mat-icon> Disable
|
||||
<fa-icon icon="lock"></fa-icon>
|
||||
Disable
|
||||
</button>
|
||||
<button mat-raised-button color="accent" *ngIf="glAccount.disabled" (click)="changeGlAccountState()">
|
||||
<mat-icon>lock_open</mat-icon> Enable
|
||||
<fa-icon icon="lock-open"></fa-icon>
|
||||
Enable
|
||||
</button>
|
||||
<button mat-raised-button color="warn" (click)="deleteGlAccount()">
|
||||
<mat-icon>delete</mat-icon> Delete
|
||||
<fa-icon icon="trash"></fa-icon>
|
||||
Delete
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -29,7 +34,7 @@
|
||||
</div>
|
||||
|
||||
<div fxFlex="50%">
|
||||
{{ glAccount.type.value }}
|
||||
{{ glAccount.type?.value }}
|
||||
</div>
|
||||
|
||||
<div fxFlex="50%" class="header">
|
||||
@@ -40,19 +45,19 @@
|
||||
{{ glAccount.glCode }}
|
||||
</div>
|
||||
|
||||
<div fxFlex="50%" class="header">
|
||||
<div fxFlex="50%" class="header" *ngIf="glAccount.parent">
|
||||
Parent Account Name
|
||||
</div>
|
||||
|
||||
<div fxFlex="50%">
|
||||
<div fxFlex="50%" *ngIf="glAccount.parent">
|
||||
{{ glAccount.parent.name }}
|
||||
</div>
|
||||
|
||||
<div fxFlex="50%" class="header">
|
||||
<div fxFlex="50%" class="header" *ngIf="glAccount.tagId.id">
|
||||
Tag
|
||||
</div>
|
||||
|
||||
<div fxFlex="50%">
|
||||
<div fxFlex="50%" *ngIf="glAccount.tagId.id">
|
||||
{{ glAccount.tagId.name }}
|
||||
</div>
|
||||
|
||||
@@ -61,7 +66,7 @@
|
||||
</div>
|
||||
|
||||
<div fxFlex="50%">
|
||||
{{ glAccount.usage.value }}
|
||||
{{ glAccount.usage?.value }}
|
||||
</div>
|
||||
|
||||
<div fxFlex="50%" class="header">
|
||||
@@ -72,11 +77,11 @@
|
||||
{{ glAccount.manualEntriesAllowed }}
|
||||
</div>
|
||||
|
||||
<div fxFlex="50%" class="header">
|
||||
<div fxFlex="50%" class="header" *ngIf="glAccount.description">
|
||||
Description
|
||||
</div>
|
||||
|
||||
<div fxFlex="50%">
|
||||
<div fxFlex="50%" *ngIf="glAccount.description">
|
||||
{{ glAccount.description }}
|
||||
</div>
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
@@ -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<Object> {
|
||||
|
||||
constructor(private accountingService: AccountingService) {}
|
||||
|
||||
resolve(route: ActivatedRouteSnapshot): Observable<any> {
|
||||
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;
|
||||
}));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,12 +1,14 @@
|
||||
<div class="container m-b-20" fxLayout="row" fxLayoutAlign="end" fxLayoutGap="20px">
|
||||
<button mat-raised-button color="primary" [routerLink]="['/accounting/closing-entries/create']">
|
||||
<mat-icon>add</mat-icon> Create Closure
|
||||
<button mat-raised-button color="primary" [routerLink]="['create']">
|
||||
<fa-icon icon="plus"></fa-icon>
|
||||
Create Closure
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="container" fxLayout="row wrap" fxLayoutGap="2%" fxLayout.lt-md="column">
|
||||
<div class="container" fxLayout="row">
|
||||
<mat-form-field fxFlex>
|
||||
<input matInput placeholder="Office Name" [formControl]="officeName" [matAutocomplete]="officeNameAutocomplete">
|
||||
<mat-label>Office Name</mat-label>
|
||||
<input matInput [formControl]="officeName" [matAutocomplete]="officeNameAutocomplete">
|
||||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
@@ -44,7 +46,7 @@
|
||||
</ng-container>
|
||||
|
||||
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: displayedColumns;" [routerLink]="['/accounting/closing-entries/view', row.id]"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: displayedColumns;" [routerLink]="['view', row.id]"></tr>
|
||||
|
||||
</table>
|
||||
|
||||
|
||||
@@ -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<any>;
|
||||
/** 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()));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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<Object> {
|
||||
|
||||
/**
|
||||
* @param {AccountingService} accountingService Accounting service.
|
||||
*/
|
||||
constructor(private accountingService: AccountingService) {}
|
||||
|
||||
/**
|
||||
* Returns the gl account closures data.
|
||||
* @returns {Observable<any>}
|
||||
*/
|
||||
resolve(): Observable<any> {
|
||||
return this.accountingService.getAccountingClosures();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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<Object> {
|
||||
|
||||
/**
|
||||
* @param {AccountingService} accountingService Accounting service.
|
||||
*/
|
||||
constructor(private accountingService: AccountingService) {}
|
||||
|
||||
/**
|
||||
* Returns the gl account closure data.
|
||||
* @returns {Observable<any>}
|
||||
*/
|
||||
resolve(route: ActivatedRouteSnapshot): Observable<any> {
|
||||
const glAccountClosureId = route.paramMap.get('id');
|
||||
return this.accountingService.getAccountingClosure(glAccountClosureId);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -9,21 +9,30 @@
|
||||
<div fxLayout="column">
|
||||
|
||||
<mat-form-field>
|
||||
<mat-select placeholder="Office" required formControlName="officeId">
|
||||
<mat-label>Office</mat-label>
|
||||
<mat-select required formControlName="officeId">
|
||||
<mat-option *ngFor="let office of officeData" [value]="office.id">
|
||||
{{ office.name }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
<mat-error *ngIf="accountingClosureForm.controls.officeId.hasError('required')">
|
||||
Office is <strong>required</strong>
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field>
|
||||
<input matInput [min]="minDate" [max]="maxDate" [matDatepicker]="closingDatePicker" placeholder="Closing Date" required formControlName="closingDate">
|
||||
<mat-label>Closing Date</mat-label>
|
||||
<input matInput [min]="minDate" [max]="maxDate" [matDatepicker]="closingDatePicker" required formControlName="closingDate">
|
||||
<mat-datepicker-toggle matSuffix [for]="closingDatePicker"></mat-datepicker-toggle>
|
||||
<mat-datepicker #closingDatePicker></mat-datepicker>
|
||||
<mat-error *ngIf="accountingClosureForm.controls.closingDate.hasError('required')">
|
||||
Closing Date is <strong>required</strong>
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field>
|
||||
<textarea matInput placeholder="Comments" formControlName="comments"></textarea>
|
||||
<mat-label>Comments</mat-label>
|
||||
<textarea matInput formControlName="comments"></textarea>
|
||||
</mat-form-field>
|
||||
|
||||
</div>
|
||||
@@ -31,8 +40,8 @@
|
||||
</mat-card-content>
|
||||
|
||||
<mat-card-actions fxLayout="row" fxLayout.xs="column" fxLayoutAlign="center" fxLayoutGap="5px">
|
||||
<button type="button" mat-raised-button [routerLink]="['/accounting/closing-entries']">Cancel</button>
|
||||
<button mat-raised-button color="primary">Submit</button>
|
||||
<button type="button" mat-raised-button [routerLink]="['../']">Cancel</button>
|
||||
<button mat-raised-button color="primary" [disabled]="!accountingClosureForm.valid">Submit</button>
|
||||
</mat-card-actions>
|
||||
|
||||
</form>
|
||||
|
||||
@@ -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 });
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,8 @@
|
||||
<div fxLayout="column">
|
||||
|
||||
<mat-form-field>
|
||||
<mat-select placeholder="Office" required formControlName="officeId">
|
||||
<mat-label>Office</mat-label>
|
||||
<mat-select required formControlName="officeId">
|
||||
<mat-option *ngFor="let office of officeData" [value]="office.id">
|
||||
{{ office.name }}
|
||||
</mat-option>
|
||||
@@ -17,13 +18,15 @@
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field>
|
||||
<input matInput [matDatepicker]="closingDatePicker" placeholder="Closing Date" required formControlName="closingDate">
|
||||
<mat-label>Closing Date</mat-label>
|
||||
<input matInput [matDatepicker]="closingDatePicker" required formControlName="closingDate">
|
||||
<mat-datepicker-toggle matSuffix [for]="closingDatePicker"></mat-datepicker-toggle>
|
||||
<mat-datepicker #closingDatePicker></mat-datepicker>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field>
|
||||
<textarea matInput placeholder="Comments" formControlName="comments"></textarea>
|
||||
<mat-label>Comments</mat-label>
|
||||
<textarea matInput formControlName="comments"></textarea>
|
||||
</mat-form-field>
|
||||
|
||||
</div>
|
||||
@@ -31,8 +34,8 @@
|
||||
</mat-card-content>
|
||||
|
||||
<mat-card-actions fxLayout="row" fxLayout.xs="column" fxLayoutAlign="center" fxLayoutGap="5px">
|
||||
<button type="button" mat-raised-button [routerLink]="['/accounting/closing-entries/view', accountingClosure?.id]">Cancel</button>
|
||||
<button mat-raised-button color="primary">Submit</button>
|
||||
<button type="button" mat-raised-button [routerLink]="['../']">Cancel</button>
|
||||
<button mat-raised-button color="primary" [disabled]="accountingClosureForm.pristine">Submit</button>
|
||||
</mat-card-actions>
|
||||
|
||||
</form>
|
||||
|
||||
@@ -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 });
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
<div fxLayout="row" fxLayoutAlign="end" fxLayoutGap="2%" fxLayout.lt-md="column" class="container m-b-20">
|
||||
<button mat-raised-button color="primary" [routerLink]="['/accounting/closing-entries/view', accountingClosure?.id, 'edit']">
|
||||
<mat-icon>edit</mat-icon> Edit
|
||||
<button mat-raised-button color="primary" [routerLink]="['edit']">
|
||||
<fa-icon icon="edit"></fa-icon>
|
||||
Edit
|
||||
</button>
|
||||
<button mat-raised-button color="warn" (click)="deleteAccountingClosure()">
|
||||
<mat-icon>delete</mat-icon> Delete
|
||||
<fa-icon icon="trash"></fa-icon>
|
||||
Delete
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="container" *ngIf="accountingClosure">
|
||||
<div class="container" *ngIf="glAccountClosure">
|
||||
|
||||
<mat-card>
|
||||
|
||||
@@ -20,7 +22,7 @@
|
||||
</div>
|
||||
|
||||
<div fxFlex="50%">
|
||||
{{ accountingClosure.officeName }}
|
||||
{{ glAccountClosure.officeName }}
|
||||
</div>
|
||||
|
||||
<div fxFlex="50%" class="header">
|
||||
@@ -28,7 +30,7 @@
|
||||
</div>
|
||||
|
||||
<div fxFlex="50%">
|
||||
{{ accountingClosure.closingDate }}
|
||||
{{ glAccountClosure.closingDate }}
|
||||
</div>
|
||||
|
||||
<div fxFlex="50%" class="header">
|
||||
@@ -36,7 +38,7 @@
|
||||
</div>
|
||||
|
||||
<div fxFlex="50%">
|
||||
{{ accountingClosure.createdByUsername }}
|
||||
{{ glAccountClosure.createdByUsername }}
|
||||
</div>
|
||||
|
||||
<div fxFlex="50%" class="header">
|
||||
@@ -44,7 +46,7 @@
|
||||
</div>
|
||||
|
||||
<div fxFlex="50%">
|
||||
{{ accountingClosure.lastUpdatedByUsername }}
|
||||
{{ glAccountClosure.lastUpdatedByUsername }}
|
||||
</div>
|
||||
|
||||
<div fxFlex="50%" class="header">
|
||||
@@ -52,7 +54,7 @@
|
||||
</div>
|
||||
|
||||
<div fxFlex="50%">
|
||||
{{ accountingClosure.lastUpdatedDate }}
|
||||
{{ glAccountClosure.lastUpdatedDate }}
|
||||
</div>
|
||||
|
||||
<div fxFlex="50%" class="header">
|
||||
@@ -60,7 +62,7 @@
|
||||
</div>
|
||||
|
||||
<div fxFlex="50%">
|
||||
{{ accountingClosure.createdDate }}
|
||||
{{ glAccountClosure.createdDate }}
|
||||
</div>
|
||||
|
||||
<div fxFlex="50%" class="header">
|
||||
@@ -68,7 +70,7 @@
|
||||
</div>
|
||||
|
||||
<div fxFlex="50%">
|
||||
{{ accountingClosure.comments }}
|
||||
{{ glAccountClosure.comments }}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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']);
|
||||
});
|
||||
|
||||
@@ -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<Object> {
|
||||
|
||||
/**
|
||||
* @param {AccountingService} accountingService Accounting service.
|
||||
*/
|
||||
constructor(private accountingService: AccountingService) {}
|
||||
|
||||
/**
|
||||
* Returns the accounting rules associations data.
|
||||
* @returns {Observable<any>}
|
||||
*/
|
||||
resolve(): Observable<any> {
|
||||
return this.accountingService.getAccountingRules(true);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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<Object> {
|
||||
|
||||
/**
|
||||
* @param {AccountingService} accountingService Accounting service.
|
||||
*/
|
||||
constructor(private accountingService: AccountingService) {}
|
||||
|
||||
/**
|
||||
* Returns the currencies data.
|
||||
* @returns {Observable<any>}
|
||||
*/
|
||||
resolve(): Observable<any> {
|
||||
return this.accountingService.getCurrencies();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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<Object> {
|
||||
|
||||
/**
|
||||
* @param {AccountingService} accountingService Accounting service.
|
||||
*/
|
||||
constructor(private accountingService: AccountingService) {}
|
||||
|
||||
/**
|
||||
* Returns the gl accounts data.
|
||||
* @returns {Observable<any>}
|
||||
*/
|
||||
resolve(): Observable<any> {
|
||||
return this.accountingService.getGlAccounts();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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<Object> {
|
||||
|
||||
/**
|
||||
* @param {AccountingService} accountingService Accounting service.
|
||||
*/
|
||||
constructor(private accountingService: AccountingService) {}
|
||||
|
||||
/**
|
||||
* Returns the loan products data.
|
||||
* @returns {Observable<any>}
|
||||
*/
|
||||
resolve(): Observable<any> {
|
||||
return this.accountingService.getLoanProducts();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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<Object> {
|
||||
|
||||
/**
|
||||
* @param {AccountingService} accountingService Accounting service.
|
||||
*/
|
||||
constructor(private accountingService: AccountingService) {}
|
||||
|
||||
/**
|
||||
* Returns the offices data.
|
||||
* @returns {Observable<any>}
|
||||
*/
|
||||
resolve(): Observable<any> {
|
||||
return this.accountingService.getOffices();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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<Object> {
|
||||
|
||||
/**
|
||||
* @param {AccountingService} accountingService Accounting service.
|
||||
*/
|
||||
constructor(private accountingService: AccountingService) {}
|
||||
|
||||
/**
|
||||
* Returns the payment types data.
|
||||
* @returns {Observable<any>}
|
||||
*/
|
||||
resolve(): Observable<any> {
|
||||
return this.accountingService.getPaymentTypes();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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<Object> {
|
||||
|
||||
/**
|
||||
* @param {AccountingService} accountingService Accounting service.
|
||||
*/
|
||||
constructor(private accountingService: AccountingService) {}
|
||||
|
||||
/**
|
||||
* Returns the Provisioning categories data.
|
||||
* @returns {Observable<any>}
|
||||
*/
|
||||
resolve(): Observable<any> {
|
||||
return this.accountingService.getProvisioningCategories();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,131 +1,178 @@
|
||||
<mat-card class="container">
|
||||
<div class="container">
|
||||
|
||||
<form [formGroup]="journalEntryForm" (ngSubmit)="submit()">
|
||||
<mat-card>
|
||||
|
||||
<mat-card-content>
|
||||
<form [formGroup]="journalEntryForm" (ngSubmit)="submit()">
|
||||
|
||||
<div fxLayout="row wrap" fxLayoutGap="2%" fxLayout.lt-md="column">
|
||||
<mat-card-content>
|
||||
|
||||
<mat-form-field fxFlex="48%">
|
||||
<mat-select placeholder="Office" required formControlName="officeId">
|
||||
<mat-option *ngFor="let office of officeData" [value]="office.id">
|
||||
{{ office.name }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
<div fxLayout="row wrap" fxLayoutGap="2%" fxLayout.lt-md="column">
|
||||
|
||||
<mat-form-field fxFlex="48%">
|
||||
<mat-select placeholder="Currency" required formControlName="currencyCode">
|
||||
<mat-option *ngFor="let currency of currencyData" [value]="currency.code">
|
||||
{{ currency.displayLabel }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
<mat-form-field fxFlex="48%">
|
||||
<mat-label>Office</mat-label>
|
||||
<mat-select required formControlName="officeId">
|
||||
<mat-option *ngFor="let office of officeData" [value]="office.id">
|
||||
{{ office.name }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
<mat-error *ngIf="journalEntryForm.controls.officeId.hasError('required')">
|
||||
Office is <strong>required</strong>
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
<div fxFlexFill fxLayout="row wrap" fxLayout.lt-md="column" formArrayName="debits"
|
||||
*ngFor="let debit of getFormArrayData('debits').controls; let i = index;">
|
||||
<mat-form-field fxFlex="48%">
|
||||
<mat-label>Currency</mat-label>
|
||||
<mat-select required formControlName="currencyCode">
|
||||
<mat-option *ngFor="let currency of currencyData" [value]="currency.code">
|
||||
{{ currency.displayLabel }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
<mat-error *ngIf="journalEntryForm.controls.currencyCode.hasError('required')">
|
||||
Currency is <strong>required</strong>
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
<div fxFlexFill fxLayout="row wrap" fxLayoutGap="2%" fxLayout.lt-md="column" [formGroupName]="i">
|
||||
<div fxFlexFill fxLayout="row wrap" fxLayout.lt-md="column" formArrayName="debits"
|
||||
*ngFor="let debit of debits.controls; let i = index;">
|
||||
|
||||
<mat-form-field fxFlex="48%">
|
||||
<mat-select placeholder="Affected GL Entry (Debit)" required formControlName="glAccountId">
|
||||
<mat-option *ngFor="let affectedGLEntryDebit of glAccountData" [value]="affectedGLEntryDebit.id">
|
||||
{{ affectedGLEntryDebit.name + ' (' + affectedGLEntryDebit.glCode + ')' }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
<div fxFlexFill fxLayout="row wrap" fxLayoutGap="2%" fxLayout.lt-md="column" [formGroupName]="i">
|
||||
|
||||
<mat-form-field fxFlex="43%">
|
||||
<input type="number" matInput placeholder="Debit Amount" required formControlName="amount">
|
||||
</mat-form-field>
|
||||
<mat-form-field fxFlex="48%">
|
||||
<mat-label>Affected GL Entry (Debit)</mat-label>
|
||||
<mat-select required formControlName="glAccountId">
|
||||
<mat-option *ngFor="let affectedGLEntryDebit of glAccountData" [value]="affectedGLEntryDebit.id">
|
||||
{{ affectedGLEntryDebit.name + ' (' + affectedGLEntryDebit.glCode + ')' }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
<mat-error *ngIf="debits.at(i).controls.glAccountId.hasError('required')">
|
||||
Affected GL Entry (Debit) is <strong>required</strong>
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
<span fxFlex>
|
||||
<button *ngIf="i !== 0" type="button" mat-icon-button (click)="removeAffectedGLEntry(journalEntryForm.get('debits'), i)"><mat-icon>remove_circle</mat-icon></button>
|
||||
<button *ngIf="i === 0" type="button" mat-icon-button color="primary" (click)="addAffectedGLEntry(journalEntryForm.get('debits'))"><mat-icon>add_circle</mat-icon></button>
|
||||
</span>
|
||||
<mat-form-field fxFlex="43%">
|
||||
<mat-label>Debit Amount</mat-label>
|
||||
<input type="number" matInput required formControlName="amount">
|
||||
<mat-error *ngIf="debits.at(i).controls.amount.hasError('required')">
|
||||
Debit Amount is <strong>required</strong>
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
<span fxFlex>
|
||||
<button *ngIf="i !== 0" type="button" mat-icon-button (click)="removeAffectedGLEntry(debits, i)">
|
||||
<fa-icon icon="minus-circle" size="lg"></fa-icon>
|
||||
</button>
|
||||
<button *ngIf="i === 0" type="button" mat-icon-button color="primary" (click)="addAffectedGLEntry(debits)">
|
||||
<fa-icon icon="plus-circle" size="lg"></fa-icon>
|
||||
</button>
|
||||
</span>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div fxFlexFill fxLayout="row wrap" fxLayout.lt-md="column" formArrayName="credits"
|
||||
*ngFor="let credit of credits.controls; let i = index;">
|
||||
|
||||
<div fxFlexFill fxLayout="row wrap" fxLayout.lt-md="column" formArrayName="credits"
|
||||
*ngFor="let credit of getFormArrayData('credits').controls; let i = index;">
|
||||
<div fxFlexFill fxLayout="row wrap" fxLayoutGap="2%" fxLayout.lt-md="column" [formGroupName]="i">
|
||||
|
||||
<div fxFlexFill fxLayout="row wrap" fxLayoutGap="2%" fxLayout.lt-md="column" [formGroupName]="i">
|
||||
<mat-form-field fxFlex="48%">
|
||||
<mat-label>Affected GL Entry (Credit)</mat-label>
|
||||
<mat-select required formControlName="glAccountId">
|
||||
<mat-option *ngFor="let affectedGLEntryCredit of glAccountData" [value]="affectedGLEntryCredit.id">
|
||||
{{ affectedGLEntryCredit.name + ' (' + affectedGLEntryCredit.glCode + ')' }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
<mat-error *ngIf="credits.at(i).controls.glAccountId.hasError('required')">
|
||||
Affected GL Entry (Credit) is <strong>required</strong>
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field fxFlex="48%">
|
||||
<mat-select placeholder="Affected GL Entry (Credit)" required formControlName="glAccountId">
|
||||
<mat-option *ngFor="let affectedGLEntryCredit of glAccountData" [value]="affectedGLEntryCredit.id">
|
||||
{{ affectedGLEntryCredit.name + ' (' + affectedGLEntryCredit.glCode + ')' }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
<mat-form-field fxFlex="43%">
|
||||
<mat-label>Credit Amount</mat-label>
|
||||
<input type="number" matInput required formControlName="amount">
|
||||
<mat-error *ngIf="credits.at(i).controls.amount.hasError('required')">
|
||||
Credit Amount is <strong>required</strong>
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field fxFlex="43%">
|
||||
<input type="number" matInput placeholder="Credit Amount" required formControlName="amount">
|
||||
</mat-form-field>
|
||||
<span fxFlex>
|
||||
<button *ngIf="i !== 0" type="button" mat-icon-button (click)="removeAffectedGLEntry(credits, i)">
|
||||
<fa-icon icon="minus-circle" size="lg"></fa-icon>
|
||||
</button>
|
||||
<button *ngIf="i === 0" type="button" mat-icon-button color="primary" (click)="addAffectedGLEntry(credits)">
|
||||
<fa-icon icon="plus-circle" size="lg"></fa-icon>
|
||||
</button>
|
||||
</span>
|
||||
|
||||
<span fxFlex>
|
||||
<button *ngIf="i !== 0" type="button" mat-icon-button (click)="removeAffectedGLEntry(journalEntryForm.get('credits'), i)"><mat-icon>remove_circle</mat-icon></button>
|
||||
<button *ngIf="i === 0 glAccountData" type="button" mat-icon-button color="primary" (click)="addAffectedGLEntry(journalEntryForm.get('credits'))"><mat-icon>add_circle</mat-icon></button>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<mat-form-field fxFlex="48%">
|
||||
<mat-label>Reference Number</mat-label>
|
||||
<input matInput formControlName="referenceNumber">
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field fxFlex="48%">
|
||||
<mat-label>Transaction Date</mat-label>
|
||||
<input matInput [min]="minDate" [max]="maxDate" [matDatepicker]="transactionDatePicker" required formControlName="transactionDate">
|
||||
<mat-datepicker-toggle matSuffix [for]="transactionDatePicker"></mat-datepicker-toggle>
|
||||
<mat-datepicker #transactionDatePicker></mat-datepicker>
|
||||
<mat-error *ngIf="journalEntryForm.controls.transactionDate.hasError('required')">
|
||||
Transaction Date is <strong>required</strong>
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field fxFlex="48%">
|
||||
<mat-label>Payment Type</mat-label>
|
||||
<mat-select formControlName="paymentTypeId">
|
||||
<mat-option *ngFor="let paymentType of paymentTypeData" [value]="paymentType.id">
|
||||
{{ paymentType.name }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field fxFlex="48%">
|
||||
<mat-label>Account Number</mat-label>
|
||||
<input matInput formControlName="accountNumber">
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field fxFlex="48%">
|
||||
<mat-label>Cheque Number</mat-label>
|
||||
<input matInput formControlName="checkNumber">
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field fxFlex="48%">
|
||||
<mat-label>Routing Code</mat-label>
|
||||
<input matInput formControlName="routingCode">
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field fxFlex="48%">
|
||||
<mat-label>Receipt Number</mat-label>
|
||||
<input matInput formControlName="receiptNumber">
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field fxFlex="48%">
|
||||
<mat-label>Bank Number</mat-label>
|
||||
<input matInput formControlName="bankNumber">
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field fxFlex="98%">
|
||||
<mat-label>Comments</mat-label>
|
||||
<textarea matInput formControlName="comments"></textarea>
|
||||
</mat-form-field>
|
||||
|
||||
</div>
|
||||
|
||||
<mat-form-field fxFlex="48%">
|
||||
<input matInput placeholder="Reference Number" formControlName="referenceNumber">
|
||||
</mat-form-field>
|
||||
</mat-card-content>
|
||||
|
||||
<mat-form-field fxFlex="48%">
|
||||
<input matInput [min]="minDate" [max]="maxDate" [matDatepicker]="transactionDatePicker" placeholder="Transaction Date" required formControlName="transactionDate">
|
||||
<mat-datepicker-toggle matSuffix [for]="transactionDatePicker"></mat-datepicker-toggle>
|
||||
<mat-datepicker #transactionDatePicker></mat-datepicker>
|
||||
</mat-form-field>
|
||||
<mat-card-actions fxLayout="row" fxLayout.xs="column" fxLayoutAlign="center" fxLayoutGap="5px">
|
||||
<button type="button" mat-raised-button [routerLink]="['../']">Cancel</button>
|
||||
<button mat-raised-button color="primary" [disabled]="!journalEntryForm.valid">Submit</button>
|
||||
</mat-card-actions>
|
||||
|
||||
<mat-form-field fxFlex="48%">
|
||||
<mat-select placeholder="Payment Type" formControlName="paymentTypeId">
|
||||
<mat-option *ngFor="let paymentType of paymentTypeData" [value]="paymentType.id">
|
||||
{{ paymentType.name }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</form>
|
||||
|
||||
<mat-form-field fxFlex="48%">
|
||||
<input matInput placeholder="Account Number" formControlName="accountNumber">
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field fxFlex="48%">
|
||||
<input matInput placeholder="Cheque Number" formControlName="checkNumber">
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field fxFlex="48%">
|
||||
<input matInput placeholder="Routing Code" formControlName="routingCode">
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field fxFlex="48%">
|
||||
<input matInput placeholder="Receipt Number" formControlName="receiptNumber">
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field fxFlex="48%">
|
||||
<input matInput placeholder="Bank Number" formControlName="bankNumber">
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field fxFlex="98%">
|
||||
<textarea matInput placeholder="Comments" formControlName="comments"></textarea>
|
||||
</mat-form-field>
|
||||
|
||||
</div>
|
||||
|
||||
</mat-card-content>
|
||||
|
||||
<mat-card-actions fxLayout="row" fxLayout.xs="column" fxLayoutAlign="center" fxLayoutGap="5px">
|
||||
<button type="button" mat-raised-button [routerLink]="['/accounting']">Cancel</button>
|
||||
<button mat-raised-button color="primary">Submit</button>
|
||||
</mat-card-actions>
|
||||
|
||||
</form>
|
||||
|
||||
</mat-card>
|
||||
</mat-card>
|
||||
|
||||
</div>
|
||||
|
||||
@@ -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 <FormArray>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 });
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
+13
-5
@@ -1,7 +1,7 @@
|
||||
<div class="container">
|
||||
|
||||
<mat-card>
|
||||
|
||||
|
||||
<form [formGroup]="financialActivityMappingForm" (ngSubmit)="submit()">
|
||||
|
||||
<mat-card-content>
|
||||
@@ -9,19 +9,27 @@
|
||||
<div fxLayout="column">
|
||||
|
||||
<mat-form-field>
|
||||
<mat-select placeholder="Financial Activity" required formControlName="financialActivityId">
|
||||
<mat-label>Financial Activity</mat-label>
|
||||
<mat-select required formControlName="financialActivityId">
|
||||
<mat-option *ngFor="let financialActivity of financialActivityData" [value]="financialActivity.id">
|
||||
{{ financialActivity.name }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
<mat-error *ngIf="financialActivityMappingForm.controls.financialActivityId.hasError('required')">
|
||||
Financial Activity is <strong>required</strong>
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field>
|
||||
<mat-select placeholder="Account" required formControlName="glAccountId">
|
||||
<mat-label>Account</mat-label>
|
||||
<mat-select required formControlName="glAccountId">
|
||||
<mat-option *ngFor="let glAccount of glAccountData" [value]="glAccount.id">
|
||||
{{ glAccount.name + ' (' + glAccount.glCode + ')' }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
<mat-error *ngIf="financialActivityMappingForm.controls.glAccountId.hasError('required')">
|
||||
Account is <strong>required</strong>
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
</div>
|
||||
@@ -29,8 +37,8 @@
|
||||
</mat-card-content>
|
||||
|
||||
<mat-card-actions fxLayout="row" fxLayout.xs="column" fxLayoutAlign="center" fxLayoutGap="5px">
|
||||
<button type="button" mat-raised-button [routerLink]="['/accounting/financial-activity-mappings']">Cancel</button>
|
||||
<button mat-raised-button color="primary">Submit</button>
|
||||
<button type="button" mat-raised-button [routerLink]="['../']">Cancel</button>
|
||||
<button mat-raised-button color="primary" [disabled]="!financialActivityMappingForm.valid">Submit</button>
|
||||
</mat-card-actions>
|
||||
|
||||
</form>
|
||||
|
||||
+60
-32
@@ -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 });
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
+30
@@ -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<Object> {
|
||||
|
||||
/**
|
||||
* @param {AccountingService} accountingService Accounting service.
|
||||
*/
|
||||
constructor(private accountingService: AccountingService) {}
|
||||
|
||||
/**
|
||||
* Returns the financial activity mappings template data.
|
||||
* @returns {Observable<any>}
|
||||
*/
|
||||
resolve(): Observable<any> {
|
||||
return this.accountingService.getFinancialActivityAccountsTemplate();
|
||||
}
|
||||
|
||||
}
|
||||
+6
-4
@@ -9,7 +9,8 @@
|
||||
<div fxLayout="column">
|
||||
|
||||
<mat-form-field>
|
||||
<mat-select placeholder="Financial Activity" required formControlName="financialActivityId">
|
||||
<mat-label>Financial Activity</mat-label>
|
||||
<mat-select required formControlName="financialActivityId">
|
||||
<mat-option *ngFor="let financialActivity of financialActivityData" [value]="financialActivity.id">
|
||||
{{ financialActivity.name }}
|
||||
</mat-option>
|
||||
@@ -17,7 +18,8 @@
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field>
|
||||
<mat-select placeholder="Account" required formControlName="glAccountId">
|
||||
<mat-label>Account</mat-label>
|
||||
<mat-select required formControlName="glAccountId">
|
||||
<mat-option *ngFor="let glAccount of glAccountData" [value]="glAccount.id">
|
||||
{{ glAccount.name + ' (' + glAccount.glCode + ')' }}
|
||||
</mat-option>
|
||||
@@ -29,8 +31,8 @@
|
||||
</mat-card-content>
|
||||
|
||||
<mat-card-actions fxLayout="row" fxLayout.xs="column" fxLayoutAlign="center" fxLayoutGap="5px">
|
||||
<button type="button" mat-raised-button [routerLink]="['/accounting/financial-activity-mappings/view', financialActivityAccountId]">Cancel</button>
|
||||
<button mat-raised-button color="primary">Submit</button>
|
||||
<button type="button" mat-raised-button [routerLink]="['../']">Cancel</button>
|
||||
<button mat-raised-button color="primary" [disabled]="financialActivityMappingForm.pristine">Submit</button>
|
||||
</mat-card-actions>
|
||||
|
||||
</form>
|
||||
|
||||
+68
-35
@@ -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 });
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
+31
@@ -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<Object> {
|
||||
|
||||
/**
|
||||
* @param {AccountingService} accountingService Accounting service.
|
||||
*/
|
||||
constructor(private accountingService: AccountingService) {}
|
||||
|
||||
/**
|
||||
* Returns the financial activity mapping and template data.
|
||||
* @returns {Observable<any>}
|
||||
*/
|
||||
resolve(route: ActivatedRouteSnapshot): Observable<any> {
|
||||
const financialActivityAccountId = route.paramMap.get('id');
|
||||
return this.accountingService.getFinancialActivityAccount(financialActivityAccountId, true);
|
||||
}
|
||||
|
||||
}
|
||||
+3
-3
@@ -1,6 +1,6 @@
|
||||
<div fxLayout="row" fxLayoutAlign="end" class="container m-b-20">
|
||||
<button mat-raised-button color="primary" [routerLink]="['/accounting/financial-activity-mappings/create']">
|
||||
<mat-icon>add</mat-icon> Define New Mapping
|
||||
<button mat-raised-button color="primary" [routerLink]="['create']">
|
||||
<fa-icon icon="plus"></fa-icon> Define New Mapping
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
</ng-container>
|
||||
|
||||
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: displayedColumns;" [routerLink]="['/accounting/financial-activity-mappings/view', row.id]"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: displayedColumns;" [routerLink]="['view', row.id]" class="select-row"></tr>
|
||||
|
||||
</table>
|
||||
|
||||
|
||||
+4
@@ -1,3 +1,7 @@
|
||||
table {
|
||||
width: 100%;
|
||||
|
||||
.select-row:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
+43
-22
@@ -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<any>;
|
||||
|
||||
/** 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+30
@@ -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<Object> {
|
||||
|
||||
/**
|
||||
* @param {AccountingService} accountingService Accounting service.
|
||||
*/
|
||||
constructor(private accountingService: AccountingService) {}
|
||||
|
||||
/**
|
||||
* Returns the financial activity mappings data.
|
||||
* @returns {Observable<any>}
|
||||
*/
|
||||
resolve(): Observable<any> {
|
||||
return this.accountingService.getFinancialActivityAccounts();
|
||||
}
|
||||
|
||||
}
|
||||
+31
@@ -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<Object> {
|
||||
|
||||
/**
|
||||
* @param {AccountingService} accountingService Accounting service.
|
||||
*/
|
||||
constructor(private accountingService: AccountingService) {}
|
||||
|
||||
/**
|
||||
* Returns the financial activity mapping data.
|
||||
* @returns {Observable<any>}
|
||||
*/
|
||||
resolve(route: ActivatedRouteSnapshot): Observable<any> {
|
||||
const financialActivityAccountId = route.paramMap.get('id');
|
||||
return this.accountingService.getFinancialActivityAccount(financialActivityAccountId, false);
|
||||
}
|
||||
|
||||
}
|
||||
+7
-5
@@ -1,9 +1,11 @@
|
||||
<div fxLayout="row" fxLayoutAlign="end" fxLayoutGap="2%" class="container m-b-20">
|
||||
<button mat-raised-button color="primary" [routerLink]="['/accounting/financial-activity-mappings/view', financialActivityAccountId, 'edit']">
|
||||
<mat-icon>edit</mat-icon> Edit
|
||||
<button mat-raised-button color="primary" [routerLink]="['edit']">
|
||||
<fa-icon icon="edit"></fa-icon>
|
||||
Edit
|
||||
</button>
|
||||
<button mat-raised-button color="warn" (click)="deleteFinancialActivityAccount()">
|
||||
<mat-icon>delete</mat-icon> Delete
|
||||
<fa-icon icon="trash"></fa-icon>
|
||||
Delete
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -20,7 +22,7 @@
|
||||
</div>
|
||||
|
||||
<div fxFlex="50%">
|
||||
{{ financialActivityAccountData?.financialActivityData.name }}
|
||||
{{ financialActivityAccount?.financialActivityData.name }}
|
||||
</div>
|
||||
|
||||
<div fxFlex="50%" class="header">
|
||||
@@ -28,7 +30,7 @@
|
||||
</div>
|
||||
|
||||
<div fxFlex="50%">
|
||||
{{ financialActivityAccountData?.glAccountData.name }}
|
||||
{{ financialActivityAccount?.glAccountData.name }}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
+8
-8
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+28
-13
@@ -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}` }
|
||||
|
||||
@@ -1,140 +1,192 @@
|
||||
<mat-card class="container">
|
||||
<div class="container">
|
||||
|
||||
<form [formGroup]="frequentPostingsForm" (ngSubmit)="submit()">
|
||||
<mat-card>
|
||||
|
||||
<mat-card-content>
|
||||
<form [formGroup]="frequentPostingsForm" (ngSubmit)="submit()">
|
||||
|
||||
<div fxLayout="row wrap" fxLayoutGap="2%" fxLayout.lt-md="column">
|
||||
<mat-card-content>
|
||||
|
||||
<mat-form-field fxFlex="48%">
|
||||
<mat-select placeholder="Office" required formControlName="officeId">
|
||||
<mat-option *ngFor="let office of officeData" [value]="office.id">
|
||||
{{ office.name }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
<div fxLayout="row wrap" fxLayoutGap="2%" fxLayout.lt-md="column">
|
||||
|
||||
<mat-form-field fxFlex="48%">
|
||||
<mat-select placeholder="Accounting Rule" required formControlName="accountingRule">
|
||||
<mat-option *ngFor="let accountingRule of accountingRuleData" [value]="accountingRule">
|
||||
{{ accountingRule.name }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
<mat-form-field fxFlex="48%">
|
||||
<mat-label>Office</mat-label>
|
||||
<mat-select required formControlName="officeId">
|
||||
<mat-option *ngFor="let office of officeData" [value]="office.id">
|
||||
{{ office.name }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
<mat-error *ngIf="frequentPostingsForm.controls.officeId.hasError('required')">
|
||||
Office is <strong>required</strong>
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field fxFlex="48%">
|
||||
<mat-select placeholder="Currency" required formControlName="currencyCode">
|
||||
<mat-option *ngFor="let currency of currencyData" [value]="currency.code">
|
||||
{{ currency.displayLabel }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
<mat-form-field fxFlex="48%">
|
||||
<mat-label>Accounting Rule</mat-label>
|
||||
<mat-select required formControlName="accountingRule">
|
||||
<mat-option *ngFor="let accountingRule of accountingRuleData" [value]="accountingRule">
|
||||
{{ accountingRule.name }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
<mat-error *ngIf="frequentPostingsForm.controls.accountingRule.hasError('required')">
|
||||
Accounting Rule is <strong>required</strong>
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
<span fxFlex="48%" fxHide.lt-md></span>
|
||||
<mat-form-field fxFlex="48%">
|
||||
<mat-label>Currency</mat-label>
|
||||
<mat-select required formControlName="currencyCode">
|
||||
<mat-option *ngFor="let currency of currencyData" [value]="currency.code">
|
||||
{{ currency.displayLabel }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
<mat-error *ngIf="frequentPostingsForm.controls.currencyCode.hasError('required')">
|
||||
Currency is <strong>required</strong>
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
<div fxFlexFill fxLayout="row wrap" fxLayout.lt-md="column" formArrayName="debits"
|
||||
*ngFor="let debit of getFormArrayData('debits').controls; let i = index;">
|
||||
<span fxFlex="48%" fxHide.lt-md></span>
|
||||
|
||||
<div fxFlexFill fxLayout="row wrap" fxLayoutGap="2%" fxLayout.lt-md="column" [formGroupName]="i">
|
||||
<div fxFlexFill fxLayout="row wrap" fxLayout.lt-md="column" formArrayName="debits"
|
||||
*ngFor="let debit of debits.controls; let i = index;">
|
||||
|
||||
<mat-form-field fxFlex="48%">
|
||||
<mat-select placeholder="Affected GL Entry (Debit)" required formControlName="glAccountId">
|
||||
<mat-option *ngFor="let affectedGLEntryDebit of debitAccountData" [value]="affectedGLEntryDebit.id">
|
||||
{{ affectedGLEntryDebit.name }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
<div fxFlexFill fxLayout="row wrap" fxLayoutGap="2%" fxLayout.lt-md="column" [formGroupName]="i">
|
||||
|
||||
<mat-form-field fxFlex="43%">
|
||||
<input type="number" matInput placeholder="Debit Amount" required formControlName="amount">
|
||||
</mat-form-field>
|
||||
<mat-form-field fxFlex="48%">
|
||||
<mat-label>Affected GL Entry (Debit)</mat-label>
|
||||
<mat-select required formControlName="glAccountId">
|
||||
<mat-option *ngFor="let affectedGLEntryDebit of debitAccountData" [value]="affectedGLEntryDebit.id">
|
||||
{{ affectedGLEntryDebit.name }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
<mat-error *ngIf="debits.at(i).controls.glAccountId.hasError('required')">
|
||||
Affected GL Entry (Debit) is <strong>required</strong>
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
<span fxFlex>
|
||||
<button *ngIf="i !== 0" type="button" mat-icon-button (click)="removeAffectedGLEntry(frequentPostingsForm.get('debits'), i)"><mat-icon>remove_circle</mat-icon></button>
|
||||
<button *ngIf="i === 0 && allowMultipleDebitEntries" type="button" mat-icon-button color="primary" (click)="addAffectedGLEntry(frequentPostingsForm.get('debits'))"><mat-icon>add_circle</mat-icon></button>
|
||||
</span>
|
||||
<mat-form-field fxFlex="43%">
|
||||
<mat-label>Debit Amount</mat-label>
|
||||
<input type="number" matInput required formControlName="amount">
|
||||
<mat-error *ngIf="debits.at(i).controls.amount.hasError('required')">
|
||||
Debit Amount is <strong>required</strong>
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
<span fxFlex>
|
||||
<button *ngIf="i !== 0" type="button" mat-icon-button (click)="removeAffectedGLEntry(debits, i)">
|
||||
<fa-icon icon="minus-circle" size="lg"></fa-icon>
|
||||
</button>
|
||||
<button *ngIf="i === 0 && allowMultipleDebitEntries" type="button" mat-icon-button color="primary" (click)="addAffectedGLEntry(debits)">
|
||||
<fa-icon icon="plus-circle" size="lg"></fa-icon>
|
||||
</button>
|
||||
</span>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div fxFlexFill fxLayout="row wrap" fxLayout.lt-md="column" formArrayName="credits"
|
||||
*ngFor="let credit of credits.controls; let i = index;">
|
||||
|
||||
<div fxFlexFill fxLayout="row wrap" fxLayout.lt-md="column" formArrayName="credits"
|
||||
*ngFor="let credit of getFormArrayData('credits').controls; let i = index;">
|
||||
<div fxFlexFill fxLayout="row wrap" fxLayoutGap="2%" fxLayout.lt-md="column" [formGroupName]="i">
|
||||
|
||||
<div fxFlexFill fxLayout="row wrap" fxLayoutGap="2%" fxLayout.lt-md="column" [formGroupName]="i">
|
||||
<mat-form-field fxFlex="48%">
|
||||
<mat-label>Affected GL Entry (Credit)</mat-label>
|
||||
<mat-select required formControlName="glAccountId">
|
||||
<mat-option *ngFor="let affectedGLEntryCredit of creditAccountData" [value]="affectedGLEntryCredit.id">
|
||||
{{ affectedGLEntryCredit.name }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
<mat-error *ngIf="credits.at(i).controls.glAccountId.hasError('required')">
|
||||
Affected GL Entry (Credit) is <strong>required</strong>
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field fxFlex="48%">
|
||||
<mat-select placeholder="Affected GL Entry (Credit)" required formControlName="glAccountId">
|
||||
<mat-option *ngFor="let affectedGLEntryCredit of creditAccountData" [value]="affectedGLEntryCredit.id">
|
||||
{{ affectedGLEntryCredit.name }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
<mat-form-field fxFlex="43%">
|
||||
<mat-label>Credit Amount</mat-label>
|
||||
<input type="number" matInput required formControlName="amount">
|
||||
<mat-error *ngIf="credits.at(i).controls.amount.hasError('required')">
|
||||
Credit Amount is <strong>required</strong>
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field fxFlex="43%">
|
||||
<input type="number" matInput placeholder="Credit Amount" required formControlName="amount">
|
||||
</mat-form-field>
|
||||
<span fxFlex>
|
||||
<button *ngIf="i !== 0" type="button" mat-icon-button (click)="removeAffectedGLEntry(credits, i)">
|
||||
<fa-icon icon="minus-circle" size="lg"></fa-icon>
|
||||
</button>
|
||||
<button *ngIf="i === 0 && allowMultipleCreditEntries" type="button" mat-icon-button color="primary" (click)="addAffectedGLEntry(credits)">
|
||||
<fa-icon icon="plus-circle" size="lg"></fa-icon>
|
||||
</button>
|
||||
</span>
|
||||
|
||||
<span fxFlex>
|
||||
<button *ngIf="i !== 0" type="button" mat-icon-button (click)="removeAffectedGLEntry(frequentPostingsForm.get('credits'), i)"><mat-icon>remove_circle</mat-icon></button>
|
||||
<button *ngIf="i === 0 && allowMultipleCreditEntries" type="button" mat-icon-button color="primary" (click)="addAffectedGLEntry(frequentPostingsForm.get('credits'))"><mat-icon>add_circle</mat-icon></button>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<mat-form-field fxFlex="48%">
|
||||
<mat-label>Reference Number</mat-label>
|
||||
<input matInput formControlName="referenceNumber">
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field fxFlex="48%">
|
||||
<mat-label>Transaction Date</mat-label>
|
||||
<input matInput [min]="minDate" [max]="maxDate" [matDatepicker]="transactionDatePicker" required formControlName="transactionDate">
|
||||
<mat-datepicker-toggle matSuffix [for]="transactionDatePicker"></mat-datepicker-toggle>
|
||||
<mat-datepicker #transactionDatePicker></mat-datepicker>
|
||||
<mat-error *ngIf="frequentPostingsForm.controls.transactionDate.hasError('required')">
|
||||
Transaction Date is <strong>required</strong>
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field fxFlex="48%">
|
||||
<mat-label>Payment Type</mat-label>
|
||||
<mat-select formControlName="paymentTypeId">
|
||||
<mat-option *ngFor="let paymentType of paymentTypeData" [value]="paymentType.id">
|
||||
{{ paymentType.name }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field fxFlex="48%">
|
||||
<mat-label>Account Number</mat-label>
|
||||
<input matInput formControlName="accountNumber">
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field fxFlex="48%">
|
||||
<mat-label>Cheque Number</mat-label>
|
||||
<input matInput formControlName="checkNumber">
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field fxFlex="48%">
|
||||
<mat-label>Routing Code</mat-label>
|
||||
<input matInput formControlName="routingCode">
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field fxFlex="48%">
|
||||
<mat-label>Receipt Number</mat-label>
|
||||
<input matInput formControlName="receiptNumber">
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field fxFlex="48%">
|
||||
<mat-label>Bank Number</mat-label>
|
||||
<input matInput formControlName="bankNumber">
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field fxFlex="98%">
|
||||
<mat-label>Comments</mat-label>
|
||||
<textarea matInput formControlName="comments"></textarea>
|
||||
</mat-form-field>
|
||||
|
||||
</div>
|
||||
|
||||
<mat-form-field fxFlex="48%">
|
||||
<input matInput placeholder="Reference Number" formControlName="referenceNumber">
|
||||
</mat-form-field>
|
||||
</mat-card-content>
|
||||
|
||||
<mat-form-field fxFlex="48%">
|
||||
<input matInput [min]="minDate" [max]="maxDate" [matDatepicker]="transactionDatePicker" placeholder="Transaction Date" required formControlName="transactionDate">
|
||||
<mat-datepicker-toggle matSuffix [for]="transactionDatePicker"></mat-datepicker-toggle>
|
||||
<mat-datepicker #transactionDatePicker></mat-datepicker>
|
||||
</mat-form-field>
|
||||
<mat-card-actions fxLayout="row" fxLayout.xs="column" fxLayoutAlign="center" fxLayoutGap="5px">
|
||||
<button type="button" mat-raised-button [routerLink]="['../']">Cancel</button>
|
||||
<button mat-raised-button color="primary" [disabled]="!frequentPostingsForm.valid">Submit</button>
|
||||
</mat-card-actions>
|
||||
|
||||
<mat-form-field fxFlex="48%">
|
||||
<mat-select placeholder="Payment Type" formControlName="paymentTypeId">
|
||||
<mat-option *ngFor="let paymentType of paymentTypeData" [value]="paymentType.id">
|
||||
{{ paymentType.name }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</form>
|
||||
|
||||
<mat-form-field fxFlex="48%">
|
||||
<input matInput placeholder="Account Number" formControlName="accountNumber">
|
||||
</mat-form-field>
|
||||
</mat-card>
|
||||
|
||||
<mat-form-field fxFlex="48%">
|
||||
<input matInput placeholder="Cheque Number" formControlName="checkNumber">
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field fxFlex="48%">
|
||||
<input matInput placeholder="Routing Code" formControlName="routingCode">
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field fxFlex="48%">
|
||||
<input matInput placeholder="Receipt Number" formControlName="receiptNumber">
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field fxFlex="48%">
|
||||
<input matInput placeholder="Bank Number" formControlName="bankNumber">
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field fxFlex="98%">
|
||||
<textarea matInput placeholder="Comments" formControlName="comments"></textarea>
|
||||
</mat-form-field>
|
||||
|
||||
</div>
|
||||
|
||||
</mat-card-content>
|
||||
|
||||
<mat-card-actions fxLayout="row" fxLayout.xs="column" fxLayoutAlign="center" fxLayoutGap="5px">
|
||||
<button type="button" mat-raised-button [routerLink]="['/accounting']">Cancel</button>
|
||||
<button mat-raised-button color="primary">Submit</button>
|
||||
</mat-card-actions>
|
||||
|
||||
</form>
|
||||
|
||||
</mat-card>
|
||||
</div>
|
||||
|
||||
@@ -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 <FormArray>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 });
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
+23
-8
@@ -1,3 +1,4 @@
|
||||
<!-- TODO: Responsiveness/design enhancement and reduction in form loading time -->
|
||||
<div class="container">
|
||||
|
||||
<mat-card>
|
||||
@@ -9,11 +10,15 @@
|
||||
<div fxLayout="row wrap" fxLayoutGap="2%" fxLayout.lt-md="column">
|
||||
|
||||
<mat-form-field fxFlex="88%">
|
||||
<mat-select placeholder="Office" required formControlName="officeId">
|
||||
<mat-label>Office</mat-label>
|
||||
<mat-select required formControlName="officeId">
|
||||
<mat-option *ngFor="let office of officeData" [value]="office.id">
|
||||
{{ office.name }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
<mat-error *ngIf="openingBalancesForm.controls.officeId.hasError('required')">
|
||||
Office is <strong>required</strong>
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
<button mat-button type="button" color="primary" fxFlex="4%" (click)="retrieveOpeningBalances()">Retrieve</button>
|
||||
@@ -25,17 +30,25 @@
|
||||
<label fxFlex="98%" class="content"><span class="header">Opening Balances Contra Account:</span> {{ openingBalancesData.contraAccount.name + ' (' + openingBalancesData.contraAccount.glCode + ')' }}</label>
|
||||
|
||||
<mat-form-field fxFlex="48%">
|
||||
<mat-select placeholder="Currency" required formControlName="currencyCode">
|
||||
<mat-label>Currency</mat-label>
|
||||
<mat-select required formControlName="currencyCode">
|
||||
<mat-option *ngFor="let currency of currencyData" [value]="currency.code">
|
||||
{{ currency.displayLabel }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
<mat-error *ngIf="openingBalancesForm.controls.currencyCode.hasError('required')">
|
||||
Currency is <strong>required</strong>
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field fxFlex="48%">
|
||||
<input matInput [min]="minDate" [max]="maxDate" [matDatepicker]="openingBalancesDatePicker" placeholder="Opening Balances Date" required formControlName="transactionDate">
|
||||
<mat-label>Opening Balances Date</mat-label>
|
||||
<input matInput [min]="minDate" [max]="maxDate" [matDatepicker]="openingBalancesDatePicker" required formControlName="transactionDate">
|
||||
<mat-datepicker-toggle matSuffix [for]="openingBalancesDatePicker"></mat-datepicker-toggle>
|
||||
<mat-datepicker #openingBalancesDatePicker></mat-datepicker>
|
||||
<mat-error *ngIf="openingBalancesForm.controls.transactionDate.hasError('required')">
|
||||
Opening Balances Date is <strong>required</strong>
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
<div fxLayout="row wrap" fxLayoutGap="2%" fxLayout.lt-md="column" class="content" fxFlex="100%">
|
||||
@@ -63,7 +76,7 @@
|
||||
</div>
|
||||
|
||||
<div fxFlexFill fxLayout="row wrap" fxLayout.lt-md="column" class="content" formArrayName="glAccountEntries"
|
||||
*ngFor="let credit of getFormArrayData('glAccountEntries').controls; let i = index;">
|
||||
*ngFor="let credit of glAccountEntries.controls; let i = index;">
|
||||
|
||||
<div fxFlexFill fxLayout="row wrap" fxLayoutGap="2%" fxLayout.lt-md="column" [formGroupName]="i">
|
||||
|
||||
@@ -81,13 +94,15 @@
|
||||
|
||||
<div fxFlex="23%">
|
||||
<mat-form-field>
|
||||
<input matInput type="number" placeholder="Debit" formControlName="debit">
|
||||
<mat-label>Debit</mat-label>
|
||||
<input matInput type="number" formControlName="debit">
|
||||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
<div fxFlex="23%">
|
||||
<mat-form-field>
|
||||
<input matInput type="number" placeholder="Credit" formControlName="credit">
|
||||
<mat-label>Credit</mat-label>
|
||||
<input matInput type="number" formControlName="credit">
|
||||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
@@ -100,8 +115,8 @@
|
||||
</mat-card-content>
|
||||
|
||||
<mat-card-actions fxLayout="row" fxLayout.xs="column" fxLayoutAlign="center" fxLayoutGap="5px" *ngIf="openingBalancesData">
|
||||
<button type="button" mat-raised-button [routerLink]="['/accounting']">Cancel</button>
|
||||
<button mat-raised-button color="primary">Submit</button>
|
||||
<button type="button" mat-raised-button [routerLink]="['../']">Cancel</button>
|
||||
<button mat-raised-button color="primary" [disabled]="!openingBalancesForm.valid">Submit</button>
|
||||
</mat-card-actions>
|
||||
|
||||
</form>
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
div {
|
||||
margin: 1rem 0;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
.header {
|
||||
font-weight: 500;
|
||||
&.header {
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 <FormArray>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]);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+9
@@ -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 };
|
||||
};
|
||||
@@ -9,9 +9,13 @@
|
||||
<div fxLayout="row">
|
||||
|
||||
<mat-form-field fxFlex>
|
||||
<input matInput [min]="minDate" [max]="maxDate" [matDatepicker]="tillDatePicker" placeholder="Accrue Till Date" required formControlName="tillDate">
|
||||
<mat-label>Accrue Till Date</mat-label>
|
||||
<input matInput [min]="minDate" [max]="maxDate" [matDatepicker]="tillDatePicker" required formControlName="tillDate">
|
||||
<mat-datepicker-toggle matSuffix [for]="tillDatePicker"></mat-datepicker-toggle>
|
||||
<mat-datepicker #tillDatePicker></mat-datepicker>
|
||||
<mat-error *ngIf="periodicAccrualsForm.controls.tillDate.hasError('required')">
|
||||
Accrue Date is <strong>required</strong>
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
</div>
|
||||
@@ -19,8 +23,11 @@
|
||||
</mat-card-content>
|
||||
|
||||
<mat-card-actions fxLayout="row" fxLayout.xs="column" fxLayoutAlign="center" fxLayoutGap="5px">
|
||||
<button type="button" mat-raised-button [routerLink]="['/accounting']">Cancel</button>
|
||||
<button mat-raised-button color="primary"><mat-icon>play_arrow</mat-icon> Run Periodic Accruals</button>
|
||||
<button type="button" mat-raised-button [routerLink]="['../']">Cancel</button>
|
||||
<button mat-raised-button color="primary" [disabled]="!periodicAccrualsForm.valid">
|
||||
<fa-icon icon="play"></fa-icon>
|
||||
Run Periodic Accruals
|
||||
</button>
|
||||
</mat-card-actions>
|
||||
|
||||
</form>
|
||||
|
||||
@@ -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 });
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
+8
-4
@@ -2,16 +2,20 @@
|
||||
|
||||
<mat-card>
|
||||
|
||||
<form [formGroup]="provisioningEntriesForm" (ngSubmit)="submit()">
|
||||
<form [formGroup]="provisioningEntryForm" (ngSubmit)="submit()">
|
||||
|
||||
<mat-card-content>
|
||||
|
||||
<div fxLayout="column">
|
||||
|
||||
<mat-form-field fxFlex>
|
||||
<input matInput [min]="minDate" [max]="maxDate" [matDatepicker]="datePicker" placeholder="Date" required formControlName="date">
|
||||
<mat-label>Date</mat-label>
|
||||
<input matInput [min]="minDate" [max]="maxDate" [matDatepicker]="datePicker" required formControlName="date">
|
||||
<mat-datepicker-toggle matSuffix [for]="datePicker"></mat-datepicker-toggle>
|
||||
<mat-datepicker #datePicker></mat-datepicker>
|
||||
<mat-error *ngIf="provisioningEntryForm.controls.date.hasError('required')">
|
||||
Date is <strong>required</strong>
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
<div fxFlex>
|
||||
@@ -25,8 +29,8 @@
|
||||
</mat-card-content>
|
||||
|
||||
<mat-card-actions fxLayout="row" fxLayout.xs="column" fxLayoutAlign="center" fxLayoutGap="5px">
|
||||
<button type="button" mat-raised-button [routerLink]="['/accounting/provisioning-entries']">Cancel</button>
|
||||
<button mat-raised-button color="primary">Submit</button>
|
||||
<button type="button" mat-raised-button [routerLink]="['../']">Cancel</button>
|
||||
<button mat-raised-button color="primary" [disabled]="!provisioningEntryForm.valid">Submit</button>
|
||||
</mat-card-actions>
|
||||
|
||||
</form>
|
||||
|
||||
+47
-9
@@ -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 });
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
<div class="container m-b-20" fxLayout="row" fxLayoutAlign="end" fxLayoutGap="20px">
|
||||
<button mat-raised-button color="primary" [routerLink]="['/accounting/provisioning-entries/create']">
|
||||
<mat-icon>add</mat-icon> Create Provisioning Entry
|
||||
<button mat-raised-button color="primary" [routerLink]="['create']">
|
||||
<fa-icon icon="plus"></fa-icon>
|
||||
Create Provisioning Entry
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
|
||||
<div fxLayout="row" fxLayoutGap="20px">
|
||||
<div fxLayout="row">
|
||||
<mat-form-field fxFlex>
|
||||
<input matInput (keyup)="applyFilter($event.target.value)" placeholder="Filter">
|
||||
<mat-label>Filter</mat-label>
|
||||
<input matInput (keyup)="applyFilter($event.target.value)">
|
||||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
@@ -36,9 +38,8 @@
|
||||
<ng-container matColumnDef="viewReport">
|
||||
<th mat-header-cell *matHeaderCellDef> View Report </th>
|
||||
<td mat-cell *matCellDef="let provisioningEntry">
|
||||
<button mat-button (click)="viewReport($event, provisioningEntry.id)" [disabled]="true" color="primary">
|
||||
<!-- Tenant Customizable -->
|
||||
<mat-icon>remove_red_eye</mat-icon> View
|
||||
<button mat-button color="primary" [routerLink]="['view', provisioningEntry.id]">
|
||||
<fa-icon icon="eye"></fa-icon> View
|
||||
</button>
|
||||
</td>
|
||||
</ng-container>
|
||||
@@ -47,7 +48,7 @@
|
||||
<th mat-header-cell *matHeaderCellDef> Recreate Provisioning </th>
|
||||
<td mat-cell *matCellDef="let provisioningEntry">
|
||||
<button mat-button (click)="recreateProvisioning($event, provisioningEntry.id)" [disabled]="provisioningEntry.journalEntry" color="primary">
|
||||
<mat-icon>edit</mat-icon> Recreate
|
||||
<fa-icon icon="edit"></fa-icon> Recreate
|
||||
</button>
|
||||
</td>
|
||||
</ng-container>
|
||||
@@ -56,13 +57,13 @@
|
||||
<th mat-header-cell *matHeaderCellDef> View Journal Entry </th>
|
||||
<td mat-cell *matCellDef="let provisioningEntry">
|
||||
<button mat-button (click)="viewJournalEntry($event, provisioningEntry.id)" [disabled]="!provisioningEntry.journalEntry" color="primary">
|
||||
<mat-icon>remove_red_eye</mat-icon> View
|
||||
<fa-icon icon="eye"></fa-icon> View
|
||||
</button>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: displayedColumns;" [routerLink]="['/accounting/provisioning-entries/view', row.id]"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
|
||||
|
||||
</table>
|
||||
|
||||
|
||||
@@ -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<any>;
|
||||
|
||||
/** 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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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<Object> {
|
||||
|
||||
/**
|
||||
* @param {AccountingService} accountingService Accounting service.
|
||||
*/
|
||||
constructor(private accountingService: AccountingService) {}
|
||||
|
||||
/**
|
||||
* Returns the provisioning entries data.
|
||||
* @returns {Observable<any>}
|
||||
*/
|
||||
resolve(): Observable<any> {
|
||||
return this.accountingService.getProvisioningEntries();
|
||||
}
|
||||
|
||||
}
|
||||
+31
@@ -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<Object> {
|
||||
|
||||
/**
|
||||
* @param {AccountingService} accountingService Accounting service.
|
||||
*/
|
||||
constructor(private accountingService: AccountingService) {}
|
||||
|
||||
/**
|
||||
* Returns the provisioning entry entries data.
|
||||
* @returns {Observable<any>}
|
||||
*/
|
||||
resolve(route: ActivatedRouteSnapshot): Observable<any> {
|
||||
const provisioningEntryId = route.paramMap.get('id');
|
||||
return this.accountingService.getProvisioningEntryEntries(provisioningEntryId);
|
||||
}
|
||||
|
||||
}
|
||||
+31
@@ -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<Object> {
|
||||
|
||||
/**
|
||||
* @param {AccountingService} accountingService Accounting service.
|
||||
*/
|
||||
constructor(private accountingService: AccountingService) {}
|
||||
|
||||
/**
|
||||
* Returns the provisioning entry data.
|
||||
* @returns {Observable<any>}
|
||||
*/
|
||||
resolve(route: ActivatedRouteSnapshot): Observable<any> {
|
||||
const provisioningEntryId = route.paramMap.get('id');
|
||||
return this.accountingService.getProvisioningEntry(provisioningEntryId);
|
||||
}
|
||||
|
||||
}
|
||||
+9
-5
@@ -2,7 +2,8 @@
|
||||
|
||||
<div class="m-b-20" fxLayout="row" fxLayoutAlign="end" fxLayoutGap="20px">
|
||||
<button mat-raised-button color="primary" (click)="createProvisioningJournalEntries()" [disabled]="provisioningEntry.journalEntry">
|
||||
<mat-icon>add</mat-icon> Create Journal Entries
|
||||
<fa-icon icon="plus"></fa-icon>
|
||||
Create Journal Entries
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -48,15 +49,18 @@
|
||||
<div class="container m-t-20" fxLayout="row wrap" fxLayoutGap="2%" fxLayout.lt-md="column">
|
||||
|
||||
<mat-form-field fxFlex="31%">
|
||||
<input matInput placeholder="Office Name" [formControl]="officeName" [matAutocomplete]="officeNameAutocomplete">
|
||||
<mat-label>Office Name</mat-label>
|
||||
<input matInput [formControl]="officeName" [matAutocomplete]="officeNameAutocomplete">
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field fxFlex="31%">
|
||||
<input matInput placeholder="Loan Product" [formControl]="loanProduct" [matAutocomplete]="loanProductAutocomplete">
|
||||
<mat-label>Loan Product</mat-label>
|
||||
<input matInput [formControl]="loanProduct" [matAutocomplete]="loanProductAutocomplete">
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field fxFlex="31%">
|
||||
<input matInput placeholder="Provisioning Category" [formControl]="provisioningCategory" [matAutocomplete]="provisioningCategoryAutocomplete">
|
||||
<mat-label>Provisioning Category</mat-label>
|
||||
<input matInput [formControl]="provisioningCategory" [matAutocomplete]="provisioningCategoryAutocomplete">
|
||||
</mat-form-field>
|
||||
|
||||
</div>
|
||||
@@ -122,7 +126,7 @@
|
||||
</ng-container>
|
||||
|
||||
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: displayedColumns;" [routerLink]="['/accounting/provisioning-entries/view', row.id]"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
|
||||
|
||||
</table>
|
||||
|
||||
|
||||
+3
-3
@@ -6,9 +6,9 @@ table {
|
||||
div {
|
||||
margin: 1rem 0;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
.header {
|
||||
font-weight: 500;
|
||||
&.header {
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+156
-81
@@ -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<any>;
|
||||
/** 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 });
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
+31
@@ -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<Object> {
|
||||
|
||||
/**
|
||||
* @param {AccountingService} accountingService Accounting service.
|
||||
*/
|
||||
constructor(private accountingService: AccountingService) {}
|
||||
|
||||
/**
|
||||
* Returns the provisioning journal entries data.
|
||||
* @returns {Observable<any>}
|
||||
*/
|
||||
resolve(route: ActivatedRouteSnapshot): Observable<any> {
|
||||
const id = route.paramMap.get('id');
|
||||
return this.accountingService.getProvisioningJournalEntries(id);
|
||||
}
|
||||
|
||||
}
|
||||
+2
-1
@@ -2,7 +2,8 @@
|
||||
|
||||
<div fxLayout="row" fxLayoutGap="20px">
|
||||
<mat-form-field fxFlex>
|
||||
<input matInput (keyup)="applyFilter($event.target.value)" placeholder="Filter">
|
||||
<mat-label>Filter</mat-label>
|
||||
<input matInput (keyup)="applyFilter($event.target.value)">
|
||||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
|
||||
+43
-24
@@ -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<any>;
|
||||
|
||||
/** 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();
|
||||
}
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
<div mat-dialog-content>
|
||||
<p>Are you sure you want to revert this transaction?</p>
|
||||
<mat-form-field fxFlexFill>
|
||||
<textarea matInput placeholder="Comments" [formControl]="comments"></textarea>
|
||||
<mat-label>Comments</mat-label>
|
||||
<textarea matInput [formControl]="comments"></textarea>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<mat-dialog-actions align="end">
|
||||
|
||||
@@ -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<RevertTransactionComponent>,
|
||||
@Inject(MAT_DIALOG_DATA) public data: any) { }
|
||||
|
||||
|
||||
@@ -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<any> {
|
||||
|
||||
/** Journal entries behavior subject to represent loaded journal entries page. */
|
||||
private journalEntriesSubject = new BehaviorSubject<any[]>([]);
|
||||
/** Records subject to represent total number of filtered journal entry records. */
|
||||
private recordsSubject = new BehaviorSubject<number>(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<any[]> {
|
||||
return this.journalEntriesSubject.asObservable();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {CollectionViewer} collectionViewer
|
||||
*/
|
||||
disconnect(collectionViewer: CollectionViewer): void {
|
||||
this.journalEntriesSubject.complete();
|
||||
this.recordsSubject.complete();
|
||||
|
||||
@@ -1,15 +1,18 @@
|
||||
<div class="container" fxLayout="row wrap" fxLayoutGap="2%" fxLayout.lt-md="column">
|
||||
|
||||
<mat-form-field fxFlex="31%">
|
||||
<input matInput placeholder="Office Name" [formControl]="officeName" [matAutocomplete]="officeNameAutocomplete">
|
||||
<mat-label>Office Name</mat-label>
|
||||
<input matInput [formControl]="officeName" [matAutocomplete]="officeNameAutocomplete">
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field fxFlex="31%">
|
||||
<input matInput placeholder="GL Account Name or Code" [formControl]="glAccount" [matAutocomplete]="glAccountAutocomplete">
|
||||
<mat-label>GL Account Name or Code</mat-label>
|
||||
<input matInput [formControl]="glAccount" [matAutocomplete]="glAccountAutocomplete">
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field fxFlex="31%">
|
||||
<mat-select placeholder="Filter" [formControl]="filter" (selectionChange)="applyFilter($event.value, 'manualEntriesOnly')">
|
||||
<mat-label>Filter</mat-label>
|
||||
<mat-select [formControl]="entryTypeFilter" (selectionChange)="applyFilter($event.value, 'manualEntriesOnly')">
|
||||
<mat-option *ngFor="let filter of entryTypeFilterData" [value]="filter.value">
|
||||
{{ filter.option }}
|
||||
</mat-option>
|
||||
@@ -17,19 +20,22 @@
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field fxFlex="31%">
|
||||
<input matInput [min]="minDate" [max]="maxDate" [matDatepicker]="transactionDateFromPicker" placeholder="Transaction Date From" [formControl]="transactionDateFrom">
|
||||
<mat-label>Transaction Date From</mat-label>
|
||||
<input matInput [min]="minDate" [max]="maxDate" [matDatepicker]="transactionDateFromPicker" [formControl]="transactionDateFrom">
|
||||
<mat-datepicker-toggle matSuffix [for]="transactionDateFromPicker"></mat-datepicker-toggle>
|
||||
<mat-datepicker #transactionDateFromPicker></mat-datepicker>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field fxFlex="31%">
|
||||
<input matInput [min]="minDate" [max]="maxDate" [matDatepicker]="transactionDateToPicker" placeholder="Transaction Date To" [formControl]="transactionDateTo">
|
||||
<mat-label>Transaction Date To</mat-label>
|
||||
<input matInput [min]="minDate" [max]="maxDate" [matDatepicker]="transactionDateToPicker" [formControl]="transactionDateTo">
|
||||
<mat-datepicker-toggle matSuffix [for]="transactionDateToPicker"></mat-datepicker-toggle>
|
||||
<mat-datepicker #transactionDateToPicker></mat-datepicker>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field fxFlex="31%">
|
||||
<input matInput placeholder="Transaction ID" [formControl]="transactionId">
|
||||
<mat-label>Transaction ID</mat-label>
|
||||
<input matInput [formControl]="transactionId">
|
||||
</mat-form-field>
|
||||
|
||||
</div>
|
||||
@@ -104,7 +110,7 @@
|
||||
</ng-container>
|
||||
|
||||
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: displayedColumns;" (click)="viewTransaction(row)" class="select"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: displayedColumns;" [routerLink]="['transactions/view', row.transactionId]" class="select-row"></tr>
|
||||
|
||||
</table>
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user