diff --git a/barker/barker/models/voucher.py b/barker/barker/models/voucher.py index d5916cd..03af2b0 100644 --- a/barker/barker/models/voucher.py +++ b/barker/barker/models/voucher.py @@ -28,7 +28,7 @@ class Voucher: id: Mapped[uuid.UUID] = mapped_column(Uuid, primary_key=True, server_default=text("gen_random_uuid()")) date: Mapped[datetime] = mapped_column(DateTime, nullable=False, index=True) pax: Mapped[int] = mapped_column(Integer, nullable=False) - kot_id: Mapped[uuid.UUID] = mapped_column(Integer, nullable=False, unique=True) + kot_id: Mapped[int] = mapped_column(Integer, nullable=False, unique=True) creation_date: Mapped[datetime] = mapped_column(DateTime(), nullable=False) last_edit_date: Mapped[datetime] = mapped_column(DateTime(), nullable=False) food_table_id: Mapped[uuid.UUID] = mapped_column( @@ -44,7 +44,7 @@ class Voucher: user: Mapped["User"] = relationship(backref="vouchers") food_table: Mapped["FoodTable"] = relationship(backref="vouchers") - customer: Mapped["Customer"] = relationship(backref="vouchers") + customer: Mapped["Customer | None"] = relationship(backref="vouchers") bills: Mapped[list["Bill"]] = relationship( back_populates="voucher", diff --git a/barker/barker/routers/customer.py b/barker/barker/routers/customer.py index ed14780..c3bf5b2 100644 --- a/barker/barker/routers/customer.py +++ b/barker/barker/routers/customer.py @@ -8,13 +8,12 @@ from sqlalchemy import delete, or_, select from sqlalchemy.exc import SQLAlchemyError from sqlalchemy.orm import Session -import barker.schemas.customer as schemas - from ..core.security import get_current_active_user as get_user from ..db.session import SessionFuture from ..models.customer import Customer from ..models.customer_discount import CustomerDiscount from ..models.sale_category import SaleCategory +from ..schemas import customer as schemas from ..schemas.user_token import UserToken diff --git a/barker/barker/routers/customer_discount.py b/barker/barker/routers/customer_discount.py index 60cb743..1365c9c 100644 --- a/barker/barker/routers/customer_discount.py +++ b/barker/barker/routers/customer_discount.py @@ -6,13 +6,12 @@ from fastapi import APIRouter, Security from sqlalchemy import select from sqlalchemy.orm import Session -import barker.schemas.discount_item as schemas - from ..core.security import get_current_active_user as get_user from ..db.session import SessionFuture from ..models.customer import Customer from ..models.db_setting import DbSetting from ..models.sale_category import SaleCategory +from ..schemas import discount_item as schemas from ..schemas.user_token import UserToken diff --git a/barker/barker/routers/device.py b/barker/barker/routers/device.py index 5ef3088..5e1c86c 100644 --- a/barker/barker/routers/device.py +++ b/barker/barker/routers/device.py @@ -4,12 +4,11 @@ from fastapi import APIRouter, Depends, HTTPException, Security, status from sqlalchemy import delete, select from sqlalchemy.exc import SQLAlchemyError -import barker.schemas.device as schemas - from ..core.security import get_current_active_user as get_user from ..db.session import SessionFuture from ..models.device import Device from ..models.login_history import LoginHistory +from ..schemas import device as schemas from ..schemas.user_token import UserToken diff --git a/barker/barker/routers/guest_book.py b/barker/barker/routers/guest_book.py index 2ec893b..f79db47 100644 --- a/barker/barker/routers/guest_book.py +++ b/barker/barker/routers/guest_book.py @@ -7,8 +7,6 @@ from sqlalchemy import and_, desc, func, or_, select from sqlalchemy.exc import SQLAlchemyError from sqlalchemy.orm import joinedload -import barker.schemas.guest_book as schemas - from ..core.config import settings from ..core.security import get_current_active_user as get_user from ..db.session import SessionFuture @@ -17,6 +15,7 @@ from ..models.guest_book import GuestBook from ..models.guest_book_status import GuestBookStatus from ..models.guest_book_type import GuestBookType from ..models.voucher import Voucher +from ..schemas import guest_book as schemas from ..schemas.user_token import UserToken diff --git a/barker/barker/routers/header_footer.py b/barker/barker/routers/header_footer.py index faf00be..fed4786 100644 --- a/barker/barker/routers/header_footer.py +++ b/barker/barker/routers/header_footer.py @@ -2,11 +2,10 @@ from fastapi import APIRouter, HTTPException, Security, status from sqlalchemy import select from sqlalchemy.exc import SQLAlchemyError -import barker.schemas.header_footer as schemas - from ..core.security import get_current_active_user as get_user from ..db.session import SessionFuture from ..models.db_setting import DbSetting +from ..schemas import header_footer as schemas from ..schemas.user_token import UserToken diff --git a/barker/barker/routers/menu_category.py b/barker/barker/routers/menu_category.py index 529d3c3..8d43975 100644 --- a/barker/barker/routers/menu_category.py +++ b/barker/barker/routers/menu_category.py @@ -7,12 +7,11 @@ from fastapi import APIRouter, Depends, HTTPException, Security, status from sqlalchemy import distinct, select, update from sqlalchemy.exc import SQLAlchemyError -import barker.schemas.menu_category as schemas - from ..core.security import get_current_active_user as get_user from ..db.session import SessionFuture from ..models.menu_category import MenuCategory from ..models.product_version import ProductVersion +from ..schemas import menu_category as schemas from ..schemas.user_token import UserToken from . import effective_date diff --git a/barker/barker/routers/modifier.py b/barker/barker/routers/modifier.py index 087a36e..0c4b38f 100644 --- a/barker/barker/routers/modifier.py +++ b/barker/barker/routers/modifier.py @@ -6,11 +6,10 @@ from fastapi import APIRouter, Depends, HTTPException, Security, status from sqlalchemy import select from sqlalchemy.exc import SQLAlchemyError -import barker.schemas.modifier as schemas - from ..core.security import get_current_active_user as get_user from ..db.session import SessionFuture from ..models.modifier import Modifier +from ..schemas import modifier as schemas from ..schemas.user_token import UserToken diff --git a/barker/barker/routers/printer.py b/barker/barker/routers/printer.py index c0dd080..b940468 100644 --- a/barker/barker/routers/printer.py +++ b/barker/barker/routers/printer.py @@ -4,11 +4,10 @@ from fastapi import APIRouter, Depends, HTTPException, Security, status from sqlalchemy import select from sqlalchemy.exc import SQLAlchemyError -import barker.schemas.printer as schemas - from ..core.security import get_current_active_user as get_user from ..db.session import SessionFuture from ..models.printer import Printer +from ..schemas import printer as schemas from ..schemas.user_token import UserToken diff --git a/barker/barker/routers/product.py b/barker/barker/routers/product.py index bbd88c8..4bc9295 100644 --- a/barker/barker/routers/product.py +++ b/barker/barker/routers/product.py @@ -6,11 +6,9 @@ from decimal import Decimal from fastapi import APIRouter, Depends, HTTPException, Security, status from sqlalchemy import and_, insert, or_, select, update from sqlalchemy.exc import SQLAlchemyError -from sqlalchemy.orm import Session, joinedload +from sqlalchemy.orm import Session, contains_eager from sqlalchemy.sql.functions import count, func -import barker.schemas.product as schemas - from ..core.config import settings from ..core.security import get_current_active_user as get_user from ..db.session import SessionFuture @@ -23,6 +21,7 @@ from ..models.product import Product from ..models.product_version import ProductVersion from ..models.sale_category import SaleCategory from ..models.voucher import Voucher +from ..schemas import product as schemas from ..schemas.user_token import UserToken from . import effective_date @@ -122,24 +121,22 @@ def add_modifiers(product_id: uuid.UUID, menu_category_id: uuid.UUID, date_: dat ) ) ).scalar() + product_version_onclause = and_( + ProductVersion.product_id == Product.id, + or_( + ProductVersion.valid_from == None, # noqa: E711 + ProductVersion.valid_from <= date_, + ), + or_( + ProductVersion.valid_till == None, # noqa: E711 + ProductVersion.valid_till >= date_, + ), + ) categories = db.execute( select(count(ProductVersion.id), ModifierCategory.id) - .join(Product.versions) - .join(ModifierCategoryProduct, Product.id == ModifierCategoryProduct.product_id) - .join(ModifierCategory, ModifierCategory.id == ModifierCategoryProduct.modifier_category_id) - .where( - and_( - ProductVersion.menu_category_id == menu_category_id, - or_( - ProductVersion.valid_from == None, # noqa: E711 - ProductVersion.valid_from <= date_, - ), - or_( - ProductVersion.valid_till == None, # noqa: E711 - ProductVersion.valid_till >= date_, - ), - ) - ) + .select_from(ProductVersion) + .join(Product, onclause=product_version_onclause) + .join(Product.modifier_categories) .group_by(ModifierCategory.id) ).all() for c, mc in categories: @@ -157,9 +154,7 @@ def update_route( try: with SessionFuture() as db: item: ProductVersion = db.execute( - select(ProductVersion) - .join(ProductVersion.menu_category) - .where( + select(ProductVersion).where( and_( ProductVersion.product_id == id_, or_( @@ -323,8 +318,8 @@ def product_list(date_: date, db: Session) -> list[schemas.Product]: .order_by(ProductVersion.sort_order) .order_by(ProductVersion.name) .options( - joinedload(ProductVersion.menu_category, innerjoin=True), - joinedload(ProductVersion.sale_category, innerjoin=True), + contains_eager(ProductVersion.menu_category), + contains_eager(ProductVersion.sale_category), ) ) .scalars() @@ -341,9 +336,9 @@ def show_term( ): list_ = [] query = select(ProductVersion) - if sc is not None: - query = query.join(ProductVersion.menu_category) if mc is not None: + query = query.join(ProductVersion.menu_category) + if sc is not None: query = query.join(ProductVersion.sale_category).join(SaleCategory.tax) query = query.where( and_( @@ -366,15 +361,14 @@ def show_term( MenuCategory.sort_order, ProductVersion.sort_order, ProductVersion.name ) - if mc is not None: - query = query.options( - joinedload(ProductVersion.sale_category, innerjoin=True), - joinedload(ProductVersion.sale_category, SaleCategory.tax, innerjoin=True), - ) - if sc is not None: query = query.options( - joinedload(ProductVersion.menu_category, innerjoin=True), + contains_eager(ProductVersion.sale_category).contains_eager(SaleCategory.tax), + ) + + if mc is not None: + query = query.options( + contains_eager(ProductVersion.menu_category), ) with SessionFuture() as db: @@ -422,8 +416,8 @@ def product_list_of_sale_category(date_: date, db: Session) -> list[schemas.Prod .order_by(ProductVersion.sort_order) .order_by(ProductVersion.name) .options( - joinedload(ProductVersion.menu_category, innerjoin=True), - joinedload(ProductVersion.sale_category, innerjoin=True), + contains_eager(ProductVersion.menu_category), + contains_eager(ProductVersion.sale_category), ) ) .scalars() @@ -457,8 +451,7 @@ def show_id( ) .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), ) ).scalar_one() return product_info(item) diff --git a/barker/barker/routers/regime.py b/barker/barker/routers/regime.py index e58beea..88f1f4f 100644 --- a/barker/barker/routers/regime.py +++ b/barker/barker/routers/regime.py @@ -3,13 +3,12 @@ from sqlalchemy import func, select from sqlalchemy.exc import SQLAlchemyError from sqlalchemy.sql.functions import count -import barker.schemas.regime as schemas - from ..core.security import get_current_active_user as get_user from ..db.session import SessionFuture from ..models.bill import Bill from ..models.regime import Regime from ..models.tax import Tax +from ..schemas import regime as schemas from ..schemas.user_token import UserToken diff --git a/barker/barker/routers/reports/beer_sale_report.py b/barker/barker/routers/reports/beer_sale_report.py index 487af54..f467391 100644 --- a/barker/barker/routers/reports/beer_sale_report.py +++ b/barker/barker/routers/reports/beer_sale_report.py @@ -3,6 +3,7 @@ from operator import or_ from typing import Any from fastapi import APIRouter, Depends, Security +from sqlalchemy import and_ from sqlalchemy.sql.expression import func, select from ...core.config import settings @@ -36,23 +37,26 @@ def beer_consumption( "day", Voucher.date + timedelta(minutes=settings.NEW_DAY_OFFSET_MINUTES - settings.TIMEZONE_OFFSET_MINUTES) ).label("day") sum_ = func.sum(Inventory.quantity * ProductVersion.quantity).label("sum") + product_version_onclause = and_( + ProductVersion.product_id == Product.id, + or_( + ProductVersion.valid_from == None, # noqa: E711 + ProductVersion.valid_from <= Voucher.date, + ), + or_( + ProductVersion.valid_till == None, # noqa: E711 + ProductVersion.valid_till >= Voucher.date, + ), + ) query = ( select(day, ProductVersion.name, sum_) .join(Voucher.kots) .join(Kot.inventories) .join(Inventory.product) - .join(Product.versions) + .join(ProductVersion, onclause=product_version_onclause) .where( day >= start_date, day <= finish_date, - or_( - ProductVersion.valid_from == None, # noqa: E711 - ProductVersion.valid_from <= day, - ), - or_( - ProductVersion.valid_till == None, # noqa: E711 - ProductVersion.valid_till >= day, - ), ) ) if h is False and r is not False: diff --git a/barker/barker/routers/reports/discount_report.py b/barker/barker/routers/reports/discount_report.py index 307ad66..95c2af4 100644 --- a/barker/barker/routers/reports/discount_report.py +++ b/barker/barker/routers/reports/discount_report.py @@ -1,9 +1,9 @@ import uuid -from datetime import date, datetime, time, timedelta +from datetime import date, timedelta from fastapi import APIRouter, Cookie, Depends, Security -from sqlalchemy import func, or_, select +from sqlalchemy import and_, func, or_, select from sqlalchemy.orm import Session from ...core.config import settings @@ -40,35 +40,33 @@ def discount_report( ) -def get_discount_report(s: date, f: date, db: Session) -> list[DiscountReportItem]: - start_date = datetime.combine(s, time()) + timedelta( - minutes=settings.NEW_DAY_OFFSET_MINUTES - settings.TIMEZONE_OFFSET_MINUTES - ) - finish_date = datetime.combine(f, time()) + timedelta( - days=1, minutes=settings.NEW_DAY_OFFSET_MINUTES - settings.TIMEZONE_OFFSET_MINUTES - ) - - day = func.date_trunc("day", Voucher.date - timedelta(minutes=settings.NEW_DAY_OFFSET_MINUTES)).label("day") +def get_discount_report(start_date: date, finish_date: date, db: Session) -> list[DiscountReportItem]: + day = func.date_trunc( + "day", Voucher.date + timedelta(minutes=settings.NEW_DAY_OFFSET_MINUTES - settings.TIMEZONE_OFFSET_MINUTES) + ).label("day") amount = func.sum(Inventory.quantity * Inventory.effective_price * Inventory.discount).label("Amount") + product_version_onclause = and_( + ProductVersion.product_id == Product.id, + or_( + ProductVersion.valid_from == None, # noqa: E711 + ProductVersion.valid_from <= Voucher.date, + ), + or_( + ProductVersion.valid_till == None, # noqa: E711 + ProductVersion.valid_till >= Voucher.date, + ), + ) list_ = db.execute( select(SaleCategory.name, amount) .join(Voucher.kots) .join(Kot.inventories) .join(Inventory.product) - .join(Product.versions) + .join(ProductVersion, onclause=product_version_onclause) .join(ProductVersion.sale_category) .where( Inventory.discount != 0, - Voucher.date >= start_date, - Voucher.date <= finish_date, - or_( - ProductVersion.valid_from == None, # noqa: E711 - ProductVersion.valid_from <= day, - ), - or_( - ProductVersion.valid_till == None, # noqa: E711 - ProductVersion.valid_till >= day, - ), + day >= start_date, + day <= finish_date, Voucher.voucher_type.in_([VoucherType.REGULAR_BILL, VoucherType.KOT]), ) .group_by(SaleCategory.name) diff --git a/barker/barker/routers/reports/menu_engineering_report.py b/barker/barker/routers/reports/menu_engineering_report.py index bea06be..2c76d34 100644 --- a/barker/barker/routers/reports/menu_engineering_report.py +++ b/barker/barker/routers/reports/menu_engineering_report.py @@ -1,10 +1,10 @@ import uuid -from datetime import date, datetime, time, timedelta +from datetime import date, timedelta from decimal import Decimal from fastapi import APIRouter, Cookie, Depends, Security -from sqlalchemy import func, nulls_last, or_, select +from sqlalchemy import and_, func, nulls_last, or_, select from sqlalchemy.orm import Session from ...core.config import settings @@ -42,15 +42,21 @@ def menu_engineering_report_view( ) -def menu_engineering_report(s: date, f: date, db: Session): - start_date = datetime.combine(s, time()) + timedelta( - minutes=settings.NEW_DAY_OFFSET_MINUTES - settings.TIMEZONE_OFFSET_MINUTES +def menu_engineering_report(start_date: date, finish_date: date, db: Session): + day = func.date_trunc( + "day", Voucher.date + timedelta(minutes=settings.NEW_DAY_OFFSET_MINUTES - settings.TIMEZONE_OFFSET_MINUTES) + ).label("day") + product_version_onclause = and_( + ProductVersion.product_id == Product.id, + or_( + ProductVersion.valid_from == None, # noqa: E711 + ProductVersion.valid_from <= start_date, + ), + or_( + ProductVersion.valid_till == None, # noqa: E711 + ProductVersion.valid_till >= finish_date, + ), ) - finish_date = datetime.combine(f, time()) + timedelta( - days=1, minutes=settings.NEW_DAY_OFFSET_MINUTES - settings.TIMEZONE_OFFSET_MINUTES - ) - - day = func.date_trunc("day", Voucher.date - timedelta(minutes=settings.NEW_DAY_OFFSET_MINUTES)).label("day") list_ = db.execute( select( SaleCategory.name, @@ -61,34 +67,21 @@ def menu_engineering_report(s: date, f: date, db: Session): func.sum(Inventory.quantity), func.sum(Inventory.net), ) + .select_from(ProductVersion) .join(ProductVersion.sale_category) .join(ProductVersion.menu_category) - .join(ProductVersion.product) + .join(Product, onclause=product_version_onclause) .join(Product.inventories, isouter=True) .join(Inventory.kot, isouter=True) .join(Kot.voucher, isouter=True) .where( or_( - Voucher.date == None, # noqa: E711 - Voucher.date >= start_date, - ), - or_( - Voucher.date == None, # noqa: E711 - Voucher.date <= finish_date, - ), - or_( - Voucher.voucher_type == None, # noqa: E711 - Voucher.voucher_type == VoucherType.REGULAR_BILL, - ), - or_( - ProductVersion.valid_from == None, # noqa: E711 - ProductVersion.valid_from <= day, - Voucher.date == None, # noqa: E711 - ), - or_( - ProductVersion.valid_till == None, # noqa: E711 - ProductVersion.valid_till >= day, - Voucher.date == None, # noqa: E711 + day == None, # noqa: E711 + and_( + day >= start_date, + day <= finish_date, + Voucher.voucher_type == VoucherType.REGULAR_BILL, + ), ), ) .group_by( diff --git a/barker/barker/routers/reports/product_sale_report.py b/barker/barker/routers/reports/product_sale_report.py index 45e27e1..1cebe72 100644 --- a/barker/barker/routers/reports/product_sale_report.py +++ b/barker/barker/routers/reports/product_sale_report.py @@ -1,10 +1,10 @@ import uuid -from datetime import date, datetime, time, timedelta +from datetime import date, timedelta from typing import Annotated, Any from fastapi import APIRouter, Cookie, Depends, Query, Security -from sqlalchemy import func, or_, select +from sqlalchemy import and_, func, or_, select from sqlalchemy.orm import Session from ...core.config import settings @@ -45,15 +45,21 @@ def product_sale_report_view( } -def product_sale_report(s: date, f: date, id_: uuid.UUID | None, db: Session): - start_date = datetime.combine(s, time()) + timedelta( - minutes=settings.NEW_DAY_OFFSET_MINUTES - settings.TIMEZONE_OFFSET_MINUTES +def product_sale_report(start_date: date, finish_date: date, id_: uuid.UUID | None, db: Session): + day = func.date_trunc( + "day", Voucher.date + timedelta(minutes=settings.NEW_DAY_OFFSET_MINUTES - settings.TIMEZONE_OFFSET_MINUTES) + ).label("day") + product_version_onclause = and_( + ProductVersion.product_id == Product.id, + or_( + ProductVersion.valid_from == None, # noqa: E711 + ProductVersion.valid_from <= Voucher.date, + ), + or_( + ProductVersion.valid_till == None, # noqa: E711 + ProductVersion.valid_till >= Voucher.date, + ), ) - finish_date = datetime.combine(f, time()) + timedelta( - days=1, minutes=settings.NEW_DAY_OFFSET_MINUTES - settings.TIMEZONE_OFFSET_MINUTES - ) - - day = func.date_trunc("day", Voucher.date - timedelta(minutes=settings.NEW_DAY_OFFSET_MINUTES)).label("day") query = ( select( ProductVersion.id, @@ -65,21 +71,13 @@ def product_sale_report(s: date, f: date, id_: uuid.UUID | None, db: Session): .join(Inventory.kot) .join(Kot.voucher) .join(Inventory.product) - .join(Product.versions) + .join(ProductVersion, onclause=product_version_onclause) .join(ProductVersion.sale_category) .join(ProductVersion.menu_category) .join(Voucher.food_table) .where( - Voucher.date >= start_date, - Voucher.date <= finish_date, - or_( - ProductVersion.valid_from == None, # noqa: E711 - ProductVersion.valid_from <= day, - ), - or_( - ProductVersion.valid_till == None, # noqa: E711 - ProductVersion.valid_till >= day, - ), + day >= start_date, + day <= finish_date, ) ) if id_: diff --git a/barker/barker/routers/reports/product_updates_report.py b/barker/barker/routers/reports/product_updates_report.py index ae944ea..20149be 100644 --- a/barker/barker/routers/reports/product_updates_report.py +++ b/barker/barker/routers/reports/product_updates_report.py @@ -2,7 +2,7 @@ from datetime import date, timedelta from fastapi import APIRouter, Depends, Security from sqlalchemy import and_, or_, select -from sqlalchemy.orm import Session, joinedload +from sqlalchemy.orm import Session, contains_eager from ...core.security import get_current_active_user as get_user from ...db.session import SessionFuture @@ -29,7 +29,7 @@ def product_updates_report_view( } -def product_updates_report(s: date, f: date, db: Session) -> list[str]: +def product_updates_report(start_date: date, finish_date: date, db: Session) -> list[str]: list_ = ( db.execute( select(ProductVersion) @@ -37,12 +37,12 @@ def product_updates_report(s: date, f: date, db: Session) -> list[str]: .where( or_( and_( - ProductVersion.valid_from >= s, - ProductVersion.valid_from <= f, + ProductVersion.valid_from >= start_date, + ProductVersion.valid_from <= finish_date, ), and_( - ProductVersion.valid_till >= s - timedelta(days=1), - ProductVersion.valid_till <= f, + ProductVersion.valid_till >= start_date - timedelta(days=1), + ProductVersion.valid_till <= finish_date, ), ) ) @@ -54,7 +54,7 @@ def product_updates_report(s: date, f: date, db: Session) -> list[str]: ProductVersion.valid_from.nullsfirst(), ) .options( - joinedload(ProductVersion.menu_category, innerjoin=True), + contains_eager(ProductVersion.menu_category), ) ) .scalars() diff --git a/barker/barker/routers/reports/sale_report.py b/barker/barker/routers/reports/sale_report.py index 85eacca..47a98f2 100644 --- a/barker/barker/routers/reports/sale_report.py +++ b/barker/barker/routers/reports/sale_report.py @@ -1,11 +1,11 @@ import uuid -from datetime import date, datetime, time, timedelta +from datetime import date, timedelta from decimal import Decimal from typing import Annotated from fastapi import APIRouter, Cookie, Depends, Query, Security -from sqlalchemy import func, or_, select +from sqlalchemy import and_, func, or_, select from sqlalchemy.orm import Session from ...core.config import settings @@ -56,36 +56,34 @@ def get_sale_report( ) -def get_sale(s: date, f: date, id_: uuid.UUID | None, db: Session) -> list[SaleReportItem]: - start_date = datetime.combine(s, time()) + timedelta( - minutes=settings.NEW_DAY_OFFSET_MINUTES - settings.TIMEZONE_OFFSET_MINUTES +def get_sale(start_date: date, finish_date: date, id_: uuid.UUID | None, db: Session) -> list[SaleReportItem]: + day = func.date_trunc( + "day", Voucher.date + timedelta(minutes=settings.NEW_DAY_OFFSET_MINUTES - settings.TIMEZONE_OFFSET_MINUTES) + ).label("day") + product_version_onclause = and_( + ProductVersion.product_id == Product.id, + or_( + ProductVersion.valid_from == None, # noqa: E711 + ProductVersion.valid_from <= Voucher.date, + ), + or_( + ProductVersion.valid_till == None, # noqa: E711 + ProductVersion.valid_till >= Voucher.date, + ), ) - finish_date = datetime.combine(f, time()) + timedelta( - days=1, minutes=settings.NEW_DAY_OFFSET_MINUTES - settings.TIMEZONE_OFFSET_MINUTES - ) - - day = func.date_trunc("day", Voucher.date - timedelta(minutes=settings.NEW_DAY_OFFSET_MINUTES)).label("day") query = ( select(SaleCategory.name, func.sum(Inventory.net)) .join(Inventory.kot) .join(Kot.voucher) .join(Inventory.product) - .join(Product.versions) + .join(ProductVersion, onclause=product_version_onclause) .join(ProductVersion.sale_category) .join(Voucher.food_table) .where( - Voucher.date >= start_date, - Voucher.date <= finish_date, + day >= start_date, + day <= finish_date, Voucher.voucher_type == VoucherType.REGULAR_BILL, ~Voucher.settlements.any(Settlement.settled == SettleOption.UNSETTLED()), - or_( - ProductVersion.valid_from == None, # noqa: E711 - ProductVersion.valid_from <= day, - ), - or_( - ProductVersion.valid_till == None, # noqa: E711 - ProductVersion.valid_till >= day, - ), ) ) if id_: @@ -100,20 +98,16 @@ def get_sale(s: date, f: date, id_: uuid.UUID | None, db: Session) -> list[SaleR return info + [SaleReportItem(name="Total Settled", amount=total)] -def get_settlements(s: date, f: date, id_: uuid.UUID | None, db: Session) -> list[SaleReportItem]: - start_date = datetime.combine(s, time()) + timedelta( - minutes=settings.NEW_DAY_OFFSET_MINUTES - settings.TIMEZONE_OFFSET_MINUTES - ) - finish_date = datetime.combine(f, time()) + timedelta( - days=1, minutes=settings.NEW_DAY_OFFSET_MINUTES - settings.TIMEZONE_OFFSET_MINUTES - ) - +def get_settlements(start_date: date, finish_date: date, id_: uuid.UUID | None, db: Session) -> list[SaleReportItem]: + day = func.date_trunc( + "day", Voucher.date + timedelta(minutes=settings.NEW_DAY_OFFSET_MINUTES - settings.TIMEZONE_OFFSET_MINUTES) + ).label("day") query = ( select(SettleOption.name, func.sum(Settlement.amount)) .join(Voucher.settlements) .join(Settlement.settle_option) .join(Voucher.food_table) - .where(Voucher.date >= start_date, Voucher.date <= finish_date) + .where(day >= start_date, day <= finish_date) ) if id_: query = query.where(FoodTable.section_id == id_) diff --git a/barker/barker/routers/role.py b/barker/barker/routers/role.py index 646febd..f890459 100644 --- a/barker/barker/routers/role.py +++ b/barker/barker/routers/role.py @@ -6,14 +6,13 @@ from sqlalchemy.exc import SQLAlchemyError from sqlalchemy.orm import Session from sqlalchemy.sql.functions import count -import barker.schemas.role as schemas - from ..core.security import get_current_active_user as get_user from ..db.session import SessionFuture from ..models.permission import Permission from ..models.role import Role from ..models.role_permission import RolePermission from ..models.user_role import UserRole +from ..schemas import role as schemas from ..schemas.user_token import UserToken diff --git a/barker/barker/routers/sale_category.py b/barker/barker/routers/sale_category.py index 7f82134..f0de834 100644 --- a/barker/barker/routers/sale_category.py +++ b/barker/barker/routers/sale_category.py @@ -7,12 +7,11 @@ from sqlalchemy import select from sqlalchemy.exc import SQLAlchemyError from sqlalchemy.sql.functions import count -import barker.schemas.sale_category as schemas - from ..core.security import get_current_active_user as get_user from ..db.session import SessionFuture from ..models.product_version import ProductVersion from ..models.sale_category import SaleCategory +from ..schemas import sale_category as schemas from ..schemas.user_token import UserToken diff --git a/barker/barker/routers/section.py b/barker/barker/routers/section.py index 65e4408..e27eb01 100644 --- a/barker/barker/routers/section.py +++ b/barker/barker/routers/section.py @@ -4,12 +4,11 @@ from fastapi import APIRouter, Depends, HTTPException, Security, status from sqlalchemy import delete, select from sqlalchemy.exc import SQLAlchemyError -import barker.schemas.section as schemas - from ..core.security import get_current_active_user as get_user from ..db.session import SessionFuture from ..models.section import Section from ..models.section_printer import SectionPrinter +from ..schemas import section as schemas from ..schemas.user_token import UserToken diff --git a/barker/barker/routers/section_printer.py b/barker/barker/routers/section_printer.py index 95d31a7..bc355de 100644 --- a/barker/barker/routers/section_printer.py +++ b/barker/barker/routers/section_printer.py @@ -8,12 +8,11 @@ from sqlalchemy.dialects.postgresql import insert as pg_insert from sqlalchemy.exc import SQLAlchemyError from sqlalchemy.orm import Session -import barker.schemas.section_printer as schemas - from ..core.security import get_current_active_user as get_user from ..db.session import SessionFuture from ..models.sale_category import SaleCategory from ..models.section_printer import SectionPrinter +from ..schemas import section_printer as schemas from ..schemas.user_token import UserToken diff --git a/barker/barker/routers/settle_option.py b/barker/barker/routers/settle_option.py index 0b0416f..7994a53 100644 --- a/barker/barker/routers/settle_option.py +++ b/barker/barker/routers/settle_option.py @@ -2,13 +2,12 @@ from fastapi import APIRouter, Depends, HTTPException, Security, status from sqlalchemy import func, select from sqlalchemy.exc import SQLAlchemyError -import barker.schemas.settle_option as schemas - from ..core.security import get_current_active_user as get_user from ..db.session import SessionFuture from ..models.reporting_level import ReportingLevel from ..models.settle_option import SettleOption from ..models.voucher_type import VoucherType +from ..schemas import settle_option as schemas from ..schemas.user_token import UserToken diff --git a/barker/barker/routers/table.py b/barker/barker/routers/table.py index c2fedc2..16d9251 100644 --- a/barker/barker/routers/table.py +++ b/barker/barker/routers/table.py @@ -6,13 +6,12 @@ from fastapi import APIRouter, Depends, HTTPException, Security, status from sqlalchemy import select, update from sqlalchemy.exc import SQLAlchemyError -import barker.schemas.table as schemas - from ..core.config import settings from ..core.security import get_current_active_user as get_user from ..db.session import SessionFuture from ..models.food_table import FoodTable from ..models.overview import Overview +from ..schemas import table as schemas from ..schemas.user_token import UserToken diff --git a/barker/barker/routers/tax.py b/barker/barker/routers/tax.py index ae27a67..bbcfcc2 100644 --- a/barker/barker/routers/tax.py +++ b/barker/barker/routers/tax.py @@ -8,13 +8,12 @@ from sqlalchemy import select from sqlalchemy.exc import SQLAlchemyError from sqlalchemy.sql.functions import count -import barker.schemas.tax as schemas - from ..core.security import get_current_active_user as get_user from ..db.session import SessionFuture from ..models.inventory import Inventory from ..models.sale_category import SaleCategory from ..models.tax import Tax +from ..schemas import tax as schemas from ..schemas.user_token import UserToken diff --git a/barker/barker/routers/temporal_product.py b/barker/barker/routers/temporal_product.py index 976bf52..78d1a50 100644 --- a/barker/barker/routers/temporal_product.py +++ b/barker/barker/routers/temporal_product.py @@ -5,11 +5,9 @@ from datetime import date, timedelta from fastapi import APIRouter, HTTPException, Security, status from sqlalchemy import and_, delete, distinct, or_, select, update from sqlalchemy.exc import SQLAlchemyError -from sqlalchemy.orm import Session, joinedload +from sqlalchemy.orm import Session, contains_eager from sqlalchemy.sql.functions import count, func -import barker.schemas.product as schemas - from ..core.config import settings from ..core.security import get_current_active_user as get_user from ..db.session import SessionFuture @@ -21,6 +19,7 @@ from ..models.product import Product from ..models.product_version import ProductVersion from ..models.sale_category import SaleCategory from ..models.voucher import Voucher +from ..schemas import product as schemas from ..schemas.user_token import UserToken @@ -204,10 +203,11 @@ def product_list(db: Session) -> list[list[schemas.Product]]: .order_by(ProductVersion.sort_order) .order_by(ProductVersion.name) .options( - joinedload(ProductVersion.menu_category, innerjoin=True), - joinedload(ProductVersion.sale_category, innerjoin=True), + contains_eager(ProductVersion.menu_category), + contains_eager(ProductVersion.sale_category), ) ) + .unique() .scalars() .all() ) @@ -225,19 +225,23 @@ def show_id( user: UserToken = Security(get_user, scopes=["products"]), ) -> schemas.Product: with SessionFuture() as db: - item = db.execute( - select(ProductVersion) - .join(ProductVersion.menu_category) - .join(ProductVersion.sale_category) - .join(SaleCategory.tax) - .where(ProductVersion.id == version_id) - .order_by(ProductVersion.valid_till) - .options( - joinedload(ProductVersion.menu_category, innerjoin=True), - joinedload(ProductVersion.sale_category, innerjoin=True), - joinedload(ProductVersion.sale_category, SaleCategory.tax, innerjoin=True), + item = ( + db.execute( + select(ProductVersion) + .join(ProductVersion.menu_category) + .join(ProductVersion.sale_category) + .join(SaleCategory.tax) + .where(ProductVersion.id == version_id) + .order_by(ProductVersion.valid_till) + .options( + contains_eager(ProductVersion.menu_category), + contains_eager(ProductVersion.sale_category), + contains_eager(ProductVersion.sale_category).contains_eager(SaleCategory.tax), + ) ) - ).scalar_one() + .unique() + .scalar_one() + ) return product_info(item) diff --git a/barker/barker/routers/update_product_prices.py b/barker/barker/routers/update_product_prices.py index c94903c..757efdf 100644 --- a/barker/barker/routers/update_product_prices.py +++ b/barker/barker/routers/update_product_prices.py @@ -5,7 +5,7 @@ from decimal import Decimal from fastapi import APIRouter, Depends, HTTPException, Security, status from sqlalchemy import and_, or_, select -from sqlalchemy.orm import Session, joinedload +from sqlalchemy.orm import Session, contains_eager from ..core.security import get_current_active_user as get_user from ..db.session import SessionFuture @@ -84,7 +84,7 @@ def update_product_prices_list( ProductVersion.name, ProductVersion.valid_from.nullsfirst(), ).options( - joinedload(ProductVersion.menu_category, innerjoin=True), + contains_eager(ProductVersion.menu_category), ) return [ @@ -127,9 +127,7 @@ def save_update_product_prices_id( def update_product(id_: uuid.UUID, price: Decimal, date_: date, db: Session): item: ProductVersion = db.execute( - select(ProductVersion) - .join(ProductVersion.menu_category) - .where( + select(ProductVersion).where( and_( ProductVersion.product_id == id_, or_( diff --git a/barker/barker/routers/user.py b/barker/barker/routers/user.py index 24fb3ff..db6c7a6 100644 --- a/barker/barker/routers/user.py +++ b/barker/barker/routers/user.py @@ -5,8 +5,6 @@ from sqlalchemy import delete, select, update from sqlalchemy.exc import SQLAlchemyError from sqlalchemy.orm import Session -import barker.schemas.user as schemas - from ..core.security import get_current_active_user as get_user from ..db.session import SessionFuture from ..models.kot import Kot @@ -16,6 +14,7 @@ from ..models.role import Role from ..models.user import User from ..models.user_role import UserRole from ..models.voucher import Voucher +from ..schemas import user as schemas from ..schemas.user_token import UserToken diff --git a/barker/barker/routers/voucher/__init__.py b/barker/barker/routers/voucher/__init__.py index 3034b2d..d97e17d 100644 --- a/barker/barker/routers/voucher/__init__.py +++ b/barker/barker/routers/voucher/__init__.py @@ -8,8 +8,6 @@ from sqlalchemy import func from sqlalchemy.orm import Session from sqlalchemy.sql import expression -import barker.schemas.voucher as schemas - from barker.models.guest_book_type import GuestBookType from barker.models.overview_status import OverviewStatus @@ -21,6 +19,7 @@ from ...models.settle_option import SettleOption from ...models.settlement import Settlement from ...models.voucher import Voucher from ...models.voucher_type import VoucherType +from ...schemas import voucher as schemas from ...schemas.receive_payment import ReceivePaymentItem as SettleSchema diff --git a/barker/barker/routers/voucher/change.py b/barker/barker/routers/voucher/change.py index 7c8713a..cca555d 100644 --- a/barker/barker/routers/voucher/change.py +++ b/barker/barker/routers/voucher/change.py @@ -5,8 +5,6 @@ from sqlalchemy import select, update from sqlalchemy.exc import SQLAlchemyError from sqlalchemy.orm import Session -import barker.schemas.voucher as schemas - from ...core.security import get_current_active_user as get_user from ...db.session import SessionFuture from ...models.overview import Overview @@ -22,6 +20,7 @@ from ...routers.voucher import ( do_update_settlements, get_guest_book, ) +from ...schemas import voucher as schemas from ...schemas.receive_payment import ReceivePaymentItem as SettleSchema from ...schemas.user_token import UserToken from .save import do_save diff --git a/barker/barker/routers/voucher/merge_move.py b/barker/barker/routers/voucher/merge_move.py index 467fedd..4a04b97 100644 --- a/barker/barker/routers/voucher/merge_move.py +++ b/barker/barker/routers/voucher/merge_move.py @@ -8,8 +8,6 @@ from sqlalchemy import delete, func, select, update from sqlalchemy.exc import SQLAlchemyError from sqlalchemy.orm import Session -import barker.schemas.merge_move as schemas - from ...core.security import get_current_active_user as get_user from ...db.session import SessionFuture from ...models.bill import Bill @@ -23,6 +21,7 @@ from ...routers.voucher import ( do_update_settlements, do_update_table, ) +from ...schemas import merge_move as schemas from ...schemas.user_token import UserToken diff --git a/barker/barker/routers/voucher/receive_payment.py b/barker/barker/routers/voucher/receive_payment.py index 91e15cb..a8cfdb1 100644 --- a/barker/barker/routers/voucher/receive_payment.py +++ b/barker/barker/routers/voucher/receive_payment.py @@ -4,14 +4,13 @@ from fastapi import APIRouter, HTTPException, Security, status from sqlalchemy import delete, select from sqlalchemy.exc import SQLAlchemyError -import barker.schemas.receive_payment as schemas - from ...core.security import get_current_active_user as get_user from ...db.session import SessionFuture from ...models.overview import Overview from ...models.settle_option import SettleOption from ...models.voucher import Voucher from ...models.voucher_type import VoucherType +from ...schemas import receive_payment as schemas from ...schemas.user_token import UserToken from . import do_update_settlements diff --git a/barker/barker/routers/voucher/save.py b/barker/barker/routers/voucher/save.py index 2945359..8a643cf 100644 --- a/barker/barker/routers/voucher/save.py +++ b/barker/barker/routers/voucher/save.py @@ -7,9 +7,7 @@ from decimal import Decimal from fastapi import APIRouter, HTTPException, Security, status from sqlalchemy import and_, func, or_, select from sqlalchemy.exc import SQLAlchemyError -from sqlalchemy.orm import Session - -import barker.schemas.voucher as schemas +from sqlalchemy.orm import Session, contains_eager from ...core.config import settings from ...core.security import get_current_active_user as get_user @@ -33,6 +31,7 @@ from ...routers.voucher import ( happy_hour_has_discount, happy_hour_items_balanced, ) +from ...schemas import voucher as schemas from ...schemas.user_token import UserToken @@ -124,7 +123,9 @@ def do_save( 2, ) product: ProductVersion = db.execute( - select(ProductVersion).where( + select(ProductVersion) + .join(ProductVersion.sale_category) + .where( and_( ProductVersion.product_id == i.product.id_, or_( @@ -137,6 +138,7 @@ def do_save( ), ) ) + .options(contains_eager(ProductVersion.sale_category)) ).scalar_one() if total_quantity < 0: raise HTTPException( diff --git a/barker/barker/routers/voucher/show.py b/barker/barker/routers/voucher/show.py index dc3fd34..28b15f5 100644 --- a/barker/barker/routers/voucher/show.py +++ b/barker/barker/routers/voucher/show.py @@ -1,24 +1,42 @@ import re import uuid -from datetime import datetime, timedelta - from fastapi import APIRouter, HTTPException, Security, status from sqlalchemy import and_, or_, select -from sqlalchemy.orm import Session, joinedload +from sqlalchemy.orm import Session, contains_eager -from ...core.config import settings from ...core.security import get_current_active_user as get_user from ...db.session import SessionFuture from ...models.bill import Bill from ...models.food_table import FoodTable from ...models.guest_book import GuestBook +from ...models.inventory import Inventory +from ...models.kot import Kot from ...models.overview import Overview +from ...models.product import Product from ...models.product_version import ProductVersion from ...models.regime import Regime from ...models.voucher import Voucher from ...models.voucher_type import VoucherType from ...schemas.user_token import UserToken +from ...schemas.voucher_out import ( + CustomerLink, + InventoryProduct, + ModifierLink, + ProductMenuCategory, + ProductSaleCategory, + TableLink, + TaxLink, + UserLink, + VoucherBlank, + VoucherOut, +) +from ...schemas.voucher_out import ( + Inventory as InventoryOut, +) +from ...schemas.voucher_out import ( + Kot as KotOut, +) router = APIRouter() @@ -28,9 +46,37 @@ router = APIRouter() def from_id( id_: str, user: UserToken = Security(get_user), -): +) -> VoucherOut: with SessionFuture() as db: - item: Voucher = db.execute(select(Voucher).where(Voucher.id == id_)).scalar_one() + product_version_onclause = and_( + ProductVersion.product_id == Product.id, + or_( + ProductVersion.valid_from == None, # noqa: E711 + ProductVersion.valid_from <= Voucher.date, + ), + or_( + ProductVersion.valid_till == None, # noqa: E711 + ProductVersion.valid_till >= Voucher.date, + ), + ) + item: Voucher = db.execute( + select(Voucher) + .join(Voucher.food_table) + .join(Voucher.customer, isouter=True) + .join(Voucher.kots) + .join(Kot.inventories) + .join(Inventory.product) + .join(ProductVersion, onclause=product_version_onclause) + .where(Voucher.id == id_) + .options( + contains_eager(Voucher.food_table), + contains_eager(Voucher.customer), + contains_eager(Voucher.kots) + .contains_eager(Kot.inventories) + .contains_eager(Inventory.product) + .contains_eager(Product.versions), + ) + ).scalar_one() return voucher_info(item, db) @@ -38,21 +84,50 @@ def from_id( def from_bill( id_: str, user: UserToken = Security(get_user), -): +) -> VoucherOut: with SessionFuture() as db: + product_version_onclause = and_( + ProductVersion.product_id == Product.id, + or_( + ProductVersion.valid_from == None, # noqa: E711 + ProductVersion.valid_from <= Voucher.date, + ), + or_( + ProductVersion.valid_till == None, # noqa: E711 + ProductVersion.valid_till >= Voucher.date, + ), + ) match = re.compile(r"^(\w+)-(\d+)$").match(id_) if not match or len(match.groups()) != 2: raise HTTPException( status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, detail="Bill Number is invalid", ) - voucher_id = ( - select(Bill.voucher_id) - .join(Bill.regime) - .where(Regime.prefix == match.group(1), Bill.bill_number == int(match.group(2))) - .options(joinedload(Regime.prefix, innerjoin=True)) + item = ( + db.execute( + select(Voucher) + .join(Voucher.bills) + .join(Bill.regime) + .join(Voucher.food_table) + .join(Voucher.customer, isouter=True) + .join(Voucher.kots) + .join(Kot.inventories) + .join(Inventory.product) + .join(ProductVersion, onclause=product_version_onclause) + .where(Regime.prefix == match.group(1), Bill.bill_number == int(match.group(2))) + .options( + contains_eager(Voucher.food_table), + contains_eager(Voucher.customer), + contains_eager(Voucher.kots) + .contains_eager(Kot.inventories) + .contains_eager(Inventory.product) + .contains_eager(Product.versions), + contains_eager(Voucher.bills).contains_eager(Bill.regime), + ) + ) + .scalars() + .one_or_none() ) - item = db.execute(select(Voucher).where(Voucher.id.in_(voucher_id))).scalars().one_or_none() if item is None: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, @@ -67,11 +142,22 @@ def from_table( v: uuid.UUID | None = None, # voucher id g: uuid.UUID | None = None, # guest id user: UserToken = Security(get_user), -): +) -> VoucherOut | VoucherBlank: with SessionFuture() as db: + product_version_onclause = and_( + ProductVersion.product_id == Product.id, + or_( + ProductVersion.valid_from == None, # noqa: E711 + ProductVersion.valid_from <= Voucher.date, + ), + or_( + ProductVersion.valid_till == None, # noqa: E711 + ProductVersion.valid_till >= Voucher.date, + ), + ) guest = None if g is None else db.execute(select(GuestBook).where(GuestBook.id == g)).scalar_one() if v is not None: - item = ( + overview = ( db.execute( select(Overview).where( Overview.voucher_id == v, @@ -81,115 +167,112 @@ def from_table( .scalars() .one_or_none() ) - if item is None: + if overview is None: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="Voucher not found", ) else: + item = ( + db.execute( + select(Voucher) + .join(Voucher.food_table) + .join(Voucher.customer, isouter=True) + .join(Voucher.kots) + .join(Kot.inventories) + .join(Inventory.product) + .join(ProductVersion, onclause=product_version_onclause) + .where(Voucher.id == v) + .options( + contains_eager(Voucher.food_table), + contains_eager(Voucher.customer), + contains_eager(Voucher.kots) + .contains_eager(Kot.inventories) + .contains_eager(Inventory.product) + .contains_eager(Product.versions), + ) + ) + .unique() + .scalars() + .one() + ) + if guest is not None: - item.voucher.customer = guest.customer - return voucher_info(item.voucher, db) + item.customer = guest.customer + return voucher_info(item, db) table = db.execute(select(FoodTable).where(FoodTable.id == id_)).scalar_one() return voucher_blank(table, guest) -def voucher_product(product_id: uuid.UUID, date_: datetime, is_happy_hour: bool, db: Session): - product_date = ( - date_ + timedelta(minutes=settings.TIMEZONE_OFFSET_MINUTES - settings.NEW_DAY_OFFSET_MINUTES) - ).date() - product: ProductVersion = db.execute( - select(ProductVersion).where( - and_( - ProductVersion.product_id == product_id, - or_( - ProductVersion.valid_from == None, # noqa: E711 - ProductVersion.valid_from <= product_date, - ), - or_( - ProductVersion.valid_till == None, # noqa: E711 - ProductVersion.valid_till >= product_date, - ), - ) - ) - ).scalar_one() - - return { - "id": product_id, - "name": ("H H " if is_happy_hour else "") + product.full_name, - "menuCategory": { - "id": product.menu_category_id, - "name": product.menu_category.name, - }, - "saleCategory": { - "id": product.sale_category_id, - "name": product.sale_category.name, - "discountLimit": product.sale_category.discount_limit, - }, - } - - -def voucher_info(item: Voucher, db: Session): - return { - "id": item.id, - "date": (item.date + timedelta(minutes=settings.TIMEZONE_OFFSET_MINUTES)).strftime("%H:%M"), - "dateTip": (item.date + timedelta(minutes=settings.TIMEZONE_OFFSET_MINUTES)).strftime("%d-%b-%Y %H:%M:%S"), - "pax": item.pax, - "user": {"id": item.user_id, "name": item.user.name}, - "creationDate": (item.creation_date + timedelta(minutes=settings.TIMEZONE_OFFSET_MINUTES)).strftime("%H:%M"), - "creationDateTip": (item.creation_date + timedelta(minutes=settings.TIMEZONE_OFFSET_MINUTES)).strftime( - "%d-%b-%Y %H:%M:%S" - ), - "lastEditDate": (item.last_edit_date + timedelta(minutes=settings.TIMEZONE_OFFSET_MINUTES)).strftime("%H:%M"), - "lastEditDateTip": (item.last_edit_date + timedelta(minutes=settings.TIMEZONE_OFFSET_MINUTES)).strftime( - "%d-%b-%Y %H:%M:%S" - ), - "kotId": item.kot_id, - "billId": ", ".join(f"{x.regime.prefix}-{x.bill_number}" for x in item.bills), - "table": {"id": item.food_table_id, "name": item.food_table.name}, - "customer": {"id": item.customer.id, "name": item.customer.name} if item.customer is not None else None, - "narration": item.narration, - "reason": item.reason, - "voucherType": item.voucher_type, - "kots": [ - { - "id": k.id, - "code": k.code, - "date": (k.date + timedelta(minutes=settings.TIMEZONE_OFFSET_MINUTES)).strftime("%d-%b-%Y %H:%M:%S"), - "user": {"id": k.user_id, "name": k.user.name}, - "inventories": [ - { - "id": i.id, - "sortOrder": i.sort_order, - "product": voucher_product(i.product_id, item.date, i.is_happy_hour, db), - "quantity": i.quantity, - "price": i.price, - "isHappyHour": i.is_happy_hour, - "taxRate": i.tax_rate, - "tax": {"id": i.tax_id, "name": i.tax.name}, - "discount": i.discount, - "modifiers": [ - { - "id": m.modifier.id, - "name": m.modifier.name, - "price": m.price, - } +def voucher_info(item: Voucher, db: Session) -> VoucherOut: + return VoucherOut( + id_=item.id, + date_=item.date, + date_tip=item.date, + pax=item.pax, + user=UserLink(id_=item.user_id, name=item.user.name), + creation_date=item.creation_date, + creation_date_tip=item.creation_date, + last_edit_date=item.last_edit_date, + last_edit_date_tip=item.last_edit_date, + kot_id=item.kot_id, + bill_id=", ".join(f"{x.regime.prefix}-{x.bill_number}" for x in item.bills), + table=TableLink(id_=item.food_table_id, name=item.food_table.name), + customer=CustomerLink(id_=item.customer.id, name=item.customer.name) if item.customer is not None else None, + narration=item.narration, + reason=item.reason, + voucher_type=str(item.voucher_type), + kots=[ + KotOut( + id_=k.id, + code=k.code, + date_=k.date, + user=UserLink(id_=k.user_id, name=k.user.name), + inventories=[ + InventoryOut( + id_=i.id, + sort_order=i.sort_order, + product=InventoryProduct( + id_=i.product_id, + name=("H H " if i.is_happy_hour else "") + i.product.versions[0].full_name, + menu_category=ProductMenuCategory( + id_=i.product.versions[0].menu_category_id, + name=i.product.versions[0].menu_category.name, + ), + sale_category=ProductSaleCategory( + id_=i.product.versions[0].sale_category_id, + name=i.product.versions[0].sale_category.name, + discount_limit=i.product.versions[0].sale_category.discount_limit, + ), + ), + quantity=i.quantity, + price=i.price, + is_happy_hour=i.is_happy_hour, + tax_rate=i.tax_rate, + tax=TaxLink(id_=i.tax_id, name=i.tax.name), + discount=i.discount, + modifiers=[ + ModifierLink( + id_=m.modifier.id, + name=m.modifier.name, + price=m.price, + ) for m in i.modifiers ], - } + ) for i in k.inventories ], - } + ) for k in item.kots ], - } + ) -def voucher_blank(table: FoodTable, guest: GuestBook | None): - return { - "pax": table.seats if guest is None else guest.pax, - "table": {"id": table.id, "name": table.name}, - "voucherType": VoucherType.KOT, - "customer": {"id": guest.customer_id, "name": guest.customer.name} if guest is not None else None, - "kots": [], - } +def voucher_blank(table: FoodTable, guest: GuestBook | None) -> VoucherBlank: + return VoucherBlank( + pax=table.seats if guest is None else guest.pax, + table=TableLink(id_=table.id, name=table.name), + voucher_type=str(VoucherType.KOT), + customer=CustomerLink(id_=guest.customer_id, name=guest.customer.name) if guest is not None else None, + kots=[], + ) diff --git a/barker/barker/routers/voucher/split.py b/barker/barker/routers/voucher/split.py index 47e6d52..a83c853 100644 --- a/barker/barker/routers/voucher/split.py +++ b/barker/barker/routers/voucher/split.py @@ -9,8 +9,6 @@ from sqlalchemy import delete, func, select from sqlalchemy.exc import SQLAlchemyError from sqlalchemy.orm import Session -import barker.schemas.split as schemas - from ...core.security import get_current_active_user as get_user from ...db.session import SessionFuture from ...models.inventory import Inventory @@ -26,6 +24,7 @@ from ...routers.voucher import ( do_update_settlements, do_update_table, ) +from ...schemas import split as schemas from ...schemas.receive_payment import ReceivePaymentItem as SettleSchema from ...schemas.user_token import UserToken diff --git a/barker/barker/routers/voucher/update.py b/barker/barker/routers/voucher/update.py index 3f2db9a..466e0e9 100644 --- a/barker/barker/routers/voucher/update.py +++ b/barker/barker/routers/voucher/update.py @@ -7,8 +7,6 @@ from fastapi import APIRouter, HTTPException, Security, status from sqlalchemy import and_, func, or_, select from sqlalchemy.exc import SQLAlchemyError -import barker.schemas.voucher as schemas - from ...core.config import settings from ...core.security import get_current_active_user as get_user from ...db.session import SessionFuture @@ -31,6 +29,7 @@ from ...routers.voucher import ( happy_hour_items_balanced, happy_hour_items_more_than_regular, ) +from ...schemas import voucher as schemas from ...schemas.user_token import UserToken diff --git a/barker/barker/schemas/discount_report.py b/barker/barker/schemas/discount_report.py index 2be8e08..4ea8921 100644 --- a/barker/barker/schemas/discount_report.py +++ b/barker/barker/schemas/discount_report.py @@ -19,7 +19,7 @@ class DiscountReport(BaseModel): @field_validator("start_date", mode="before") @classmethod - def parse_start_date(cls, value): + def parse_start_date(cls, value: date | str): if isinstance(value, date): return value return datetime.strptime(value, "%d-%b-%Y").date() @@ -30,7 +30,7 @@ class DiscountReport(BaseModel): @field_validator("finish_date", mode="before") @classmethod - def parse_finish_date(cls, value): + def parse_finish_date(cls, value: date | str): if isinstance(value, date): return value return datetime.strptime(value, "%d-%b-%Y").date() diff --git a/barker/barker/schemas/modifier.py b/barker/barker/schemas/modifier.py index 341df23..582e12a 100644 --- a/barker/barker/schemas/modifier.py +++ b/barker/barker/schemas/modifier.py @@ -24,6 +24,7 @@ class Modifier(ModifierIn): class ModifierLink(BaseModel): id_: uuid.UUID = Field(...) name: str | None = None + price: Daf | None = None model_config = ConfigDict(alias_generator=to_camel, populate_by_name=True) diff --git a/barker/barker/schemas/voucher.py b/barker/barker/schemas/voucher.py index cb2dbff..06d90b0 100644 --- a/barker/barker/schemas/voucher.py +++ b/barker/barker/schemas/voucher.py @@ -57,7 +57,8 @@ class Inventory(BaseModel): def calculate_amount(self) -> "Inventory": price = 0 if self.is_happy_hour else self.price self.amount = round( - Decimal(price * self.quantity * (1 - self.discount) * (1 + (self.tax_rate or Decimal(0)))), 2 + Decimal(price * self.quantity * (1 - self.discount) * (1 + (self.tax_rate or Decimal(0)))), + 2, # type: ignore ) return self diff --git a/barker/barker/schemas/voucher_out.py b/barker/barker/schemas/voucher_out.py new file mode 100644 index 0000000..22e2daf --- /dev/null +++ b/barker/barker/schemas/voucher_out.py @@ -0,0 +1,159 @@ +import uuid + +from datetime import datetime, timedelta +from decimal import Decimal + +from pydantic import BaseModel, ConfigDict, Field, field_serializer, field_validator, model_validator + +from ..core.config import settings +from . import Daf, to_camel +from .customer import CustomerLink +from .modifier import ModifierLink +from .table import TableLink +from .tax import TaxLink +from .user import UserLink + + +class ProductSaleCategory(BaseModel): + id_: uuid.UUID = Field(...) + name: str | None = None + discount_limit: Daf + model_config = ConfigDict(alias_generator=to_camel, populate_by_name=True) + + +class ProductMenuCategory(BaseModel): + id_: uuid.UUID = Field(...) + name: str | None = None + model_config = ConfigDict(alias_generator=to_camel, populate_by_name=True) + + +class InventoryProduct(BaseModel): + id_: uuid.UUID = Field(...) + name: str | None = None + menu_category: ProductMenuCategory | None = None + sale_category: ProductSaleCategory | None = None + model_config = ConfigDict(alias_generator=to_camel, populate_by_name=True) + + +class Inventory(BaseModel): + id_: uuid.UUID | None = None + sort_order: int | None = None + product: InventoryProduct + quantity: Daf + price: Daf | None = None + tax: TaxLink | None = None + tax_rate: Daf | None = None + discount: Daf = Field(ge=0, le=1) + is_happy_hour: bool + modifiers: list[ModifierLink] + amount: Daf | None = None + model_config = ConfigDict(alias_generator=to_camel, populate_by_name=True) + + @field_validator("quantity") + @classmethod + def _quantity(cls, value: Daf | None) -> Daf: + if value is None: + return Decimal(0) + return round(value, 2) + + @field_validator("price") + @classmethod + def _price(cls, value: Daf | None) -> Daf: + if value is None: + return Decimal(0) + return round(value, 2) + + @field_validator("tax_rate") + @classmethod + def _tax_rate(cls, value: Daf | None) -> Daf: + if value is None: + return Decimal(0) + return round(value, 5) + + @field_validator("discount") + @classmethod + def _discount(cls, value: Daf | None) -> Daf: + if value is None: + return Decimal(0) + return round(value, 5) + + @model_validator(mode="after") + def calculate_amount(self) -> "Inventory": + price = 0 if self.is_happy_hour else self.price + self.amount = round( + Decimal(price * self.quantity * (1 - self.discount) * (1 + (self.tax_rate or Decimal(0)))), # type: ignore + 2, # type: ignore + ) + return self + + +class Kot(BaseModel): + id_: uuid.UUID | None = None + code: int | None = None + date_: datetime | None = None + user: UserLink | None = None + inventories: list[Inventory] + model_config = ConfigDict(str_strip_whitespace=True, alias_generator=to_camel, populate_by_name=True) + + @field_serializer("date_") + def serialize_date(self, value: datetime, _info): # type: ignore + return (value + timedelta(minutes=settings.TIMEZONE_OFFSET_MINUTES)).strftime("%d-%b-%Y %H:%M:%S") + + +class VoucherBlank(BaseModel): + pax: int + table: TableLink + customer: CustomerLink | None = None + voucher_type: str + kots: list[Kot] + model_config = ConfigDict(str_strip_whitespace=True, alias_generator=to_camel, populate_by_name=True) + + +class VoucherOut(BaseModel): + id_: uuid.UUID + date_: datetime + date_tip: datetime + pax: int + user: UserLink + creation_date: datetime + creation_date_tip: datetime + last_edit_date: datetime + last_edit_date_tip: datetime + kot_id: int + bill_id: str + table: TableLink + customer: CustomerLink | None = None + narration: str | None + reason: str | None + voucher_type: str + kots: list[Kot] + + model_config = ConfigDict( + str_strip_whitespace=True, + alias_generator=to_camel, + populate_by_name=True, + ) + + @field_serializer("date_") + def serialize_date(self, value: datetime, _info): # type: ignore + return (value + timedelta(minutes=settings.TIMEZONE_OFFSET_MINUTES)).strftime("%H:%M") + + @field_serializer("date_tip") + def serialize_date_tip(self, value: datetime, _info): # type: ignore + return (value + timedelta(minutes=settings.TIMEZONE_OFFSET_MINUTES)).strftime("%d-%b-%Y %H:%M:%S") + + @field_serializer("creation_date") + def serialize_creation_date(self, value: datetime, _info): # type: ignore + return (value + timedelta(minutes=settings.TIMEZONE_OFFSET_MINUTES)).strftime("%H:%M") + + @field_serializer("creation_date_tip") + def serialize_creation_date_tip(self, value: datetime, _info): # type: ignore + return (value + timedelta(minutes=settings.TIMEZONE_OFFSET_MINUTES)).strftime("%d-%b-%Y %H:%M:%S") + + @field_serializer("last_edit_date") + def serialize_last_edit_date(self, value: datetime, _info): # type: ignore + return (value + timedelta(minutes=settings.TIMEZONE_OFFSET_MINUTES)).strftime("%H:%M") + + @field_serializer("last_edit_date_tip") + def serialize_last_edit_date_tip(self, value: datetime, _info): # type: ignore + return (value + timedelta(minutes=settings.TIMEZONE_OFFSET_MINUTES)).strftime("%d-%b-%Y %H:%M:%S") diff --git a/bookie/src/app/sales/bill.service.ts b/bookie/src/app/sales/bill.service.ts index 0ae2703..76d6e21 100644 --- a/bookie/src/app/sales/bill.service.ts +++ b/bookie/src/app/sales/bill.service.ts @@ -151,6 +151,9 @@ export class BillService { position: { top: '10vh', }, + width: '80vw', + maxWidth: 'none', + maxHeight: 'none', data: { list: this.modifierCategoryService.listForProduct(item.product.id as string), selected: Object.assign([], item.modifiers),