Feature: Filtering and tagging now available in Ledger
This commit is contained in:
1 parent
336a8f59c5
commit
c0eca311c7
40 files changed
+973
-72
No files matched your search
@@ -1,5 +1,6 @@
|
||||
import { SelectionModel } from '@angular/cdk/collections';
|
||||
import { AsyncPipe, CurrencyPipe } from '@angular/common';
|
||||
import { AfterViewInit, Component, ElementRef, OnInit, ViewChild } from '@angular/core';
|
||||
import { AfterViewInit, Component, ElementRef, HostListener, OnInit, ViewChild } from '@angular/core';
|
||||
import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms';
|
||||
import { MatAutocompleteSelectedEvent, MatAutocompleteTrigger, MatAutocomplete } from '@angular/material/autocomplete';
|
||||
import { MatIconButton, MatButton } from '@angular/material/button';
|
||||
@@ -11,6 +12,7 @@ import { MatIcon } from '@angular/material/icon';
|
||||
import { MatInput } from '@angular/material/input';
|
||||
import { MatPaginator } from '@angular/material/paginator';
|
||||
import { MatSort, MatSortHeader } from '@angular/material/sort';
|
||||
import { MatCheckbox } from '@angular/material/checkbox';
|
||||
import {
|
||||
MatTable,
|
||||
MatColumnDef,
|
||||
@@ -29,7 +31,7 @@ import {
|
||||
} from '@angular/material/table';
|
||||
import { ActivatedRoute, Router, RouterLink } from '@angular/router';
|
||||
import moment from 'moment';
|
||||
import { Observable, of as observableOf } from 'rxjs';
|
||||
import { BehaviorSubject, Observable, of as observableOf } from 'rxjs';
|
||||
import { debounceTime, distinctUntilChanged, switchMap } from 'rxjs/operators';
|
||||
|
||||
import { Account } from '../core/account';
|
||||
@@ -40,7 +42,13 @@ import { ToCsvService } from '../shared/to-csv.service';
|
||||
|
||||
import { Ledger } from './ledger';
|
||||
import { LedgerDataSource } from './ledger-datasource';
|
||||
import { LedgerService } from './ledger.service';
|
||||
import { MatSelect } from '@angular/material/select';
|
||||
import { MatChip, MatChipSet } from '@angular/material/chips';
|
||||
import { LedgerItem } from './ledger-item';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { TagService } from '../tag/tag.service';
|
||||
import { Tag } from '../tag/tag';
|
||||
import { TagDialogComponent } from '../tag-dialog/tag-dialog.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-ledger',
|
||||
@@ -52,6 +60,9 @@ import { LedgerService } from './ledger.service';
|
||||
MatCardHeader,
|
||||
MatCardTitleGroup,
|
||||
MatCardTitle,
|
||||
MatCheckbox,
|
||||
MatChip,
|
||||
MatChipSet,
|
||||
MatIconButton,
|
||||
MatIcon,
|
||||
MatCardContent,
|
||||
@@ -68,6 +79,7 @@ import { LedgerService } from './ledger.service';
|
||||
MatOption,
|
||||
MatButton,
|
||||
MatTable,
|
||||
MatSelect,
|
||||
MatSort,
|
||||
MatColumnDef,
|
||||
MatHeaderCellDef,
|
||||
@@ -97,17 +109,21 @@ export class LedgerComponent implements OnInit, AfterViewInit {
|
||||
@ViewChild(MatPaginator, { static: true }) paginator!: MatPaginator;
|
||||
@ViewChild(MatSort, { static: true }) sort!: MatSort;
|
||||
info: Ledger = new Ledger();
|
||||
dataSource: LedgerDataSource = new LedgerDataSource(this.info.body);
|
||||
body = new BehaviorSubject<LedgerItem[]>([]);
|
||||
filter = new BehaviorSubject<string[]>([]);
|
||||
dataSource: LedgerDataSource = new LedgerDataSource(this.body, this.filter);
|
||||
selection = new SelectionModel<LedgerItem>(true, []);
|
||||
|
||||
allTags: Tag[] = [];
|
||||
tags: string[] = [];
|
||||
form: FormGroup<{
|
||||
startDate: FormControl<Date>;
|
||||
finishDate: FormControl<Date>;
|
||||
account: FormControl<string | null>;
|
||||
tags: FormControl<string[]>;
|
||||
}>;
|
||||
|
||||
selectedRowId = '';
|
||||
debit = 0;
|
||||
credit = 0;
|
||||
running = 0;
|
||||
/** Columns displayed in the table. Columns IDs can be added, removed, or reordered. */
|
||||
displayedColumns = ['date', 'particulars', 'type', 'narration', 'debit', 'credit', 'running'];
|
||||
|
||||
@@ -123,14 +139,16 @@ export class LedgerComponent implements OnInit, AfterViewInit {
|
||||
constructor(
|
||||
private route: ActivatedRoute,
|
||||
private router: Router,
|
||||
private dialog: MatDialog,
|
||||
private toCsv: ToCsvService,
|
||||
private ser: LedgerService,
|
||||
private accountSer: AccountService,
|
||||
private tagSer: TagService,
|
||||
) {
|
||||
this.form = new FormGroup({
|
||||
startDate: new FormControl(new Date(), { nonNullable: true }),
|
||||
finishDate: new FormControl(new Date(), { nonNullable: true }),
|
||||
account: new FormControl<string | null>(null),
|
||||
tags: new FormControl<string[]>([], { nonNullable: true }),
|
||||
});
|
||||
|
||||
this.accounts = this.form.controls.account.valueChanges.pipe(
|
||||
@@ -142,16 +160,29 @@ export class LedgerComponent implements OnInit, AfterViewInit {
|
||||
|
||||
ngOnInit() {
|
||||
this.route.data.subscribe((value) => {
|
||||
const data = value as { info: Ledger };
|
||||
const data = value as { tags: Tag[]; info: Ledger };
|
||||
|
||||
this.allTags = data.tags;
|
||||
this.info = data.info;
|
||||
this.calculateTotals();
|
||||
this.tags = [
|
||||
'(None)',
|
||||
...Array.from(new Set(this.info.body.map((x) => x.tags).reduce((list, tags) => list.concat(tags), []))),
|
||||
];
|
||||
this.form.setValue({
|
||||
account: this.info.account?.name ?? '',
|
||||
startDate: moment(this.info.startDate, 'DD-MMM-YYYY').toDate(),
|
||||
finishDate: moment(this.info.finishDate, 'DD-MMM-YYYY').toDate(),
|
||||
tags: this.tags,
|
||||
});
|
||||
this.dataSource = new LedgerDataSource(this.info.body, this.paginator, this.sort);
|
||||
this.body.next(this.info.body);
|
||||
this.filter.next(this.tags);
|
||||
if (!this.dataSource.paginator) {
|
||||
this.dataSource.paginator = this.paginator;
|
||||
}
|
||||
if (!this.dataSource.sort) {
|
||||
this.dataSource.sort = this.sort;
|
||||
}
|
||||
this.selection.clear();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -167,22 +198,8 @@ export class LedgerComponent implements OnInit, AfterViewInit {
|
||||
return !account ? '' : typeof account === 'string' ? account : account.name;
|
||||
}
|
||||
|
||||
calculateTotals() {
|
||||
this.debit = 0;
|
||||
this.credit = 0;
|
||||
this.running = 0;
|
||||
this.info.body.forEach((item) => {
|
||||
if (item.type !== 'Opening Balance') {
|
||||
this.debit += item.debit;
|
||||
this.credit += item.credit;
|
||||
if (item.posted) {
|
||||
this.running += item.debit - item.credit;
|
||||
}
|
||||
} else {
|
||||
this.running += item.debit - item.credit;
|
||||
}
|
||||
item.running = this.running;
|
||||
});
|
||||
tagChanges(value: string[]) {
|
||||
this.filter.next(value);
|
||||
}
|
||||
|
||||
selected(event: MatAutocompleteSelectedEvent): void {
|
||||
@@ -193,6 +210,46 @@ export class LedgerComponent implements OnInit, AfterViewInit {
|
||||
this.selectedRowId = id;
|
||||
}
|
||||
|
||||
/** Whether the number of selected elements matches the total number of rows. */
|
||||
isAllSelected() {
|
||||
const numSelected = this.selection.selected.length;
|
||||
const numRows = this.dataSource.data.length;
|
||||
return numSelected === numRows;
|
||||
}
|
||||
|
||||
/** Selects all rows if they are not all selected; otherwise clear selection. */
|
||||
toggleAllRows() {
|
||||
if (this.isAllSelected()) {
|
||||
this.selection.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
this.selection.select(...this.dataSource.data);
|
||||
}
|
||||
|
||||
labels() {
|
||||
if (!this.selection.selected.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.dialog
|
||||
.open(TagDialogComponent, {
|
||||
width: '750px',
|
||||
data: {
|
||||
tags: this.allTags,
|
||||
vouchers: this.selection.selected,
|
||||
},
|
||||
})
|
||||
.afterClosed()
|
||||
.subscribe({
|
||||
next: (result) => {
|
||||
if (result) {
|
||||
this.router.navigateByUrl(this.router.url);
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
show() {
|
||||
const info = this.getInfo();
|
||||
if (info.account) {
|
||||
|
||||
Reference in new issue
Block a user