From a92726f5e605559aeb64c17604d815fc7916dcd8 Mon Sep 17 00:00:00 2001 From: tanshu Date: Mon, 9 Nov 2020 12:43:18 +0530 Subject: [PATCH] Product list / detail / router fully working. Need to test the sale / reports --- .../versions/34fe3d061c5f_finish_import.py | 164 +++++----- barker/barker/models/__init__.py | 4 +- barker/barker/models/master.py | 46 +-- barker/barker/models/voucher.py | 6 +- barker/barker/routers/menu_category.py | 2 +- barker/barker/routers/modifier_category.py | 2 +- barker/barker/routers/product.py | 297 +++++++++++------- .../reports/beer_consumption_report.py | 2 +- .../barker/routers/reports/discount_report.py | 9 +- .../routers/reports/product_sale_report.py | 2 +- barker/barker/routers/reports/sale_report.py | 2 +- barker/barker/routers/voucher/save.py | 2 +- barker/barker/routers/voucher/update.py | 2 +- barker/barker/schemas/product.py | 1 + barker/barker/schemas/sale_category.py | 3 + .../product-detail.component.html | 1 - .../product-detail.component.ts | 3 - .../product-list/product-list-datasource.ts | 47 ++- .../product-list/product-list.component.css | 4 + .../product-list/product-list.component.html | 19 +- .../product-list/product-list.component.ts | 21 +- bookie/src/app/product/product.service.ts | 2 +- 22 files changed, 370 insertions(+), 271 deletions(-) diff --git a/barker/alembic/versions/34fe3d061c5f_finish_import.py b/barker/alembic/versions/34fe3d061c5f_finish_import.py index 293c870..eff2542 100644 --- a/barker/alembic/versions/34fe3d061c5f_finish_import.py +++ b/barker/alembic/versions/34fe3d061c5f_finish_import.py @@ -21,10 +21,10 @@ branch_labels = None depends_on = None -def upgrade(): - # ### commands auto generated by Alembic - please adjust! ### +def update_sale_categories(): product = table( "products", + column("product_id", postgresql.UUID(as_uuid=True)), column("sale_category_id", postgresql.UUID(as_uuid=True)), column("sale_category_name", sa.Unicode(length=255)), ) @@ -40,7 +40,12 @@ def upgrade(): } ) ) + with op.batch_alter_table("products") as batch_op: + batch_op.alter_column("sale_category_id", nullable=False) + batch_op.drop_column("sale_category_name") + +def update_section_printers(): section_printer = table( "section_printers", column("section_id", postgresql.UUID(as_uuid=True)), @@ -66,75 +71,8 @@ def upgrade(): batch_op.alter_column("printer_id", nullable=False) batch_op.drop_column("printer_name") - with op.batch_alter_table("products") as batch_op: - batch_op.alter_column("sale_category_id", nullable=False) - batch_op.drop_column("sale_category_name") - - # Migration to temporal products - op.rename_table("products", "product_histories") - with op.batch_alter_table("product_histories") as batch_op: - batch_op.add_column(sa.Column("valid_from", sa.Date(), nullable=True)) - batch_op.add_column(sa.Column("valid_till", sa.Date(), nullable=True)) - batch_op.drop_column("is_active") - - op.create_table( - "product_versions", - sa.Column("id", postgresql.UUID(as_uuid=True), server_default=sa.text("gen_random_uuid()"), nullable=False), - sa.Column("version_id", postgresql.UUID(as_uuid=True), nullable=False), - sa.ForeignKeyConstraint( - ["version_id"], ["product_histories.id"], name=op.f("fk_product_versions_version_id_product_histories") - ), - sa.PrimaryKeyConstraint("id", name=op.f("pk_product_versions")), - sa.UniqueConstraint("id", "version_id", name=op.f("uq_product_versions_id")), - ) - op.drop_constraint("fk_inventories_product_id_products", "inventories", type_="foreignkey") - op.create_foreign_key( - op.f("fk_inventories_product_id_product_versions"), "inventories", "product_versions", ["product_id"], ["id"] - ) - - pv = table( - "product_versions", - column("id", postgresql.UUID(as_uuid=True)), - column("version_id", postgresql.UUID(as_uuid=True)), - ) - - prod = table( - "product_histories", - column("id", postgresql.UUID(as_uuid=True)), - column("name", sa.Unicode(length=255)), - column("units", sa.Unicode(length=255)), - column("valid_from", sa.Date()), - column("valid_till", sa.Date()), - ) - op.execute(pv.insert().from_select([pv.c.id, pv.c.version_id], select([prod.c.id, prod.c.id.label("vid")]))) - - op.alter_column("modifier_categories_products", "modifier_categories_id", new_column_name="modifier_category_id") - op.drop_constraint("uq_modifier_categories_products_product_id", "modifier_categories_products", type_="unique") - op.create_unique_constraint( - op.f("uq_modifier_categories_products_product_id"), - "modifier_categories_products", - ["product_id", "modifier_category_id"], - ) - - op.drop_constraint( - "fk_modifier_categories_products_product_id_products", "modifier_categories_products", type_="foreignkey" - ) - op.create_foreign_key( - op.f("fk_modifier_categories_products_product_id_product_versions"), - "modifier_categories_products", - "product_versions", - ["product_id"], - ["id"], - ) - op.create_exclude_constraint( - "uq_product_histories_name", - "product_histories", - (prod.c.name, "="), - (prod.c.units, "="), - (func.daterange(prod.c.valid_from, prod.c.valid_till, text("'[]'")), "&&"), - ) - op.drop_constraint("uq_products_name", "product_histories", type_="unique") +def add_all_roles_to_owner(): r = table( "roles", column("id", postgresql.UUID(as_uuid=True)), @@ -159,6 +97,92 @@ def upgrade(): ) .on_conflict_do_nothing(), ) + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + update_sale_categories() + update_section_printers() + + # Migration to temporal products + op.drop_constraint("fk_inventories_product_id_products", "inventories", type_="foreignkey") + op.drop_constraint( + "fk_modifier_categories_products_product_id_products", "modifier_categories_products", type_="foreignkey" + ) + op.drop_constraint("uq_products_name", "products", type_="unique") + op.drop_constraint("pk_products", "products", type_="primary") + op.rename_table("products", "product_versions") + with op.batch_alter_table("product_versions") as batch_op: + batch_op.add_column(sa.Column("product_id", postgresql.UUID(as_uuid=True), nullable=True)), + batch_op.add_column(sa.Column("valid_from", sa.Date(), nullable=True)) + batch_op.add_column(sa.Column("valid_till", sa.Date(), nullable=True)) + batch_op.drop_column("is_active") + batch_op.create_primary_key("pk_product_versions", ["id"]) + + prod_v = table( + "product_versions", + column("id", postgresql.UUID(as_uuid=True)), + column("product_id", postgresql.UUID(as_uuid=True)), + ) + + op.execute(prod_v.update(values={"product_id": prod_v.c.id})) + op.create_table( + "products", + sa.Column("id", postgresql.UUID(as_uuid=True), server_default=sa.text("gen_random_uuid()"), nullable=False), + # sa.Column("version_id", postgresql.UUID(as_uuid=True), nullable=False), + # sa.ForeignKeyConstraint( + # ["version_id"], ["product_versions.id"], name=op.f("fk_products_version_id_product_versions") + # ), + sa.PrimaryKeyConstraint("id", name=op.f("pk_products")), + # sa.UniqueConstraint("id", "version_id", name=op.f("uq_products_id")), + ) + op.alter_column("product_versions", "product_id", nullable=False) + + prod = table( + "product_versions", + column("id", postgresql.UUID(as_uuid=True)), + column("name", sa.Unicode(length=255)), + column("units", sa.Unicode(length=255)), + column("valid_from", sa.Date()), + column("valid_till", sa.Date()), + ) + + pv = table( + "products", + column("id", postgresql.UUID(as_uuid=True)), + ) + + op.execute(pv.insert().from_select([pv.c.id], select([prod.c.id]))) + + op.create_foreign_key( + op.f("fk_product_versions_product_id_products"), "product_versions", "products", ["product_id"], ["id"] + ) + + op.create_foreign_key(op.f("fk_inventories_product_id_products"), "inventories", "products", ["product_id"], ["id"]) + op.create_foreign_key( + op.f("fk_modifier_categories_products_product_id_products"), + "modifier_categories_products", + "products", + ["product_id"], + ["id"], + ) + + op.alter_column("modifier_categories_products", "modifier_categories_id", new_column_name="modifier_category_id") + op.drop_constraint("uq_modifier_categories_products_product_id", "modifier_categories_products", type_="unique") + op.create_unique_constraint( + op.f("uq_modifier_categories_products_product_id"), + "modifier_categories_products", + ["product_id", "modifier_category_id"], + ) + + op.create_exclude_constraint( + "uq_product_versions_name", + "product_versions", + (prod.c.name, "="), + (prod.c.units, "="), + (func.daterange(prod.c.valid_from, prod.c.valid_till, text("'[]'")), "&&"), + ) + add_all_roles_to_owner() # ### end Alembic commands ### diff --git a/barker/barker/models/__init__.py b/barker/barker/models/__init__.py index 1c5221b..0266113 100644 --- a/barker/barker/models/__init__.py +++ b/barker/barker/models/__init__.py @@ -10,8 +10,8 @@ from .master import ( Modifier, ModifierCategory, Printer, - ProductHistory, - ProductVersions, + ProductNew, + ProductVersion, SaleCategory, Section, SectionPrinter, diff --git a/barker/barker/models/master.py b/barker/barker/models/master.py index 6cbf615..766a77a 100644 --- a/barker/barker/models/master.py +++ b/barker/barker/models/master.py @@ -138,21 +138,23 @@ class SaleCategory(Base): self.id = id_ -class ProductVersions(Base): - __tablename__ = "product_versions" - __table_args__ = (UniqueConstraint("id", "version_id"),) +class ProductNew(Base): + __tablename__ = "products" id = Column( "id", UUID(as_uuid=True), primary_key=True, server_default=text("gen_random_uuid()"), default=uuid.uuid4 ) - version_id = Column("version_id", UUID(as_uuid=True), ForeignKey("product_histories.id"), nullable=False) - histories = relationship("ProductHistory") + versions = relationship("ProductVersion") + + def __init__(self, id_=None, version_id=None): + self.id = id_ + self.version_id = version_id modifier_categories_products = Table( "modifier_categories_products", Base.metadata, Column("id", UUID(as_uuid=True), primary_key=True, server_default=text("gen_random_uuid()"), default=uuid.uuid4), - Column("product_id", UUID(as_uuid=True), ForeignKey("product_versions.id"), nullable=False), + Column("product_id", UUID(as_uuid=True), ForeignKey("products.id"), nullable=False), Column( "modifier_category_id", UUID(as_uuid=True), @@ -163,11 +165,12 @@ modifier_categories_products = Table( ) -class ProductHistory(Base): - __tablename__ = "product_histories" +class ProductVersion(Base): + __tablename__ = "product_versions" id = Column( "id", UUID(as_uuid=True), primary_key=True, server_default=text("gen_random_uuid()"), default=uuid.uuid4 ) + product_id = Column("product_id", UUID(as_uuid=True), ForeignKey("products.id"), nullable=False) name = Column("name", Unicode(255), nullable=False) units = Column("units", Unicode(255), nullable=False) menu_category_id = Column( @@ -193,15 +196,16 @@ class ProductHistory(Base): menu_category = relationship("MenuCategory", backref="products") sale_category = relationship("SaleCategory", backref="products") - modifier_categories = relationship( - "ModifierCategory", - secondary="join(product_versions, modifier_categories_products, " - "ProductVersions.id==modifier_categories_products.c.product_id)", - primaryjoin="ProductHistory.id==ProductVersions.version_id", - secondaryjoin="modifier_categories_products.c.modifier_category_id==ModifierCategory.id", - order_by="ModifierCategory.sort_order", - backref="products", - ) + # modifier_categories = relationship( + # "ModifierCategory", + # secondary="join(products, modifier_categories_products, " + # "ProductNew.id==modifier_categories_products.c.product_id)", + # primaryjoin="ProductVersion.id==ProductNew.version_id", + # secondaryjoin="modifier_categories_products.c.modifier_category_id==ModifierCategory.id", + # order_by="ModifierCategory.sort_order", + # backref="products", + # ) + product = relationship("ProductNew") __table_args__ = ( postgresql.ExcludeConstraint( @@ -213,6 +217,7 @@ class ProductHistory(Base): def __init__( self, + product_id=None, name=None, units=None, menu_category_id=None, @@ -221,10 +226,12 @@ class ProductHistory(Base): has_happy_hour=None, is_not_available=None, quantity=None, - is_active=None, + valid_from=None, + valid_till=None, sort_order=0, id_=None, ): + self.product_id = product_id self.name = name self.units = units self.menu_category_id = menu_category_id @@ -233,7 +240,8 @@ class ProductHistory(Base): self.has_happy_hour = has_happy_hour self.is_not_available = is_not_available self.quantity = quantity - self.is_active = is_active + self.valid_from = valid_from + self.valid_till = valid_till self.sort_order = sort_order self.id = id_ diff --git a/barker/barker/models/voucher.py b/barker/barker/models/voucher.py index a2c40e7..68754ae 100644 --- a/barker/barker/models/voucher.py +++ b/barker/barker/models/voucher.py @@ -21,7 +21,7 @@ from sqlalchemy.dialects.postgresql import UUID from sqlalchemy.ext.hybrid import hybrid_property from sqlalchemy.orm import backref, relationship, synonym -from .master import ProductVersions +from .master import ProductNew from .meta import Base @@ -311,7 +311,7 @@ class Inventory(Base): "id", UUID(as_uuid=True), primary_key=True, server_default=text("gen_random_uuid()"), default=uuid.uuid4 ) kot_id = Column("kot_id", UUID(as_uuid=True), ForeignKey("kots.id"), nullable=False, index=True) - product_id = Column("product_id", UUID(as_uuid=True), ForeignKey("product_versions.id"), nullable=False) + product_id = Column("product_id", UUID(as_uuid=True), ForeignKey("products.id"), nullable=False) quantity = Column("quantity", Numeric(precision=15, scale=2)) price = Column("price", Numeric(precision=15, scale=2)) is_happy_hour = Column("is_happy_hour", Boolean, nullable=False) @@ -322,7 +322,7 @@ class Inventory(Base): kot = relationship("Kot", backref="inventories") tax = relationship("Tax", foreign_keys=tax_id) - product = relationship("ProductHistory", secondary=ProductVersions.__table__, backref="inventories") + product = relationship("ProductVersion", secondary=ProductNew.__table__, backref="inventories") def __init__( self, diff --git a/barker/barker/routers/menu_category.py b/barker/barker/routers/menu_category.py index 270a4ed..52dc83a 100644 --- a/barker/barker/routers/menu_category.py +++ b/barker/barker/routers/menu_category.py @@ -10,7 +10,7 @@ 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, ProductVersions +from ..models.master import MenuCategory, ProductNew from ..schemas.auth import UserToken diff --git a/barker/barker/routers/modifier_category.py b/barker/barker/routers/modifier_category.py index 4e254e7..ad4c3f4 100644 --- a/barker/barker/routers/modifier_category.py +++ b/barker/barker/routers/modifier_category.py @@ -11,7 +11,7 @@ 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, ModifierCategory, ProductVersions +from ..models.master import MenuCategory, ModifierCategory, ProductNew from ..schemas.auth import UserToken diff --git a/barker/barker/routers/product.py b/barker/barker/routers/product.py index 97dbb46..45449e0 100644 --- a/barker/barker/routers/product.py +++ b/barker/barker/routers/product.py @@ -1,9 +1,10 @@ import uuid from datetime import date, datetime, timedelta -from typing import List, Optional +from typing import List import barker.schemas.master as schemas +import barker.schemas.product as product_schemas from fastapi import APIRouter, Depends, HTTPException, Security, status from sqlalchemy import and_, or_ @@ -13,7 +14,7 @@ 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, ProductHistory, ProductVersions, SaleCategory +from ..models.master import MenuCategory, ProductNew, ProductVersion, SaleCategory from ..schemas.auth import UserToken @@ -29,9 +30,18 @@ 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], + date_: date = Depends(effective_date), db: Session = Depends(get_db), user: UserToken = Security(get_user, scopes=["products"]), ): @@ -42,19 +52,15 @@ def sort_order( indexes[item.menu_category.id_] += 1 else: indexes[item.menu_category.id_] = 0 - db.query(Product).filter(Product.id == item.id_).update( - {Product.sort_order: indexes[item.menu_category.id_]} - ) + db.query(ProductVersion).filter( + and_( + ProductVersion.product_id == item.id_, + or_(ProductVersion.valid_from == None, ProductVersion.valid_from <= date_), + or_(ProductVersion.valid_till == None, ProductVersion.valid_till >= date_), + ) + ).update({ProductVersion.sort_order: indexes[item.menu_category.id_]}) db.commit() - return [ - product_info(item) - for item in db.query(Product) - .order_by(Product.menu_category_id) - .order_by(Product.sort_order) - .order_by(Product.name) - .options(joinedload(Product.menu_category), joinedload(Product.sale_category)) - .all() - ] + return product_list(date_, db) except SQLAlchemyError as e: db.rollback() raise HTTPException( @@ -69,11 +75,16 @@ def sort_order( @router.post("", response_model=schemas.Product) def save( data: schemas.ProductIn, + date_: date = Depends(effective_date), db: Session = Depends(get_db), user: UserToken = Security(get_user, scopes=["products"]), ): try: - item = ProductHistory( + item = ProductNew() + db.add(item) + db.flush() + product_version = ProductVersion( + product_id=item.id, name=data.name, units=data.units, menu_category_id=data.menu_category.id_, @@ -82,11 +93,12 @@ def save( has_happy_hour=data.has_happy_hour, is_not_available=data.is_not_available, quantity=data.quantity, - is_active=data.is_active, + valid_from=date_, + valid_till=None, ) - db.add(item) + db.add(product_version) db.commit() - return product_info(item) + return product_info(product_version) except SQLAlchemyError as e: db.rollback() raise HTTPException( @@ -102,22 +114,59 @@ def save( def update( id_: uuid.UUID, data: schemas.ProductIn, + date_: date = Depends(effective_date), db: Session = Depends(get_db), user: UserToken = Security(get_user, scopes=["products"]), -): +) -> schemas.Product: try: - item: Product = db.query(Product).filter(Product.id == id_).first() - item.name = data.name - item.units = data.units - item.menu_category_id = data.menu_category.id_ - item.sale_category_id = data.sale_category.id_ - item.price = data.price - item.has_happy_hour = data.has_happy_hour - item.is_not_available = data.is_not_available - item.quantity = data.quantity - item.is_active = data.is_active - db.commit() - return product_info(item) + item: ProductVersion = ( + db.query(ProductVersion) + .join(ProductVersion.menu_category) + .filter( + and_( + ProductVersion.product_id == id_, + or_(ProductVersion.valid_from == None, ProductVersion.valid_from <= date_), + or_(ProductVersion.valid_till == None, ProductVersion.valid_till >= date_), + ) + ) + .first() + ) + if item.valid_till != None: + # Allow adding a product here splitting the valid from and to, but not implemented right now + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail="Product has been invalidated", + ) + if item.valid_from == date_: # Update the product as valid from the the same + item.name = data.name + item.units = data.units + item.menu_category_id = data.menu_category.id_ + item.sale_category_id = data.sale_category.id_ + item.price = data.price + item.has_happy_hour = data.has_happy_hour + item.is_not_available = data.is_not_available + item.quantity = data.quantity + db.commit() + return product_info(item) + else: # Create a new version of the product from the new details + item.valid_till = date_ - timedelta(days=1) + product_version = ProductVersion( + product_id=item.product_id, + name=data.name, + units=data.units, + menu_category_id=data.menu_category.id_, + sale_category_id=data.sale_category.id_, + price=data.price, + has_happy_hour=data.has_happy_hour, + is_not_available=data.is_not_available, + quantity=data.quantity, + valid_from=date_, + valid_till=None, + ) + db.add(product_version) + db.commit() + return product_info(product_version) + except SQLAlchemyError as e: db.rollback() raise HTTPException( @@ -132,21 +181,32 @@ def update( @router.delete("/{id_}") def delete( id_: uuid.UUID, + date_: date = Depends(effective_date), db: Session = Depends(get_db), user: UserToken = Security(get_user, scopes=["products"]), ): try: - item: Product = db.query(Product).filter(Product.id == id_).first() + item: ProductVersion = ( + db.query(ProductVersion) + .filter( + and_( + ProductVersion.product_id == id_, + or_(ProductVersion.valid_from == None, ProductVersion.valid_from <= date_), + or_(ProductVersion.valid_till == None, ProductVersion.valid_till >= date_), + ) + ) + .first() + ) if item is None: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="Product not found", ) + elif item.valid_from == date_: + db.delete(item) else: - raise HTTPException( - status_code=status.HTTP_501_NOT_IMPLEMENTED, - detail="Product deletion not implemented", - ) + item.valid_till = date_ - timedelta(days=1) + db.commit() except Exception: db.rollback() raise @@ -157,38 +217,47 @@ def show_blank( db: Session = Depends(get_db), user: UserToken = Security(get_user, scopes=["products"]), ): - return product_info(None) - - -@router.get("/list") -def show_list(d: str = None, db: Session = Depends(get_db), user: UserToken = Depends(get_user)): - date_ = ( - (datetime.now() - timedelta(minutes=settings.NEW_DAY_OFFSET_MINUTES)).date() - if d is None - else datetime.strptime(d, "%d-%b-%Y").date() + return schemas.Product( + name="", + units="", + menuCategory={}, + saleCategory={}, + price=0, + hasHappyHour=False, + isNotAvailable=False, + isActive=True, + sortOrder=0, ) + + +@router.get("/list", response_model=List[schemas.Product]) +def show_list( + date_: date = Depends(effective_date), db: Session = Depends(get_db), user: UserToken = Depends(get_user) +) -> List[schemas.Product]: + return product_list(date_, db) + + +def product_list(date_: date, db: Session) -> List[schemas.Product]: return [ - product_info(item.histories) - for item in db.query(ProductVersions) - .join(ProductVersions.histories) - .join(ProductHistory.menu_category) + product_info(item) + for item in db.query(ProductVersion) + .join(ProductVersion.menu_category) + .join(ProductVersion.sale_category) .filter( and_( - or_(ProductHistory.valid_from == None, ProductHistory.valid_from <= date_), - or_(ProductHistory.valid_till == None, ProductHistory.valid_till >= date_), + or_(ProductVersion.valid_from == None, ProductVersion.valid_from <= date_), + or_(ProductVersion.valid_till == None, ProductVersion.valid_till >= date_), ) ) .order_by(MenuCategory.sort_order) .order_by(MenuCategory.name) - .order_by(ProductHistory.sort_order) - .order_by(ProductHistory.name) + .order_by(ProductVersion.sort_order) + .order_by(ProductVersion.name) .options( - joinedload(ProductVersions.histories, innerjoin=True), - joinedload(ProductVersions.histories, ProductHistory.menu_category, innerjoin=True), - joinedload(ProductVersions.histories, ProductHistory.sale_category, innerjoin=True), - contains_eager(ProductVersions.histories), - contains_eager(ProductVersions.histories, ProductHistory.menu_category), - contains_eager(ProductVersions.histories, ProductHistory.sale_category), + joinedload(ProductVersion.menu_category, innerjoin=True), + joinedload(ProductVersion.sale_category, innerjoin=True), + contains_eager(ProductVersion.menu_category), + contains_eager(ProductVersion.sale_category), ) .all() ] @@ -197,56 +266,68 @@ def show_list(d: str = None, db: Session = Depends(get_db), user: UserToken = De @router.get("/query") async def show_term( mc: uuid.UUID = None, - d: str = None, + date_: date = Depends(effective_date), db: Session = Depends(get_db), current_user: UserToken = Depends(get_user), ): - date_ = ( - (datetime.now() - timedelta(minutes=settings.NEW_DAY_OFFSET_MINUTES)).date() - if d is None - else datetime.strptime(d, "%d-%b-%Y").date() - ) list_ = [] for item in ( - db.query(ProductVersions) - .join(ProductVersions.histories) - .join(ProductVersions.histories, ProductHistory.sale_category) - .join(ProductVersions.histories, ProductHistory.sale_category, SaleCategory.tax) + db.query(ProductVersion) + .join(ProductVersion.sale_category) + .join(ProductVersion.sale_category, SaleCategory.tax) .filter( and_( - ProductHistory.menu_category_id == mc, - or_(ProductHistory.valid_from == None, ProductHistory.valid_from <= date_), - or_(ProductHistory.valid_till == None, ProductHistory.valid_till >= date_), + ProductVersion.menu_category_id == mc, + or_(ProductVersion.valid_from == None, ProductVersion.valid_from <= date_), + or_(ProductVersion.valid_till == None, ProductVersion.valid_till >= date_), ) ) - .order_by(ProductHistory.sort_order, ProductHistory.name) + .order_by(ProductVersion.sort_order, ProductVersion.name) .options( - joinedload(ProductVersions.histories, innerjoin=True), - joinedload(ProductVersions.histories, ProductHistory.sale_category, innerjoin=True), - joinedload(ProductVersions.histories, ProductHistory.sale_category, SaleCategory.tax, innerjoin=True), - contains_eager(ProductVersions.histories), - contains_eager(ProductVersions.histories, ProductHistory.sale_category), - contains_eager(ProductVersions.histories, ProductHistory.sale_category, SaleCategory.tax), + joinedload(ProductVersion.sale_category, innerjoin=True), + joinedload(ProductVersion.sale_category, SaleCategory.tax, innerjoin=True), + contains_eager(ProductVersion.sale_category), + contains_eager(ProductVersion.sale_category, SaleCategory.tax), ) .all() ): - list_.append(query_product_info(item.histories, False)) - if item.histories.has_happy_hour: - list_.append(query_product_info(item.histories, True)) + list_.append(query_product_info(item, False)) + if item.has_happy_hour: + list_.append(query_product_info(item, True)) return list_ -@router.get("/{id_}") +@router.get("/{id_}", response_model=schemas.Product) def show_id( id_: uuid.UUID, + date_: date = Depends(effective_date), db: Session = Depends(get_db), user: UserToken = Security(get_user, scopes=["products"]), -): - item: Product = db.query(Product).filter(Product.id == id_).first() +) -> schemas.Product: + item: ProductVersion = ( + db.query(ProductVersion) + .join(ProductVersion.sale_category) + .join(ProductVersion.sale_category, SaleCategory.tax) + .filter( + and_( + ProductVersion.product_id == id_, + or_(ProductVersion.valid_from == None, ProductVersion.valid_from <= date_), + or_(ProductVersion.valid_till == None, ProductVersion.valid_till >= date_), + ) + ) + .order_by(ProductVersion.sort_order, ProductVersion.name) + .options( + joinedload(ProductVersion.sale_category, innerjoin=True), + joinedload(ProductVersion.sale_category, SaleCategory.tax, innerjoin=True), + contains_eager(ProductVersion.sale_category), + contains_eager(ProductVersion.sale_category, SaleCategory.tax), + ) + .first() + ) return product_info(item) -def query_product_info(item: ProductHistory, happy_hour: bool): +def query_product_info(item: ProductVersion, happy_hour: bool): return { "id": item.id, "name": ("H H " if happy_hour else "") + item.full_name, @@ -266,31 +347,19 @@ def query_product_info(item: ProductHistory, happy_hour: bool): } -def product_info(item: Optional[ProductHistory]): - if item is None: - return { - "name": "", - "units": "", - "menuCategory": {}, - "saleCategory": {}, - "price": 0, - "hasHappyHour": False, - "isNotAvailable": False, - "isActive": True, - "sortOrder": 0, - } - return { - "id": item.id, - "name": item.name, - "units": item.units, - "menuCategory": {"id": item.menu_category_id, "name": item.menu_category.name}, - "saleCategory": { - "id": item.sale_category_id, - "name": item.sale_category.name, - }, - "price": item.price, - "hasHappyHour": item.has_happy_hour, - "isNotAvailable": item.is_not_available, - "isActive": True, - "sortOrder": item.sort_order, - } +def product_info(item: ProductVersion) -> schemas.Product: + return 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( + id=item.sale_category_id, + name=item.sale_category.name, + ), + price=item.price, + hasHappyHour=item.has_happy_hour, + isNotAvailable=item.is_not_available, + isActive=True, + sortOrder=item.sort_order, + ) diff --git a/barker/barker/routers/reports/beer_consumption_report.py b/barker/barker/routers/reports/beer_consumption_report.py index ff552aa..9f24ae9 100644 --- a/barker/barker/routers/reports/beer_consumption_report.py +++ b/barker/barker/routers/reports/beer_consumption_report.py @@ -7,7 +7,7 @@ from sqlalchemy.sql.expression import func from ...core.config import settings from ...core.security import get_current_active_user as get_user from ...db.session import SessionLocal -from ...models import Inventory, Kot, ProductVersions, Voucher, VoucherType +from ...models import Inventory, Kot, ProductNew, Voucher, VoucherType from ...schemas.auth import UserToken diff --git a/barker/barker/routers/reports/discount_report.py b/barker/barker/routers/reports/discount_report.py index abd4569..5c7f52d 100644 --- a/barker/barker/routers/reports/discount_report.py +++ b/barker/barker/routers/reports/discount_report.py @@ -10,14 +10,7 @@ from sqlalchemy.orm import Session from ...core.config import settings from ...core.security import get_current_active_user as get_user from ...db.session import SessionLocal -from ...models import ( - Inventory, - Kot, - ProductVersions, - SaleCategory, - Voucher, - VoucherType, -) +from ...models import Inventory, Kot, ProductNew, SaleCategory, Voucher, VoucherType from ...printing.discount_report import print_discount_report from ...schemas.auth import UserToken from ...schemas.discount_report import DiscountReport, DiscountReportItem diff --git a/barker/barker/routers/reports/product_sale_report.py b/barker/barker/routers/reports/product_sale_report.py index 9a23b39..86a5b71 100644 --- a/barker/barker/routers/reports/product_sale_report.py +++ b/barker/barker/routers/reports/product_sale_report.py @@ -11,7 +11,7 @@ from ...models import ( Inventory, Kot, MenuCategory, - ProductVersions, + ProductNew, SaleCategory, Voucher, VoucherType, diff --git a/barker/barker/routers/reports/sale_report.py b/barker/barker/routers/reports/sale_report.py index da65f82..5275ff0 100644 --- a/barker/barker/routers/reports/sale_report.py +++ b/barker/barker/routers/reports/sale_report.py @@ -13,7 +13,7 @@ from ...db.session import SessionLocal from ...models import ( Inventory, Kot, - ProductVersions, + ProductNew, SaleCategory, Settlement, SettleOption, diff --git a/barker/barker/routers/voucher/save.py b/barker/barker/routers/voucher/save.py index 16f5fd1..8f1ca39 100644 --- a/barker/barker/routers/voucher/save.py +++ b/barker/barker/routers/voucher/save.py @@ -17,7 +17,7 @@ from ...models import ( Inventory, InventoryModifier, Kot, - ProductVersions, + ProductNew, Voucher, VoucherType, ) diff --git a/barker/barker/routers/voucher/update.py b/barker/barker/routers/voucher/update.py index a2cb7b7..b9d9892 100644 --- a/barker/barker/routers/voucher/update.py +++ b/barker/barker/routers/voucher/update.py @@ -16,7 +16,7 @@ from ...models import ( Inventory, InventoryModifier, Kot, - ProductVersions, + ProductNew, Voucher, VoucherType, ) diff --git a/barker/barker/schemas/product.py b/barker/barker/schemas/product.py index 1d05283..80ef06e 100644 --- a/barker/barker/schemas/product.py +++ b/barker/barker/schemas/product.py @@ -20,6 +20,7 @@ class ProductLink(BaseModel): class MenuCategoryLink(BaseModel): # Hack to prevent circular error id_: uuid.UUID = Field(...) + name: Optional[str] products: Optional[List[ProductLink]] class Config: diff --git a/barker/barker/schemas/sale_category.py b/barker/barker/schemas/sale_category.py index 51fad52..0059917 100644 --- a/barker/barker/schemas/sale_category.py +++ b/barker/barker/schemas/sale_category.py @@ -1,5 +1,7 @@ import uuid +from typing import Optional + from pydantic import BaseModel, Field from . import to_camel @@ -27,6 +29,7 @@ class SaleCategory(SaleCategoryIn): class SaleCategoryLink(BaseModel): id_: uuid.UUID = Field(...) + name: Optional[str] class Config: fields = {"id_": "id"} diff --git a/bookie/src/app/product/product-detail/product-detail.component.html b/bookie/src/app/product/product-detail/product-detail.component.html index 3986d27..9402fb1 100644 --- a/bookie/src/app/product/product-detail/product-detail.component.html +++ b/bookie/src/app/product/product-detail/product-detail.component.html @@ -58,7 +58,6 @@ > Has Happy Hour? Is Not Available? - Is Active?
{ public data: Product[]; - public viewData: Product[]; - private filterValue: string; + public filteredData: Product[]; + public filterValue: string; constructor( private readonly filter: Observable, @@ -15,39 +15,38 @@ export class ProductListDataSource extends DataSource { ) { super(); this.data = []; - this.viewData = []; - this.filter = filter.pipe( - tap((x) => { - this.filterValue = x; - }), - ); - this.dataObs = dataObs.pipe( - tap((x) => { - this.data = x; - }), - ); + this.filteredData = []; + this.filterValue = ''; } connect(): Observable { - const dataMutations = [this.dataObs, this.filter]; - + const dataMutations = [ + this.dataObs.pipe( + tap((x) => { + this.data = x; + }), + ), + this.filter.pipe( + tap((x) => { + this.filterValue = x; + }), + ), + ]; return merge(...dataMutations).pipe( - map(() => { - this.viewData = this.getFilteredData([...this.data]); - return this.viewData; + map(() => this.getFilteredData(this.data, this.filterValue)), + tap((x) => { + this.filteredData = x; }), ); } disconnect() {} - private getFilteredData(data: Product[]): Product[] { - const filter = this.filterValue === undefined ? '' : this.filterValue; - if (filter === '') { + // eslint-disable-next-line class-methods-use-this + private getFilteredData(data: Product[], filter: string): Product[] { + if (filter === undefined) { return data; } - return data.filter((x) => { - return x.menuCategory.id === filter; - }); + return data.filter((x) => x.menuCategory.id === filter); } } diff --git a/bookie/src/app/product/product-list/product-list.component.css b/bookie/src/app/product/product-list/product-list.component.css index e69de29..a9626b3 100644 --- a/bookie/src/app/product/product-list/product-list.component.css +++ b/bookie/src/app/product/product-list/product-list.component.css @@ -0,0 +1,4 @@ +.right { + display: flex; + justify-content: flex-end; +} diff --git a/bookie/src/app/product/product-list/product-list.component.html b/bookie/src/app/product/product-list/product-list.component.html index 8f05218..d5a13e1 100644 --- a/bookie/src/app/product/product-list/product-list.component.html +++ b/bookie/src/app/product/product-list/product-list.component.html @@ -4,7 +4,8 @@ - @@ -26,7 +27,7 @@ -- All Products -- @@ -54,8 +55,8 @@ - Price - {{ row.price | currency: 'INR' }} + Price + {{ row.price | currency: 'INR' }} @@ -87,20 +88,14 @@ {{ row.isNotAvailable ? 'Not Available' : 'Available' }}
-
- - {{ row.isActive ? 'visibility' : 'visibility_off' }} - - {{ row.isActive ? 'Active' : 'Deactivated' }} -
- Quantity - {{ row.quantity | currency: 'INR' }} + Quantity + {{ row.quantity | number: '1.2-2' }} 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 524e713..7ac86ce 100644 --- a/bookie/src/app/product/product-list/product-list.component.ts +++ b/bookie/src/app/product/product-list/product-list.component.ts @@ -44,15 +44,15 @@ export class ProductListComponent implements OnInit { this.form = this.fb.group({ menuCategory: '', }); - this.filter = new BehaviorSubject(''); + this.filter = new BehaviorSubject(undefined); this.data = new BehaviorSubject([]); this.data.subscribe((data: Product[]) => { this.list = data; }); } - filterOn(val: any) { - this.filter.next(val.value); + filterOn(val: string) { + this.filter.next(val); } ngOnInit() { @@ -68,7 +68,7 @@ export class ProductListComponent implements OnInit { } updateSortOrder() { - this.ser.updateSortOrder(this.dataSource.viewData).subscribe( + this.ser.updateSortOrder(this.dataSource.filteredData).subscribe( (result: Product[]) => { this.toaster.show('Success', ''); this.loadData(result, this.menuCategories); @@ -80,8 +80,15 @@ export class ProductListComponent implements OnInit { } dropTable(event: CdkDragDrop) { - const prevIndex = this.list.indexOf(event.item.data); - moveItemInArray(this.list, prevIndex, event.currentIndex); + const prevIndex = this.dataSource.filteredData.indexOf(event.item.data); + moveItemInArray(this.dataSource.filteredData, prevIndex, event.currentIndex); + if (this.dataSource.filterValue === undefined) { + this.list = this.dataSource.filteredData; + } else { + this.list = this.list + .filter((x) => x.menuCategory.id !== this.dataSource.filterValue) + .concat(this.dataSource.filteredData); + } this.data.next(this.list); } @@ -95,7 +102,7 @@ export class ProductListComponent implements OnInit { SaleCategory: 'saleCategory', }; - const csvData = new Blob([this.toCsv.toCsv(headers, this.dataSource.viewData)], { + const csvData = new Blob([this.toCsv.toCsv(headers, this.dataSource.filteredData)], { type: 'text/csv;charset=utf-8;', }); const link = document.createElement('a'); diff --git a/bookie/src/app/product/product.service.ts b/bookie/src/app/product/product.service.ts index fff7fbc..0133bef 100644 --- a/bookie/src/app/product/product.service.ts +++ b/bookie/src/app/product/product.service.ts @@ -35,7 +35,7 @@ export class ProductService { } listIsActiveOfCategory(id: string): Observable { - const options = { params: new HttpParams().set('mc', id).set('a', 'true') }; + const options = { params: new HttpParams().set('mc', id) }; return >( this.http .get(`${url}/query`, options)