4 Commits

Author SHA1 Message Date
302ed4a18f Version Bump v11.4.2 2023-03-24 12:46:54 +05:30
2fb2e01ca1 Feature: Allow edit of time in guest book.
Feature: Guest book row color is the same as running table colors
2023-03-24 12:46:42 +05:30
94cc8ccd47 Version Bump v11.4.1 2023-03-24 10:17:16 +05:30
c13047e812 Fix: Check if moved/merged kots could lead to negative products of a situation where happy hour products were more than regular products.
Case:
  Kot 1:
    Beer Mug => 2 regular, 2 happy hour
  Kot 2:
    Beer Mug => 2 Regular
  Kot 3:
    Beer Mug => -2 Regular

Then move Kot 2 to new table would leave the voucher with 2 hh and 0 regular
Also, just move did not check if products were going negative.
2023-03-24 10:17:06 +05:30
18 changed files with 127 additions and 46 deletions

View File

@ -1 +1 @@
__version__ = "11.4.0"
__version__ = "11.4.2"

View File

@ -30,11 +30,11 @@ class GuestBook:
customer: Mapped["Customer"] = relationship("Customer")
status: Mapped[Optional["Overview"]] = relationship(back_populates="guest", uselist=False)
def __init__(self, pax=None, id_=None, customer_id=None, customer=None):
def __init__(self, pax=None, id_=None, customer_id=None, customer=None, date_=None):
self.customer_id = customer_id
self.pax = pax
self.id = id_
self.date = datetime.utcnow()
self.date = datetime.utcnow() if date_ is None else date_
if customer is None:
self.customer_id = customer_id
else:

View File

@ -38,7 +38,9 @@ def save(
else:
customer.name = data.name or customer.name
customer.address = data.address or customer.address
item = GuestBook(pax=data.pax, customer=customer)
item = GuestBook(
pax=data.pax, customer=customer, date_=data.date_ - timedelta(minutes=settings.TIMEZONE_OFFSET_MINUTES)
)
db.add(item)
db.commit()
return guest_book_info(item)
@ -62,6 +64,7 @@ def update_route(
item.customer.phone = data.phone
item.customer.address = data.address
item.pax = data.pax
item.date = data.date_ - timedelta(minutes=settings.TIMEZONE_OFFSET_MINUTES)
db.commit()
return guest_book_info(item)
except SQLAlchemyError as e:
@ -159,9 +162,15 @@ def guest_book_info(item: GuestBook) -> schemas.GuestBook:
phone=item.customer.phone,
pax=item.pax,
address=item.customer.address,
date=item.date,
date=item.date + timedelta(minutes=settings.TIMEZONE_OFFSET_MINUTES),
)
def blank_guest_book_info() -> schemas.GuestBookIn:
return schemas.GuestBookIn(name="", phone="", pax=0, address="")
return schemas.GuestBookIn(
name="",
phone="",
pax=0,
address="",
date=datetime.utcnow() + timedelta(minutes=settings.TIMEZONE_OFFSET_MINUTES),
)

View File

@ -1,17 +1,16 @@
import uuid
from datetime import datetime
from decimal import Decimal
from typing import List, Set
from typing import Set
import barker.schemas.voucher as schemas
from barker.models.bill import Bill
from fastapi import HTTPException, status
from sqlalchemy import func
from sqlalchemy.orm import Session
from sqlalchemy.sql import expression
from ...models.bill import Bill
from ...models.guest_book import GuestBook
from ...models.overview import Overview
from ...models.regime import Regime

View File

@ -50,7 +50,20 @@ def merge_kot(
.where(Kot.id == data.kot_id, Kot.voucher_id == data.voucher_id)
.values(voucher_id=data.new_voucher_id)
)
update_settlements([data.voucher_id, data.new_voucher_id], db)
donor: Voucher = db.execute(select(Voucher).where(Voucher.id == data.voucher_id)).scalar_one()
recipent: Voucher = db.execute(select(Voucher).where(Voucher.id == data.new_voucher_id)).scalar_one()
if has_negatives(donor.kots) or happy_hour_exceeds_regular(donor.kots):
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="Donor voucher will have negative products or too many happy hours",
)
if has_negatives(recipent.kots) or happy_hour_exceeds_regular(recipent.kots):
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="Recipient voucher will have negative products or too many happy hours",
)
do_update_settlements(donor, [], db)
do_update_settlements(recipent, [], db)
db.commit()
except SQLAlchemyError as e:
raise HTTPException(
@ -91,7 +104,19 @@ def move_kot(
db.flush()
do_update_bill_numbers(item, db)
do_update_table(item, None, db)
update_settlements([data.voucher_id, item.id], db)
donor: Voucher = db.execute(select(Voucher).where(Voucher.id == data.voucher_id)).scalar_one()
if has_negatives(donor.kots) or happy_hour_exceeds_regular(donor.kots):
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="Donor voucher will have negative products or too many happy hours",
)
if has_negatives(item.kots) or happy_hour_exceeds_regular(item.kots):
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="Recipient voucher will have negative products or too many happy hours",
)
do_update_settlements(donor, [], db)
do_update_settlements(item, [], db)
db.commit()
except SQLAlchemyError as e:
raise HTTPException(
@ -133,7 +158,8 @@ def merge_table(
db.execute(delete(Overview).where(Overview.voucher_id == data.voucher_id))
db.execute(delete(Settlement).where(Settlement.voucher_id == data.voucher_id))
db.execute(delete(Voucher).where(Voucher.id == data.voucher_id))
update_settlements([data.new_voucher_id], db)
recipent: Voucher = db.execute(select(Voucher).where(Voucher.id == data.new_voucher_id)).scalar_one()
do_update_settlements(recipent, [], db)
db.commit()
except SQLAlchemyError as e:
traceback.print_exc()
@ -143,12 +169,6 @@ def merge_table(
)
def update_settlements(vouchers: list[uuid.UUID], db: Session):
for v in vouchers:
voucher: Voucher = db.execute(select(Voucher).where(Voucher.id == v)).scalar_one()
do_update_settlements(voucher, [], db)
def check_if_voucher_is_unprinted(voucher_id: uuid.UUID, db: Session) -> None:
voucher_type: VoucherType = db.execute(select(Voucher.voucher_type).where(Voucher.id == voucher_id)).scalar_one()
if voucher_type is None:
@ -161,3 +181,26 @@ def check_if_voucher_is_unprinted(voucher_id: uuid.UUID, db: Session) -> None:
status_code=status.HTTP_403_FORBIDDEN,
detail="Bill is printed or void.",
)
def has_negatives(kots: list[Kot]) -> bool:
inv = [i for k in kots for i in k.inventories]
products = set((i.product_id, i.is_happy_hour) for i in inv)
for id_, hh in products:
quantity = sum(i.quantity for i in inv if i.product_id == id_ and i.is_happy_hour == hh)
if quantity < 0:
return True
return False
# This is for the whole bill. eg. Kot 1 => Reg 2 + HH 2; Kot 2 => Reg 4; Kot 3 => Reg - 4
# This is pass okay in happy hours items balanced, but overall this is wrong. Hence this check
def happy_hour_exceeds_regular(kots: list[Kot]) -> bool:
inv = [i for k in kots for i in k.inventories]
products = set(i.product_id for i in inv if i.is_happy_hour)
for p in products:
r = sum(i.quantity for i in inv if i.product_id == p and not i.is_happy_hour)
h = sum(i.quantity for i in inv if i.product_id == p and i.is_happy_hour)
if r < h:
return True
return False

View File

@ -147,7 +147,7 @@ def voucher_info(item: Voucher, db: Session):
"kotId": item.kot_id,
"billId": ", ".join(f"{x.regime.prefix}-{x.bill_number}" for x in item.bills),
"table": {"id": item.food_table_id, "name": item.food_table.name},
"customer": {"id": item.customer_id, "name": item.customer.name} if item.customer is not None else None,
"customer": {"id": item.customer.id, "name": item.customer.name} if item.customer is not None else None,
"narration": item.narration,
"reason": item.reason,
"voucherType": item.voucher_type,

View File

@ -12,16 +12,23 @@ class GuestBookIn(BaseModel):
phone: str
address: str | None
pax: int = Field(ge=0)
date_: datetime
@validator("date_", pre=True)
def parse_date(cls, value):
if isinstance(value, datetime):
return value
return datetime.strptime(value, "%d-%b-%Y %H:%M")
class Config:
fields = {"id_": "id"}
anystr_strip_whitespace = True
alias_generator = to_camel
json_encoders = {datetime: lambda v: v.strftime("%d-%b-%Y %H:%M")}
class GuestBook(GuestBookIn):
id_: uuid.UUID
date_: datetime
@validator("date_", pre=True)
def parse_date(cls, value):

View File

@ -1,6 +1,6 @@
[tool.poetry]
name = "barker"
version = "11.4.0"
version = "11.4.2"
description = "Point of Sale for a restaurant"
authors = ["tanshu <git@tanshu.com>"]

View File

@ -1,6 +1,6 @@
{
"name": "bookie",
"version": "11.4.0",
"version": "11.4.2",
"scripts": {
"ng": "ng",
"start": "ng serve",

View File

@ -39,6 +39,16 @@
<textarea matInput formControlName="address"></textarea>
</mat-form-field>
</div>
<div class="flex flex-row justify-around content-start items-start">
<mat-form-field class="flex-auto">
<mat-label>Hours</mat-label>
<input matInput formControlName="hours" />
</mat-form-field>
<mat-form-field class="flex-auto">
<mat-label>Minutes</mat-label>
<input matInput formControlName="minutes" />
</mat-form-field>
</div>
</form>
</mat-card-content>
<mat-card-actions>

View File

@ -23,6 +23,8 @@ export class GuestBookDetailComponent implements OnInit, AfterViewInit {
phone: FormControl<string | Customer>;
pax: FormControl<number>;
address: FormControl<string | null>;
hours: FormControl<number>;
minutes: FormControl<number>;
}>;
item: GuestBook = new GuestBook();
@ -44,6 +46,8 @@ export class GuestBookDetailComponent implements OnInit, AfterViewInit {
}),
pax: new FormControl(0, { validators: Validators.required, nonNullable: true }),
address: new FormControl<string | null>(null),
hours: new FormControl(0, { validators: [Validators.min(0), Validators.max(23)], nonNullable: true }),
minutes: new FormControl(0, { validators: [Validators.min(0), Validators.max(59)], nonNullable: true }),
});
// Setup Account Autocomplete
this.customers = this.form.controls.phone.valueChanges.pipe(
@ -77,6 +81,8 @@ export class GuestBookDetailComponent implements OnInit, AfterViewInit {
phone: item.phone,
pax: item.pax,
address: item.address,
hours: +item.date.substring(12, 14),
minutes: +item.date.substring(15, 17),
});
}
@ -115,6 +121,9 @@ export class GuestBookDetailComponent implements OnInit, AfterViewInit {
}
this.item.pax = formModel.pax ?? 0;
this.item.address = formModel.address ?? null;
const hours = ('' + (formModel.hours as number)).padStart(2, '0');
const minutes = ('' + (formModel.minutes as number)).padStart(2, '0');
this.item.date = this.item.date.substring(0, 12) + hours + ':' + minutes;
return this.item;
}
}

View File

@ -1,17 +0,0 @@
.full-width-table {
width: 100%;
}
.running {
background-color: #f53d24;
color: #000000;
}
.printed {
background-color: #00f518;
color: #000000;
}
.center {
display: flex;
justify-content: center;
}

View File

@ -23,7 +23,7 @@
<!-- SNo Column -->
<ng-container matColumnDef="sno">
<mat-header-cell *matHeaderCellDef>S. No</mat-header-cell>
<mat-cell *matCellDef="let row">{{ row.serial }} {{ row.status }}</mat-cell>
<mat-cell *matCellDef="let row">{{ row.serial }}</mat-cell>
</ng-container>
<!-- Name Column -->
@ -78,8 +78,8 @@
<mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
<mat-row
*matRowDef="let row; columns: displayedColumns"
[class.running]="row.status === 'running'"
[class.printed]="row.status === 'printed'"
[class.accent]="row.status === 'running'"
[class.strong-accent]="row.status === 'printed'"
></mat-row>
</mat-table>
</mat-card-content>

View File

@ -0,0 +1,21 @@
@use '@angular/material' as mat
$my-primary: mat.define-palette(mat.$indigo-palette, 500)
$my-accent: mat.define-palette(mat.$amber-palette, A200, A100, A400)
table
width: 100%
.center
display: flex
justify-content: center
.accent
/* Read the 200 hue from the primary color palete.*/
color: mat.get-color-from-palette($my-accent, '200-contrast')
background: mat.get-color-from-palette($my-accent, 200)
.strong-accent
/* Read the 700 hue from the primary color palete.*/
color: mat.get-color-from-palette($my-accent, '700-contrast')
background: mat.get-color-from-palette($my-accent, 700)

View File

@ -17,7 +17,7 @@ import { GuestBookListDataSource } from './guest-book-list-datasource';
@Component({
selector: 'app-guest-book-list',
templateUrl: './guest-book-list.component.html',
styleUrls: ['./guest-book-list.component.css'],
styleUrls: ['./guest-book-list.component.sass'],
})
export class GuestBookListComponent implements OnInit {
data: BehaviorSubject<GuestBook[]> = new BehaviorSubject<GuestBook[]>([]);

View File

@ -1,5 +1,5 @@
export const environment = {
production: true,
ACCESS_TOKEN_REFRESH_MINUTES: 10, // refresh token 10 minutes before expiry
version: '11.4.0',
version: '11.4.2',
};

View File

@ -5,7 +5,7 @@
export const environment = {
production: false,
ACCESS_TOKEN_REFRESH_MINUTES: 10, // refresh token 10 minutes before expiry
version: '11.4.0',
version: '11.4.2',
};
/*

View File

@ -1,6 +1,6 @@
[tool.poetry]
name = "frank"
version = "11.4.0"
version = "11.4.2"
description = "Point of Sale for a restaurant"
authors = ["tanshu <git@tanshu.com>"]