2020-10-11 05:26:29 +00:00
|
|
|
import { SelectionModel } from '@angular/cdk/collections';
|
2019-07-11 06:47:41 +00:00
|
|
|
import { Injectable } from '@angular/core';
|
2020-06-14 13:13:10 +00:00
|
|
|
import { MatDialog } from '@angular/material/dialog';
|
2020-10-11 05:26:29 +00:00
|
|
|
import * as math from 'mathjs';
|
2019-07-13 16:02:18 +00:00
|
|
|
import { BehaviorSubject } from 'rxjs';
|
2019-08-08 08:01:30 +00:00
|
|
|
import { Observable } from 'rxjs/internal/Observable';
|
2020-10-11 05:26:29 +00:00
|
|
|
|
2020-11-22 04:43:37 +00:00
|
|
|
import { BillViewItem } from '../core/bill-view-item';
|
2020-10-11 05:26:29 +00:00
|
|
|
import { ModifierCategory } from '../core/modifier-category';
|
2019-07-11 06:47:41 +00:00
|
|
|
import { Product } from '../core/product';
|
2020-12-13 04:14:32 +00:00
|
|
|
import { ReceivePaymentItem } from '../core/receive-payment-item';
|
2020-10-11 05:26:29 +00:00
|
|
|
import { Table } from '../core/table';
|
|
|
|
import { ToasterService } from '../core/toaster.service';
|
2019-07-11 06:47:41 +00:00
|
|
|
import { ModifierCategoryService } from '../modifier-categories/modifier-category.service';
|
2020-10-11 05:26:29 +00:00
|
|
|
|
|
|
|
import { Bill } from './bills/bill';
|
|
|
|
import { Inventory } from './bills/inventory';
|
|
|
|
import { Kot } from './bills/kot';
|
2020-11-22 04:43:37 +00:00
|
|
|
import { VoucherType } from './bills/voucher-type';
|
2019-08-08 08:01:30 +00:00
|
|
|
import { VoucherService } from './bills/voucher.service';
|
2020-10-11 05:26:29 +00:00
|
|
|
import { ModifiersComponent } from './modifiers/modifiers.component';
|
2019-07-11 06:47:41 +00:00
|
|
|
|
|
|
|
@Injectable()
|
|
|
|
export class BillService {
|
2020-11-25 03:57:42 +00:00
|
|
|
public dataObs: BehaviorSubject<BillViewItem[]>;
|
|
|
|
public data: BillViewItem[];
|
2020-11-22 04:43:37 +00:00
|
|
|
public bill: Bill = new Bill();
|
2019-08-10 20:07:14 +00:00
|
|
|
public netAmount: BehaviorSubject<number>;
|
|
|
|
public discountAmount: BehaviorSubject<number>;
|
|
|
|
public taxAmount: BehaviorSubject<number>;
|
|
|
|
public amount: BehaviorSubject<number>;
|
2020-11-25 03:57:42 +00:00
|
|
|
public selection = new SelectionModel<BillViewItem>(true, []);
|
2019-07-12 07:06:38 +00:00
|
|
|
|
2019-07-11 06:47:41 +00:00
|
|
|
constructor(
|
2019-07-12 07:06:38 +00:00
|
|
|
private dialog: MatDialog,
|
2019-08-08 08:01:30 +00:00
|
|
|
private toaster: ToasterService,
|
2019-07-13 16:02:18 +00:00
|
|
|
private ser: VoucherService,
|
2020-10-11 05:26:29 +00:00
|
|
|
private modifierCategoryService: ModifierCategoryService,
|
2019-07-12 07:06:38 +00:00
|
|
|
) {
|
|
|
|
this.data = [];
|
2020-11-25 03:57:42 +00:00
|
|
|
this.dataObs = new BehaviorSubject<BillViewItem[]>(this.data);
|
2019-08-10 20:07:14 +00:00
|
|
|
this.netAmount = new BehaviorSubject(0);
|
|
|
|
this.discountAmount = new BehaviorSubject(0);
|
|
|
|
this.taxAmount = new BehaviorSubject(0);
|
|
|
|
this.amount = new BehaviorSubject(0);
|
2019-07-12 07:06:38 +00:00
|
|
|
}
|
2019-07-11 06:47:41 +00:00
|
|
|
|
2019-07-13 16:02:18 +00:00
|
|
|
loadData(bill: Bill): void {
|
|
|
|
this.bill = bill;
|
2020-11-22 04:43:37 +00:00
|
|
|
const view: BillViewItem[][] = this.bill.kots.map((k: Kot) => [
|
|
|
|
new BillViewItem({
|
|
|
|
id: k.id,
|
|
|
|
isKot: true,
|
|
|
|
info: `Kot: ${k.code} / ${k.date} (${k.user.name}) `,
|
|
|
|
}),
|
|
|
|
...k.inventories.map(
|
|
|
|
(i) =>
|
|
|
|
new BillViewItem({
|
2020-10-11 05:26:29 +00:00
|
|
|
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,
|
2020-11-22 04:43:37 +00:00
|
|
|
}),
|
|
|
|
),
|
|
|
|
]);
|
2020-10-11 05:26:29 +00:00
|
|
|
this.data = view.reduce((a, c) => a.concat(c), []);
|
2020-11-25 03:57:42 +00:00
|
|
|
this.data.push(new BillViewItem({ isKot: true, info: '== New Kot ==' }));
|
2019-07-12 07:06:38 +00:00
|
|
|
this.dataObs.next(this.data);
|
2019-08-10 20:07:14 +00:00
|
|
|
this.updateAmounts();
|
2019-07-12 07:06:38 +00:00
|
|
|
}
|
2020-10-11 05:26:29 +00:00
|
|
|
|
2020-10-11 15:23:43 +00:00
|
|
|
minimum(productId: string, happyHour: boolean): number {
|
|
|
|
return this.data.reduce(
|
|
|
|
(a, c) => (c.productId === productId && c.isHappyHour === happyHour ? a + c.quantity : a),
|
|
|
|
0,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
addProduct(product: Product, quantity: number, discount: number): void {
|
2019-08-08 11:05:03 +00:00
|
|
|
const old = this.data.find(
|
2020-10-11 05:26:29 +00:00
|
|
|
(x) =>
|
|
|
|
!x.isKot && !x.id && x.productId === product.id && x.isHappyHour === product.hasHappyHour,
|
2019-08-08 11:05:03 +00:00
|
|
|
);
|
2020-10-11 15:23:43 +00:00
|
|
|
if (quantity < 0) {
|
2020-11-22 04:43:37 +00:00
|
|
|
const minimum = this.minimum(product.id as string, product.hasHappyHour) + quantity;
|
2020-10-11 15:23:43 +00:00
|
|
|
if (minimum + quantity < 0) {
|
|
|
|
this.toaster.show('Error', 'Total quantity cannot be negative!');
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2019-07-12 07:06:38 +00:00
|
|
|
if (old !== undefined) {
|
2020-10-11 15:23:43 +00:00
|
|
|
old.quantity += quantity;
|
2019-07-12 07:06:38 +00:00
|
|
|
} else {
|
2020-11-25 03:57:42 +00:00
|
|
|
const item = new BillViewItem({
|
2019-07-12 07:06:38 +00:00
|
|
|
isKot: false,
|
2020-10-11 05:26:29 +00:00
|
|
|
product,
|
2019-07-12 07:06:38 +00:00
|
|
|
productId: product.id,
|
|
|
|
isHappyHour: product.hasHappyHour,
|
2019-08-21 08:57:11 +00:00
|
|
|
info: `${product.name} @ ${product.price} - ${0}%`,
|
2019-07-13 16:02:18 +00:00
|
|
|
price: product.price,
|
2020-10-11 15:23:43 +00:00
|
|
|
quantity,
|
|
|
|
discount,
|
2019-08-10 20:07:14 +00:00
|
|
|
taxRate: product.tax.rate,
|
|
|
|
tax: product.tax,
|
2020-10-11 05:26:29 +00:00
|
|
|
modifiers: [],
|
2020-11-25 03:57:42 +00:00
|
|
|
});
|
2019-07-12 07:06:38 +00:00
|
|
|
this.data.push(item);
|
2020-11-22 04:43:37 +00:00
|
|
|
this.modifierCategoryService.listForProduct(product.id as string).subscribe((result) => {
|
2020-12-08 13:20:46 +00:00
|
|
|
if (result.reduce((a: number, c: ModifierCategory) => a + c.minimum, 0)) {
|
2019-07-12 07:06:38 +00:00
|
|
|
this.showModifier(item);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
2019-07-11 06:47:41 +00:00
|
|
|
this.dataObs.next(this.data);
|
2019-08-10 20:07:14 +00:00
|
|
|
this.updateAmounts();
|
2019-07-11 06:47:41 +00:00
|
|
|
}
|
|
|
|
|
2020-11-25 03:57:42 +00:00
|
|
|
showModifier(item: BillViewItem): void {
|
2019-07-11 06:47:41 +00:00
|
|
|
// [routerLink]="['/sales', 'modifiers', item.id]"
|
|
|
|
const dialogRef = this.dialog.open(ModifiersComponent, {
|
|
|
|
position: {
|
2020-10-11 05:26:29 +00:00
|
|
|
top: '10vh',
|
2019-07-11 06:47:41 +00:00
|
|
|
},
|
|
|
|
data: {
|
2020-11-25 03:57:42 +00:00
|
|
|
list: this.modifierCategoryService.listForProduct(item.productId as string),
|
2020-10-11 05:26:29 +00:00
|
|
|
selected: item.modifiers,
|
|
|
|
},
|
2019-07-11 06:47:41 +00:00
|
|
|
});
|
|
|
|
|
2020-10-11 05:26:29 +00:00
|
|
|
dialogRef.afterClosed().subscribe((result) => {
|
2019-07-12 07:06:38 +00:00
|
|
|
if (result !== undefined) {
|
2019-07-11 06:47:41 +00:00
|
|
|
item.modifiers = result;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2020-11-25 03:57:42 +00:00
|
|
|
addOne(item: BillViewItem): void {
|
2019-07-11 06:47:41 +00:00
|
|
|
item.quantity += 1;
|
|
|
|
this.dataObs.next(this.data);
|
2019-08-10 20:07:14 +00:00
|
|
|
this.updateAmounts();
|
2019-07-11 06:47:41 +00:00
|
|
|
}
|
|
|
|
|
2020-11-25 03:57:42 +00:00
|
|
|
quantity(item: BillViewItem, quantity: number): void {
|
2019-07-13 16:02:18 +00:00
|
|
|
item.quantity = quantity;
|
|
|
|
this.dataObs.next(this.data);
|
2019-08-10 20:07:14 +00:00
|
|
|
this.updateAmounts();
|
2019-07-11 06:47:41 +00:00
|
|
|
}
|
|
|
|
|
2020-11-25 03:57:42 +00:00
|
|
|
subtractOne(item: BillViewItem, canEdit: boolean): void {
|
|
|
|
if (
|
|
|
|
item.quantity > 1 ||
|
|
|
|
(canEdit && this.minimum(item.productId as string, item.isHappyHour) >= 1)
|
|
|
|
) {
|
2019-07-11 06:47:41 +00:00
|
|
|
item.quantity -= 1;
|
|
|
|
this.dataObs.next(this.data);
|
2019-08-10 20:07:14 +00:00
|
|
|
this.updateAmounts();
|
2019-07-11 06:47:41 +00:00
|
|
|
} else if (item.quantity === 0) {
|
|
|
|
this.removeItem(item);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-25 03:57:42 +00:00
|
|
|
removeItem(item: BillViewItem): void {
|
2019-07-11 06:47:41 +00:00
|
|
|
this.data.splice(this.data.indexOf(item), 1);
|
|
|
|
this.dataObs.next(this.data);
|
2019-08-10 20:07:14 +00:00
|
|
|
this.updateAmounts();
|
2019-07-11 06:47:41 +00:00
|
|
|
}
|
|
|
|
|
2020-11-25 03:57:42 +00:00
|
|
|
modifier(item: BillViewItem): void {
|
2019-07-11 06:47:41 +00:00
|
|
|
this.showModifier(item);
|
|
|
|
}
|
|
|
|
|
2020-10-11 05:26:29 +00:00
|
|
|
discount(discounts: { id: string; name: string; discount: number }[]): void {
|
|
|
|
this.data.forEach((x) => {
|
2019-08-08 08:01:30 +00:00
|
|
|
if (!x.isKot) {
|
2020-11-22 04:43:37 +00:00
|
|
|
x.discount =
|
|
|
|
(discounts.find((d) => d.id === x.product.saleCategory.id) as {
|
|
|
|
id: string;
|
|
|
|
name: string;
|
|
|
|
discount: number;
|
|
|
|
}).discount / 100;
|
2019-08-21 08:57:11 +00:00
|
|
|
x.info = `${x.product.name} @ ${x.price} - ${math.round(x.discount * 100, 2)}%`;
|
2019-08-08 08:01:30 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
this.dataObs.next(this.data);
|
2019-08-10 20:07:14 +00:00
|
|
|
this.updateAmounts();
|
2019-08-08 08:01:30 +00:00
|
|
|
}
|
|
|
|
|
2020-11-22 04:43:37 +00:00
|
|
|
printKot(guestBookId: string | null): Observable<boolean> {
|
2019-08-10 20:07:14 +00:00
|
|
|
const item = JSON.parse(JSON.stringify(this.bill));
|
|
|
|
const newKot = this.getKot();
|
2019-08-20 17:32:17 +00:00
|
|
|
if (newKot.inventories.length === 0) {
|
2019-08-08 08:01:30 +00:00
|
|
|
this.toaster.show('Error', 'Cannot print a blank KOT\nPlease add some products!');
|
|
|
|
}
|
|
|
|
item.kots.push(newKot);
|
2020-11-22 04:43:37 +00:00
|
|
|
return this.ser.saveOrUpdate(item, VoucherType.Kot, guestBookId, true);
|
2019-07-13 16:02:18 +00:00
|
|
|
}
|
|
|
|
|
2020-11-22 04:43:37 +00:00
|
|
|
printBill(guest_book_id: string | null, voucherType: VoucherType): Observable<boolean> {
|
2019-08-10 20:07:14 +00:00
|
|
|
const item = JSON.parse(JSON.stringify(this.bill));
|
2020-11-22 04:43:37 +00:00
|
|
|
item.kots.forEach((k: Kot) => {
|
|
|
|
k.inventories.forEach((i: Inventory) => {
|
2020-11-25 03:57:42 +00:00
|
|
|
i.discount = (this.data.find((x) => !x.isKot && x.id === i.id) as BillViewItem).discount;
|
2019-08-10 20:07:14 +00:00
|
|
|
});
|
2019-08-08 08:01:30 +00:00
|
|
|
});
|
|
|
|
item.kots.push(this.getKot());
|
2020-11-22 04:43:37 +00:00
|
|
|
return this.ser.saveOrUpdate(item, voucherType, guest_book_id, true);
|
2019-07-13 16:02:18 +00:00
|
|
|
}
|
|
|
|
|
2019-08-09 09:25:38 +00:00
|
|
|
type() {
|
|
|
|
return this.bill.voucherType;
|
|
|
|
}
|
|
|
|
|
2020-12-13 04:14:32 +00:00
|
|
|
receivePayment(value: { choices: ReceivePaymentItem[]; reason: string }): Observable<boolean> {
|
|
|
|
return this.ser.receivePayment(this.bill.id as string, value.choices, value.reason, true);
|
2019-08-09 09:25:38 +00:00
|
|
|
}
|
2019-08-10 08:51:40 +00:00
|
|
|
|
|
|
|
moveTable(table: Table): Observable<boolean> {
|
2020-11-22 04:43:37 +00:00
|
|
|
return this.ser.moveTable(this.bill.id as string, table);
|
2019-08-10 08:51:40 +00:00
|
|
|
}
|
2019-08-10 13:19:05 +00:00
|
|
|
|
2019-08-17 19:35:59 +00:00
|
|
|
mergeTable(table: Table): Observable<boolean> {
|
2020-11-22 04:43:37 +00:00
|
|
|
return this.ser.mergeTable(this.bill.id as string, table);
|
2019-08-17 19:35:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
moveKot(kotId: string, table: Table): Observable<boolean> {
|
2020-11-22 04:43:37 +00:00
|
|
|
return this.ser.moveKotToNewTable(this.bill.id as string, kotId, table);
|
2019-08-17 19:35:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
mergeKot(kotId: string, table: Table): Observable<boolean> {
|
2020-11-22 04:43:37 +00:00
|
|
|
return this.ser.mergeKotWithOldBill(this.bill.id as string, kotId, table);
|
2019-08-10 13:19:05 +00:00
|
|
|
}
|
2019-08-10 05:00:27 +00:00
|
|
|
|
2019-08-10 20:07:14 +00:00
|
|
|
voidBill(reason: string): Observable<boolean> {
|
2020-11-22 04:43:37 +00:00
|
|
|
return this.ser.voidBill(this.bill.id as string, reason, true);
|
2019-08-10 05:00:27 +00:00
|
|
|
}
|
|
|
|
|
2019-08-10 20:07:14 +00:00
|
|
|
updateAmounts() {
|
|
|
|
this.netAmount.next(
|
2020-10-11 05:26:29 +00:00
|
|
|
math.round(
|
|
|
|
this.data
|
|
|
|
.filter((x) => !x.isKot)
|
2020-11-25 03:57:42 +00:00
|
|
|
.reduce(
|
|
|
|
(ca: number, c: BillViewItem) => ca + (c.isHappyHour ? 0 : c.price) * c.quantity,
|
|
|
|
0,
|
|
|
|
),
|
2020-10-11 05:26:29 +00:00
|
|
|
),
|
2019-08-10 20:07:14 +00:00
|
|
|
);
|
|
|
|
this.discountAmount.next(
|
2020-10-11 05:26:29 +00:00
|
|
|
math.round(
|
|
|
|
this.data
|
|
|
|
.filter((x) => !x.isKot)
|
|
|
|
.reduce(
|
2020-11-25 03:57:42 +00:00
|
|
|
(ca: number, c: BillViewItem) =>
|
2020-10-11 05:26:29 +00:00
|
|
|
ca + (c.isHappyHour ? 0 : c.price) * c.quantity * c.discount,
|
|
|
|
0,
|
|
|
|
),
|
|
|
|
),
|
2019-08-10 20:07:14 +00:00
|
|
|
);
|
|
|
|
this.taxAmount.next(
|
2020-10-11 05:26:29 +00:00
|
|
|
math.round(
|
|
|
|
this.data
|
|
|
|
.filter((x) => !x.isKot)
|
|
|
|
.reduce(
|
2020-11-25 03:57:42 +00:00
|
|
|
(ca: number, c: BillViewItem) =>
|
2020-10-11 05:26:29 +00:00
|
|
|
ca + (c.isHappyHour ? 0 : c.price) * c.quantity * (1 - c.discount) * c.taxRate,
|
|
|
|
0,
|
|
|
|
),
|
|
|
|
),
|
2019-08-10 20:07:14 +00:00
|
|
|
);
|
|
|
|
this.amount.next(
|
2020-10-11 05:26:29 +00:00
|
|
|
math.round(
|
|
|
|
this.data
|
|
|
|
.filter((x) => !x.isKot)
|
|
|
|
.reduce(
|
2020-11-25 03:57:42 +00:00
|
|
|
(ca: number, c: BillViewItem) =>
|
2020-10-11 05:26:29 +00:00
|
|
|
ca + (c.isHappyHour ? 0 : c.price) * c.quantity * (1 - c.discount) * (1 + c.taxRate),
|
|
|
|
0,
|
|
|
|
),
|
|
|
|
),
|
2019-08-10 20:07:14 +00:00
|
|
|
);
|
2019-08-10 05:00:27 +00:00
|
|
|
}
|
|
|
|
|
2019-08-10 20:07:14 +00:00
|
|
|
amountVal(): number {
|
2020-10-11 05:26:29 +00:00
|
|
|
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,
|
|
|
|
),
|
|
|
|
);
|
2019-08-10 05:00:27 +00:00
|
|
|
}
|
2019-08-17 19:35:59 +00:00
|
|
|
|
|
|
|
splitBill(table: Table): Observable<boolean> {
|
2020-11-25 03:57:42 +00:00
|
|
|
const inventoriesToMove: string[] = this.selection.selected.map(
|
|
|
|
(x: BillViewItem) => x.id as string,
|
|
|
|
);
|
2020-11-22 04:43:37 +00:00
|
|
|
return this.ser.splitBill(this.bill.id as string, inventoriesToMove, table);
|
2019-08-17 19:35:59 +00:00
|
|
|
}
|
2020-12-08 13:20:46 +00:00
|
|
|
|
|
|
|
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,
|
|
|
|
}),
|
|
|
|
),
|
|
|
|
});
|
|
|
|
}
|
2019-07-11 06:47:41 +00:00
|
|
|
}
|