Chore: In overlord / sale / bill.service now the BillViewItem is just a view item
The data is kept as the original bill object and this view generated on every change. It has no sanctity. To deal with the challenges of Selection of items in the bill.component.html created a bill selection item. This is converted to string while checking else the selection model fails. Feature: It now checks if Happy Hour items have equivalent regular items in each kot. Feature: Discount won't apply to happy hour items. Checks for both are both in front end and back end.
This commit is contained in:
@ -2,6 +2,8 @@ import uuid
|
|||||||
|
|
||||||
from typing import List, Optional
|
from typing import List, Optional
|
||||||
|
|
||||||
|
import barker.schemas.voucher as schemas
|
||||||
|
|
||||||
from fastapi import HTTPException, status
|
from fastapi import HTTPException, status
|
||||||
from sqlalchemy import func
|
from sqlalchemy import func
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
@ -116,3 +118,17 @@ def do_update_settlements(voucher: Voucher, others: List[SettleSchema], db: Sess
|
|||||||
for i in (i for i in voucher.settlements if i.settled not in [x.id_ for x in settlements]):
|
for i in (i for i in voucher.settlements if i.settled not in [x.id_ for x in settlements]):
|
||||||
voucher.settlements.remove(i)
|
voucher.settlements.remove(i)
|
||||||
db.delete(i)
|
db.delete(i)
|
||||||
|
|
||||||
|
|
||||||
|
def happy_hour_items_balanced(inventories: [schemas.Inventory]):
|
||||||
|
happy = set((i.product.id_, i.quantity) for i in inventories if i.is_happy_hour)
|
||||||
|
other = set(
|
||||||
|
(i.product.id_, i.quantity) for i in inventories if not i.is_happy_hour and (i.product.id_, i.quantity) in happy
|
||||||
|
)
|
||||||
|
return happy == other
|
||||||
|
|
||||||
|
|
||||||
|
def happy_hour_has_discount(inventories: [schemas.Inventory]):
|
||||||
|
happy = set(i.product.id_ for i in inventories if i.is_happy_hour)
|
||||||
|
offenders = [i for i in inventories if i.product.id_ in happy and i.discount != 0]
|
||||||
|
return len(offenders) > 0
|
||||||
|
|||||||
@ -24,6 +24,8 @@ from ...routers.voucher import (
|
|||||||
get_bill_id,
|
get_bill_id,
|
||||||
get_guest_book,
|
get_guest_book,
|
||||||
get_tax,
|
get_tax,
|
||||||
|
happy_hour_has_discount,
|
||||||
|
happy_hour_items_balanced,
|
||||||
)
|
)
|
||||||
from ...schemas.auth import UserToken
|
from ...schemas.auth import UserToken
|
||||||
|
|
||||||
@ -99,6 +101,16 @@ def do_save(
|
|||||||
for k in data.kots:
|
for k in data.kots:
|
||||||
if not len(k.inventories):
|
if not len(k.inventories):
|
||||||
continue
|
continue
|
||||||
|
if not happy_hour_items_balanced(k.inventories):
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
|
||||||
|
detail="Happy hour products are not balanced.",
|
||||||
|
)
|
||||||
|
if happy_hour_has_discount(k.inventories):
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
|
||||||
|
detail="Discount is not allowed on happy hour products.",
|
||||||
|
)
|
||||||
code = db.query(func.coalesce(func.max(Kot.code), 0) + 1).scalar()
|
code = db.query(func.coalesce(func.max(Kot.code), 0) + 1).scalar()
|
||||||
kot = Kot(item.id, code, item.food_table_id, item.date, item.user_id)
|
kot = Kot(item.id, code, item.food_table_id, item.date, item.user_id)
|
||||||
item.kots.append(kot)
|
item.kots.append(kot)
|
||||||
|
|||||||
@ -24,6 +24,8 @@ from ...routers.voucher import (
|
|||||||
get_bill_id,
|
get_bill_id,
|
||||||
get_guest_book,
|
get_guest_book,
|
||||||
get_tax,
|
get_tax,
|
||||||
|
happy_hour_has_discount,
|
||||||
|
happy_hour_items_balanced,
|
||||||
)
|
)
|
||||||
from ...schemas.auth import UserToken
|
from ...schemas.auth import UserToken
|
||||||
|
|
||||||
@ -87,6 +89,17 @@ def update(
|
|||||||
i.discount = next(
|
i.discount = next(
|
||||||
round(inv.discount, 5) for ko in data.kots for inv in ko.inventories if inv.id_ == i.id
|
round(inv.discount, 5) for ko in data.kots for inv in ko.inventories if inv.id_ == i.id
|
||||||
)
|
)
|
||||||
|
for k in data.kots:
|
||||||
|
if happy_hour_has_discount(k.inventories):
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
|
||||||
|
detail="Discount is not allowed on happy hour products.",
|
||||||
|
)
|
||||||
|
if not happy_hour_items_balanced(k.inventories):
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
|
||||||
|
detail="Happy hour products are not balanced.",
|
||||||
|
)
|
||||||
for k in (k for k in data.kots if k.id_ is None and len(k.inventories) > 0):
|
for k in (k for k in data.kots if k.id_ is None and len(k.inventories) > 0):
|
||||||
need_to_print_kot = True
|
need_to_print_kot = True
|
||||||
code = db.query(func.coalesce(func.max(Kot.code), 0) + 1).scalar()
|
code = db.query(func.coalesce(func.max(Kot.code), 0) + 1).scalar()
|
||||||
|
|||||||
@ -4,42 +4,32 @@ import { Tax } from './tax';
|
|||||||
|
|
||||||
export class BillViewItem {
|
export class BillViewItem {
|
||||||
id: string | undefined;
|
id: string | undefined;
|
||||||
|
kotId: string | undefined;
|
||||||
isKot: boolean;
|
isKot: boolean;
|
||||||
info: string;
|
info: string;
|
||||||
|
|
||||||
kotId: string;
|
productId: string;
|
||||||
product: Product;
|
|
||||||
productId: string | undefined;
|
|
||||||
isHappyHour: boolean;
|
isHappyHour: boolean;
|
||||||
isPrinted: boolean;
|
isPrinted: boolean;
|
||||||
price: number;
|
|
||||||
quantity: number;
|
quantity: number;
|
||||||
discount: number;
|
|
||||||
taxRate: number;
|
|
||||||
tax: Tax;
|
|
||||||
modifiers: Modifier[];
|
modifiers: Modifier[];
|
||||||
|
|
||||||
public get isOldKot(): boolean {
|
public get isOldKot(): boolean {
|
||||||
return this.isKot && this.id !== undefined;
|
return this.isKot && this.kotId !== undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
public get isNewKot(): boolean {
|
public get isNewKot(): boolean {
|
||||||
return this.isKot && this.id === undefined;
|
return this.isKot && this.kotId === undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
public constructor(init?: Partial<BillViewItem>) {
|
public constructor(init?: Partial<BillViewItem>) {
|
||||||
this.isKot = true;
|
this.isKot = true;
|
||||||
this.info = '';
|
this.info = '';
|
||||||
this.kotId = '';
|
this.kotId = '';
|
||||||
this.product = new Product();
|
this.productId = '';
|
||||||
this.productId = this.product.id;
|
|
||||||
this.isHappyHour = false;
|
this.isHappyHour = false;
|
||||||
this.isPrinted = false;
|
this.isPrinted = false;
|
||||||
this.price = 0;
|
|
||||||
this.quantity = 0;
|
this.quantity = 0;
|
||||||
this.discount = 0;
|
|
||||||
this.taxRate = 0;
|
|
||||||
this.tax = new Tax();
|
|
||||||
this.modifiers = [];
|
this.modifiers = [];
|
||||||
Object.assign(this, init);
|
Object.assign(this, init);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,8 +2,9 @@ import { SelectionModel } from '@angular/cdk/collections';
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { MatDialog } from '@angular/material/dialog';
|
import { MatDialog } from '@angular/material/dialog';
|
||||||
import { round } from 'mathjs';
|
import { round } from 'mathjs';
|
||||||
import { BehaviorSubject } from 'rxjs';
|
import { BehaviorSubject, throwError } from 'rxjs';
|
||||||
import { Observable } from 'rxjs/internal/Observable';
|
import { Observable } from 'rxjs/internal/Observable';
|
||||||
|
import { tap } from 'rxjs/operators';
|
||||||
|
|
||||||
import { BillViewItem } from '../core/bill-view-item';
|
import { BillViewItem } from '../core/bill-view-item';
|
||||||
import { ModifierCategory } from '../core/modifier-category';
|
import { ModifierCategory } from '../core/modifier-category';
|
||||||
@ -15,6 +16,7 @@ import { ToasterService } from '../core/toaster.service';
|
|||||||
import { ModifierCategoryService } from '../modifier-categories/modifier-category.service';
|
import { ModifierCategoryService } from '../modifier-categories/modifier-category.service';
|
||||||
|
|
||||||
import { Bill } from './bills/bill';
|
import { Bill } from './bills/bill';
|
||||||
|
import { BillSelectionItem } from './bills/bill-selection-item';
|
||||||
import { Inventory } from './bills/inventory';
|
import { Inventory } from './bills/inventory';
|
||||||
import { Kot } from './bills/kot';
|
import { Kot } from './bills/kot';
|
||||||
import { VoucherType } from './bills/voucher-type';
|
import { VoucherType } from './bills/voucher-type';
|
||||||
@ -24,13 +26,13 @@ import { ModifiersComponent } from './modifiers/modifiers.component';
|
|||||||
@Injectable()
|
@Injectable()
|
||||||
export class BillService {
|
export class BillService {
|
||||||
public dataObs: BehaviorSubject<BillViewItem[]>;
|
public dataObs: BehaviorSubject<BillViewItem[]>;
|
||||||
public data: BillViewItem[];
|
|
||||||
public bill: Bill = new Bill();
|
public bill: Bill = new Bill();
|
||||||
public netAmount: BehaviorSubject<number>;
|
public netAmount: BehaviorSubject<number>;
|
||||||
public discountAmount: BehaviorSubject<number>;
|
public discountAmount: BehaviorSubject<number>;
|
||||||
public taxAmount: BehaviorSubject<number>;
|
public taxAmount: BehaviorSubject<number>;
|
||||||
public amount: BehaviorSubject<number>;
|
public amount: BehaviorSubject<number>;
|
||||||
public selection = new SelectionModel<BillViewItem>(true, []);
|
public amountVal: number;
|
||||||
|
public selection = new SelectionModel<string>(true, []);
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private dialog: MatDialog,
|
private dialog: MatDialog,
|
||||||
@ -38,21 +40,28 @@ export class BillService {
|
|||||||
private ser: VoucherService,
|
private ser: VoucherService,
|
||||||
private modifierCategoryService: ModifierCategoryService,
|
private modifierCategoryService: ModifierCategoryService,
|
||||||
) {
|
) {
|
||||||
this.data = [];
|
this.dataObs = new BehaviorSubject<BillViewItem[]>([]);
|
||||||
this.dataObs = new BehaviorSubject<BillViewItem[]>(this.data);
|
|
||||||
this.netAmount = new BehaviorSubject(0);
|
this.netAmount = new BehaviorSubject(0);
|
||||||
this.discountAmount = new BehaviorSubject(0);
|
this.discountAmount = new BehaviorSubject(0);
|
||||||
this.taxAmount = new BehaviorSubject(0);
|
this.taxAmount = new BehaviorSubject(0);
|
||||||
this.amount = new BehaviorSubject(0);
|
this.amount = new BehaviorSubject(0);
|
||||||
|
this.amountVal = 0;
|
||||||
|
this.amount.pipe(tap((x) => (this.amountVal = x)));
|
||||||
}
|
}
|
||||||
|
|
||||||
loadData(bill: Bill): void {
|
displayBill(): void {
|
||||||
this.bill = bill;
|
const data = this.transformBillToView(this.bill);
|
||||||
const view: BillViewItem[][] = this.bill.kots.map((k: Kot) => [
|
this.dataObs.next(data);
|
||||||
|
this.updateAmounts();
|
||||||
|
}
|
||||||
|
|
||||||
|
transformBillToView(bill: Bill): BillViewItem[] {
|
||||||
|
const view: BillViewItem[] = bill.kots
|
||||||
|
.map((k: Kot) => [
|
||||||
new BillViewItem({
|
new BillViewItem({
|
||||||
id: k.id,
|
kotId: k.id,
|
||||||
isKot: true,
|
isKot: true,
|
||||||
info: `Kot: ${k.code} / ${k.date} (${k.user.name}) `,
|
info: k.id ? `Kot: ${k.code} / ${k.date} (${k.user.name})` : '== New Kot ==',
|
||||||
}),
|
}),
|
||||||
...k.inventories.map(
|
...k.inventories.map(
|
||||||
(i) =>
|
(i) =>
|
||||||
@ -60,37 +69,41 @@ export class BillService {
|
|||||||
id: i.id,
|
id: i.id,
|
||||||
kotId: k.id,
|
kotId: k.id,
|
||||||
isKot: false,
|
isKot: false,
|
||||||
product: i.product,
|
|
||||||
productId: i.product.id,
|
productId: i.product.id,
|
||||||
isHappyHour: i.isHappyHour,
|
isHappyHour: i.isHappyHour,
|
||||||
isPrinted: true,
|
isPrinted: !!k.id,
|
||||||
info: `${i.product.name} @ ${i.price} - ${round(i.discount * 100, 2)}%`,
|
info: `${i.product.name} @ ${i.price} - ${round(i.discount * 100, 2)}%`,
|
||||||
price: i.price,
|
|
||||||
quantity: i.quantity,
|
quantity: i.quantity,
|
||||||
discount: i.discount,
|
|
||||||
taxRate: i.taxRate,
|
|
||||||
tax: i.tax,
|
|
||||||
modifiers: i.modifiers,
|
modifiers: i.modifiers,
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
]);
|
])
|
||||||
this.data = view.reduce((a, c) => a.concat(c), []);
|
.reduce((a, c) => a.concat(c), []);
|
||||||
this.data.push(new BillViewItem({ isKot: true, info: '== New Kot ==' }));
|
return view;
|
||||||
this.dataObs.next(this.data);
|
}
|
||||||
this.updateAmounts();
|
|
||||||
|
loadData(bill: Bill): void {
|
||||||
|
bill.kots.push(new Kot());
|
||||||
|
this.bill = bill;
|
||||||
|
this.displayBill();
|
||||||
}
|
}
|
||||||
|
|
||||||
minimum(productId: string, happyHour: boolean): number {
|
minimum(productId: string, happyHour: boolean): number {
|
||||||
return this.data.reduce(
|
return this.bill.kots.reduce(
|
||||||
(a, c) => (c.productId === productId && c.isHappyHour === happyHour ? a + c.quantity : a),
|
(t, k) =>
|
||||||
|
k.inventories.reduce(
|
||||||
|
(a, c) =>
|
||||||
|
c.product.id === productId && c.isHappyHour === happyHour ? a + c.quantity : a,
|
||||||
|
0,
|
||||||
|
) + t,
|
||||||
0,
|
0,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
addProduct(product: Product, quantity: number, discount: number): void {
|
addProduct(product: Product, quantity: number, discount: number): void {
|
||||||
const old = this.data.find(
|
const newKot = this.bill.kots.find((k) => k.id === undefined) as Kot;
|
||||||
(x) =>
|
const old = newKot.inventories.find(
|
||||||
!x.isKot && !x.id && x.productId === product.id && x.isHappyHour === product.hasHappyHour,
|
(x) => x.product.id === product.id && x.isHappyHour === product.hasHappyHour,
|
||||||
);
|
);
|
||||||
if (quantity < 0) {
|
if (quantity < 0) {
|
||||||
const minimum = this.minimum(product.id as string, product.hasHappyHour) + quantity;
|
const minimum = this.minimum(product.id as string, product.hasHappyHour) + quantity;
|
||||||
@ -102,38 +115,34 @@ export class BillService {
|
|||||||
if (old !== undefined) {
|
if (old !== undefined) {
|
||||||
old.quantity += quantity;
|
old.quantity += quantity;
|
||||||
} else {
|
} else {
|
||||||
const item = new BillViewItem({
|
const item = new Inventory({
|
||||||
isKot: false,
|
|
||||||
product,
|
product,
|
||||||
productId: product.id,
|
|
||||||
isHappyHour: product.hasHappyHour,
|
|
||||||
info: `${product.name} @ ${product.price} - ${0}%`,
|
|
||||||
price: product.price,
|
|
||||||
quantity,
|
quantity,
|
||||||
discount,
|
price: product.price,
|
||||||
|
isHappyHour: product.hasHappyHour,
|
||||||
taxRate: product.tax.rate,
|
taxRate: product.tax.rate,
|
||||||
tax: product.tax,
|
tax: product.tax,
|
||||||
|
discount,
|
||||||
modifiers: [],
|
modifiers: [],
|
||||||
});
|
});
|
||||||
this.data.push(item);
|
newKot.inventories.push(item);
|
||||||
this.modifierCategoryService.listForProduct(product.id as string).subscribe((result) => {
|
this.modifierCategoryService.listForProduct(product.id as string).subscribe((result) => {
|
||||||
if (result.reduce((a: number, c: ModifierCategory) => a + c.minimum, 0)) {
|
if (result.reduce((a: number, c: ModifierCategory) => a + c.minimum, 0)) {
|
||||||
this.showModifier(item);
|
this.showModifier(item);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
this.dataObs.next(this.data);
|
this.displayBill();
|
||||||
this.updateAmounts();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
showModifier(item: BillViewItem): void {
|
showModifier(item: Inventory): void {
|
||||||
// [routerLink]="['/sales', 'modifiers', item.id]"
|
// [routerLink]="['/sales', 'modifiers', item.id]"
|
||||||
const dialogRef = this.dialog.open(ModifiersComponent, {
|
const dialogRef = this.dialog.open(ModifiersComponent, {
|
||||||
position: {
|
position: {
|
||||||
top: '10vh',
|
top: '10vh',
|
||||||
},
|
},
|
||||||
data: {
|
data: {
|
||||||
list: this.modifierCategoryService.listForProduct(item.productId as string),
|
list: this.modifierCategoryService.listForProduct(item.product.id as string),
|
||||||
selected: item.modifiers,
|
selected: item.modifiers,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@ -143,75 +152,99 @@ export class BillService {
|
|||||||
item.modifiers = result;
|
item.modifiers = result;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
this.displayBill();
|
||||||
}
|
}
|
||||||
|
|
||||||
addOne(item: BillViewItem): void {
|
addOne(item: BillViewItem): void {
|
||||||
item.quantity += 1;
|
const newKot = this.bill.kots.find((k) => k.id === undefined) as Kot;
|
||||||
this.dataObs.next(this.data);
|
const old = newKot.inventories.find(
|
||||||
this.updateAmounts();
|
(x) => x.product.id === item.productId && x.isHappyHour === item.isHappyHour,
|
||||||
|
) as Inventory;
|
||||||
|
old.quantity += 1;
|
||||||
|
this.displayBill();
|
||||||
}
|
}
|
||||||
|
|
||||||
quantity(item: BillViewItem, quantity: number): void {
|
quantity(item: BillViewItem, quantity: number): void {
|
||||||
item.quantity = quantity;
|
const newKot = this.bill.kots.find((k) => k.id === undefined) as Kot;
|
||||||
this.dataObs.next(this.data);
|
const old = newKot.inventories.find(
|
||||||
this.updateAmounts();
|
(x) => x.product.id === item.productId && x.isHappyHour === item.isHappyHour,
|
||||||
|
) as Inventory;
|
||||||
|
old.quantity = quantity;
|
||||||
|
this.displayBill();
|
||||||
}
|
}
|
||||||
|
|
||||||
subtractOne(item: BillViewItem, canEdit: boolean): void {
|
subtractOne(item: BillViewItem, canEdit: boolean): void {
|
||||||
|
const newKot = this.bill.kots.find((k) => k.id === undefined) as Kot;
|
||||||
|
const old = newKot.inventories.find(
|
||||||
|
(x) => x.product.id === item.productId && x.isHappyHour === item.isHappyHour,
|
||||||
|
) as Inventory;
|
||||||
if (
|
if (
|
||||||
item.quantity > 1 ||
|
item.quantity > 1 ||
|
||||||
(canEdit && this.minimum(item.productId as string, item.isHappyHour) >= 1)
|
(canEdit && this.minimum(item.productId as string, item.isHappyHour) >= 1)
|
||||||
) {
|
) {
|
||||||
item.quantity -= 1;
|
old.quantity -= 1;
|
||||||
this.dataObs.next(this.data);
|
|
||||||
this.updateAmounts();
|
|
||||||
} else if (item.quantity === 0) {
|
} else if (item.quantity === 0) {
|
||||||
this.removeItem(item);
|
newKot.inventories.splice(newKot.inventories.indexOf(old), 1);
|
||||||
}
|
}
|
||||||
|
this.displayBill();
|
||||||
}
|
}
|
||||||
|
|
||||||
removeItem(item: BillViewItem): void {
|
removeItem(item: BillViewItem): void {
|
||||||
this.data.splice(this.data.indexOf(item), 1);
|
const newKot = this.bill.kots.find((k) => k.id === undefined) as Kot;
|
||||||
this.dataObs.next(this.data);
|
const old = newKot.inventories.find(
|
||||||
this.updateAmounts();
|
(x) => x.product.id === item.productId && x.isHappyHour === item.isHappyHour,
|
||||||
|
) as Inventory;
|
||||||
|
newKot.inventories.splice(newKot.inventories.indexOf(old), 1);
|
||||||
|
this.displayBill();
|
||||||
}
|
}
|
||||||
|
|
||||||
modifier(item: BillViewItem): void {
|
modifier(item: BillViewItem): void {
|
||||||
this.showModifier(item);
|
const newKot = this.bill.kots.find((k) => k.id === undefined) as Kot;
|
||||||
|
const old = newKot.inventories.find(
|
||||||
|
(x) => x.product.id === item.productId && x.isHappyHour === item.isHappyHour,
|
||||||
|
) as Inventory;
|
||||||
|
this.showModifier(old);
|
||||||
}
|
}
|
||||||
|
|
||||||
discount(discounts: { id: string; name: string; discount: number }[]): void {
|
discount(discounts: { id: string; name: string; discount: number }[]): void {
|
||||||
this.data.forEach((x) => {
|
for (const kot of this.bill.kots) {
|
||||||
if (!x.isKot) {
|
const noDiscount = kot.inventories
|
||||||
const e = discounts.find((d) => d.id === (x.product.saleCategory as SaleCategory).id);
|
.filter((x) => x.isHappyHour)
|
||||||
if (e !== undefined) {
|
.map((x) => x.product.id as string);
|
||||||
x.discount = e.discount;
|
for (const inventory of kot.inventories) {
|
||||||
x.info = `${x.product.name} @ ${x.price} - ${round(x.discount * 100, 2)}%`;
|
const e = discounts.find(
|
||||||
|
(d) => d.id === (inventory.product.saleCategory as SaleCategory).id,
|
||||||
|
);
|
||||||
|
if (e === undefined || noDiscount.indexOf(inventory.product.id as string) !== -1) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
inventory.discount = e.discount;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
this.displayBill();
|
||||||
this.dataObs.next(this.data);
|
|
||||||
this.updateAmounts();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
printKot(guestBookId: string | null): Observable<boolean> {
|
printKot(guestBookId: string | null): Observable<boolean> {
|
||||||
const item = JSON.parse(JSON.stringify(this.bill));
|
const item = JSON.parse(JSON.stringify(this.bill));
|
||||||
const newKot = this.getKot();
|
const newKot = this.bill.kots.find((k) => k.id === undefined) as Kot;
|
||||||
if (newKot.inventories.length === 0) {
|
if (newKot.inventories.length === 0) {
|
||||||
this.toaster.show('Error', 'Cannot print a blank KOT\nPlease add some products!');
|
return throwError('Cannot print a blank KOT\nPlease add some products!');
|
||||||
|
}
|
||||||
|
if (!this.happyHourItemsBalanced()) {
|
||||||
|
return throwError('Happy hour products are not balanced.');
|
||||||
}
|
}
|
||||||
item.kots.push(newKot);
|
|
||||||
return this.ser.saveOrUpdate(item, VoucherType.Kot, guestBookId, true);
|
return this.ser.saveOrUpdate(item, VoucherType.Kot, guestBookId, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
printBill(guest_book_id: string | null, voucherType: VoucherType): Observable<boolean> {
|
printBill(guest_book_id: string | null, voucherType: VoucherType): Observable<boolean> {
|
||||||
const item = JSON.parse(JSON.stringify(this.bill));
|
const item = JSON.parse(JSON.stringify(this.bill));
|
||||||
item.kots.forEach((k: Kot) => {
|
const newKot = this.bill.kots.find((k) => k.id === undefined) as Kot;
|
||||||
k.inventories.forEach((i: Inventory) => {
|
if (item.kots.length === 1 && newKot.inventories.length === 0) {
|
||||||
i.discount = (this.data.find((x) => !x.isKot && x.id === i.id) as BillViewItem).discount;
|
return throwError('Cannot print a blank Bill\nPlease add some products!');
|
||||||
});
|
}
|
||||||
});
|
if (!this.happyHourItemsBalanced()) {
|
||||||
item.kots.push(this.getKot());
|
return throwError('Happy hour products are not balanced.');
|
||||||
|
}
|
||||||
return this.ser.saveOrUpdate(item, voucherType, guest_book_id, true);
|
return this.ser.saveOrUpdate(item, voucherType, guest_book_id, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -246,88 +279,73 @@ export class BillService {
|
|||||||
updateAmounts() {
|
updateAmounts() {
|
||||||
this.netAmount.next(
|
this.netAmount.next(
|
||||||
round(
|
round(
|
||||||
this.data
|
this.bill.kots.reduce(
|
||||||
.filter((x) => !x.isKot)
|
(t, k) =>
|
||||||
.reduce(
|
k.inventories.reduce((a, c) => a + (c.isHappyHour ? 0 : c.price) * c.quantity, 0) + t,
|
||||||
(ca: number, c: BillViewItem) => ca + (c.isHappyHour ? 0 : c.price) * c.quantity,
|
|
||||||
0,
|
0,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
this.discountAmount.next(
|
this.discountAmount.next(
|
||||||
round(
|
round(
|
||||||
this.data
|
this.bill.kots.reduce(
|
||||||
.filter((x) => !x.isKot)
|
(t, k) =>
|
||||||
.reduce(
|
k.inventories.reduce(
|
||||||
(ca: number, c: BillViewItem) =>
|
(a, c) => a + (c.isHappyHour ? 0 : c.price) * c.quantity * c.discount,
|
||||||
ca + (c.isHappyHour ? 0 : c.price) * c.quantity * c.discount,
|
0,
|
||||||
|
) + t,
|
||||||
0,
|
0,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
this.taxAmount.next(
|
this.taxAmount.next(
|
||||||
round(
|
round(
|
||||||
this.data
|
this.bill.kots.reduce(
|
||||||
.filter((x) => !x.isKot)
|
(t, k) =>
|
||||||
.reduce(
|
k.inventories.reduce(
|
||||||
(ca: number, c: BillViewItem) =>
|
(a, c) =>
|
||||||
ca + (c.isHappyHour ? 0 : c.price) * c.quantity * (1 - c.discount) * c.taxRate,
|
a + (c.isHappyHour ? 0 : c.price) * c.quantity * (1 - c.discount) * c.taxRate,
|
||||||
|
0,
|
||||||
|
) + t,
|
||||||
0,
|
0,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
this.amount.next(
|
this.amount.next(
|
||||||
round(
|
round(
|
||||||
this.data
|
|
||||||
.filter((x) => !x.isKot)
|
|
||||||
.reduce(
|
|
||||||
(ca: number, c: BillViewItem) =>
|
|
||||||
ca + (c.isHappyHour ? 0 : c.price) * c.quantity * (1 - c.discount) * (1 + c.taxRate),
|
|
||||||
0,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
amountVal(): number {
|
|
||||||
return round(
|
|
||||||
this.bill.kots.reduce(
|
this.bill.kots.reduce(
|
||||||
(ka: number, k: Kot) =>
|
(t, k) =>
|
||||||
ka +
|
|
||||||
k.inventories.reduce(
|
k.inventories.reduce(
|
||||||
(ca: number, c: Inventory) =>
|
(a, c) =>
|
||||||
ca + (c.isHappyHour ? 0 : c.price) * c.quantity * (1 - c.discount) * (1 + c.taxRate),
|
a + (c.isHappyHour ? 0 : c.price) * c.quantity * (1 - c.discount) * (1 + c.taxRate),
|
||||||
|
0,
|
||||||
|
) + t,
|
||||||
0,
|
0,
|
||||||
),
|
),
|
||||||
0,
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
splitBill(table: Table): Observable<boolean> {
|
splitBill(table: Table): Observable<boolean> {
|
||||||
const inventoriesToMove: string[] = this.selection.selected.map(
|
const inventoriesToMove: string[] = this.selection.selected.map(
|
||||||
(x: BillViewItem) => x.id as string,
|
(x: string) => (JSON.parse(x) as BillSelectionItem).inventoryId as string,
|
||||||
);
|
);
|
||||||
return this.ser.splitBill(this.bill.id as string, inventoriesToMove, table);
|
return this.ser.splitBill(this.bill.id as string, inventoriesToMove, table);
|
||||||
}
|
}
|
||||||
|
|
||||||
private getKot(): Kot {
|
private happyHourItemsBalanced(): boolean {
|
||||||
return new Kot({
|
const newKot = this.bill.kots.find((k) => k.id === undefined) as Kot;
|
||||||
inventories: this.data
|
const happyHourItems = newKot.inventories
|
||||||
.filter((x) => !x.isKot && !x.isPrinted)
|
.filter((x) => x.isHappyHour)
|
||||||
.map(
|
.map((x) => ({ id: x.product.id as string, quantity: x.quantity }));
|
||||||
(y) =>
|
for (const item of happyHourItems) {
|
||||||
new Inventory({
|
const q = newKot.inventories.find(
|
||||||
product: y.product,
|
(x) => !x.isHappyHour && x.product.id === item.id && x.quantity === item.quantity,
|
||||||
quantity: y.quantity,
|
);
|
||||||
price: y.price,
|
if (q === undefined) {
|
||||||
isHappyHour: y.isHappyHour,
|
return false;
|
||||||
discount: y.discount,
|
}
|
||||||
modifiers: y.modifiers,
|
}
|
||||||
taxRate: y.taxRate,
|
return true;
|
||||||
tax: y.tax,
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
14
bookie/src/app/sales/bills/bill-selection-item.ts
Normal file
14
bookie/src/app/sales/bills/bill-selection-item.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
export class BillSelectionItem {
|
||||||
|
kotId?: string;
|
||||||
|
inventoryId?: string;
|
||||||
|
productId: string;
|
||||||
|
isHappyHour: boolean;
|
||||||
|
|
||||||
|
public constructor(init?: Partial<BillSelectionItem>) {
|
||||||
|
this.kotId = undefined;
|
||||||
|
this.inventoryId = undefined;
|
||||||
|
this.productId = '';
|
||||||
|
this.isHappyHour = false;
|
||||||
|
Object.assign(this, init);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -62,8 +62,8 @@
|
|||||||
*ngIf="!row.isKot"
|
*ngIf="!row.isKot"
|
||||||
[disabled]="!row.id"
|
[disabled]="!row.id"
|
||||||
(click)="$event.stopPropagation()"
|
(click)="$event.stopPropagation()"
|
||||||
(change)="$event ? bs.selection.toggle(row) : null"
|
(change)="$event ? toggle(row) : null"
|
||||||
[checked]="bs.selection.isSelected(row)"
|
[checked]="isSelected(row)"
|
||||||
>
|
>
|
||||||
</mat-checkbox>
|
</mat-checkbox>
|
||||||
</mat-cell>
|
</mat-cell>
|
||||||
|
|||||||
@ -16,7 +16,9 @@ import { QuantityComponent } from '../quantity/quantity.component';
|
|||||||
import { TablesDialogComponent } from '../tables-dialog/tables-dialog.component';
|
import { TablesDialogComponent } from '../tables-dialog/tables-dialog.component';
|
||||||
|
|
||||||
import { Bill } from './bill';
|
import { Bill } from './bill';
|
||||||
|
import { BillSelectionItem } from './bill-selection-item';
|
||||||
import { BillsDataSource } from './bills-datasource';
|
import { BillsDataSource } from './bills-datasource';
|
||||||
|
import { Inventory } from './inventory';
|
||||||
import { Kot } from './kot';
|
import { Kot } from './kot';
|
||||||
import { VoucherType } from './voucher-type';
|
import { VoucherType } from './voucher-type';
|
||||||
|
|
||||||
@ -66,33 +68,88 @@ export class BillsComponent implements OnInit {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
isAllSelected(kot: Kot) {
|
isAllSelected(kotView: BillViewItem): boolean {
|
||||||
return this.bs.data
|
const kot = this.bs.bill.kots.find((k) => k.id === kotView.kotId) as Kot;
|
||||||
.filter((x) => x.kotId === kot.id)
|
return kot.inventories.reduce(
|
||||||
.reduce((p: boolean, c: BillViewItem) => p && this.bs.selection.isSelected(c), true);
|
(p: boolean, c: Inventory) =>
|
||||||
|
p &&
|
||||||
|
this.bs.selection.isSelected(
|
||||||
|
JSON.stringify(
|
||||||
|
new BillSelectionItem({
|
||||||
|
kotId: kot.id,
|
||||||
|
inventoryId: c.id,
|
||||||
|
productId: c.product.id,
|
||||||
|
isHappyHour: c.isHappyHour,
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
true,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
isAnySelected(kot: Kot) {
|
toggle(invView: BillViewItem) {
|
||||||
|
const key = JSON.stringify(
|
||||||
|
new BillSelectionItem({
|
||||||
|
kotId: invView.kotId,
|
||||||
|
inventoryId: invView.id,
|
||||||
|
productId: invView.productId,
|
||||||
|
isHappyHour: invView.isHappyHour,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
this.bs.selection.toggle(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
isSelected(invView: BillViewItem): boolean {
|
||||||
|
const key = JSON.stringify(
|
||||||
|
new BillSelectionItem({
|
||||||
|
kotId: invView.kotId,
|
||||||
|
inventoryId: invView.id,
|
||||||
|
productId: invView.productId,
|
||||||
|
isHappyHour: invView.isHappyHour,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
return this.bs.selection.isSelected(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
isAnySelected(kotView: BillViewItem) {
|
||||||
|
const kot = this.bs.bill.kots.find((k) => k.id === kotView.kotId) as Kot;
|
||||||
let total = 0;
|
let total = 0;
|
||||||
let found = 0;
|
let found = 0;
|
||||||
this.bs.data
|
for (const item of kot.inventories) {
|
||||||
.filter((x) => x.kotId === kot.id)
|
const key = JSON.stringify(
|
||||||
.forEach((c: BillViewItem) => {
|
new BillSelectionItem({
|
||||||
|
kotId: kot.id,
|
||||||
|
inventoryId: item.id,
|
||||||
|
productId: item.product.id,
|
||||||
|
isHappyHour: item.isHappyHour,
|
||||||
|
}),
|
||||||
|
);
|
||||||
total += 1;
|
total += 1;
|
||||||
if (this.bs.selection.isSelected(c)) {
|
if (this.bs.selection.isSelected(key)) {
|
||||||
found += 1;
|
found += 1;
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
return found > 0 && found < total;
|
return found > 0 && found < total;
|
||||||
}
|
}
|
||||||
|
|
||||||
masterToggle(kot: Kot) {
|
masterToggle(kotView: BillViewItem) {
|
||||||
const isAllSelected = this.isAllSelected(kot);
|
const isAllSelected = this.isAllSelected(kotView);
|
||||||
this.bs.data
|
const kot = this.bs.bill.kots.find((k) => k.id === kotView.kotId) as Kot;
|
||||||
.filter((x) => x.kotId === kot.id)
|
for (const item of kot.inventories) {
|
||||||
.forEach((row) =>
|
const key = JSON.stringify(
|
||||||
isAllSelected ? this.bs.selection.deselect(row) : this.bs.selection.select(row),
|
new BillSelectionItem({
|
||||||
|
kotId: kot.id,
|
||||||
|
inventoryId: item.id,
|
||||||
|
productId: item.product.id,
|
||||||
|
isHappyHour: item.isHappyHour,
|
||||||
|
}),
|
||||||
);
|
);
|
||||||
|
if (isAllSelected) {
|
||||||
|
this.bs.selection.deselect(key);
|
||||||
|
} else {
|
||||||
|
this.bs.selection.select(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
addOne(item: BillViewItem): void {
|
addOne(item: BillViewItem): void {
|
||||||
@ -109,18 +166,7 @@ export class BillsComponent implements OnInit {
|
|||||||
if (!result) {
|
if (!result) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!item.isPrinted) {
|
|
||||||
this.bs.quantity(item, result as number);
|
this.bs.quantity(item, result as number);
|
||||||
} else {
|
|
||||||
const quantity = result as number;
|
|
||||||
const product = {
|
|
||||||
...item.product,
|
|
||||||
hasHappyHour: item.isHappyHour,
|
|
||||||
tax: item.tax,
|
|
||||||
price: item.price,
|
|
||||||
};
|
|
||||||
this.bs.addProduct(product, quantity, item.discount);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -191,7 +191,7 @@ export class SalesHomeComponent {
|
|||||||
if (!this.receivePaymentAllowed()) {
|
if (!this.receivePaymentAllowed()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const amount = this.bs.amountVal();
|
const amount = this.bs.amountVal;
|
||||||
const type = this.bs.type();
|
const type = this.bs.type();
|
||||||
this.dialog
|
this.dialog
|
||||||
.open(ReceivePaymentComponent, {
|
.open(ReceivePaymentComponent, {
|
||||||
|
|||||||
Reference in New Issue
Block a user