diff --git a/src/app/accounting/accounting-routing.module.ts b/src/app/accounting/accounting-routing.module.ts index 23ac8bf27..987500a55 100644 --- a/src/app/accounting/accounting-routing.module.ts +++ b/src/app/accounting/accounting-routing.module.ts @@ -3,13 +3,24 @@ import { Routes, RouterModule } from '@angular/router'; import { Route, extract } from '../core'; import { AccountingComponent } from './accounting.component'; +import { FrequentPostingsComponent } from './frequent-postings/frequent-postings.component'; const routes: Routes = [ Route.withShell([ { path: 'accounting', - component: AccountingComponent, - data: { title: extract('Accounting'), breadcrumb: 'Accounting' } + data: { title: extract('Accounting'), breadcrumb: 'Accounting' }, + children: [ + { + path: '', + component: AccountingComponent + }, + { + path: 'frequent-postings', + component: FrequentPostingsComponent, + data: { title: extract('Frequent Postings'), breadcrumb: 'Frequent Postings' } + } + ] } ]) ]; diff --git a/src/app/accounting/accounting.component.html b/src/app/accounting/accounting.component.html index f98735e6e..a4826d62f 100644 --- a/src/app/accounting/accounting.component.html +++ b/src/app/accounting/accounting.component.html @@ -6,11 +6,9 @@ - + repeat - - Frequent Postings - +

Frequent Postings

These are predefined postings

diff --git a/src/app/accounting/accounting.module.ts b/src/app/accounting/accounting.module.ts index 1cd7372e8..8ec1341fe 100644 --- a/src/app/accounting/accounting.module.ts +++ b/src/app/accounting/accounting.module.ts @@ -1,5 +1,6 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; +import { ReactiveFormsModule } from '@angular/forms'; import { CoreModule } from '../core/core.module'; import { AccountingComponent } from './accounting.component'; @@ -10,6 +11,7 @@ import { FrequentPostingsComponent } from './frequent-postings/frequent-postings imports: [ CoreModule, CommonModule, + ReactiveFormsModule, AccountingRoutingModule ], declarations: [AccountingComponent, FrequentPostingsComponent] diff --git a/src/app/accounting/accounting.service.spec.ts b/src/app/accounting/accounting.service.spec.ts new file mode 100644 index 000000000..cad4c3bde --- /dev/null +++ b/src/app/accounting/accounting.service.spec.ts @@ -0,0 +1,15 @@ +import { TestBed, inject } from '@angular/core/testing'; + +import { AccountingService } from './accounting.service'; + +describe('AccountingService', () => { + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [AccountingService] + }); + }); + + it('should be created', inject([AccountingService], (service: AccountingService) => { + expect(service).toBeTruthy(); + })); +}); diff --git a/src/app/accounting/accounting.service.ts b/src/app/accounting/accounting.service.ts new file mode 100644 index 000000000..bd25c549a --- /dev/null +++ b/src/app/accounting/accounting.service.ts @@ -0,0 +1,33 @@ +import { Injectable } from '@angular/core'; +import { HttpClient } from '@angular/common/http'; +import { Observable } from 'rxjs'; + +@Injectable({ + providedIn: 'root' +}) +export class AccountingService { + + constructor(private http: HttpClient) { } + + getOffices(): Observable { + return this.http.get('/offices?orderBy=id'); + } + + getAccountingRules(): Observable { + return this.http.get('/accountingrules?associations=all'); + } + + getCurrencies(): Observable { + return this.http.get('/currencies'); + } + + getPaymentTypes(): Observable { + return this.http.get('/paymenttypes'); + } + + createJournalEntry(journalEntry: any): Observable { + return this.http.post('/journalentries', journalEntry); + } + + +} diff --git a/src/app/accounting/frequent-postings/frequent-postings.component.html b/src/app/accounting/frequent-postings/frequent-postings.component.html new file mode 100644 index 000000000..dbec4e327 --- /dev/null +++ b/src/app/accounting/frequent-postings/frequent-postings.component.html @@ -0,0 +1,140 @@ + + +
+ + + +
+ + + + + {{ office.name }} + + + + + + + + {{ accountingRule.name }} + + + + + + + + {{ currency.displayLabel }} + + + + + + +
+ +
+ + + + + {{ affectedGLEntryDebit.name }} + + + + + + + + + + + + + +
+ +
+ +
+ +
+ + + + + {{ affectedGLEntryCredit.name }} + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + + + + {{ paymentType.name }} + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + +
+ +
diff --git a/src/app/accounting/frequent-postings/frequent-postings.component.scss b/src/app/accounting/frequent-postings/frequent-postings.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/src/app/accounting/frequent-postings/frequent-postings.component.spec.ts b/src/app/accounting/frequent-postings/frequent-postings.component.spec.ts new file mode 100644 index 000000000..1fd110b01 --- /dev/null +++ b/src/app/accounting/frequent-postings/frequent-postings.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { FrequentPostingsComponent } from './frequent-postings.component'; + +describe('FrequentPostingsComponent', () => { + let component: FrequentPostingsComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ FrequentPostingsComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(FrequentPostingsComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/accounting/frequent-postings/frequent-postings.component.ts b/src/app/accounting/frequent-postings/frequent-postings.component.ts new file mode 100644 index 000000000..6a88b0709 --- /dev/null +++ b/src/app/accounting/frequent-postings/frequent-postings.component.ts @@ -0,0 +1,132 @@ +import { Component, OnInit } from '@angular/core'; +import { FormGroup, FormBuilder, Validators, FormArray } from '@angular/forms'; + +import { AccountingService } from '../accounting.service'; + +@Component({ + selector: 'mifosx-frequent-postings', + templateUrl: './frequent-postings.component.html', + styleUrls: ['./frequent-postings.component.scss'] +}) +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 + // TESTING: transactionId: 8b5748a57b73 + + minDate = new Date(2000, 0, 1); + maxDate = new Date(); + frequentPostingsForm: FormGroup; + officeData: any; + accountingRuleData: any; + currencyData: any; + paymentTypeData: any; + debitAccountData: any; + creditAccountData: any; + + allowMultipleCreditEntries: boolean; + allowMultipleDebitEntries: boolean; + + constructor(private formBuilder: FormBuilder, + private accountingService: AccountingService) { } + + ngOnInit() { + this.createFrequentPostingsForm(); + this.setAffectedGLEntryForm(); + this.getOffices(); + this.getAccountingRules(); + this.getCurrencies(); + this.getPaymentTypes(); + } + + createFrequentPostingsForm() { + this.frequentPostingsForm = this.formBuilder.group({ + 'officeId': ['', Validators.required], + 'accountingRule': ['', Validators.required], + 'currencyCode': ['', Validators.required], + 'debits': this.formBuilder.array([]), + 'credits': this.formBuilder.array([]), + 'referenceNumber': [''], + 'transactionDate': ['', Validators.required], + 'paymentTypeId': [''], + 'accountNumber': [''], + 'checkNumber': [''], + 'routingCode': [''], + 'receiptNumber': [''], + 'bankNumber': [''], + 'comments': [''] + }); + } + + 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 (credits.length) { + 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); + }); + } + + createAffectedGLEntryForm(): FormGroup { + return this.formBuilder.group({ + 'glAccountId': ['', Validators.required], + 'amount': ['', Validators.required] + }); + } + + addAffectedGLEntry(affectedGLEntryFormArray: FormArray) { + affectedGLEntryFormArray.push(this.createAffectedGLEntryForm()); + } + + removeAffectedGLEntry(affectedGLEntryFormArray: FormArray, index: number) { + affectedGLEntryFormArray.removeAt(index); + } + + getOffices() { + this.accountingService.getOffices().subscribe(officeData => { + this.officeData = officeData; + }); + } + + getAccountingRules() { + this.accountingService.getAccountingRules().subscribe(accountingRuleData => { + this.accountingRuleData = accountingRuleData; + }); + } + + getCurrencies() { + this.accountingService.getCurrencies().subscribe(currencyData => { + this.currencyData = currencyData.selectedCurrencyOptions; + }); + } + + getPaymentTypes() { + this.accountingService.getPaymentTypes().subscribe(paymentTypeData => { + this.paymentTypeData = paymentTypeData; + }); + } + + 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'; + this.accountingService.createJournalEntry(journalEntry).subscribe(response => { + console.log(response); + // navigate to view transaction with response.transactionId + }); + } + +}