From e4500f0d4615532cb8b068d5251b8a5db14e9378 Mon Sep 17 00:00:00 2001 From: tanshu Date: Wed, 16 Dec 2020 11:49:22 +0530 Subject: [PATCH] Breaking: Discount is applicable on sale category and not on menu category Fix the import, etc on this. While entering discount in sale, it checks the max allowed. --- DB/migrate.cmd | 4 +- .../versions/8c06ac60d125_initial_commit.py | 2 +- barker/barker/models/master.py | 8 +- barker/barker/routers/__init__.py | 12 +- barker/barker/routers/customer.py | 2 +- barker/barker/routers/device.py | 2 +- barker/barker/routers/menu_category.py | 103 +++++++++++------- barker/barker/routers/modifier_category.py | 19 +--- barker/barker/routers/printer.py | 46 ++++---- barker/barker/routers/product.py | 27 ++--- barker/barker/routers/sale_category.py | 56 +++++----- barker/barker/routers/section.py | 36 +++--- barker/barker/routers/section_printer.py | 2 +- barker/barker/routers/table.py | 2 +- barker/barker/routers/tax.py | 2 +- barker/barker/routers/voucher/save.py | 2 +- barker/barker/routers/voucher/show.py | 2 +- barker/barker/routers/voucher/update.py | 5 +- barker/barker/schemas/master.py | 17 --- barker/barker/schemas/menu_category.py | 14 ++- barker/barker/schemas/printer.py | 11 +- barker/barker/schemas/product.py | 36 +++--- barker/barker/schemas/product_link.py | 14 +++ barker/barker/schemas/sale_category.py | 22 +++- barker/barker/schemas/section.py | 15 ++- barker/barker/schemas/tax.py | 1 + barker/barker/schemas/voucher.py | 2 +- bookie/src/app/core/menu-category.ts | 2 - bookie/src/app/core/product.ts | 6 +- bookie/src/app/core/sale-category.ts | 5 +- .../menu-category-detail.component.html | 19 ---- .../menu-category-detail.component.ts | 3 - .../menu-category-list.component.html | 6 - .../menu-category-list.component.ts | 5 +- .../menu-category/menu-category.service.ts | 11 +- .../product-detail.component.ts | 10 +- .../product-list/product-list-datasource.ts | 5 +- .../product-list/product-list.component.ts | 2 +- .../sale-category-detail.component.html | 19 ++++ .../sale-category-detail.component.ts | 9 +- .../sale-category-list.component.html | 6 + .../sale-category-list.component.ts | 2 +- .../sale-category/sale-category.service.ts | 6 +- bookie/src/app/sales/bill.service.ts | 27 +++-- .../sales/discount/discount.component.html | 1 + .../app/sales/discount/discount.component.ts | 15 ++- .../menu-categories-resolver.service.ts | 2 +- docker/app/Dockerfile | 5 +- 48 files changed, 353 insertions(+), 277 deletions(-) create mode 100644 barker/barker/schemas/product_link.py diff --git a/DB/migrate.cmd b/DB/migrate.cmd index f619d7b..75bf1cd 100644 --- a/DB/migrate.cmd +++ b/DB/migrate.cmd @@ -7,8 +7,8 @@ call:copyQuery e-UserRoles "SELECT UserGroupID, UserID, GroupID FROM Test.dbo.Au call:copyQuery f-Customers "SELECT CustomerID, Name, CASE WHEN Address = '' THEN '\N' ELSE Address END, Phone + ' ' + Name FROM Test.dbo.Customers WHERE CustomerID != '2c716f4b-0736-429a-ad51-610d7c47cb5e';" call:copyQuery g-FoodTables "SELECT FoodTableID, Name, 0, CASE WHEN IsActive = 1 THEN 't' ELSE 'f' END, '3f13f6e7-dc76-4fca-8fdb-b2bbf29b35df', SortOrder FROM Test.dbo.FoodTables;" call:copyQuery h-Taxes "SELECT TaxID, Name, Rate, 'f' FROM Test.dbo.Taxes;" -call:copyQuery i-MenuCategories "SELECT ProductGroupID, Name, DiscountLimit, CASE WHEN IsActive = 1 THEN 't' ELSE 'f' END, 'f', SortOrder FROM Test.dbo.ProductGroups;" -call:copyQuery j-SaleCategories "SELECT newid(), pgo.GroupType, CAST((select top 1 t.TaxID from Test.dbo.Taxes t inner join Test.dbo.Products p on t.TaxID = p.VatID inner join Test.dbo.ProductGroups pg on p.ProductGroupID = pg.ProductGroupID where pg.GroupType = pgo.GroupType) AS Nvarchar(36)) FROM (select distinct pgx.GroupType from Test.dbo.ProductGroups pgx) as pgo;" +call:copyQuery i-MenuCategories "SELECT ProductGroupID, Name, CASE WHEN IsActive = 1 THEN 't' ELSE 'f' END, 'f', SortOrder FROM Test.dbo.ProductGroups;" +call:copyQuery j-SaleCategories "SELECT newid(), pgo.GroupType, 1, CAST((select top 1 t.TaxID from Test.dbo.Taxes t inner join Test.dbo.Products p on t.TaxID = p.VatID inner join Test.dbo.ProductGroups pg on p.ProductGroupID = pg.ProductGroupID where pg.GroupType = pgo.GroupType) AS Nvarchar(36)) FROM (select distinct pgx.GroupType from Test.dbo.ProductGroups pgx) as pgo;" call:copyQuery k-Products "SELECT ProductID, Name, COALESCE(Units, '\N'), ProductGroupID, (SELECT GroupType FROM Test.dbo.ProductGroups pgs WHERE pgs.ProductGroupID = Products.ProductGroupID), Price, CASE WHEN HasHappyHour = 1 THEN 't' ELSE 'f' END , CASE WHEN IsActive = 1 THEN 't' ELSE 'f' END, CASE WHEN IsNotAvailable = 1 THEN 't' ELSE 'f' END, SortOrder, Quantity FROM Test.dbo.Products;" call:copyQuery l-Modifiers "SELECT ModifierID, Name, CASE WHEN ShowInBill = 1 THEN 't' ELSE 'f' END, 't', 'e046ad33-dc65-4c78-8833-c3d3538d44c0', Price FROM Test.dbo.Modifiers;" call:copyQuery m-Sections "SELECT NewID(), Location, 'f' FROM Test.dbo.PrintLocations GROUP BY Location;" diff --git a/barker/alembic/versions/8c06ac60d125_initial_commit.py b/barker/alembic/versions/8c06ac60d125_initial_commit.py index 92ed545..67602e0 100644 --- a/barker/alembic/versions/8c06ac60d125_initial_commit.py +++ b/barker/alembic/versions/8c06ac60d125_initial_commit.py @@ -98,7 +98,6 @@ def upgrade(): "menu_categories", sa.Column("id", postgresql.UUID(as_uuid=True), server_default=sa.text("gen_random_uuid()"), nullable=False), sa.Column("name", sa.Unicode(length=255), nullable=False), - sa.Column("discount_limit", sa.Numeric(precision=15, scale=5), nullable=False), sa.Column("is_active", sa.Boolean(), nullable=False), sa.Column("is_fixture", sa.Boolean(), nullable=False), sa.Column("sort_order", sa.Integer(), nullable=False), @@ -268,6 +267,7 @@ def upgrade(): op.create_table( "sale_categories", sa.Column("id", postgresql.UUID(as_uuid=True), server_default=sa.text("gen_random_uuid()"), nullable=False), + sa.Column("discount_limit", sa.Numeric(precision=15, scale=5), nullable=False), sa.Column("name", sa.Unicode(length=255), nullable=False), sa.Column("tax_id", postgresql.UUID(as_uuid=True), nullable=False), sa.ForeignKeyConstraint(["tax_id"], ["taxes.id"], name=op.f("fk_sale_categories_tax_id_taxes")), diff --git a/barker/barker/models/master.py b/barker/barker/models/master.py index be597c9..51f6549 100644 --- a/barker/barker/models/master.py +++ b/barker/barker/models/master.py @@ -124,15 +124,13 @@ class MenuCategory(Base): "id", UUID(as_uuid=True), primary_key=True, server_default=text("gen_random_uuid()"), default=uuid.uuid4 ) name = Column("name", Unicode(255), nullable=False, unique=True) - discount_limit = Column("discount_limit", Numeric(precision=15, scale=5), nullable=False) is_active = Column("is_active", Boolean, nullable=False) is_fixture = Column("is_fixture", Boolean, nullable=False) sort_order = Column("sort_order", Integer, nullable=False) - def __init__(self, name, discount_limit, is_active, sort_order, is_fixture=False, id_=None): + def __init__(self, name, is_active, sort_order, is_fixture=False, id_=None): self.name = name - self.discount_limit = discount_limit self.is_active = is_active self.sort_order = sort_order self.is_fixture = is_fixture @@ -146,11 +144,13 @@ class SaleCategory(Base): "id", UUID(as_uuid=True), primary_key=True, server_default=text("gen_random_uuid()"), default=uuid.uuid4 ) name = Column("name", Unicode(255), nullable=False, unique=True) + discount_limit = Column("discount_limit", Numeric(precision=15, scale=5), nullable=False) tax_id = Column("tax_id", UUID(as_uuid=True), ForeignKey("taxes.id"), nullable=False) tax = relationship("Tax", foreign_keys=tax_id) - def __init__(self, name, tax_id=False, id_=None): + def __init__(self, name, discount_limit=None, tax_id=False, id_=None): self.name = name + self.discount_limit = discount_limit if discount_limit is not None else 1 self.tax_id = tax_id self.id = id_ diff --git a/barker/barker/routers/__init__.py b/barker/barker/routers/__init__.py index ab31af0..7e8d9bb 100644 --- a/barker/barker/routers/__init__.py +++ b/barker/barker/routers/__init__.py @@ -1,6 +1,8 @@ -from datetime import date, datetime +from datetime import date, datetime, timedelta from typing import Optional +from barker.core.config import settings + def query_date(d: str = None) -> date: return date.today() if d is None else datetime.strptime(d, "%d-%b-%Y").date() @@ -8,3 +10,11 @@ def query_date(d: str = None) -> date: def optional_query_date(d: str = None) -> Optional[date]: return None if d is None else datetime.strptime(d, "%d-%b-%Y").date() + + +def effective_date(d: str = None) -> date: + return ( + (datetime.now() - timedelta(minutes=settings.NEW_DAY_OFFSET_MINUTES)).date() + if d is None + else datetime.strptime(d, "%d-%b-%Y").date() + ) diff --git a/barker/barker/routers/customer.py b/barker/barker/routers/customer.py index 45939ff..748f383 100644 --- a/barker/barker/routers/customer.py +++ b/barker/barker/routers/customer.py @@ -2,7 +2,7 @@ import uuid from typing import List -import barker.schemas.master as schemas +import barker.schemas.customer as schemas from fastapi import APIRouter, Depends, HTTPException, Security, status from sqlalchemy.exc import SQLAlchemyError diff --git a/barker/barker/routers/device.py b/barker/barker/routers/device.py index dc51430..0d65463 100644 --- a/barker/barker/routers/device.py +++ b/barker/barker/routers/device.py @@ -2,7 +2,7 @@ import uuid from typing import List -import barker.schemas.master as schemas +import barker.schemas.device as schemas from fastapi import APIRouter, Depends, HTTPException, Security, status from sqlalchemy.exc import SQLAlchemyError diff --git a/barker/barker/routers/menu_category.py b/barker/barker/routers/menu_category.py index d88e520..637df56 100644 --- a/barker/barker/routers/menu_category.py +++ b/barker/barker/routers/menu_category.py @@ -1,17 +1,21 @@ import uuid -from typing import Optional +from datetime import date +from operator import and_, or_ +from typing import List, Optional -import barker.schemas.master as schemas +import barker.schemas.menu_category as schemas from fastapi import APIRouter, Depends, HTTPException, Security, status +from sqlalchemy import distinct from sqlalchemy.exc import SQLAlchemyError from sqlalchemy.orm import Session from ..core.security import get_current_active_user as get_user from ..db.session import SessionLocal -from ..models.master import MenuCategory +from ..models.master import MenuCategory, ProductVersion from ..schemas.auth import UserToken +from . import effective_date router = APIRouter() @@ -31,11 +35,10 @@ def save( data: schemas.MenuCategoryIn, db: Session = Depends(get_db), user: UserToken = Security(get_user, scopes=["products"]), -): +) -> schemas.MenuCategory: try: item = MenuCategory( name=data.name, - discount_limit=data.discount_limit, is_active=data.is_active, sort_order=0, ) @@ -59,7 +62,7 @@ def update( data: schemas.MenuCategoryIn, db: Session = Depends(get_db), user: UserToken = Security(get_user, scopes=["products"]), -): +) -> schemas.MenuCategory: try: item: MenuCategory = db.query(MenuCategory).filter(MenuCategory.id == id_).first() if item.is_fixture: @@ -68,8 +71,7 @@ def update( detail=f"{item.name} is a fixture and cannot be edited or deleted.", ) item.name = data.name - item.discount_limit = data.discount_limit - item.is_active = data.discount_limit + item.is_active = data.is_active db.commit() return menu_category_info(item) except SQLAlchemyError as e: @@ -83,12 +85,12 @@ def update( raise -@router.delete("/{id_}") +@router.delete("/{id_}", response_model=schemas.MenuCategoryBlank) def delete( id_: uuid.UUID, db: Session = Depends(get_db), user: UserToken = Security(get_user, scopes=["products"]), -): +) -> schemas.MenuCategoryBlank: try: item: MenuCategory = db.query(MenuCategory).filter(MenuCategory.id == id_).first() if item is None: @@ -111,28 +113,53 @@ def delete( raise -@router.get("") +@router.get("", response_model=schemas.MenuCategoryBlank) def show_blank( db: Session = Depends(get_db), user: UserToken = Security(get_user, scopes=["products"]), -): - return menu_category_info(None) +) -> schemas.MenuCategoryBlank: + return menu_category_blank() -@router.get("/list") -def show_list(db: Session = Depends(get_db), user: UserToken = Depends(get_user)): - return [ - menu_category_info(item) - for item in db.query(MenuCategory).order_by(MenuCategory.sort_order).order_by(MenuCategory.name).all() - ] +@router.get("/list", response_model=List[schemas.MenuCategory]) +def show_list( + p: Optional[bool] = None, + date_: date = Depends(effective_date), + db: Session = Depends(get_db), + user: UserToken = Depends(get_user), +) -> List[schemas.MenuCategory]: + + if p is not None: + sq = db.query(distinct(ProductVersion.menu_category_id)).filter( + and_( + or_( + ProductVersion.valid_from == None, # noqa: E711 + ProductVersion.valid_from <= date_, + ), + or_( + ProductVersion.valid_till == None, # noqa: E711 + ProductVersion.valid_till >= date_, + ), + ) + ) + query = ( + db.query(MenuCategory) + .filter(MenuCategory.id.in_(sq)) + .order_by(MenuCategory.sort_order) + .order_by(MenuCategory.name) + .all() + ) + else: + query = db.query(MenuCategory).order_by(MenuCategory.sort_order).order_by(MenuCategory.name).all() + return [menu_category_info(item) for item in query] -@router.get("/{id_}") +@router.get("/{id_}", response_model=schemas.MenuCategory) def show_id( id_: uuid.UUID, db: Session = Depends(get_db), user: UserToken = Security(get_user, scopes=["products"]), -): +) -> schemas.MenuCategory: item: MenuCategory = db.query(MenuCategory).filter(MenuCategory.id == id_).first() return menu_category_info(item) @@ -149,20 +176,20 @@ def show_id( # return True -def menu_category_info(item: Optional[MenuCategory]): - if item is None: - return { - "name": "", - "discountLimit": 0, - "isActive": True, - "isFixture": False, - "sortOrder": 0, - } - return { - "id": item.id, - "name": item.name, - "discountLimit": item.discount_limit * 100, - "isActive": item.is_active, - "isFixture": item.is_fixture, - "sortOrder": item.sort_order, - } +def menu_category_info(item: MenuCategory) -> schemas.MenuCategory: + return schemas.MenuCategory( + id=item.id, + name=item.name, + isActive=item.is_active, + isFixture=item.is_fixture, + sortOrder=item.sort_order, + ) + + +def menu_category_blank() -> schemas.MenuCategoryBlank: + return schemas.MenuCategoryBlank( + name="", + isActive=True, + isFixture=False, + sortOrder=0, + ) diff --git a/barker/barker/routers/modifier_category.py b/barker/barker/routers/modifier_category.py index 7dc06e9..a8a37c2 100644 --- a/barker/barker/routers/modifier_category.py +++ b/barker/barker/routers/modifier_category.py @@ -1,21 +1,22 @@ import uuid -from datetime import date, datetime, timedelta +from datetime import date from functools import reduce from typing import List -import barker.schemas.master as schemas +import barker.schemas.modifier_category as schemas +from barker.schemas.product_link import ProductLink as ProductLinkSchema from fastapi import APIRouter, Depends, HTTPException, Security, status from sqlalchemy import and_, or_ from sqlalchemy.exc import SQLAlchemyError from sqlalchemy.orm import Session, contains_eager, joinedload -from ..core.config import settings from ..core.security import get_current_active_user as get_user from ..db.session import SessionLocal from ..models.master import MenuCategory, ModifierCategory, Product, ProductVersion from ..schemas.auth import UserToken +from . import effective_date router = APIRouter() @@ -30,14 +31,6 @@ def get_db(): db.close() -def effective_date(d: str = None) -> date: - return ( - (datetime.now() - timedelta(minutes=settings.NEW_DAY_OFFSET_MINUTES)).date() - if d is None - else datetime.strptime(d, "%d-%b-%Y").date() - ) - - @router.post("", response_model=schemas.ModifierCategory) def save( data: schemas.ModifierCategoryIn, @@ -252,7 +245,7 @@ def modifier_category_info(item: ModifierCategory, date_: date, db: Session) -> name=mc.name, enabled=False, products=[ - schemas.ProductLink( + ProductLinkSchema( id=p.id, name=p.name, enabled=True if p.product_id in products else False, @@ -299,7 +292,7 @@ def modifier_category_blank(date_: date, db: Session) -> schemas.ModifierCategor id=mc.id, name=mc.name, enabled=False, - products=[schemas.ProductLink(id=p.id, name=p.name, enabled=False) for p in mc.products], + products=[ProductLinkSchema(id=p.id, name=p.name, enabled=False) for p in mc.products], ) for mc in menu_categories ], diff --git a/barker/barker/routers/printer.py b/barker/barker/routers/printer.py index eba28ff..52f4e23 100644 --- a/barker/barker/routers/printer.py +++ b/barker/barker/routers/printer.py @@ -1,8 +1,8 @@ import uuid -from typing import Optional +from typing import List -import barker.schemas.master as schemas +import barker.schemas.printer as schemas from fastapi import APIRouter, Depends, HTTPException, Security, status from sqlalchemy.exc import SQLAlchemyError @@ -31,7 +31,7 @@ def save( data: schemas.PrinterIn, db: Session = Depends(get_db), user: UserToken = Security(get_user, scopes=["printers"]), -): +) -> schemas.Printer: try: item = Printer(name=data.name, address=data.address, cut_code=data.cut_code) db.add(item) @@ -54,7 +54,7 @@ def update( data: schemas.PrinterIn, db: Session = Depends(get_db), user: UserToken = Security(get_user, scopes=["printers"]), -): +) -> schemas.Printer: try: item: Printer = db.query(Printer).filter(Printer.id == id_).first() item.name = data.name @@ -73,17 +73,17 @@ def update( raise -@router.delete("/{id_}") +@router.delete("/{id_}", response_model=schemas.PrinterBlank) def delete( id_: uuid.UUID, db: Session = Depends(get_db), user: UserToken = Security(get_user, scopes=["printers"]), -): +) -> schemas.PrinterBlank: try: item: Printer = db.query(Printer).filter(Printer.id == id_).first() db.delete(item) db.commit() - return printer_info(None) + return printer_blank() except SQLAlchemyError as e: db.rollback() raise HTTPException( @@ -95,35 +95,37 @@ def delete( raise -@router.get("") +@router.get("", response_model=schemas.PrinterBlank) def show_blank( db: Session = Depends(get_db), user: UserToken = Security(get_user, scopes=["printers"]), -): - return printer_info(None) +) -> schemas.PrinterBlank: + return printer_blank() -@router.get("/list") +@router.get("/list", response_model=List[schemas.Printer]) def show_list(db: Session = Depends(get_db), user: UserToken = Depends(get_user)): return [printer_info(item) for item in db.query(Printer).order_by(Printer.name).all()] -@router.get("/{id_}") +@router.get("/{id_}", response_model=schemas.Printer) def show_id( id_: uuid.UUID, db: Session = Depends(get_db), user: UserToken = Security(get_user, scopes=["printers"]), -): +) -> schemas.Printer: item: Printer = db.query(Printer).filter(Printer.id == id_).first() return printer_info(item) -def printer_info(item: Optional[Printer]): - if item is None: - return {"name": "", "address": "", "cutCode": ""} - return { - "id": item.id, - "name": item.name, - "address": item.address, - "cutCode": item.cut_code, - } +def printer_info(item: Printer) -> schemas.Printer: + return schemas.Printer( + id=item.id, + name=item.name, + address=item.address, + cutCode=item.cut_code, + ) + + +def printer_blank() -> schemas.PrinterBlank: + return schemas.PrinterBlank(name="", address="", cutCode="") diff --git a/barker/barker/routers/product.py b/barker/barker/routers/product.py index f7cde53..3ba4c41 100644 --- a/barker/barker/routers/product.py +++ b/barker/barker/routers/product.py @@ -1,21 +1,20 @@ import uuid -from datetime import date, datetime, timedelta +from datetime import date, timedelta from typing import List -import barker.schemas.master as schemas -import barker.schemas.product as product_schemas +import barker.schemas.product as schemas from fastapi import APIRouter, Depends, HTTPException, Security, status from sqlalchemy import and_, or_ from sqlalchemy.exc import SQLAlchemyError from sqlalchemy.orm import Session, contains_eager, joinedload -from ..core.config import settings from ..core.security import get_current_active_user as get_user from ..db.session import SessionLocal from ..models.master import MenuCategory, Product, ProductVersion, SaleCategory from ..schemas.auth import UserToken +from . import effective_date router = APIRouter() @@ -30,14 +29,6 @@ def get_db(): db.close() -def effective_date(d: str = None) -> date: - return ( - (datetime.now() - timedelta(minutes=settings.NEW_DAY_OFFSET_MINUTES)).date() - if d is None - else datetime.strptime(d, "%d-%b-%Y").date() - ) - - @router.post("/list", response_model=List[schemas.Product]) def sort_order( data: List[schemas.Product], @@ -231,16 +222,14 @@ def delete( raise -@router.get("") +@router.get("", response_model=schemas.ProductBlank) def show_blank( db: Session = Depends(get_db), user: UserToken = Security(get_user, scopes=["products"]), -): - return schemas.Product( +) -> schemas.ProductBlank: + return schemas.ProductBlank( name="", units="", - menuCategory={}, - saleCategory={}, price=0, hasHappyHour=False, isNotAvailable=False, @@ -389,8 +378,8 @@ def product_info(item: ProductVersion) -> schemas.Product: id=item.product_id, name=item.name, units=item.units, - menuCategory=product_schemas.MenuCategoryLink(id=item.menu_category_id, name=item.menu_category.name), - saleCategory=product_schemas.SaleCategoryLink( + menuCategory=schemas.MenuCategoryLink(id=item.menu_category_id, name=item.menu_category.name), + saleCategory=schemas.SaleCategoryLink( id=item.sale_category_id, name=item.sale_category.name, ), diff --git a/barker/barker/routers/sale_category.py b/barker/barker/routers/sale_category.py index ac8fe43..4f56c88 100644 --- a/barker/barker/routers/sale_category.py +++ b/barker/barker/routers/sale_category.py @@ -1,8 +1,8 @@ import uuid -from typing import Optional +from typing import List -import barker.schemas.master as schemas +import barker.schemas.sale_category as schemas from fastapi import APIRouter, Depends, HTTPException, Security, status from sqlalchemy.exc import SQLAlchemyError @@ -31,9 +31,9 @@ def save( data: schemas.SaleCategoryIn, db: Session = Depends(get_db), user: UserToken = Security(get_user, scopes=["products"]), -): +) -> schemas.SaleCategory: try: - item = SaleCategory(name=data.name, tax_id=data.tax.id_) + item = SaleCategory(name=data.name, discount_limit=round(data.discount_limit, 2), tax_id=data.tax.id_) db.add(item) db.commit() return sale_category_info(item) @@ -54,10 +54,11 @@ def update( data: schemas.SaleCategoryIn, db: Session = Depends(get_db), user: UserToken = Security(get_user, scopes=["products"]), -): +) -> schemas.SaleCategory: try: item: SaleCategory = db.query(SaleCategory).filter(SaleCategory.id == id_).first() item.name = data.name + item.discount_limit = round(data.discount_limit, 2) item.tax_id = data.tax.id_ db.commit() return sale_category_info(item) @@ -72,12 +73,12 @@ def update( raise -@router.delete("/{id_}") +@router.delete("/{id_}", response_model=schemas.SaleCategoryBlank) def delete( id_: uuid.UUID, db: Session = Depends(get_db), user: UserToken = Security(get_user, scopes=["products"]), -): +) -> schemas.SaleCategoryBlank: try: item: SaleCategory = db.query(SaleCategory).filter(SaleCategory.id == id_).first() if item is None: @@ -95,42 +96,45 @@ def delete( raise -@router.get("") +@router.get("", response_model=schemas.SaleCategoryBlank) def show_blank( db: Session = Depends(get_db), user: UserToken = Security(get_user, scopes=["products"]), -): - return sale_category_info(None) +) -> schemas.SaleCategoryBlank: + return sale_category_blank() -@router.get("/list") -def show_list(db: Session = Depends(get_db), user: UserToken = Depends(get_user)): +@router.get("/list", response_model=List[schemas.SaleCategory]) +def show_list(db: Session = Depends(get_db), user: UserToken = Depends(get_user)) -> List[schemas.SaleCategory]: return [sale_category_info(item) for item in db.query(SaleCategory).order_by(SaleCategory.name).all()] -@router.get("/for-discount") -def for_discount(db: Session = Depends(get_db), user: UserToken = Depends(get_user)): +@router.get("/for-discount", response_model=List[schemas.SaleCategoryForDiscount]) +def for_discount(db: Session = Depends(get_db), user: UserToken = Depends(get_user)) -> List[schemas.SaleCategory]: return [ - {"id": item.id, "name": item.name, "discount": 0} - for item in db.query(SaleCategory).order_by(SaleCategory.name).all() + schemas.SaleCategoryForDiscount(id=item.id, name=item.name, discountLimit=item.discount_limit, discount=0) + for item in db.query(SaleCategory).filter(SaleCategory.discount_limit != 0).order_by(SaleCategory.name).all() ] -@router.get("/{id_}") +@router.get("/{id_}", response_model=schemas.SaleCategory) def show_id( id_: uuid.UUID, db: Session = Depends(get_db), user: UserToken = Security(get_user, scopes=["products"]), -): +) -> schemas.SaleCategory: item: SaleCategory = db.query(SaleCategory).filter(SaleCategory.id == id_).first() return sale_category_info(item) -def sale_category_info(item: Optional[SaleCategory]): - if item is None: - return {"name": "", "tax": {}} - return { - "id": item.id, - "name": item.name, - "tax": {"id": item.tax_id, "name": item.tax.name, "rate": item.tax.rate}, - } +def sale_category_info(item: SaleCategory) -> schemas.SaleCategory: + return schemas.SaleCategory( + id=item.id, + name=item.name, + discountLimit=item.discount_limit, + tax=schemas.TaxLink(id=item.tax_id, name=item.tax.name, rate=item.tax.rate), + ) + + +def sale_category_blank() -> schemas.SaleCategoryBlank: + return schemas.SaleCategoryBlank(name="", discountLimit=1) diff --git a/barker/barker/routers/section.py b/barker/barker/routers/section.py index 9539308..71624ba 100644 --- a/barker/barker/routers/section.py +++ b/barker/barker/routers/section.py @@ -1,8 +1,8 @@ import uuid -from typing import Optional +from typing import List -import barker.schemas.master as schemas +import barker.schemas.section as schemas from fastapi import APIRouter, Depends, HTTPException, Security, status from sqlalchemy.exc import SQLAlchemyError @@ -31,7 +31,7 @@ def save( data: schemas.SectionIn, db: Session = Depends(get_db), user: UserToken = Security(get_user, scopes=["sections"]), -): +) -> schemas.Section: try: item = Section(name=data.name) db.add(item) @@ -54,7 +54,7 @@ def update( data: schemas.SectionIn, db: Session = Depends(get_db), user: UserToken = Security(get_user, scopes=["sections"]), -): +) -> schemas.Section: try: item: Section = db.query(Section).filter(Section.id == id_).first() if item.is_fixture: @@ -76,12 +76,12 @@ def update( raise -@router.delete("/{id_}") +@router.delete("/{id_}", response_model=schemas.SectionBlank) def delete( id_: uuid.UUID, db: Session = Depends(get_db), user: UserToken = Security(get_user, scopes=["sections"]), -): +) -> schemas.SectionBlank: try: item: Section = db.query(Section).filter(Section.id == id_).first() if item.is_fixture: @@ -91,7 +91,7 @@ def delete( ) db.delete(item) db.commit() - return section_info(None) + return section_blank() except SQLAlchemyError as e: db.rollback() raise HTTPException( @@ -103,30 +103,32 @@ def delete( raise -@router.get("") +@router.get("", response_model=schemas.SectionBlank) def show_blank( db: Session = Depends(get_db), user: UserToken = Security(get_user, scopes=["sections"]), -): - return section_info(None) +) -> schemas.SectionBlank: + return section_blank() -@router.get("/list") +@router.get("/list", response_model=List[schemas.Section]) def show_list(db: Session = Depends(get_db), user: UserToken = Depends(get_user)): return [section_info(item) for item in db.query(Section).order_by(Section.name).all()] -@router.get("/{id_}") +@router.get("/{id_}", response_model=schemas.Section) def show_id( id_: uuid.UUID, db: Session = Depends(get_db), user: UserToken = Security(get_user, scopes=["sections"]), -): +) -> schemas.Section: item: Section = db.query(Section).filter(Section.id == id_).first() return section_info(item) -def section_info(item: Optional[Section]): - if item is None: - return {"name": ""} - return {"id": item.id, "name": item.name} +def section_info(item: Section) -> schemas.Section: + return schemas.Section(id=item.id, name=item.name) + + +def section_blank() -> schemas.SectionBlank: + return schemas.SectionBlank(name="") diff --git a/barker/barker/routers/section_printer.py b/barker/barker/routers/section_printer.py index dbf7aea..7a3ef7d 100644 --- a/barker/barker/routers/section_printer.py +++ b/barker/barker/routers/section_printer.py @@ -2,7 +2,7 @@ import uuid from typing import List, Optional -import barker.schemas.master as schemas +import barker.schemas.section_printer as schemas from fastapi import APIRouter, Depends, HTTPException, Security, status from sqlalchemy import and_, or_ diff --git a/barker/barker/routers/table.py b/barker/barker/routers/table.py index a640065..b1310db 100644 --- a/barker/barker/routers/table.py +++ b/barker/barker/routers/table.py @@ -2,7 +2,7 @@ import uuid from typing import List, Optional -import barker.schemas.master as schemas +import barker.schemas.table as schemas from fastapi import APIRouter, Depends, HTTPException, Security, status from sqlalchemy.exc import SQLAlchemyError diff --git a/barker/barker/routers/tax.py b/barker/barker/routers/tax.py index 1b02c75..e723dec 100644 --- a/barker/barker/routers/tax.py +++ b/barker/barker/routers/tax.py @@ -4,7 +4,7 @@ import uuid from decimal import Decimal from typing import Optional -import barker.schemas.master as schemas +import barker.schemas.tax as schemas from fastapi import APIRouter, Depends, HTTPException, Security, status from sqlalchemy.exc import SQLAlchemyError diff --git a/barker/barker/routers/voucher/save.py b/barker/barker/routers/voucher/save.py index 66c2a0b..50db798 100644 --- a/barker/barker/routers/voucher/save.py +++ b/barker/barker/routers/voucher/save.py @@ -145,7 +145,7 @@ def do_save( product.product_id, round(i.quantity, 2), product.price, - round(i.discount, 5), + round(min(i.discount, product.sale_category.discount_limit), 5), i.is_happy_hour, product.sale_category.tax_id, tax_rate, diff --git a/barker/barker/routers/voucher/show.py b/barker/barker/routers/voucher/show.py index 6d5d27b..a7e49cc 100644 --- a/barker/barker/routers/voucher/show.py +++ b/barker/barker/routers/voucher/show.py @@ -122,11 +122,11 @@ def voucher_product(product_id: uuid.UUID, date_: date, db: Session): "menuCategory": { "id": product.menu_category_id, "name": product.menu_category.name, - "discountLimit": product.menu_category.discount_limit, }, "saleCategory": { "id": product.sale_category_id, "name": product.sale_category.name, + "discountLimit": product.sale_category.discount_limit, }, } diff --git a/barker/barker/routers/voucher/update.py b/barker/barker/routers/voucher/update.py index 0cd5d71..2e0b7ec 100644 --- a/barker/barker/routers/voucher/update.py +++ b/barker/barker/routers/voucher/update.py @@ -81,6 +81,9 @@ def update( for k in item.kots: for i in k.inventories: i.tax_rate = get_tax(i.tax_rate, voucher_type) + # TODO: Need to check from the database product for the max discount + # However simple relationship does not work as we need the product validity as well + # Still we should not fret too much as we are checking this in the frontend. i.discount = next( round(inv.discount, 5) for ko in data.kots for inv in ko.inventories if inv.id_ == i.id ) @@ -136,7 +139,7 @@ def update( product.product_id, round(i.quantity, 2), product.price, - round(i.discount, 5), + round(min(i.discount, product.sale_category.discount_limit), 5), i.is_happy_hour, product.sale_category.tax_id, tax_rate, diff --git a/barker/barker/schemas/master.py b/barker/barker/schemas/master.py index 316f1ab..40fd573 100644 --- a/barker/barker/schemas/master.py +++ b/barker/barker/schemas/master.py @@ -7,23 +7,6 @@ from typing import Optional from pydantic import BaseModel, Field, validator from . import to_camel -from .customer import Customer, CustomerIn # noqa: F401 -from .device import Device, DeviceIn, DeviceLink # noqa: F401 -from .guest_book import GuestBook, GuestBookIn, GuestBookListItem # noqa: F401 -from .menu_category import MenuCategory, MenuCategoryIn, MenuCategoryLink # noqa: F401 -from .modifier import Modifier, ModifierIn, ModifierLink # noqa: F401 -from .modifier_category import ( # noqa: F401 - ModifierCategory, - ModifierCategoryIn, - ModifierCategoryLink, -) -from .printer import Printer, PrinterIn, PrinterLink # noqa: F401 -from .product import Product, ProductIn, ProductLink # noqa: F401 -from .sale_category import SaleCategory, SaleCategoryIn, SaleCategoryLink # noqa: F401 -from .section import Section, SectionIn, SectionLink # noqa: F401 -from .section_printer import SectionPrinter # noqa: F401 -from .table import Table, TableIn, TableLink # noqa: F401 -from .tax import Tax, TaxIn, TaxLink # noqa: F401 class AccountBase(BaseModel): diff --git a/barker/barker/schemas/menu_category.py b/barker/barker/schemas/menu_category.py index 1eef5e5..3d0f761 100644 --- a/barker/barker/schemas/menu_category.py +++ b/barker/barker/schemas/menu_category.py @@ -1,21 +1,18 @@ import uuid -from decimal import Decimal from typing import List, Optional from pydantic import BaseModel, Field from . import to_camel -from .product import ProductLink +from .product_link import ProductLink class MenuCategoryIn(BaseModel): name: str = Field(..., min_length=1) - discount_limit: Decimal = Field(ge=0, multiple_of=0.0001, default=0, le=1) is_active: bool class Config: - fields = {"id_": "id"} anystr_strip_whitespace = True alias_generator = to_camel @@ -26,7 +23,14 @@ class MenuCategory(MenuCategoryIn): sort_order: int class Config: - fields = {"id_": "id"} + anystr_strip_whitespace = True + alias_generator = to_camel + + +class MenuCategoryBlank(MenuCategoryIn): + name: str + + class Config: anystr_strip_whitespace = True alias_generator = to_camel diff --git a/barker/barker/schemas/printer.py b/barker/barker/schemas/printer.py index 4cabcab..7057c90 100644 --- a/barker/barker/schemas/printer.py +++ b/barker/barker/schemas/printer.py @@ -13,7 +13,6 @@ class PrinterIn(BaseModel): cut_code: Optional[str] class Config: - fields = {"id_": "id"} anystr_strip_whitespace = True alias_generator = to_camel @@ -22,7 +21,15 @@ class Printer(PrinterIn): id_: uuid.UUID class Config: - fields = {"id_": "id"} + anystr_strip_whitespace = True + alias_generator = to_camel + + +class PrinterBlank(PrinterIn): + name: str + address: str + + class Config: anystr_strip_whitespace = True alias_generator = to_camel diff --git a/barker/barker/schemas/product.py b/barker/barker/schemas/product.py index 80ef06e..7d64ab9 100644 --- a/barker/barker/schemas/product.py +++ b/barker/barker/schemas/product.py @@ -1,32 +1,15 @@ import uuid from decimal import Decimal -from typing import List, Optional +from typing import Optional from pydantic import BaseModel, Field from . import to_camel +from .menu_category import MenuCategoryLink from .sale_category import SaleCategoryLink -class ProductLink(BaseModel): - id_: uuid.UUID = Field(...) - name: Optional[str] - enabled: Optional[bool] - - class Config: - fields = {"id_": "id"} - - -class MenuCategoryLink(BaseModel): # Hack to prevent circular error - id_: uuid.UUID = Field(...) - name: Optional[str] - products: Optional[List[ProductLink]] - - class Config: - fields = {"id_": "id"} - - class ProductIn(BaseModel): name: str = Field(..., min_length=1) units: str @@ -40,10 +23,23 @@ class ProductIn(BaseModel): sort_order: int class Config: - fields = {"id_": "id"} anystr_strip_whitespace = True alias_generator = to_camel class Product(ProductIn): id_: uuid.UUID + + class Config: + anystr_strip_whitespace = True + alias_generator = to_camel + + +class ProductBlank(ProductIn): + name: str + menu_category: Optional[MenuCategoryLink] + sale_category: Optional[SaleCategoryLink] + + class Config: + anystr_strip_whitespace = True + alias_generator = to_camel diff --git a/barker/barker/schemas/product_link.py b/barker/barker/schemas/product_link.py new file mode 100644 index 0000000..b92d147 --- /dev/null +++ b/barker/barker/schemas/product_link.py @@ -0,0 +1,14 @@ +import uuid + +from typing import Optional + +from pydantic import BaseModel, Field + + +class ProductLink(BaseModel): + id_: uuid.UUID = Field(...) + name: Optional[str] + enabled: Optional[bool] + + class Config: + fields = {"id_": "id"} diff --git a/barker/barker/schemas/sale_category.py b/barker/barker/schemas/sale_category.py index 0059917..629b08a 100644 --- a/barker/barker/schemas/sale_category.py +++ b/barker/barker/schemas/sale_category.py @@ -1,5 +1,6 @@ import uuid +from decimal import Decimal from typing import Optional from pydantic import BaseModel, Field @@ -10,10 +11,10 @@ from .tax import TaxLink class SaleCategoryIn(BaseModel): name: str = Field(..., min_length=1) + discount_limit: Decimal = Field(ge=0, multiple_of=0.0001, default=0, le=1) tax: TaxLink = Field(...) class Config: - fields = {"id_": "id"} anystr_strip_whitespace = True alias_generator = to_camel @@ -22,7 +23,24 @@ class SaleCategory(SaleCategoryIn): id_: uuid.UUID class Config: - fields = {"id_": "id"} + anystr_strip_whitespace = True + alias_generator = to_camel + + +class SaleCategoryBlank(SaleCategoryIn): + name: str + tax: Optional[TaxLink] + + class Config: + anystr_strip_whitespace = True + alias_generator = to_camel + + +class SaleCategoryForDiscount(SaleCategory): + discount: Decimal + tax: Optional[TaxLink] + + class Config: anystr_strip_whitespace = True alias_generator = to_camel diff --git a/barker/barker/schemas/section.py b/barker/barker/schemas/section.py index 6f5cd5b..1934ac4 100644 --- a/barker/barker/schemas/section.py +++ b/barker/barker/schemas/section.py @@ -4,21 +4,28 @@ from typing import Optional from pydantic import BaseModel, Field -from . import to_camel - class SectionIn(BaseModel): name: str = Field(..., min_length=1) class Config: - fields = {"id_": "id"} anystr_strip_whitespace = True - alias_generator = to_camel class Section(SectionIn): id_: uuid.UUID + class Config: + anystr_strip_whitespace = True + fields = {"id_": "id"} + + +class SectionBlank(BaseModel): + name: str + + class Config: + anystr_strip_whitespace = True + class SectionLink(BaseModel): id_: uuid.UUID = Field(...) diff --git a/barker/barker/schemas/tax.py b/barker/barker/schemas/tax.py index b22c6a3..22c5ae4 100644 --- a/barker/barker/schemas/tax.py +++ b/barker/barker/schemas/tax.py @@ -31,6 +31,7 @@ class Tax(TaxIn): class TaxLink(BaseModel): id_: uuid.UUID = Field(...) name: Optional[str] + rate: Optional[Decimal] class Config: fields = {"id_": "id"} diff --git a/barker/barker/schemas/voucher.py b/barker/barker/schemas/voucher.py index 5fd2a7f..e6b691f 100644 --- a/barker/barker/schemas/voucher.py +++ b/barker/barker/schemas/voucher.py @@ -8,7 +8,7 @@ from pydantic import BaseModel, Field, validator from . import to_camel from .customer import CustomerLink from .modifier import ModifierLink -from .product import ProductLink +from .product_link import ProductLink from .table import TableLink from .tax import TaxLink diff --git a/bookie/src/app/core/menu-category.ts b/bookie/src/app/core/menu-category.ts index 10bc902..c8af795 100644 --- a/bookie/src/app/core/menu-category.ts +++ b/bookie/src/app/core/menu-category.ts @@ -4,7 +4,6 @@ import { Product } from './product'; export class MenuCategory { id: string | undefined; name: string; - discountLimit: number; isActive: boolean; isFixture: boolean; sortOrder: number; @@ -14,7 +13,6 @@ export class MenuCategory { public constructor(init?: Partial) { this.id = undefined; this.name = ''; - this.discountLimit = 0; this.isActive = true; this.isFixture = false; this.sortOrder = 0; diff --git a/bookie/src/app/core/product.ts b/bookie/src/app/core/product.ts index f70572d..4c7b2c6 100644 --- a/bookie/src/app/core/product.ts +++ b/bookie/src/app/core/product.ts @@ -8,8 +8,8 @@ export class Product { code: number; name: string; units: string; - menuCategory: MenuCategory; - saleCategory: SaleCategory; + menuCategory?: MenuCategory; + saleCategory?: SaleCategory; price: number; hasHappyHour: boolean; isNotAvailable: boolean; @@ -25,8 +25,6 @@ export class Product { this.code = 0; this.name = ''; this.units = ''; - this.menuCategory = new MenuCategory(); - this.saleCategory = new SaleCategory(); this.price = 0; this.hasHappyHour = false; this.isNotAvailable = false; diff --git a/bookie/src/app/core/sale-category.ts b/bookie/src/app/core/sale-category.ts index ba3cf3d..bc8469d 100644 --- a/bookie/src/app/core/sale-category.ts +++ b/bookie/src/app/core/sale-category.ts @@ -3,12 +3,13 @@ import { Tax } from './tax'; export class SaleCategory { id: string | undefined; name: string; - tax: Tax; + discountLimit: number; + tax?: Tax; public constructor(init?: Partial) { this.id = undefined; this.name = ''; - this.tax = new Tax(); + this.discountLimit = 1; Object.assign(this, init); } } diff --git a/bookie/src/app/menu-category/menu-category-detail/menu-category-detail.component.html b/bookie/src/app/menu-category/menu-category-detail/menu-category-detail.component.html index 0c90cca..ee69a55 100644 --- a/bookie/src/app/menu-category/menu-category-detail/menu-category-detail.component.html +++ b/bookie/src/app/menu-category/menu-category-detail/menu-category-detail.component.html @@ -17,25 +17,6 @@ -
- - Discount Limit - - % - -
- - - Discount Limit - {{ row.discountLimit | percent: '1.2-2' }} - - Active? diff --git a/bookie/src/app/menu-category/menu-category-list/menu-category-list.component.ts b/bookie/src/app/menu-category/menu-category-list/menu-category-list.component.ts index ea8bf18..34cefde 100644 --- a/bookie/src/app/menu-category/menu-category-list/menu-category-list.component.ts +++ b/bookie/src/app/menu-category/menu-category-list/menu-category-list.component.ts @@ -21,7 +21,7 @@ export class MenuCategoryListComponent implements OnInit { data: BehaviorSubject = new BehaviorSubject(this.list); dataSource: MenuCategoryListDatasource = new MenuCategoryListDatasource(this.data); /** Columns displayed in the table. Columns IDs can be added, removed, or reordered. */ - displayedColumns = ['name', 'discountLimit', 'isActive', 'isFixture']; + displayedColumns = ['name', 'isActive', 'isFixture']; constructor( private route: ActivatedRoute, @@ -37,9 +37,6 @@ export class MenuCategoryListComponent implements OnInit { ngOnInit() { this.route.data.subscribe((value) => { const data = value as { list: MenuCategory[] }; - data.list.forEach((x) => { - x.discountLimit /= 100; - }); this.data.next(data.list); }); this.dataSource = new MenuCategoryListDatasource(this.data); diff --git a/bookie/src/app/menu-category/menu-category.service.ts b/bookie/src/app/menu-category/menu-category.service.ts index dc59aca..82d78ac 100644 --- a/bookie/src/app/menu-category/menu-category.service.ts +++ b/bookie/src/app/menu-category/menu-category.service.ts @@ -1,8 +1,9 @@ -import { HttpClient, HttpHeaders } from '@angular/common/http'; +import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { Observable } from 'rxjs/internal/Observable'; import { catchError } from 'rxjs/operators'; +import { BeerSaleReport } from '../beer-sale-report/beer-sale-report'; import { ErrorLoggerService } from '../core/error-logger.service'; import { MenuCategory } from '../core/menu-category'; @@ -27,9 +28,13 @@ export class MenuCategoryService { ) as Observable; } - list(): Observable { + list(shoudHaveActiveProducts: boolean | undefined = false): Observable { + const options = { params: new HttpParams() }; + if (shoudHaveActiveProducts !== undefined) { + options.params = options.params.set('p', 'true'); + } return this.http - .get(`${url}/list`) + .get(`${url}/list`, options) .pipe(catchError(this.log.handleError(serviceName, 'list'))) as Observable; } diff --git a/bookie/src/app/product/product-detail/product-detail.component.ts b/bookie/src/app/product/product-detail/product-detail.component.ts index 5a559b4..1e7730a 100644 --- a/bookie/src/app/product/product-detail/product-detail.component.ts +++ b/bookie/src/app/product/product-detail/product-detail.component.ts @@ -63,8 +63,8 @@ export class ProductDetailComponent implements OnInit, AfterViewInit { code: this.item.code || '(Auto)', name: this.item.name || '', units: this.item.units || '', - menuCategory: this.item.menuCategory.id ? this.item.menuCategory.id : '', - saleCategory: this.item.saleCategory.id ? this.item.saleCategory.id : '', + menuCategory: this.item.menuCategory ? this.item.menuCategory.id : '', + saleCategory: this.item.saleCategory ? this.item.saleCategory.id : '', price: this.item.price || '', hasHappyHour: this.item.hasHappyHour, isNotAvailable: this.item.isNotAvailable, @@ -121,7 +121,13 @@ export class ProductDetailComponent implements OnInit, AfterViewInit { const formModel = this.form.value; this.item.name = formModel.name; this.item.units = formModel.units; + if (this.item.menuCategory === null || this.item.menuCategory === undefined) { + this.item.menuCategory = new MenuCategory(); + } this.item.menuCategory.id = formModel.menuCategory; + if (this.item.saleCategory === null || this.item.saleCategory === undefined) { + this.item.saleCategory = new SaleCategory(); + } this.item.saleCategory.id = formModel.saleCategory; this.item.price = +formModel.price; this.item.hasHappyHour = formModel.hasHappyHour; diff --git a/bookie/src/app/product/product-list/product-list-datasource.ts b/bookie/src/app/product/product-list/product-list-datasource.ts index 6a548a3..cfa142a 100644 --- a/bookie/src/app/product/product-list/product-list-datasource.ts +++ b/bookie/src/app/product/product-list/product-list-datasource.ts @@ -2,6 +2,7 @@ import { DataSource } from '@angular/cdk/collections'; import { merge, Observable } from 'rxjs'; import { map, tap } from 'rxjs/operators'; +import { MenuCategory } from '../../core/menu-category'; import { Product } from '../../core/product'; export class ProductListDataSource extends DataSource { @@ -44,9 +45,9 @@ export class ProductListDataSource extends DataSource { // eslint-disable-next-line class-methods-use-this private getFilteredData(data: Product[], filter: string): Product[] { - if (filter === undefined) { + if (filter === null || filter === undefined || filter === '') { return data; } - return data.filter((x) => x.menuCategory.id === filter); + return data.filter((x) => (x.menuCategory as MenuCategory).id === filter); } } diff --git a/bookie/src/app/product/product-list/product-list.component.ts b/bookie/src/app/product/product-list/product-list.component.ts index 642162b..7bfd0ee 100644 --- a/bookie/src/app/product/product-list/product-list.component.ts +++ b/bookie/src/app/product/product-list/product-list.component.ts @@ -85,7 +85,7 @@ export class ProductListComponent implements OnInit { this.list = this.dataSource.filteredData; } else { this.list = this.list - .filter((x) => x.menuCategory.id !== this.dataSource.filterValue) + .filter((x) => (x.menuCategory as MenuCategory).id !== this.dataSource.filterValue) .concat(this.dataSource.filteredData); } this.data.next(this.list); diff --git a/bookie/src/app/sale-category/sale-category-detail/sale-category-detail.component.html b/bookie/src/app/sale-category/sale-category-detail/sale-category-detail.component.html index 9bf4c32..844b06c 100644 --- a/bookie/src/app/sale-category/sale-category-detail/sale-category-detail.component.html +++ b/bookie/src/app/sale-category/sale-category-detail/sale-category-detail.component.html @@ -17,6 +17,25 @@
+
+ + Discount Limit + + % + +
+ + + Discount Limit + {{ row.discountLimit | percent: '1.2-2' }} + + Tax diff --git a/bookie/src/app/sale-category/sale-category-list/sale-category-list.component.ts b/bookie/src/app/sale-category/sale-category-list/sale-category-list.component.ts index a2f0375..4d43874 100644 --- a/bookie/src/app/sale-category/sale-category-list/sale-category-list.component.ts +++ b/bookie/src/app/sale-category/sale-category-list/sale-category-list.component.ts @@ -20,7 +20,7 @@ export class SaleCategoryListComponent implements OnInit { dataSource: SaleCategoryListDatasource = new SaleCategoryListDatasource(this.data); list: SaleCategory[] = []; /** Columns displayed in the table. Columns IDs can be added, removed, or reordered. */ - displayedColumns = ['name', 'tax']; + displayedColumns = ['name', 'discountLimit', 'tax']; constructor( private route: ActivatedRoute, diff --git a/bookie/src/app/sale-category/sale-category.service.ts b/bookie/src/app/sale-category/sale-category.service.ts index 2154f6c..0b7227b 100644 --- a/bookie/src/app/sale-category/sale-category.service.ts +++ b/bookie/src/app/sale-category/sale-category.service.ts @@ -33,11 +33,11 @@ export class SaleCategoryService { .pipe(catchError(this.log.handleError(serviceName, 'list'))) as Observable; } - listForDiscount(): Observable<{ name: string; discount: number }[]> { + listForDiscount(): Observable<{ name: string; discount: number; discountLimit: number }[]> { return this.http - .get<{ name: string; discount: number }[]>(`${url}/for-discount`) + .get<{ name: string; discount: number; discountLimit: number }[]>(`${url}/for-discount`) .pipe(catchError(this.log.handleError(serviceName, 'list'))) as Observable< - { name: string; discount: number }[] + { name: string; discount: number; discountLimit: number }[] >; } diff --git a/bookie/src/app/sales/bill.service.ts b/bookie/src/app/sales/bill.service.ts index 54e472a..d44e359 100644 --- a/bookie/src/app/sales/bill.service.ts +++ b/bookie/src/app/sales/bill.service.ts @@ -1,7 +1,7 @@ import { SelectionModel } from '@angular/cdk/collections'; import { Injectable } from '@angular/core'; import { MatDialog } from '@angular/material/dialog'; -import * as math from 'mathjs'; +import { round } from 'mathjs'; import { BehaviorSubject } from 'rxjs'; import { Observable } from 'rxjs/internal/Observable'; @@ -9,6 +9,7 @@ import { BillViewItem } from '../core/bill-view-item'; import { ModifierCategory } from '../core/modifier-category'; import { Product } from '../core/product'; import { ReceivePaymentItem } from '../core/receive-payment-item'; +import { SaleCategory } from '../core/sale-category'; import { Table } from '../core/table'; import { ToasterService } from '../core/toaster.service'; import { ModifierCategoryService } from '../modifier-categories/modifier-category.service'; @@ -63,7 +64,7 @@ export class BillService { productId: i.product.id, isHappyHour: i.isHappyHour, isPrinted: true, - info: `${i.product.name} @ ${i.price} - ${math.round(i.discount * 100, 2)}%`, + info: `${i.product.name} @ ${i.price} - ${round(i.discount * 100, 2)}%`, price: i.price, quantity: i.quantity, discount: i.discount, @@ -182,13 +183,11 @@ export class BillService { discount(discounts: { id: string; name: string; discount: number }[]): void { this.data.forEach((x) => { if (!x.isKot) { - x.discount = - (discounts.find((d) => d.id === x.product.saleCategory.id) as { - id: string; - name: string; - discount: number; - }).discount / 100; - x.info = `${x.product.name} @ ${x.price} - ${math.round(x.discount * 100, 2)}%`; + const e = discounts.find((d) => d.id === (x.product.saleCategory as SaleCategory).id); + if (e !== undefined) { + x.discount = e.discount; + x.info = `${x.product.name} @ ${x.price} - ${round(x.discount * 100, 2)}%`; + } } }); this.dataObs.next(this.data); @@ -246,7 +245,7 @@ export class BillService { updateAmounts() { this.netAmount.next( - math.round( + round( this.data .filter((x) => !x.isKot) .reduce( @@ -256,7 +255,7 @@ export class BillService { ), ); this.discountAmount.next( - math.round( + round( this.data .filter((x) => !x.isKot) .reduce( @@ -267,7 +266,7 @@ export class BillService { ), ); this.taxAmount.next( - math.round( + round( this.data .filter((x) => !x.isKot) .reduce( @@ -278,7 +277,7 @@ export class BillService { ), ); this.amount.next( - math.round( + round( this.data .filter((x) => !x.isKot) .reduce( @@ -291,7 +290,7 @@ export class BillService { } amountVal(): number { - return math.round( + return round( this.bill.kots.reduce( (ka: number, k: Kot) => ka + diff --git a/bookie/src/app/sales/discount/discount.component.html b/bookie/src/app/sales/discount/discount.component.html index ca694a3..d22e201 100644 --- a/bookie/src/app/sales/discount/discount.component.html +++ b/bookie/src/app/sales/discount/discount.component.html @@ -14,6 +14,7 @@ + Maximum Discount {{ row.discountLimit | percent: '1.2-2' }} diff --git a/bookie/src/app/sales/discount/discount.component.ts b/bookie/src/app/sales/discount/discount.component.ts index 1564f57..8ac41b0 100644 --- a/bookie/src/app/sales/discount/discount.component.ts +++ b/bookie/src/app/sales/discount/discount.component.ts @@ -1,6 +1,7 @@ import { Component, Inject } from '@angular/core'; import { FormArray, FormBuilder, FormGroup, Validators } from '@angular/forms'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; +import { round } from 'mathjs'; import { Observable } from 'rxjs'; import { DiscountDataSource } from './discount-datasource'; @@ -11,7 +12,7 @@ import { DiscountDataSource } from './discount-datasource'; styleUrls: ['./discount.component.css'], }) export class DiscountComponent { - list: { name: string; discount: number }[] = []; + list: { name: string; discount: number; discountLimit: number }[] = []; form: FormGroup; dataSource: DiscountDataSource = new DiscountDataSource([]); @@ -20,12 +21,13 @@ export class DiscountComponent { constructor( public dialogRef: MatDialogRef, private fb: FormBuilder, - @Inject(MAT_DIALOG_DATA) public data: Observable<{ name: string; discount: number }[]>, + @Inject(MAT_DIALOG_DATA) + public data: Observable<{ name: string; discount: number; discountLimit: number }[]>, ) { this.form = this.fb.group({ discounts: '', }); - this.data.subscribe((list: { name: string; discount: number }[]) => { + this.data.subscribe((list: { name: string; discount: number; discountLimit: number }[]) => { this.list = list; this.form.setControl( 'discounts', @@ -33,7 +35,7 @@ export class DiscountComponent { this.list.map((x) => this.fb.group({ name: [x.name], - discount: ['', [Validators.min(0), Validators.max(100)]], + discount: ['', [Validators.min(0), Validators.max(x.discountLimit * 100)]], }), ), ), @@ -45,7 +47,10 @@ export class DiscountComponent { accept(): void { const array = this.form.get('discounts') as FormArray; this.list.forEach((item, index) => { - item.discount = Math.max(Math.min(array.controls[index].value.discount, 100), 0); + item.discount = Math.max( + Math.min(round(array.controls[index].value.discount / 100, 5), item.discountLimit), + 0, + ); }); this.dialogRef.close(this.list); } diff --git a/bookie/src/app/sales/menu-categories/menu-categories-resolver.service.ts b/bookie/src/app/sales/menu-categories/menu-categories-resolver.service.ts index fc2eab0..18f6986 100644 --- a/bookie/src/app/sales/menu-categories/menu-categories-resolver.service.ts +++ b/bookie/src/app/sales/menu-categories/menu-categories-resolver.service.ts @@ -12,6 +12,6 @@ export class MenuCategoriesResolver implements Resolve { constructor(private ser: MenuCategoryService) {} resolve(): Observable { - return this.ser.list(); + return this.ser.list(true); } } diff --git a/docker/app/Dockerfile b/docker/app/Dockerfile index 51c929b..203e99a 100644 --- a/docker/app/Dockerfile +++ b/docker/app/Dockerfile @@ -8,6 +8,9 @@ RUN npm install --unsafe-perm --legacy-peer-deps && /app/bookie/node_modules/.bi FROM python:latest LABEL maintainer="Amritanshu " +COPY --from=builder /app/barker /app +COPY --from=builder /app/frontend /app/frontend + RUN apt-get update && \ apt-get install -y locales && \ sed --in-place --expression='s/# en_IN UTF-8/en_IN UTF-8/' /etc/locale.gen && \ @@ -16,8 +19,6 @@ RUN apt-get update && \ ENV LANG en_IN ENV LC_ALL en_IN -COPY --from=builder /app/barker /app -COPY --from=builder /app/frontend /app/frontend WORKDIR /app # Install Poetry