Move / Merge KOT Done.

We need to check if it is the only kot and raise an error if it is.
Split Bill Done
This commit is contained in:
Amritanshu
2019-08-18 01:05:59 +05:30
parent dcaf23b390
commit e697631cd4
15 changed files with 539 additions and 295 deletions

View File

@ -350,6 +350,9 @@ def includeme(config):
config.add_route("v1_vouchers_new", "/v1/vouchers/new")
config.add_route("v1_vouchers_id", "/v1/vouchers/{id}")
config.add_route("v1_move_table", "/v1/move-table")
config.add_route("v1_move_kot", "/v1/move-kot")
# Done till here
config.add_route("customer", "/Customer.json")
@ -365,16 +368,10 @@ def includeme(config):
config.add_route("machine_location_id", "/MachineLocation/{id}.json")
config.add_route("merge_kot", "/MergeKot.json")
config.add_route("merge_table", "/MergeTable.json")
config.add_route("move_kot", "/MoveKot.json")
config.add_route("permission_list", "/Permissions.json")
config.add_route("print_location", "/PrintLocation.json")
config.add_route("print_location_list", "/PrintLocations.json")
config.add_route("print_location_id", "/PrintLocation/{id}.json")
config.add_route("v1_bills_new", "/v1/bills/new")
config.add_route("v1_bills_id", "/v1/bills/{id}")
@ -392,8 +389,6 @@ def includeme(config):
config.add_route("sa_tax", "/SaleAnalysis/Tax.json")
config.add_route("voucher_reprint", "/ReprintVoucher/{id}.json")
config.add_route("voucher_split", "/Split/{id}.json")
config.add_route("voucher_void", "/Void/{id}.json")
config.add_route("api_lock_info", "/api/LockInfo")
config.add_route("api_maintenance", "/api/Maintenance")

View File

@ -1,4 +1,9 @@
from barker.models import VoucherType, Settlement, SettleOption
import uuid
from sqlalchemy import func
from barker.exceptions import ValidationFailure
from barker.models import VoucherType, Settlement, SettleOption, Voucher, Overview, GuestBook
from barker.models.validation_exception import ValidationError
@ -11,45 +16,81 @@ def get_tax(tax, voucher_type):
raise ValidationError("Unexpected Voucher Type")
def get_settlements(voucher, dbsession):
amount = voucher.amount
so_amount = [s for s in voucher.settlements if s.settled == SettleOption.AMOUNT()]
if len(so_amount) == 1:
so_amount[0].amount = amount
else:
s = Settlement(voucher.id, SettleOption.AMOUNT(), amount)
voucher.settlements.append(s)
dbsession.add(s)
round_off = round(amount) - amount
so_round_off = [
s for s in voucher.settlements if s.settled == SettleOption.ROUND_OFF()
]
if len(so_round_off) == 1 and round_off != 0:
so_round_off[0].amount = round_off
elif len(so_round_off) == 1 and round_off == 0:
voucher.settlements.remove(so_round_off[0])
dbsession.delete(so_round_off[0])
elif len(so_round_off) == 0 and round_off != 0:
s = Settlement(voucher.id, SettleOption.ROUND_OFF(), round_off)
voucher.settlements.append(s)
dbsession.add(s)
unsettled = sum(
s.amount for s in voucher.settlements if s.settled != SettleOption.UNSETTLED()
def get_bill_id(voucher_type, dbsession):
if voucher_type == VoucherType.KOT:
return None
return (
dbsession.query(func.coalesce(func.max(Voucher.bill_id), 0) + 1)
.filter(Voucher.voucher_type == voucher_type.value)
.scalar()
)
so_unsettled = [
s for s in voucher.settlements if s.settled == SettleOption.UNSETTLED()
]
if len(so_unsettled) == 1 and unsettled != 0:
so_unsettled[0].amount = unsettled
elif len(so_unsettled) == 1 and unsettled == 0:
voucher.settlements.remove(so_unsettled[0])
dbsession.delete(so_unsettled[0])
elif len(so_unsettled) == 0 and unsettled != 0:
s = Settlement(voucher.id, SettleOption.UNSETTLED(), unsettled)
voucher.settlements.append(s)
dbsession.add(s)
return unsettled
def do_update_table(item, guest_book, dbsession):
status = "running" if item.voucher_type == VoucherType.KOT else "printed"
if item.status is None:
item.status = Overview(
voucher_id=item.id,
food_table_id=item.food_table_id,
guest_book_id=guest_book.id if guest_book is not None else None,
status=status,
)
dbsession.add(item.status)
else:
item.status.status = status
def check_permissions(item, voucher_type, permissions):
if voucher_type == VoucherType.KOT and "Print Kot" not in permissions:
raise ValidationFailure("You are not allowed to print a kot")
if voucher_type != VoucherType.KOT and "Print Bill" not in permissions:
raise ValidationFailure("You are not allowed to print bill")
if item is None:
return
if item.voucher_type != VoucherType.KOT and "Edit Printed Bill" not in permissions:
raise ValidationFailure("You are not allowed to edit a printed bill")
if item.voucher_type != VoucherType.KOT and voucher_type == VoucherType.KOT:
raise ValidationFailure("This Bill is already printed\nCannot add a Kot to it.")
if item.is_void:
raise ValidationFailure(
"This Bill is already void.\nReason: {0}".format(item.void_reason)
)
def get_guest_book(guest_book_id, dbsession):
if guest_book_id is None:
return guest_book_id
return (
dbsession.query(GuestBook)
.filter(GuestBook.id == uuid.UUID(guest_book_id))
.first()
)
def do_update_settlements(voucher, dbsession):
settlements = []
total_amount = voucher.amount
settlements.append({"id": SettleOption.AMOUNT(), "amount": -total_amount})
round_off = round(total_amount) - total_amount
if round_off != 0:
settlements.append({"id": SettleOption.ROUND_OFF(), "amount": -round_off})
settlements.append({"id": SettleOption.UNSETTLED(), "amount": round(total_amount)})
for i in settlements:
amount = i["amount"]
settlement_type_id = i["id"]
old = [s for s in voucher.settlements if s.settled == settlement_type_id]
if len(old) == 1:
old[0].amount = amount
else:
s = Settlement(voucher.id, settlement_type_id, amount)
voucher.settlements.append(s)
dbsession.add(s)
for i in (i for i in voucher.settlements if i.settled not in [x["id"] for x in settlements]):
voucher.settlements.remove(i)
dbsession.delete(i)

View File

@ -1,74 +1,82 @@
import uuid
import transaction
from datetime import datetime
from pyramid.view import view_config
from sqlalchemy import func
from barker.models import Kot, Voucher, Overview
from barker.views.voucher import get_bill_id, do_update_table
@view_config(
request_method="POST",
route_name="merge_kot",
route_name="v1_move_kot",
renderer="json",
request_param="merge",
permission="Merge Kots",
trans=True,
)
def merge_kot(request):
json = request.json_body
kot_id = uuid.UUID(json["KotID"])
voucher_id = uuid.UUID(json["NewVoucherID"])
kot_id = uuid.UUID(json["kot"]["id"])
new_voucher_id = uuid.UUID(json["newVoucher"]["id"])
request.dbsession.query(Kot).filter(Kot.id == kot_id).update(
{Kot.voucher_id: voucher_id}
{Kot.voucher_id: new_voucher_id}
)
transaction.commit()
return voucher_id
return True
@view_config(
request_method="POST",
route_name="move_kot",
route_name="v1_move_kot",
renderer="json",
permission="Move Kot",
request_param="move",
permission="Move Kot to New Table",
trans=True,
)
def move_kot(request):
json = request.json_body
kot_id = uuid.UUID(json["KotID"])
food_table_id = uuid.UUID(json["FoodTableID"])
now = datetime.now()
kot_id = uuid.UUID(json["kot"]["id"])
table_id = uuid.UUID(json["table"]["id"])
kot = request.dbsession.query(Kot).filter(Kot.id == kot_id).first()
bill_id = get_bill_id(kot.voucher.voucher_type, request.dbsession)
kot_id = request.dbsession.query(
func.coalesce(func.max(Voucher.kot_id), 0) + 1
).scalar()
item = Voucher(
now,
kot.voucher.pax,
food_table_id,
bill_id,
kot_id,
table_id,
kot.voucher.customer_id,
False,
kot.voucher.voucher_type,
kot.voucher.user_id,
request.dbsession,
uuid.UUID(request.authenticated_userid)
)
request.dbsession.add(item)
item.kots.append(kot)
status = "printed" if item.is_printed else "running"
item.status = Overview(
voucher_id=None, food_table_id=item.food_table_id, status=status
)
do_update_table(item, None, request.dbsession)
request.dbsession.add(item.status)
transaction.commit()
return item.id
return True
@view_config(
request_method="POST",
route_name="v1_vouchers_id",
request_param="move-table",
route_name="v1_move_table",
request_param="move",
renderer="json",
permission="Move Table",
trans=True,
)
def move_table(request):
id_ = uuid.UUID(request.matchdict["id"])
id_ = uuid.UUID(request.json_body["voucher"]["id"])
table_id = uuid.UUID(request.json_body["table"]["id"])
request.dbsession.query(Overview).filter(Overview.voucher_id == id_).update(
@ -85,22 +93,25 @@ def move_table(request):
@view_config(
request_method="POST",
route_name="merge_table",
route_name="v1_move_table",
request_param="merge",
renderer="json",
permission="Merge Tables",
trans=True,
)
def merge_table(request):
json = request.json_body
voucher_id = uuid.UUID(json["VoucherID"])
new_voucher_id = uuid.UUID(json["NewVoucherID"])
request.dbsession.query(Kot).filter(Kot.voucher_id == voucher_id).update(
id_ = uuid.UUID(json["oldVoucher"]["id"])
# table_id = uuid.UUID(json["table"]["id"])
new_voucher_id = uuid.UUID(json["newVoucher"]["id"])
request.dbsession.query(Kot).filter(Kot.voucher_id == id_).update(
{Kot.voucher_id: new_voucher_id}
)
request.dbsession.query(Voucher).filter(Voucher.id == voucher_id).delete()
request.dbsession.query(Voucher).filter(Voucher.id == id_).delete()
transaction.commit()
return voucher_id
return True
# VoucherBI.UpdateTable(x => x.FoodTableID == _voucher.Table.FoodTableID && x.VoucherID == _voucher.VoucherID, null, null);
# throw new NotImplementedException();

View File

@ -3,24 +3,27 @@ import uuid
from decimal import Decimal
import transaction
from pyramid.httpexceptions import HTTPForbidden
from pyramid.view import view_config
from sqlalchemy import func
from barker.models import (
Overview,
Voucher,
Kot,
Inventory,
InventoryModifier,
VoucherType,
Product,
GuestBook,
FoodTable,
)
from barker.models.validation_exception import ValidationError
from barker.views.voucher import get_tax, get_settlements
from barker.views.voucher.show import voucher_info
from barker.views.voucher import (
get_tax,
do_update_settlements,
get_bill_id,
do_update_table,
check_permissions,
get_guest_book,
)
@view_config(
@ -39,7 +42,7 @@ def save(request):
.first()
)
check_permissions(voucher_type, request.effective_principals)
check_permissions(None, voucher_type, request.effective_principals)
bill_id = get_bill_id(voucher_type, request.dbsession)
kot_id = request.dbsession.query(
@ -54,68 +57,20 @@ def save(request):
json["table"]["id"],
json["customer"]["id"] if "id" in json["customer"] else None,
voucher_type,
uuid.UUID(request.authenticated_userid)
uuid.UUID(request.authenticated_userid),
)
request.dbsession.add(item)
add_kots(json, item, voucher_type, request.dbsession)
if len(item.kots) == 0 or len(item.kots[0].inventories) == 0:
raise ValidationError("Please add some products!")
if update_table:
do_update_table(item, voucher_type, guest_book, request.dbsession)
transaction.commit()
item = request.dbsession.query(Voucher).filter(Voucher.id == item.id).first()
return voucher_info(item)
def check_permissions(voucher_type, permissions):
if voucher_type == VoucherType.KOT and "Print Kot" not in permissions:
raise HTTPForbidden("You are not allowed to print a kot")
if voucher_type != VoucherType.KOT and "Print Bill" not in permissions:
raise HTTPForbidden("You are not allowed to print bill")
def get_guest_book(guest_book_id, dbsession):
if guest_book_id is None:
return guest_book_id
return (
dbsession.query(GuestBook)
.filter(GuestBook.id == uuid.UUID(guest_book_id))
.first()
)
def get_bill_id(voucher_type, dbsession):
if voucher_type == VoucherType.KOT:
return None
return (
dbsession.query(func.coalesce(func.max(Voucher.bill_id), 0) + 1)
.filter(Voucher.voucher_type == voucher_type.value)
.scalar()
)
def do_update_table(item, voucher_type, guest_book, dbsession):
status = "running" if voucher_type == VoucherType.KOT else "printed"
item.status = Overview(
voucher_id=None,
food_table_id=item.food_table_id,
guest_book_id=guest_book.id if guest_book is not None else None,
status=status,
)
dbsession.add(item.status)
def add_kots(json, item, voucher_type, dbsession):
for k in json["kots"]:
code = dbsession.query(func.coalesce(func.max(Kot.code), 0) + 1).scalar()
code = request.dbsession.query(func.coalesce(func.max(Kot.code), 0) + 1).scalar()
kot = Kot(item.id, code, item.food_table_id, item.date, item.user_id)
item.kots.append(kot)
dbsession.add(kot)
request.dbsession.add(kot)
for index, i in enumerate(k["inventories"]):
product = dbsession.query(Product).filter(Product.id == uuid.UUID(i["product"]["id"])).first()
product = (
request.dbsession.query(Product)
.filter(Product.id == uuid.UUID(i["product"]["id"]))
.first()
)
tax_rate = get_tax(product.sale_category.tax.rate, voucher_type)
inv = Inventory(
kot.id,
@ -129,9 +84,16 @@ def add_kots(json, item, voucher_type, dbsession):
index,
)
kot.inventories.append(inv)
dbsession.add(inv)
request.dbsession.add(inv)
for m in i["modifiers"]:
mod = InventoryModifier(None, uuid.UUID(m["id"]), 0)
inv.modifiers.append(mod)
dbsession.add(mod)
get_settlements(item, dbsession)
request.dbsession.add(mod)
do_update_settlements(item, request.dbsession)
if len(item.kots) == 0 or len(item.kots[0].inventories) == 0:
raise ValidationError("Please add some products!")
if update_table:
do_update_table(item, guest_book, request.dbsession)
transaction.commit()
return True

View File

@ -1,40 +1,133 @@
import uuid
import transaction
from datetime import datetime
from pyramid.view import view_config
from sqlalchemy import func
from barker.models import Voucher, Overview
from barker.views.voucher.save import save
from barker.models import Voucher, Overview, Kot, Inventory, InventoryModifier
from barker.models.validation_exception import ValidationError
from barker.views.voucher import get_bill_id, do_update_settlements, do_update_table
from barker.views.voucher.update import check_permissions
from barker.views.voucher.void import do_void_settlements
@view_config(
request_method="POST",
route_name="voucher_split",
route_name="v1_vouchers_id",
renderer="json",
request_param="split-bill",
permission="Split Bill",
trans=True,
)
def split_voucher(request):
json = request.json_body
now = datetime.now()
id_ = uuid.UUID(request.matchdict["id"])
update_table = request.GET["u"] == "true"
item = request.dbsession.query(Voucher).filter(Voucher.id == id_).first()
item.void = True
item.void_reason = "Bill Split"
# TODO: Set the Void Settlement
do_void_settlements(item, request.dbsession)
if update_table:
request.dbsession.query(Overview).filter(
Overview.voucher_id == item.id
).delete()
new_one = save(json["One"], request.dbsession)
new_two = save(json["Two"], request.dbsession)
inventories = [uuid.UUID(i) for i in json["inventories"]]
status = "printed" if item.is_printed else "running"
new_one.status = Overview(
voucher_id=None, food_table_id=new_one.food_table_id, status=status
one_inventories = [i for k in item.kots for i in k.inventories if i.id in inventories]
two_inventories = [i for k in item.kots for i in k.inventories if i.id not in inventories]
save(
one_inventories,
now,
item.voucher_type,
0,
uuid.UUID(json["table"]["id"]),
item.customer_id,
update_table,
uuid.UUID(request.authenticated_userid),
request.effective_principals,
request.dbsession,
)
request.dbsession.add(new_one.status)
request.dbsession.query(Overview).filter(Overview.voucher_id == item.id).update(
{Overview.voucher_id: new_two.id}
save(
two_inventories,
now,
item.voucher_type,
item.pax,
item.food_table_id,
item.customer_id,
update_table,
uuid.UUID(request.authenticated_userid),
request.effective_principals,
request.dbsession,
)
transaction.commit()
return True
def save(
inventories,
now,
voucher_type,
pax,
table_id,
customer_id,
update_table,
user_id,
permissions,
dbsession,
):
product_quantities = {}
skip_products = set()
for i in inventories:
if i.product_id in product_quantities:
product_quantities[i.product_id] += i.quantity
else:
product_quantities[i.product_id] = i.quantity
for product, quantity in product_quantities.items():
if quantity < 0:
raise ValidationError("Quantity is negative")
elif quantity == 0:
skip_products.add(product)
check_permissions(None, voucher_type, permissions)
bill_id = get_bill_id(voucher_type, dbsession)
kot_id = dbsession.query(func.coalesce(func.max(Voucher.kot_id), 0) + 1).scalar()
item = Voucher(
now, pax, bill_id, kot_id, table_id, customer_id, voucher_type, user_id
)
dbsession.add(item)
code = dbsession.query(func.coalesce(func.max(Kot.code), 0) + 1).scalar()
kot = Kot(item.id, code, item.food_table_id, item.date, item.user_id)
item.kots.append(kot)
dbsession.add(kot)
for index, old_inventory in enumerate(
[i for i in inventories if i.product_id not in skip_products]
):
inv = Inventory(
kot.id,
old_inventory.product_id,
old_inventory.quantity,
old_inventory.price,
old_inventory.discount,
old_inventory.is_happy_hour,
old_inventory.tax_id,
old_inventory.tax_rate,
index,
)
kot.inventories.append(inv)
dbsession.add(inv)
for m in old_inventory.modifiers:
mod = InventoryModifier(None, m.modifier_id, m.price)
inv.modifiers.append(mod)
dbsession.add(mod)
do_update_settlements(item, dbsession)
if len(kot.inventories) == 0:
raise ValidationError("Please add some products!")
if update_table:
do_update_table(item, None, dbsession)

View File

@ -3,23 +3,25 @@ import uuid
from decimal import Decimal
import transaction
from pyramid.httpexceptions import HTTPForbidden
from pyramid.view import view_config
from sqlalchemy import func
from barker.exceptions import ValidationFailure
from barker.models import (
Voucher,
Kot,
Inventory,
InventoryModifier,
Overview,
VoucherType,
GuestBook,
Product,
)
from barker.views.voucher import get_tax, get_settlements
from barker.views.voucher.show import voucher_info
from barker.views.voucher import (
get_tax,
do_update_settlements,
get_bill_id,
do_update_table,
check_permissions,
get_guest_book,
)
@view_config(
@ -60,10 +62,8 @@ def update(request):
if uuid.UUID(inv["id"]) == i.id
)
for k in (k for k in json["kots"] if "id" not in k and len(k["inventories"]) > 0):
code = request.dbsession.query(
func.coalesce(func.max(Kot.code), 0) + 1
).scalar()
kot = Kot(item.id, code, item.food_table_id, item.date, item.user_id)
code = request.dbsession.query(func.coalesce(func.max(Kot.code), 0) + 1).scalar()
kot = Kot(item.id, code, item.food_table_id, now, item.user_id)
item.kots.append(kot)
request.dbsession.add(kot)
for index, i in enumerate(k["inventories"]):
@ -90,64 +90,8 @@ def update(request):
mod = InventoryModifier(None, uuid.UUID(m["id"]), 0)
inv.modifiers.append(mod)
request.dbsession.add(mod)
get_settlements(item, request.dbsession)
do_update_settlements(item, request.dbsession)
if update_table:
do_update_table(item, voucher_type, guest_book, request.dbsession)
do_update_table(item, guest_book, request.dbsession)
transaction.commit()
item = request.dbsession.query(Voucher).filter(Voucher.id == item.id).first()
return voucher_info(item)
def check_permissions(item, voucher_type, permissions):
if voucher_type == VoucherType.KOT and "Print Kot" not in permissions:
raise HTTPForbidden("You are not allowed to print a kot")
if voucher_type != VoucherType.KOT and "Print Bill" not in permissions:
raise HTTPForbidden("You are not allowed to print bill")
if item.voucher_type != VoucherType.KOT and "Edit Printed Bill" not in permissions:
raise HTTPForbidden("You are not allowed to edit a printed bill")
if item.voucher_type != VoucherType.KOT and voucher_type == VoucherType.KOT:
transaction.abort()
raise ValidationFailure("This Bill is already printed\nCannot add a Kot to it.")
if item.is_void:
transaction.abort()
raise ValidationFailure(
"This Bill is already void.\nReason: {0}".format(item.void_reason)
)
def get_guest_book(guest_book_id, dbsession):
if guest_book_id is None:
return guest_book_id
return (
dbsession.query(GuestBook)
.filter(GuestBook.id == uuid.UUID(guest_book_id))
.first()
)
def get_bill_id(voucher_type, dbsession):
if voucher_type == VoucherType.KOT:
return None
return (
dbsession.query(func.coalesce(func.max(Voucher.bill_id), 0) + 1)
.filter(Voucher.voucher_type == voucher_type.value)
.scalar()
)
def do_update_table(item, voucher_type, guest_book, dbsession):
status = "running" if voucher_type == VoucherType.KOT else "printed"
if item.status is None:
item.status = Overview(
voucher_id=item.id,
food_table_id=item.food_table_id,
guest_book_id=guest_book.id if guest_book is not None else None,
status=status,
)
dbsession.add(item.status)
else:
item.status.status = status
return True

View File

@ -17,13 +17,24 @@ from barker.models import Voucher, SettleOption, Settlement, Overview
def void_voucher(request):
id_ = uuid.UUID(request.matchdict["id"])
reason = request.json_body["reason"]
update_table = request.GET["u"]
update_table = request.GET["u"] == "true"
item = request.dbsession.query(Voucher).filter(Voucher.id == id_).first()
item.void = True
item.void_reason = reason
do_void_settlements(item, request.dbsession)
if update_table:
request.dbsession.query(Overview).filter(
Overview.voucher_id == item.id
).delete()
transaction.commit()
return True
def do_void_settlements(item, dbsession):
settlements = []
total_amount = item.amount
settlements.append({"id": SettleOption.AMOUNT(), "amount": -total_amount})
@ -41,17 +52,8 @@ def void_voucher(request):
else:
s = Settlement(item.id, settlement_type_id, amount)
item.settlements.append(s)
request.dbsession.add(s)
dbsession.add(s)
for i in (i for i in item.settlements if i.settled not in [x["id"] for x in settlements]):
item.settlements.remove(i)
request.dbsession.delete(i)
if update_table:
request.dbsession.query(Overview).filter(
Overview.voucher_id == item.id
).delete()
transaction.commit()
return True
dbsession.delete(i)

View File

@ -10,18 +10,19 @@ 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 { tap } from 'rxjs/operators';
import { Table } from '../core/table';
import { SelectionModel } from "@angular/cdk/collections";
@Injectable()
export class BillService {
public dataObs;
public data;
public data: any[];
private bill;
public netAmount: BehaviorSubject<number>;
public discountAmount: BehaviorSubject<number>;
public taxAmount: BehaviorSubject<number>;
public amount: BehaviorSubject<number>;
public selection = new SelectionModel<any>(true, []);
constructor(
private dialog: MatDialog,
@ -41,12 +42,14 @@ export class BillService {
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,
@ -178,7 +181,7 @@ export class BillService {
});
}
printKot(guest_book_id: string): Observable<Bill> {
printKot(guest_book_id: string): Observable<boolean> {
const item = JSON.parse(JSON.stringify(this.bill));
const newKot = this.getKot();
if (newKot.inventories.length == 0) {
@ -188,7 +191,7 @@ export class BillService {
return this.ser.saveOrUpdate(item, PrintType.Kot, guest_book_id, true);
}
printBill(guest_book_id: string, printType: PrintType): Observable<Bill> {
printBill(guest_book_id: string, printType: PrintType): Observable<boolean> {
const item = JSON.parse(JSON.stringify(this.bill));
item.kots.forEach(k => {
k.inventories.forEach(i => {
@ -211,8 +214,16 @@ export class BillService {
return this.ser.moveTable(this.bill.id, table);
}
moveKot(id: string, table: Table): Observable<boolean> {
return this.ser.moveKot(this.bill.id, id, table);
mergeTable(table: Table): Observable<boolean> {
return this.ser.mergeTable(this.bill.id, table);
}
moveKot(kotId: string, table: Table): Observable<boolean> {
return this.ser.moveKotToNewTable(this.bill.id, kotId, table);
}
mergeKot(kotId: string, table: Table): Observable<boolean> {
return this.ser.mergeKotWithOldBill(this.bill.id, kotId, table);
}
voidBill(reason: string): Observable<boolean> {
@ -249,4 +260,9 @@ export class BillService {
, 0)
, 0));
}
splitBill(table: Table): Observable<boolean> {
const inventoriesToMove: string[] = this.selection.selected.map((x:any)=> x.id)
return this.ser.splitBill(this.bill.id, inventoriesToMove, table);
}
}

View File

@ -13,6 +13,10 @@ table {
width: 100%;
}
.mat-column-select{
flex: 0 0 50px;
}
.grey900 {
background-color: #1b5e20;
color: #ffffff;

View File

@ -8,6 +8,22 @@
</mat-card-title-group>
<mat-card-content>
<table mat-table #table [dataSource]="dataSource" aria-label="Elements" class="mat-elevation-z8">
<!-- Checkbox Column -->
<ng-container matColumnDef="select">
<mat-cell *matCellDef="let row">
<mat-checkbox *ngIf="row.oldKot"
(change)="$event ? masterToggle(row) : null"
[checked]="bs.selection.hasValue() && isAllSelected(row)"
[indeterminate]="isAnySelected(row)">
</mat-checkbox>
<mat-checkbox *ngIf="!row.isKot" [disabled]="!row.id"
(click)="$event.stopPropagation()"
(change)="$event ? bs.selection.toggle(row) : null"
[checked]="bs.selection.isSelected(row)">
</mat-checkbox>
</mat-cell>
<mat-footer-cell *matFooterCellDef class="grey900">Amount</mat-footer-cell>
</ng-container>
<!-- Info Column -->
<ng-container matColumnDef="info">
<mat-cell *matCellDef="let row" [class.blue800]="row.newKot">
@ -18,7 +34,7 @@
<li *ngFor="let m of row.modifiers">{{m.name}}</li>
</ul>
</mat-cell>
<mat-footer-cell *matFooterCellDef class="grey900 bold">Amount</mat-footer-cell>
<mat-footer-cell *matFooterCellDef class="grey900 bold"></mat-footer-cell>
</ng-container>
<!-- Quantity Column -->
<ng-container matColumnDef="quantity">
@ -39,7 +55,7 @@
<button mat-icon-button (click)="modifier(row)" [disabled]="row.isPrinted" *ngIf="!row.isKot">
<mat-icon class="del">assignment</mat-icon>
</button>
<button mat-icon-button (click)="modifier(row)" [disabled]="row.newKot" *ngIf="row.isKot">
<button mat-icon-button (click)="moveKot(row)" [disabled]="row.newKot" *ngIf="row.isKot">
<mat-icon class="del">open_in_new</mat-icon>
</button>
</mat-cell>

View File

@ -12,6 +12,8 @@ import { map, switchMap } from 'rxjs/operators';
import { TablesDialogComponent } from '../tables-dialog/tables-dialog.component';
import { TableService } from '../../tables/table.service';
import { ToasterService } from '../../core/toaster.service';
import { AuthService } from "../../auth/auth.service";
import { SelectionModel } from "@angular/cdk/collections";
@Component({
selector: 'app-bills',
@ -22,13 +24,14 @@ export class BillsComponent implements OnInit {
dataSource: BillsDataSource;
item: Bill;
/** Columns displayed in the table. Columns IDs can be added, removed, or reordered. */
displayedColumns: string[] = ['info', 'quantity'];
displayedColumns: string[] = ['select', 'info', 'quantity'];
constructor(
private route: ActivatedRoute,
private router: Router,
private dialog: MatDialog,
private toaster: ToasterService,
private auth: AuthService,
private bs: BillService,
private tSer: TableService
) {
@ -43,6 +46,38 @@ export class BillsComponent implements OnInit {
this.dataSource = new BillsDataSource(this.bs.dataObs);
}
isAllSelected(kot: Kot) {
return this.bs.data.filter(
x => x.kotId === kot.id
).reduce(
(p: boolean, c: any) => p && this.bs.selection.isSelected(c)
, true
);
}
isAnySelected(kot: Kot) {
let total: number = 0,
found: number = 0;
this.bs.data.filter(
x => x.kotId === kot.id
).forEach((c: any) => {
total += 1;
if (this.bs.selection.isSelected(c)) {
found += 1;
}
});
return found > 0 && found < total;
}
masterToggle(kot: Kot) {
const isAllSelected = this.isAllSelected(kot);
this.bs.data.filter(
x => x.kotId === kot.id
).forEach(
row => isAllSelected ? this.bs.selection.deselect(row) : this.bs.selection.select(row)
);
}
addOne(item: any): void {
this.bs.addOne(item);
}
@ -72,4 +107,48 @@ export class BillsComponent implements OnInit {
modifier(item: any): void {
this.bs.modifier(item);
}
confirmMoveKotDialog(table: Table): Observable<{table: Table, confirmed: boolean}> {
return this.dialog.open(ConfirmDialogComponent, {
width: '250px',
data: {title: 'Move KOT?', content: 'Are you sure?'}
}).afterClosed().pipe(
map ((x: boolean) => ({table: table, confirmed: x}))
);
}
moveKot(kot: Kot) {
const canMergeTables = this.auth.hasPermission("Merge Tables");
this.dialog.open(TablesDialogComponent, {
// width: '750px',
data: {
list: this.tSer.running(),
canChooseRunning: canMergeTables
}
}).afterClosed().pipe(
switchMap((x: boolean | Table) => {
if (!!x) {
return this.confirmMoveKotDialog(x as Table);
} else {
return throwError('Please choose a table');
}
}),
switchMap((value: { table: Table, confirmed: boolean }, index: number) => {
if (!value.confirmed) {
return throwError('Please confirm move');
} else if (value.table.status) {
return this.bs.mergeKot(kot.id, value.table);
} else {
return this.bs.moveKot(kot.id, value.table);
}
}
)
).subscribe((x) => {
this.toaster.show('Success', '');
this.router.navigate(['/sales']);
},
x => {
this.toaster.show('Error', x);
});
}
}

View File

@ -11,6 +11,8 @@ const httpOptions = {
};
const url = '/v1/vouchers';
const urlMoveTable = '/v1/move-table';
const urlMoveKot = '/v1/move-kot';
const serviceName = 'VoucherService';
@Injectable({providedIn: 'root'})
@ -47,29 +49,29 @@ export class VoucherService {
);
}
save(voucher: Bill, printType: PrintType, guest_book_id: string, updateTable: boolean): Observable<Bill> {
save(voucher: Bill, printType: PrintType, guest_book_id: string, updateTable: boolean): Observable<boolean> {
const options = {params: new HttpParams().set('p', printType).set('u', updateTable.toString())};
if (guest_book_id !== null) {
options.params = options.params.set('g', guest_book_id);
}
return <Observable<Bill>>this.http.post<Bill>(`${url}/new`, voucher, options)
return <Observable<boolean>>this.http.post<boolean>(`${url}/new`, voucher, options)
.pipe(
catchError(this.log.handleError(serviceName, 'save'))
);
}
update(voucher: Bill, printType: PrintType, guest_book_id: string, updateTable: boolean): Observable<Bill> {
update(voucher: Bill, printType: PrintType, guest_book_id: string, updateTable: boolean): Observable<boolean> {
const options = {params: new HttpParams().set('p', printType).set('u', updateTable.toString())};
if (guest_book_id !== null) {
options.params = options.params.set('g', guest_book_id);
}
return <Observable<Bill>>this.http.put<Bill>(`${url}/${voucher.id}`, voucher, options)
return <Observable<boolean>>this.http.put<boolean>(`${url}/${voucher.id}`, voucher, options)
.pipe(
catchError(this.log.handleError(serviceName, 'update'))
);
}
saveOrUpdate(voucher: Bill, printType: PrintType, guest_book_id: string, updateTable: boolean): Observable<Bill> {
saveOrUpdate(voucher: Bill, printType: PrintType, guest_book_id: string, updateTable: boolean): Observable<boolean> {
if (!voucher.id) {
return this.save(voucher, printType, guest_book_id, updateTable);
} else {
@ -78,26 +80,75 @@ export class VoucherService {
}
receivePayment(id: string, amounts: { id: string; name: string; amount: number }[], updateTable: boolean): Observable<boolean> {
const options = {params: new HttpParams().set('receive-payment', '').set('u', updateTable.toString())};
return <Observable<boolean>>this.http.post<boolean>(`${url}/${id}`, amounts, options)
.pipe(
catchError(this.log.handleError(serviceName, 'receivePayment'))
);
}
moveTable(id: string, table: Table): Observable<boolean> {
const options = {params: new HttpParams().set('move-table', '')};
return <Observable<boolean>>this.http.post<boolean>(`${url}/${id}`, {table: {id: table.id}}, options)
.pipe(
catchError(this.log.handleError(serviceName, 'moveTable'))
);
const options = {params: new HttpParams().set('receive-payment', '').set('u', updateTable.toString())};
return <Observable<boolean>>this.http.post<boolean>(
`${url}/${id}`, amounts, options
).pipe(
catchError(this.log.handleError(serviceName, 'receivePayment'))
);
}
voidBill(id: string, reason: string, updateTable: boolean): Observable<boolean> {
const options = {params: new HttpParams().set('void-bill', '').set('u', updateTable.toString())};
return <Observable<boolean>>this.http.post<boolean>(`${url}/${id}`, {reason: reason}, options)
.pipe(
catchError(this.log.handleError(serviceName, 'voidBill'))
);
const options = {params: new HttpParams().set('void-bill', '').set('u', updateTable.toString())};
return <Observable<boolean>>this.http.post<boolean>(
`${url}/${id}`, {reason: reason}, options
).pipe(
catchError(this.log.handleError(serviceName, 'voidBill'))
);
}
moveTable(id: string, table: Table): Observable<boolean> {
const options = {params: new HttpParams().set('move', '')};
return <Observable<boolean>>this.http.post<boolean>(urlMoveTable, {
voucher: {id: id},
table: {id: table.id}
}, options).pipe(
catchError(this.log.handleError(serviceName, 'moveTable'))
);
}
mergeTable(id: string, table: Table): Observable<boolean> {
const options = {params: new HttpParams().set('merge', '')};
return <Observable<boolean>>this.http.post<boolean>(urlMoveTable, {
voucher: {id: id},
table: {id: table.id},
newVoucher: {id: table.voucherId}
}, options).pipe(
catchError(this.log.handleError(serviceName, 'mergeTable'))
);
}
moveKotToNewTable(id: string, kotId: string, table: Table): Observable<boolean> {
const options = {params: new HttpParams().set('move', '')};
return <Observable<boolean>>this.http.post<boolean>(urlMoveKot, {
voucher: {id: id},
kot: {id: kotId},
table: {id: table.id}
}, options).pipe(
catchError(this.log.handleError(serviceName, 'moveKotToNewTable'))
);
}
mergeKotWithOldBill(id: string, kotId: string, table: Table): Observable<boolean> {
const options = {params: new HttpParams().set('merge', '')};
return <Observable<boolean>>this.http.post<boolean>(urlMoveKot, {
voucher: {id: id},
kot: {id: kotId},
table: {id: table.id},
newVoucher: {id: table.voucherId}
}, options).pipe(
catchError(this.log.handleError(serviceName, 'mergeKotWithOldBill'))
);
}
splitBill(id: string, inventoriesToMove: string[], table: Table) {
const options = {params: new HttpParams().set('split-bill', '').set('u', 'true')};
return <Observable<boolean>>this.http.post<boolean>(`${url}/${id}`, {
voucher: {id: id},
inventories: inventoriesToMove,
table: {id: table.id}
}, options).pipe(
catchError(this.log.handleError(serviceName, 'splitBill'))
);
}
}

View File

@ -24,10 +24,7 @@
<mat-card fxLayout="column" class="square-button" matRipple (click)="voidBill()">
<h3 class="item-name">Void Bill</h3>
</mat-card>
<mat-card fxLayout="column" class="square-button" matRipple [routerLink]="['../../tables']">
<h3 class="item-name">Move KOT</h3>
</mat-card>
<mat-card fxLayout="column" class="square-button" matRipple [routerLink]="['../../tables']">
<mat-card fxLayout="column" class="square-button" matRipple (click)="splitBill()">
<h3 class="item-name">Split Bill</h3>
</mat-card>
</div>

View File

@ -1,8 +1,8 @@
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { MatDialog } from '@angular/material';
import { concatMap, map, switchMap, tap } from 'rxjs/operators';
import { iif, Observable, of as observableOf, throwError } from 'rxjs';
import { map, switchMap, tap } from 'rxjs/operators';
import { Observable, of as observableOf, throwError } from 'rxjs';
import { BillService } from '../bill.service';
import { ToasterService } from '../../core/toaster.service';
import { DiscountComponent } from '../discount/discount.component';
@ -70,13 +70,11 @@ export class SalesHomeComponent implements OnInit {
}
discountDialog (canGiveDiscount: boolean): Observable<any> {
let discObs = null;
if (canGiveDiscount) {
return discObs = this.showDiscount();
return this.showDiscount();
} else {
return discObs = observableOf('');
return observableOf('');
}
}
billTypeDialog() {
@ -89,10 +87,10 @@ export class SalesHomeComponent implements OnInit {
);
}
confirmMoveTableDialog(table: Table): Observable<{table: Table, confirmed: boolean}> {
confirmTableDialog(table: Table): Observable<{table: Table, confirmed: boolean}> {
return this.dialog.open(ConfirmDialogComponent, {
width: '250px',
data: {title: 'Move Table?', content: 'Are you sure?'}
data: {title: 'Select Table?', content: 'Are you sure?'}
}).afterClosed().pipe(
map ((x: boolean) => ({table: table, confirmed: x}))
);
@ -114,20 +112,19 @@ export class SalesHomeComponent implements OnInit {
guestBookId = this.route.snapshot.queryParamMap.get('guest');
}
this.discountDialog(canGiveDiscount).pipe(
concatMap(() => this.billTypeDialog())
).pipe(
concatMap(
(x: boolean | PrintType) => iif(
() => !!x,
this.bs.printBill(guestBookId, x as PrintType),
throwError(x)
)
),
switchMap(() => this.billTypeDialog()),
switchMap((x: boolean | PrintType) => {
if (!!x) {
return this.bs.printBill(guestBookId, x as PrintType);
} else {
return throwError(x);
}
}),
).subscribe(() => {
this.toaster.show('Success', '');
this.router.navigate(['/sales']);
},
x => {
() => {
this.toaster.show('Error', 'No Bill Type Chosen');
});
}
@ -153,29 +150,32 @@ export class SalesHomeComponent implements OnInit {
}
moveTable() {
const canMergeTables = this.auth.hasPermission("Merge Tables");
this.dialog.open(TablesDialogComponent, {
// width: '750px',
data: {
list: this.tSer.running(),
canChooseRunning: false
canChooseRunning: canMergeTables
}
}).afterClosed().pipe(
switchMap((x: boolean | Table) => {
if (!!x) {
return this.confirmMoveTableDialog(x as Table);
if (!x) {
return this.confirmTableDialog(x as Table);
} else {
return throwError('Please choose a table');
}
}),
switchMap((value: { table: Table, confirmed: boolean }, index: number) => {
if (!!value.confirmed) {
return this.bs.moveTable(value.table);
} else {
if (!value.confirmed) {
return throwError('Please confirm move');
} else if (value.table.status) {
return this.bs.mergeTable(value.table);
} else {
return this.bs.moveTable(value.table);
}
}
)
).subscribe((x) => {
).subscribe(() => {
this.toaster.show('Success', '');
this.router.navigate(['/sales']);
},
@ -210,4 +210,35 @@ export class SalesHomeComponent implements OnInit {
this.toaster.show('Error', x);
});
}
splitBill() {
this.dialog.open(TablesDialogComponent, {
// width: '750px',
data: {
list: this.tSer.running(),
canChooseRunning: false
}
}).afterClosed().pipe(
switchMap((x: boolean | Table) => {
if (!!x) {
return this.confirmTableDialog(x as Table);
} else {
return throwError('Please choose a table');
}
}),
switchMap((value: { table: Table, confirmed: boolean }, index: number) => {
if (!value.confirmed) {
return throwError('Please confirm split');
} else {
return this.bs.splitBill(value.table);
}
})
).subscribe((x) => {
this.toaster.show('Success', '');
this.router.navigate(['/sales']);
},
x => {
this.toaster.show('Error', x);
});
}
}

View File

@ -5,6 +5,7 @@ import { MatBadgeModule } from '@angular/material/badge';
import { MatButtonModule } from '@angular/material/button';
import { MatButtonToggleModule } from '@angular/material/button-toggle';
import { MatCardModule } from '@angular/material/card';
import { MatCheckboxModule } from '@angular/material/checkbox';
import { MatChipsModule } from '@angular/material/chips';
import { MatDialogModule } from '@angular/material/dialog';
import { MatDividerModule } from '@angular/material/divider';
@ -57,6 +58,7 @@ import { VoidReasonComponent } from './void-reason/void-reason.component';
MatButtonModule,
MatButtonToggleModule,
MatCardModule,
MatCheckboxModule,
MatChipsModule,
MatDialogModule,
MatDividerModule,