Feature: Voucher tags. This will eventually help in filtering ledger based on tags and also to group vouchers.
This commit is contained in:
@ -1,13 +1,15 @@
|
||||
import { COMMA, ENTER } from '@angular/cdk/keycodes';
|
||||
import { AfterViewInit, Component, ElementRef, OnInit, OnDestroy, ViewChild } from '@angular/core';
|
||||
import { FormControl, FormGroup } from '@angular/forms';
|
||||
import { MatAutocompleteSelectedEvent } from '@angular/material/autocomplete';
|
||||
import { MatChipInputEvent } from '@angular/material/chips';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { Hotkey, HotkeysService } from 'angular2-hotkeys';
|
||||
import { round } from 'mathjs';
|
||||
import moment from 'moment';
|
||||
import { BehaviorSubject, Observable, of as observableOf } from 'rxjs';
|
||||
import { debounceTime, distinctUntilChanged, switchMap } from 'rxjs/operators';
|
||||
import { debounceTime, distinctUntilChanged, map, switchMap } from 'rxjs/operators';
|
||||
|
||||
import { AuthService } from '../auth/auth.service';
|
||||
import { Account } from '../core/account';
|
||||
@ -18,6 +20,8 @@ import { DbFile } from '../core/db-file';
|
||||
import { Inventory } from '../core/inventory';
|
||||
import { Product } from '../core/product';
|
||||
import { ProductSku } from '../core/product-sku';
|
||||
import { Tag } from '../core/tag';
|
||||
import { TagService } from '../core/tag.service';
|
||||
import { ToasterService } from '../core/toaster.service';
|
||||
import { User } from '../core/user';
|
||||
import { Voucher } from '../core/voucher';
|
||||
@ -40,6 +44,8 @@ export class PurchaseComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
@ViewChild('accountElement', { static: true }) accountElement?: ElementRef;
|
||||
@ViewChild('productElement', { static: true }) productElement?: ElementRef;
|
||||
@ViewChild('dateElement', { static: true }) dateElement?: ElementRef;
|
||||
@ViewChild('tagInput') tagInput?: ElementRef<HTMLInputElement>;
|
||||
separatorKeysCodes: number[] = [ENTER, COMMA];
|
||||
public inventoryObservable = new BehaviorSubject<Inventory[]>([]);
|
||||
dataSource: PurchaseDataSource = new PurchaseDataSource(this.inventoryObservable);
|
||||
form: FormGroup<{
|
||||
@ -54,6 +60,7 @@ export class PurchaseComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
discount: FormControl<string>;
|
||||
}>;
|
||||
narration: FormControl<string>;
|
||||
tags: FormControl<string | null>;
|
||||
}>;
|
||||
|
||||
voucher: Voucher = new Voucher();
|
||||
@ -63,6 +70,7 @@ export class PurchaseComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
displayedColumns = ['product', 'quantity', 'rate', 'tax', 'discount', 'amount', 'action'];
|
||||
|
||||
accounts: Observable<Account[]>;
|
||||
tags: Observable<Tag[]>;
|
||||
products: Observable<ProductSku[]>;
|
||||
|
||||
constructor(
|
||||
@ -77,6 +85,7 @@ export class PurchaseComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
private ser: VoucherService,
|
||||
private productSer: ProductService,
|
||||
private accountSer: AccountService,
|
||||
private tagSer: TagService,
|
||||
) {
|
||||
this.form = new FormGroup({
|
||||
date: new FormControl(new Date(), { nonNullable: true }),
|
||||
@ -90,6 +99,7 @@ export class PurchaseComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
discount: new FormControl('', { nonNullable: true }),
|
||||
}),
|
||||
narration: new FormControl('', { nonNullable: true }),
|
||||
tags: new FormControl(''),
|
||||
});
|
||||
this.accBal = null;
|
||||
// Listen to Account Autocomplete Change
|
||||
@ -98,6 +108,12 @@ export class PurchaseComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
distinctUntilChanged(),
|
||||
switchMap((x) => (x === null ? observableOf([]) : this.accountSer.autocomplete(x))),
|
||||
);
|
||||
this.tags = this.form.controls.tags.valueChanges.pipe(
|
||||
debounceTime(150),
|
||||
distinctUntilChanged(),
|
||||
map((tag: string | Tag | null) => (tag === null ? '' : typeof tag !== 'string' ? tag.name.toLowerCase() : tag)),
|
||||
switchMap((tag: string) => this.tagSer.autocomplete(tag)),
|
||||
);
|
||||
// Listen to Product Autocomplete Change
|
||||
this.products = this.form.controls.addRow.controls.product.valueChanges.pipe(
|
||||
debounceTime(150),
|
||||
@ -184,6 +200,7 @@ export class PurchaseComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
discount: '',
|
||||
},
|
||||
narration: this.voucher.narration,
|
||||
tags: '',
|
||||
});
|
||||
this.dataSource = new PurchaseDataSource(this.inventoryObservable);
|
||||
this.updateView();
|
||||
@ -393,4 +410,38 @@ export class PurchaseComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
const index = this.voucher.files.indexOf(file);
|
||||
this.voucher.files.splice(index, 1);
|
||||
}
|
||||
|
||||
removeTag(tag: Tag): void {
|
||||
const index = this.voucher.tags.indexOf(tag);
|
||||
|
||||
if (index >= 0) {
|
||||
this.voucher.tags.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
addTag(event: MatChipInputEvent): void {
|
||||
const value = (event.value || '').trim();
|
||||
|
||||
// Add our tag
|
||||
if (value) {
|
||||
this.voucher.tags.push(new Tag({ name: value }));
|
||||
}
|
||||
|
||||
// Clear the input value
|
||||
event.chipInput!.clear();
|
||||
|
||||
this.form.controls.tags.setValue(null);
|
||||
}
|
||||
|
||||
selectedTag(event: MatAutocompleteSelectedEvent): void {
|
||||
const tag = event.option.value as Tag;
|
||||
const index = this.voucher.tags.findIndex((t) => (tag.id === null ? t.name === tag.name : t.id === tag.id));
|
||||
if (index === -1) {
|
||||
this.voucher.tags.push(tag);
|
||||
}
|
||||
if (this.tagInput) {
|
||||
this.tagInput.nativeElement.value = '';
|
||||
}
|
||||
this.form.controls.tags.setValue(null);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user