From 4be4cb9a68c4a55c63a5d399706aab105e25ed91 Mon Sep 17 00:00:00 2001 From: Abhay Chawla Date: Wed, 8 Aug 2018 14:29:00 +0530 Subject: [PATCH] feat(accounting): migrate opening balances (#277) Add migrate opening balances component to set the opening balances for GL Accounts for a branch or entire organisation. --- .../accounting/accounting-routing.module.ts | 6 + src/app/accounting/accounting.component.html | 6 +- src/app/accounting/accounting.module.ts | 4 +- src/app/accounting/accounting.service.ts | 12 ++ .../migrate-opening-balances.component.html | 111 ++++++++++++++++ .../migrate-opening-balances.component.scss | 10 ++ ...migrate-opening-balances.component.spec.ts | 25 ++++ .../migrate-opening-balances.component.ts | 121 ++++++++++++++++++ 8 files changed, 290 insertions(+), 5 deletions(-) create mode 100644 src/app/accounting/migrate-opening-balances/migrate-opening-balances.component.html create mode 100644 src/app/accounting/migrate-opening-balances/migrate-opening-balances.component.scss create mode 100644 src/app/accounting/migrate-opening-balances/migrate-opening-balances.component.spec.ts create mode 100644 src/app/accounting/migrate-opening-balances/migrate-opening-balances.component.ts diff --git a/src/app/accounting/accounting-routing.module.ts b/src/app/accounting/accounting-routing.module.ts index 377f72e36..974bc5e4d 100644 --- a/src/app/accounting/accounting-routing.module.ts +++ b/src/app/accounting/accounting-routing.module.ts @@ -35,6 +35,7 @@ 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'; const routes: Routes = [ Route.withShell([ @@ -241,6 +242,11 @@ const routes: Routes = [ ] } ] + }, + { + path: 'migrate-opening-balances', + component: MigrateOpeningBalancesComponent, + data: { title: extract('Migrate Opening Balances'), breadcrumb: 'Migrate Opening Balances' } } ] } diff --git a/src/app/accounting/accounting.component.html b/src/app/accounting/accounting.component.html index 4c635c5be..a2a5ac5c5 100644 --- a/src/app/accounting/accounting.component.html +++ b/src/app/accounting/accounting.component.html @@ -30,11 +30,9 @@

List of Financial Activities and GL Account Mappings

- + monetization_on - - Migrate Opening Balances (Office-wise) - +

Migrate Opening Balances (Office-wise)

Set or update office-level opening balances for GL accounts

diff --git a/src/app/accounting/accounting.module.ts b/src/app/accounting/accounting.module.ts index 41dd5f260..1e0ab29b3 100644 --- a/src/app/accounting/accounting.module.ts +++ b/src/app/accounting/accounting.module.ts @@ -33,6 +33,7 @@ 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'; @NgModule({ imports: [ @@ -70,7 +71,8 @@ import { ViewProvisioningJournalEntriesComponent } from './provisioning-entries/ ProvisioningEntriesComponent, CreateProvisioningEntryComponent, ViewProvisioningEntryComponent, - ViewProvisioningJournalEntriesComponent + ViewProvisioningJournalEntriesComponent, + MigrateOpeningBalancesComponent ], entryComponents: [ RevertTransactionComponent, diff --git a/src/app/accounting/accounting.service.ts b/src/app/accounting/accounting.service.ts index 0be4a20ce..5fc640420 100644 --- a/src/app/accounting/accounting.service.ts +++ b/src/app/accounting/accounting.service.ts @@ -209,4 +209,16 @@ export class AccountingService { return this.http.post(`/provisioningentries/${provisioningEntryId}`, {}, { params: httpParams }); } + retrieveOpeningBalances(officeId: number) { + let httpParams = new HttpParams(); + httpParams = httpParams.set('officeId', officeId.toString()); + return this.http.get('/journalentries/openingbalance', { params: httpParams }); + } + + defineOpeningBalances(openingBalances: any) { + let httpParams = new HttpParams(); + httpParams = httpParams.set('command', 'defineOpeningBalance'); + return this.http.post(`/journalentries`, openingBalances, { params: httpParams }); + } + } diff --git a/src/app/accounting/migrate-opening-balances/migrate-opening-balances.component.html b/src/app/accounting/migrate-opening-balances/migrate-opening-balances.component.html new file mode 100644 index 000000000..f9e76d3d8 --- /dev/null +++ b/src/app/accounting/migrate-opening-balances/migrate-opening-balances.component.html @@ -0,0 +1,111 @@ +
+ + + +
+ + + +
+ + + + + {{ office.name }} + + + + + + +
+ +
+ + + + + + + {{ currency.displayLabel }} + + + + + + + + + + +
+ +
+ Type +
+ +
+ GL Code +
+ +
+ Account +
+ +
+ Total Debit: {{ debitsSum }} +
+ +
+ Total Credit: {{ creditsSum }} +
+ +
+ +
+ +
+ +
+ {{ (openingBalancesData.glAccounts[i].glAccountType.value !== openingBalancesData.glAccounts[i-1]?.glAccountType.value) ? openingBalancesData.glAccounts[i].glAccountType.value : '' }} +
+ +
+ {{ openingBalancesData.glAccounts[i].glAccountCode }} +
+ +
+ {{ openingBalancesData.glAccounts[i].glAccountName }} +
+ +
+ + + +
+ +
+ + + +
+ +
+ +
+ +
+ +
+ + + + + + +
+ +
+ +
diff --git a/src/app/accounting/migrate-opening-balances/migrate-opening-balances.component.scss b/src/app/accounting/migrate-opening-balances/migrate-opening-balances.component.scss new file mode 100644 index 000000000..e8dff44e8 --- /dev/null +++ b/src/app/accounting/migrate-opening-balances/migrate-opening-balances.component.scss @@ -0,0 +1,10 @@ +.content { + div { + margin: 1rem 0; + word-wrap: break-word; + } + + .header { + font-weight: 500; + } +} diff --git a/src/app/accounting/migrate-opening-balances/migrate-opening-balances.component.spec.ts b/src/app/accounting/migrate-opening-balances/migrate-opening-balances.component.spec.ts new file mode 100644 index 000000000..e8f96de53 --- /dev/null +++ b/src/app/accounting/migrate-opening-balances/migrate-opening-balances.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { MigrateOpeningBalancesComponent } from './migrate-opening-balances.component'; + +describe('MigrateOpeningBalancesComponent', () => { + let component: MigrateOpeningBalancesComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ MigrateOpeningBalancesComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(MigrateOpeningBalancesComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/accounting/migrate-opening-balances/migrate-opening-balances.component.ts b/src/app/accounting/migrate-opening-balances/migrate-opening-balances.component.ts new file mode 100644 index 000000000..3338d7b12 --- /dev/null +++ b/src/app/accounting/migrate-opening-balances/migrate-opening-balances.component.ts @@ -0,0 +1,121 @@ +import { Component, OnInit } from '@angular/core'; +import { FormGroup, FormBuilder, Validators, FormArray, ValidatorFn, ValidationErrors } from '@angular/forms'; + +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 }; +}; + +@Component({ + selector: 'mifosx-migrate-opening-balances', + templateUrl: './migrate-opening-balances.component.html', + styleUrls: ['./migrate-opening-balances.component.scss'] +}) +export class MigrateOpeningBalancesComponent implements OnInit { + + minDate = new Date(2000, 0, 1); + maxDate = new Date(); + openingBalancesForm: FormGroup; + openingBalancesData: any; + officeData: any; + currencyData: any; + + debitsSum = 0; + creditsSum = 0; + + constructor(private accountingService: AccountingService, + private formBuilder: FormBuilder) { } + + 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; + }); + } + + createOpeningBalancesForm() { + this.openingBalancesForm = this.formBuilder.group({ + 'officeId': ['', Validators.required], + 'currencyCode': ['', Validators.required], + 'transactionDate': ['', Validators.required], + 'glAccountEntries': this.formBuilder.array([]) + }); + } + + createGLAccountEntryForm(glAccount: any): FormGroup { + return this.formBuilder.group({ + 'glAccountId': [glAccount.glAccountId], + 'debit': [null], + 'credit': [null] + }, { validator: onlyOneOfTheFieldsIsRequiredValidator }); + } + + getFormArrayData(type: string): FormArray { + return this.openingBalancesForm.get(type); + } + + 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); + openingBalancesData.glAccounts.forEach((glAccount: any) => { + entry.push(this.createGLAccountEntryForm(glAccount)); + }); + this.openingBalancesData = openingBalancesData; + + entry.valueChanges.subscribe(() => { + this.debitsSum = 0; + this.creditsSum = 0; + entry.controls.forEach(value => { + this.debitsSum += value.value.debit; + this.creditsSum += value.value.credit; + }); + }); + }); + } + + 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!'); + } + } + +}