import { Injectable } from '@angular/core'; import { MatDialog } from '@angular/material'; import { BehaviorSubject } from 'rxjs'; import { Observable } from 'rxjs/internal/Observable'; import * as math from 'mathjs'; import { Product } from '../core/product'; import { ModifiersComponent } from './modifiers/modifiers.component'; import { ModifierCategoryService } from '../modifier-categories/modifier-category.service'; import { ModifierCategory } from '../core/modifier-category'; import { Bill, Inventory, Kot, PrintType } from './bills/bill'; import { VoucherService } from './bills/voucher.service'; import { ToasterService } from '../core/toaster.service'; import { Table } from '../core/table'; import { SelectionModel } from '@angular/cdk/collections'; @Injectable() export class BillService { public dataObs; public data: any[]; public bill; public netAmount: BehaviorSubject; public discountAmount: BehaviorSubject; public taxAmount: BehaviorSubject; public amount: BehaviorSubject; public selection = new SelectionModel(true, []); constructor( private dialog: MatDialog, private toaster: ToasterService, private ser: VoucherService, private modifierCategoryService: ModifierCategoryService ) { this.data = []; this.dataObs = new BehaviorSubject(this.data); this.netAmount = new BehaviorSubject(0); this.discountAmount = new BehaviorSubject(0); this.taxAmount = new BehaviorSubject(0); this.amount = new BehaviorSubject(0); } loadData(bill: Bill): void { this.bill = bill; const view = this.bill.kots.map(k => { return [{ id: k.id, isKot: true, oldKot: true, info: `Kot: ${k.code} / ${k.date} (${k.user.name}) ` }, ...k.inventories.map(i => { return { id: i.id, kotId: k.id, isKot: false, product: i.product, productId: i.product.id, isHappyHour: i.isHappyHour, isPrinted: true, info: `${i.product.name} @ ${i.price} - ${math.round(i.discount * 100, 2)}%`, price: i.price, quantity: i.quantity, discount: i.discount, taxRate: i.taxRate, tax: i.tax, modifiers: i.modifiers }; })]; }); this.data = view.reduce((a, c) => a.concat(c) , []); this.data.push({isKot: true, newKot: true, info: '== New Kot =='}); this.dataObs.next(this.data); this.updateAmounts(); } addProduct(product: Product): void { const old = this.data.find( x => !x.isKot && !x.id && x.productId === product.id && x.isHappyHour === product.hasHappyHour ); if (old !== undefined) { old.quantity += 1; } else { const item = { isKot: false, product: product, productId: product.id, isHappyHour: product.hasHappyHour, info: `${product.name} @ ${product.price} - ${0}%`, price: product.price, quantity: 1, discount: 0, taxRate: product.tax.rate, tax: product.tax, modifiers: [] }; this.data.push(item); this.modifierCategoryService.listIsActiveOfProduct(product.id).subscribe(result => { if (result.reduce((a: any, c: ModifierCategory) => { return a + c.minimum; }, 0)) { this.showModifier(item); } }); } this.dataObs.next(this.data); this.updateAmounts(); } showModifier(item: any): void { // [routerLink]="['/sales', 'modifiers', item.id]" const dialogRef = this.dialog.open(ModifiersComponent, { position: { top: '10vh' }, data: { list: this.modifierCategoryService.listIsActiveOfProduct(item.productId), selected: item.modifiers } }); dialogRef.afterClosed().subscribe(result => { if (result !== undefined) { item.modifiers = result; } }); } addOne(item: any): void { item.quantity += 1; this.dataObs.next(this.data); this.updateAmounts(); } quantity(item: any, quantity: number): void { item.quantity = quantity; this.dataObs.next(this.data); this.updateAmounts(); } subtractOne(item: any): void { if (item.quantity > 1) { item.quantity -= 1; this.dataObs.next(this.data); this.updateAmounts(); } else if (item.quantity === 0) { this.removeItem(item); } } removeItem(item: any): void { this.data.splice(this.data.indexOf(item), 1); this.dataObs.next(this.data); this.updateAmounts(); } modifier(item: any): void { this.showModifier(item); } discount(discounts: {id: string, name: string, discount: number}[]): void { this.data.forEach(x => { if (!x.isKot) { x.discount = discounts.find(d => d.id === x.product.saleCategory.id).discount / 100; x.info = `${x.product.name} @ ${x.price} - ${math.round(x.discount * 100, 2)}%`; } }); this.dataObs.next(this.data); this.updateAmounts(); } private getKot(): Kot { return new Kot({ inventories: this.data.filter(x => !x.isKot && !x.isPrinted).map(y => new Inventory({ product: y.product, quantity: y.quantity, price: y.price, isHappyHour: y.isHappyHour, discount: y.discount, modifiers: y.modifiers, taxRate: y.taxRate, tax: y.tax })) }); } printKot(guest_book_id: string): Observable { const item = JSON.parse(JSON.stringify(this.bill)); const newKot = this.getKot(); if (newKot.inventories.length === 0) { this.toaster.show('Error', 'Cannot print a blank KOT\nPlease add some products!'); } item.kots.push(newKot); return this.ser.saveOrUpdate(item, PrintType.Kot, guest_book_id, true); } printBill(guest_book_id: string, printType: PrintType): Observable { const item = JSON.parse(JSON.stringify(this.bill)); item.kots.forEach(k => { k.inventories.forEach(i => { i.discount = this.data.find(x => !x.isKot && x.id === i.id).discount; }); }); item.kots.push(this.getKot()); return this.ser.saveOrUpdate(item, printType, guest_book_id, true); } type() { return this.bill.voucherType; } receivePayment(amounts: { id: string; name: string; amount: number }[]): Observable { return this.ser.receivePayment(this.bill.id, amounts, true); } moveTable(table: Table): Observable { return this.ser.moveTable(this.bill.id, table); } mergeTable(table: Table): Observable { return this.ser.mergeTable(this.bill.id, table); } moveKot(kotId: string, table: Table): Observable { return this.ser.moveKotToNewTable(this.bill.id, kotId, table); } mergeKot(kotId: string, table: Table): Observable { return this.ser.mergeKotWithOldBill(this.bill.id, kotId, table); } voidBill(reason: string): Observable { return this.ser.voidBill(this.bill.id, reason, true); } updateAmounts() { this.netAmount.next( math.round(this.data.filter(x => !x.isKot).reduce( (ca: number, c: any) => ca + ((c.isHappyHour ? 0 : c.price) * c.quantity) , 0)) ); this.discountAmount.next( math.round(this.data.filter(x => !x.isKot).reduce( (ca: number, c: Inventory) => ca + ((c.isHappyHour ? 0 : c.price) * c.quantity * c.discount) , 0)) ); this.taxAmount.next( math.round(this.data.filter(x => !x.isKot).reduce( (ca: number, c: Inventory) => ca + ((c.isHappyHour ? 0 : c.price) * c.quantity * (1 - c.discount) * c.taxRate) , 0)) ); this.amount.next( math.round(this.data.filter(x => !x.isKot).reduce( (ca: number, c: Inventory) => ca + ((c.isHappyHour ? 0 : c.price) * c.quantity * (1 - c.discount) * (1 + c.taxRate)) , 0)) ); } amountVal(): number { return math.round(this.bill.kots.reduce( (ka: number, k: Kot) => ka + k.inventories.reduce( (ca: number, c: Inventory) => ca + ((c.isHappyHour ? 0 : c.price) * c.quantity * (1 - c.discount) * (1 + c.taxRate)) , 0) , 0)); } splitBill(table: Table): Observable { const inventoriesToMove: string[] = this.selection.selected.map((x: any) => x.id); return this.ser.splitBill(this.bill.id, inventoriesToMove, table); } }