From ec7e871ac6f7aeb5b1c3f7a0789bfeaec3fcc14c Mon Sep 17 00:00:00 2001 From: abhay_1806 Date: Sat, 21 Jul 2018 20:37:12 +0530 Subject: [PATCH] feat(accounting): add view transactions component Add view transactions component, ability to revert transaction and viewing journal entry details. Fixes #139 --- .../accounting/accounting-routing.module.ts | 12 ++ src/app/accounting/accounting.module.ts | 15 ++- src/app/accounting/accounting.service.ts | 18 ++- .../frequent-postings.component.html | 4 +- .../frequent-postings.component.ts | 12 +- .../revert-transaction.component.html | 30 +++++ .../revert-transaction.component.scss | 0 .../revert-transaction.component.spec.ts | 25 ++++ .../revert-transaction.component.ts | 20 ++++ .../view-journal-entry.component.html | 108 ++++++++++++++++++ .../view-journal-entry.component.scss | 13 +++ .../view-journal-entry.component.spec.ts | 25 ++++ .../view-journal-entry.component.ts | 17 +++ .../view-transaction.component.html | 97 ++++++++++++++++ .../view-transaction.component.scss | 17 +++ .../view-transaction.component.spec.ts | 25 ++++ .../view-transaction.component.ts | 79 +++++++++++++ 17 files changed, 509 insertions(+), 8 deletions(-) create mode 100644 src/app/accounting/revert-transaction/revert-transaction.component.html create mode 100644 src/app/accounting/revert-transaction/revert-transaction.component.scss create mode 100644 src/app/accounting/revert-transaction/revert-transaction.component.spec.ts create mode 100644 src/app/accounting/revert-transaction/revert-transaction.component.ts create mode 100644 src/app/accounting/view-journal-entry/view-journal-entry.component.html create mode 100644 src/app/accounting/view-journal-entry/view-journal-entry.component.scss create mode 100644 src/app/accounting/view-journal-entry/view-journal-entry.component.spec.ts create mode 100644 src/app/accounting/view-journal-entry/view-journal-entry.component.ts create mode 100644 src/app/accounting/view-transaction/view-transaction.component.html create mode 100644 src/app/accounting/view-transaction/view-transaction.component.scss create mode 100644 src/app/accounting/view-transaction/view-transaction.component.spec.ts create mode 100644 src/app/accounting/view-transaction/view-transaction.component.ts diff --git a/src/app/accounting/accounting-routing.module.ts b/src/app/accounting/accounting-routing.module.ts index 987500a55..8109221d8 100644 --- a/src/app/accounting/accounting-routing.module.ts +++ b/src/app/accounting/accounting-routing.module.ts @@ -4,6 +4,7 @@ import { Routes, RouterModule } from '@angular/router'; import { Route, extract } from '../core'; import { AccountingComponent } from './accounting.component'; import { FrequentPostingsComponent } from './frequent-postings/frequent-postings.component'; +import { ViewTransactionComponent } from './view-transaction/view-transaction.component'; const routes: Routes = [ Route.withShell([ @@ -19,6 +20,17 @@ const routes: Routes = [ path: 'frequent-postings', component: FrequentPostingsComponent, data: { title: extract('Frequent Postings'), breadcrumb: 'Frequent Postings' } + }, + { + path: 'transactions', + data: { title: extract('Transactions'), breadcrumb: 'Transactions' }, + children: [ + { + path: 'view/:id', + component: ViewTransactionComponent, + data: { title: extract('View Transaction'), routeParamBreadcrumb: 'id' } + } + ] } ] } diff --git a/src/app/accounting/accounting.module.ts b/src/app/accounting/accounting.module.ts index 8ec1341fe..9aef9efb6 100644 --- a/src/app/accounting/accounting.module.ts +++ b/src/app/accounting/accounting.module.ts @@ -6,6 +6,9 @@ import { CoreModule } from '../core/core.module'; import { AccountingComponent } from './accounting.component'; import { AccountingRoutingModule } from './accounting-routing.module'; import { FrequentPostingsComponent } from './frequent-postings/frequent-postings.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'; @NgModule({ imports: [ @@ -14,6 +17,16 @@ import { FrequentPostingsComponent } from './frequent-postings/frequent-postings ReactiveFormsModule, AccountingRoutingModule ], - declarations: [AccountingComponent, FrequentPostingsComponent] + declarations: [ + AccountingComponent, + FrequentPostingsComponent, + ViewTransactionComponent, + RevertTransactionComponent, + ViewJournalEntryComponent + ], + entryComponents: [ + RevertTransactionComponent, + ViewJournalEntryComponent + ] }) export class AccountingModule { } diff --git a/src/app/accounting/accounting.service.ts b/src/app/accounting/accounting.service.ts index bd25c549a..470b7cb14 100644 --- a/src/app/accounting/accounting.service.ts +++ b/src/app/accounting/accounting.service.ts @@ -1,5 +1,5 @@ import { Injectable } from '@angular/core'; -import { HttpClient } from '@angular/common/http'; +import { HttpClient, HttpParams } from '@angular/common/http'; import { Observable } from 'rxjs'; @Injectable({ @@ -29,5 +29,21 @@ export class AccountingService { return this.http.post('/journalentries', journalEntry); } + getJournalEntry(transactionId: string) { + let httpParams = new HttpParams(); + httpParams = httpParams.set('transactionId', transactionId); + httpParams = httpParams.set('transactionDetails', 'true'); + return this.http.get(`/journalentries`, { params: httpParams }); + } + + revertTransaction(transactionId: string, comments: string) { + let httpParams = new HttpParams(); + httpParams = httpParams.set('command', 'reverse'); + let data = {}; + if (comments) { + data = { comments: comments }; + } + return this.http.post(`/journalentries/${transactionId}`, data, { params: httpParams }); + } } diff --git a/src/app/accounting/frequent-postings/frequent-postings.component.html b/src/app/accounting/frequent-postings/frequent-postings.component.html index dbec4e327..d6fd8880b 100644 --- a/src/app/accounting/frequent-postings/frequent-postings.component.html +++ b/src/app/accounting/frequent-postings/frequent-postings.component.html @@ -33,7 +33,7 @@
+ *ngFor="let debit of getFormArrayData('debits').controls; let i = index;">
@@ -59,7 +59,7 @@
+ *ngFor="let credit of getFormArrayData('credits').controls; let i = index;">
diff --git a/src/app/accounting/frequent-postings/frequent-postings.component.ts b/src/app/accounting/frequent-postings/frequent-postings.component.ts index 6a88b0709..11160db48 100644 --- a/src/app/accounting/frequent-postings/frequent-postings.component.ts +++ b/src/app/accounting/frequent-postings/frequent-postings.component.ts @@ -1,5 +1,6 @@ import { Component, OnInit } from '@angular/core'; import { FormGroup, FormBuilder, Validators, FormArray } from '@angular/forms'; +import { Router } from '@angular/router'; import { AccountingService } from '../accounting.service'; @@ -12,7 +13,6 @@ 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(); @@ -28,7 +28,8 @@ export class FrequentPostingsComponent implements OnInit { allowMultipleDebitEntries: boolean; constructor(private formBuilder: FormBuilder, - private accountingService: AccountingService) { } + private accountingService: AccountingService, + private router: Router) { } ngOnInit() { this.createFrequentPostingsForm(); @@ -84,6 +85,10 @@ export class FrequentPostingsComponent implements OnInit { }); } + getFormArrayData(type: string): FormArray { + return this.frequentPostingsForm.get(type); + } + addAffectedGLEntry(affectedGLEntryFormArray: FormArray) { affectedGLEntryFormArray.push(this.createAffectedGLEntryForm()); } @@ -124,8 +129,7 @@ export class FrequentPostingsComponent implements OnInit { 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 + this.router.navigate(['/accounting/transactions/view', response.transactionId]); }); } diff --git a/src/app/accounting/revert-transaction/revert-transaction.component.html b/src/app/accounting/revert-transaction/revert-transaction.component.html new file mode 100644 index 000000000..6a73b91d7 --- /dev/null +++ b/src/app/accounting/revert-transaction/revert-transaction.component.html @@ -0,0 +1,30 @@ +
+ +

Revert Transaction

+
+

Are you sure you want to revert this transaction?

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

Transaction Reverted

+
+

Success!

+

A new journal entry has been created to reverse this transaction:

+

Transaction ID: {{ data.transactionId }}

+
+ + + + + +
diff --git a/src/app/accounting/revert-transaction/revert-transaction.component.scss b/src/app/accounting/revert-transaction/revert-transaction.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/src/app/accounting/revert-transaction/revert-transaction.component.spec.ts b/src/app/accounting/revert-transaction/revert-transaction.component.spec.ts new file mode 100644 index 000000000..342e3e465 --- /dev/null +++ b/src/app/accounting/revert-transaction/revert-transaction.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { RevertTransactionComponent } from './revert-transaction.component'; + +describe('RevertTransactionComponent', () => { + let component: RevertTransactionComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ RevertTransactionComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(RevertTransactionComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/accounting/revert-transaction/revert-transaction.component.ts b/src/app/accounting/revert-transaction/revert-transaction.component.ts new file mode 100644 index 000000000..79579f74d --- /dev/null +++ b/src/app/accounting/revert-transaction/revert-transaction.component.ts @@ -0,0 +1,20 @@ +import { Component, OnInit, Inject } from '@angular/core'; +import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material'; +import { FormControl } from '@angular/forms'; + +@Component({ + selector: 'mifosx-revert-transaction', + templateUrl: './revert-transaction.component.html', + styleUrls: ['./revert-transaction.component.scss'] +}) +export class RevertTransactionComponent implements OnInit { + + comments = new FormControl(''); + + constructor(public dialogRef: MatDialogRef, + @Inject(MAT_DIALOG_DATA) public data: any) { } + + ngOnInit() { + } + +} diff --git a/src/app/accounting/view-journal-entry/view-journal-entry.component.html b/src/app/accounting/view-journal-entry/view-journal-entry.component.html new file mode 100644 index 000000000..ecc8d79ca --- /dev/null +++ b/src/app/accounting/view-journal-entry/view-journal-entry.component.html @@ -0,0 +1,108 @@ +

View Journal Entry

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Office{{ data.journalEntry.officeName }}
Entry ID{{ data.journalEntry.id }}
Transaction ID{{ data.journalEntry.transactionId }}
Transaction Date{{ data.journalEntry.transactionDate }}
Type{{ data.journalEntry.glAccountType.value }}
Account Code{{ data.journalEntry.glAccountId }}
Account Name{{ data.journalEntry.glAccountName }}
+ Credit + Debit + + {{ (data.journalEntry.currency.displaySymbol || data.journalEntry.currency.code) + ' ' + data.journalEntry.amount }} +
Reference Number{{ data.journalEntry.referenceNumber }}
Comments{{ data.journalEntry.comments }}
Payment Details
Payment Type{{ data.journalEntry.transactionDetails.paymentDetails.paymentType.name }}
Account Number{{ data.journalEntry.transactionDetails.paymentDetails.accountNumber }}
Cheque Number{{ data.journalEntry.transactionDetails.paymentDetails.checkNumber }}
Routing Code{{ data.journalEntry.transactionDetails.paymentDetails.routingCode }}
Receipt Number{{ data.journalEntry.transactionDetails.paymentDetails.receiptNumber }}
Bank Number{{ data.journalEntry.transactionDetails.paymentDetails.bankNumber }}
Created by{{ data.journalEntry.createdByUserName }}
Created on{{ data.journalEntry.createdDate }}
+
+ + + diff --git a/src/app/accounting/view-journal-entry/view-journal-entry.component.scss b/src/app/accounting/view-journal-entry/view-journal-entry.component.scss new file mode 100644 index 000000000..d8a0b3c67 --- /dev/null +++ b/src/app/accounting/view-journal-entry/view-journal-entry.component.scss @@ -0,0 +1,13 @@ +th { + text-align: left; + font-weight: 500; +} + +th, td { + padding: 0 0.4rem 0 0; +} + +.header { + text-align: center; + padding: 0.4rem 0.4rem 0 0; +} diff --git a/src/app/accounting/view-journal-entry/view-journal-entry.component.spec.ts b/src/app/accounting/view-journal-entry/view-journal-entry.component.spec.ts new file mode 100644 index 000000000..30c10fb32 --- /dev/null +++ b/src/app/accounting/view-journal-entry/view-journal-entry.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ViewJournalEntryComponent } from './view-journal-entry.component'; + +describe('ViewJournalEntryComponent', () => { + let component: ViewJournalEntryComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ ViewJournalEntryComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ViewJournalEntryComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/accounting/view-journal-entry/view-journal-entry.component.ts b/src/app/accounting/view-journal-entry/view-journal-entry.component.ts new file mode 100644 index 000000000..949b7bfe9 --- /dev/null +++ b/src/app/accounting/view-journal-entry/view-journal-entry.component.ts @@ -0,0 +1,17 @@ +import { Component, OnInit, Inject } from '@angular/core'; +import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material'; + +@Component({ + selector: 'mifosx-view-journal-entry', + templateUrl: './view-journal-entry.component.html', + styleUrls: ['./view-journal-entry.component.scss'] +}) +export class ViewJournalEntryComponent implements OnInit { + + constructor(public dialogRef: MatDialogRef, + @Inject(MAT_DIALOG_DATA) public data: any) { } + + ngOnInit() { + } + +} diff --git a/src/app/accounting/view-transaction/view-transaction.component.html b/src/app/accounting/view-transaction/view-transaction.component.html new file mode 100644 index 000000000..4a00272eb --- /dev/null +++ b/src/app/accounting/view-transaction/view-transaction.component.html @@ -0,0 +1,97 @@ +
+ +
+ +
+ + + + + +
+ +
+ Office +
+ +
+ {{ dataSource.data[0].officeName }} +
+ +
+ Transaction Date +
+ +
+ {{ dataSource.data[0].transactionDate }} +
+ +
+ Created By +
+ +
+ {{ dataSource.data[0].createdByUserName }} +
+ +
+ Created On +
+ +
+ {{ dataSource.data[0].createdDate }} +
+ +
+ +
+ +
+ +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Entry ID {{ transaction.id }} Type {{ transaction.glAccountType.value }} Account Code {{ transaction.glAccountCode }} Account Name {{ transaction.glAccountName }} Debit {{ (transaction.currency.displaySymbol || transaction.currency.code) + ' ' + transaction.amount }} Credit {{ (transaction.currency.displaySymbol || transaction.currency.code) + ' ' + transaction.amount }}
+ + + +
diff --git a/src/app/accounting/view-transaction/view-transaction.component.scss b/src/app/accounting/view-transaction/view-transaction.component.scss new file mode 100644 index 000000000..fff2afccc --- /dev/null +++ b/src/app/accounting/view-transaction/view-transaction.component.scss @@ -0,0 +1,17 @@ +.content { + div { + line-height: 3rem; + } + + .header { + font-weight: 500; + } +} + +table { + width: 100%; +} + +.select:hover { + cursor: pointer; +} diff --git a/src/app/accounting/view-transaction/view-transaction.component.spec.ts b/src/app/accounting/view-transaction/view-transaction.component.spec.ts new file mode 100644 index 000000000..bbff0d11b --- /dev/null +++ b/src/app/accounting/view-transaction/view-transaction.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ViewTransactionComponent } from './view-transaction.component'; + +describe('ViewTransactionComponent', () => { + let component: ViewTransactionComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ ViewTransactionComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ViewTransactionComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/accounting/view-transaction/view-transaction.component.ts b/src/app/accounting/view-transaction/view-transaction.component.ts new file mode 100644 index 000000000..4520a31c8 --- /dev/null +++ b/src/app/accounting/view-transaction/view-transaction.component.ts @@ -0,0 +1,79 @@ +import { Component, OnInit, ViewChild, OnDestroy } from '@angular/core'; +import { ActivatedRoute, Router, ParamMap } from '@angular/router'; +import { MatDialog, MatSort, MatPaginator, MatTableDataSource } from '@angular/material'; + +import { AccountingService } from '../accounting.service'; +import { RevertTransactionComponent } from '../revert-transaction/revert-transaction.component'; +import { ViewJournalEntryComponent } from '../view-journal-entry/view-journal-entry.component'; + +@Component({ + selector: 'mifosx-view-transaction', + templateUrl: './view-transaction.component.html', + styleUrls: ['./view-transaction.component.scss'] +}) +export class ViewTransactionComponent implements OnInit, OnDestroy { + + // TODO: Update once language and date settings are setup + transactionId: string; + transactionId$: any; + displayedColumns: string[] = ['id', 'glAccountType', 'glAccountCode', 'glAccountName', 'debit', 'credit']; + dataSource: any; + + @ViewChild(MatPaginator) paginator: MatPaginator; + @ViewChild(MatSort) sort: MatSort; + + constructor(private route: ActivatedRoute, + private router: Router, + private accountingService: AccountingService, + public dialog: MatDialog) { } + + ngOnInit() { + this.transactionId$ = this.route.paramMap.subscribe((params: ParamMap) => { + this.transactionId = params.get('id'); + this.getJournalEntry(); + }); + } + + getJournalEntry() { + this.accountingService.getJournalEntry(this.transactionId).subscribe((journalEntry: any) => { + this.dataSource = new MatTableDataSource(journalEntry.pageItems); + this.dataSource.paginator = this.paginator; + this.dataSource.sortingDataAccessor = (transaction: any, property: any) => { + switch (property) { + case 'glAccountType': return transaction.glAccountType.value; + case 'debit': return transaction.amount; + case 'credit': return transaction.amount; + default: return transaction[property]; + } + }; + this.dataSource.sort = this.sort; + }); + } + + viewJournalEntry(journalEntry: any) { + this.dialog.open(ViewJournalEntryComponent, { + data: { journalEntry: journalEntry } + }); + } + + revertTransaction(transactionId?: string) { + const revertTransactionDialogRef = this.dialog.open(RevertTransactionComponent, { + data: { reverted: this.dataSource.data[0].reversed, transactionId: transactionId } + }); + revertTransactionDialogRef.afterClosed().subscribe((response: any) => { + if (response.revert) { + this.accountingService.revertTransaction(this.transactionId, response.comments).subscribe((reversedTransaction: any) => { + this.dataSource.data[0].reversed = true; + this.revertTransaction(reversedTransaction.transactionId); + }); + } else if (response.redirect) { + this.router.navigate(['/accounting/transactions/view', transactionId]); + } + }); + } + + ngOnDestroy() { + this.transactionId$.unsubscribe(); + } + +}