Product update sort order working
Beer Sale report should work. But it has all the products in it now. All reports working
This commit is contained in:
@ -13,11 +13,10 @@ from ..core.config import settings
|
|||||||
from ..models.device import Device
|
from ..models.device import Device
|
||||||
from ..models.printer import Printer
|
from ..models.printer import Printer
|
||||||
from ..models.section_printer import SectionPrinter
|
from ..models.section_printer import SectionPrinter
|
||||||
from ..schemas.menu_engineering_report import MeReport
|
|
||||||
from ..schemas.product_sale_report import ProductSaleReport
|
from ..schemas.product_sale_report import ProductSaleReport
|
||||||
|
|
||||||
|
|
||||||
def print_product_sale_report(report: ProductSaleReport | MeReport, device_id: uuid.UUID, db: Session) -> None:
|
def print_product_sale_report(report: ProductSaleReport, device_id: uuid.UUID, db: Session) -> None:
|
||||||
locale.setlocale(locale.LC_MONETARY, "en_IN")
|
locale.setlocale(locale.LC_MONETARY, "en_IN")
|
||||||
data = design_product_sale_report(report)
|
data = design_product_sale_report(report)
|
||||||
section_id = db.execute(select(Device.section_id).where(Device.id == device_id)).scalar_one()
|
section_id = db.execute(select(Device.section_id).where(Device.id == device_id)).scalar_one()
|
||||||
|
|||||||
@ -35,38 +35,18 @@ from . import effective_date
|
|||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
|
|
||||||
|
|
||||||
@router.post("/list", response_model=list[schemas.Product])
|
@router.post("/list/{id_}", response_model=list[schemas.Product])
|
||||||
def sort_order(
|
def sort_order(
|
||||||
|
id_: uuid.UUID,
|
||||||
data: list[schemas.Product],
|
data: list[schemas.Product],
|
||||||
date_: date = Depends(effective_date),
|
date_: date = Depends(effective_date),
|
||||||
user: UserToken = Security(get_user, scopes=["products"]),
|
user: UserToken = Security(get_user, scopes=["products"]),
|
||||||
) -> list[schemas.Product]:
|
) -> list[schemas.Product]:
|
||||||
raise NotImplementedError("Sorting products is not yet implemented.")
|
|
||||||
try:
|
try:
|
||||||
with SessionFuture() as db:
|
with SessionFuture() as db:
|
||||||
indexes: dict[uuid.UUID, int] = {}
|
skus = [sku for product in data for sku in product.skus if sku.menu_category.id_ == id_]
|
||||||
for item in data:
|
for i, sku in enumerate(skus):
|
||||||
if item.menu_category.id_ in indexes:
|
db.execute(update(StockKeepingUnit).where(StockKeepingUnit.id == sku.id_).values(sort_order=i))
|
||||||
indexes[item.menu_category.id_] += 1
|
|
||||||
else:
|
|
||||||
indexes[item.menu_category.id_] = 0
|
|
||||||
db.execute(
|
|
||||||
update(ProductVersion)
|
|
||||||
.where(
|
|
||||||
and_(
|
|
||||||
ProductVersion.product_id == item.id_,
|
|
||||||
or_(
|
|
||||||
ProductVersion.valid_from == None, # noqa: E711
|
|
||||||
ProductVersion.valid_from <= date_,
|
|
||||||
),
|
|
||||||
or_(
|
|
||||||
ProductVersion.valid_till == None, # noqa: E711
|
|
||||||
ProductVersion.valid_till >= date_,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.values(sort_order=indexes[item.menu_category.id_])
|
|
||||||
)
|
|
||||||
db.commit()
|
db.commit()
|
||||||
return product_list(date_, db)
|
return product_list(date_, db)
|
||||||
except SQLAlchemyError as e:
|
except SQLAlchemyError as e:
|
||||||
|
|||||||
@ -5,6 +5,9 @@ from fastapi import APIRouter, Depends, Security
|
|||||||
from sqlalchemy import Date, and_
|
from sqlalchemy import Date, and_
|
||||||
from sqlalchemy.sql.expression import func, select
|
from sqlalchemy.sql.expression import func, select
|
||||||
|
|
||||||
|
from barker.models.sku_version import SkuVersion
|
||||||
|
from barker.models.stock_keeping_unit import StockKeepingUnit
|
||||||
|
|
||||||
from ...core.config import settings
|
from ...core.config import settings
|
||||||
from ...core.security import get_current_active_user as get_user
|
from ...core.security import get_current_active_user as get_user
|
||||||
from ...db.session import SessionFuture
|
from ...db.session import SessionFuture
|
||||||
@ -36,7 +39,7 @@ def beer_consumption(
|
|||||||
day = func.cast(
|
day = func.cast(
|
||||||
Voucher.date + timedelta(minutes=settings.TIMEZONE_OFFSET_MINUTES - settings.NEW_DAY_OFFSET_MINUTES), Date
|
Voucher.date + timedelta(minutes=settings.TIMEZONE_OFFSET_MINUTES - settings.NEW_DAY_OFFSET_MINUTES), Date
|
||||||
).label("day")
|
).label("day")
|
||||||
sum_ = func.sum(Inventory.quantity * ProductVersion.quantity).label("sum")
|
sum_ = func.sum(Inventory.quantity * SkuVersion.fraction).label("sum")
|
||||||
product_version_onclause = and_(
|
product_version_onclause = and_(
|
||||||
ProductVersion.product_id == Product.id,
|
ProductVersion.product_id == Product.id,
|
||||||
or_(
|
or_(
|
||||||
@ -48,11 +51,18 @@ def beer_consumption(
|
|||||||
ProductVersion.valid_till >= day,
|
ProductVersion.valid_till >= day,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
sku_version_onclause = and_(
|
||||||
|
SkuVersion.sku_id == StockKeepingUnit.id,
|
||||||
|
or_(SkuVersion.valid_from == None, SkuVersion.valid_from <= day), # noqa: E711
|
||||||
|
or_(SkuVersion.valid_till == None, SkuVersion.valid_till >= day), # noqa: E711
|
||||||
|
)
|
||||||
query = (
|
query = (
|
||||||
select(day, ProductVersion.name, sum_)
|
select(day, ProductVersion.name, sum_, ProductVersion.fraction_units)
|
||||||
.join(Voucher.kots)
|
.join(Voucher.kots)
|
||||||
.join(Kot.inventories)
|
.join(Kot.inventories)
|
||||||
.join(Inventory.product)
|
.join(Inventory.sku)
|
||||||
|
.join(SkuVersion, onclause=sku_version_onclause)
|
||||||
|
.join(StockKeepingUnit.product)
|
||||||
.join(ProductVersion, onclause=product_version_onclause)
|
.join(ProductVersion, onclause=product_version_onclause)
|
||||||
.where(
|
.where(
|
||||||
day >= start_date,
|
day >= start_date,
|
||||||
@ -74,21 +84,21 @@ def beer_consumption(
|
|||||||
with SessionFuture() as db:
|
with SessionFuture() as db:
|
||||||
list_ = db.execute(
|
list_ = db.execute(
|
||||||
query.where(Voucher.voucher_type.in_(vt))
|
query.where(Voucher.voucher_type.in_(vt))
|
||||||
.group_by(day, ProductVersion.name)
|
.group_by(day, ProductVersion.name, ProductVersion.fraction_units)
|
||||||
.having(sum_ != 0)
|
.having(sum_ != 0)
|
||||||
.order_by(day, ProductVersion.name)
|
.order_by(day, ProductVersion.name)
|
||||||
).all()
|
).all()
|
||||||
headers: list[str] = []
|
headers: list[str] = []
|
||||||
data: list[BeerConsumptionReportItem] = []
|
data: list[BeerConsumptionReportItem] = []
|
||||||
for date_, name, quantity in list_:
|
for date_, name, quantity, units in list_:
|
||||||
if name not in headers:
|
if f"{name} ({units})" not in headers:
|
||||||
headers.append(name)
|
headers.append(f"{name} ({units})")
|
||||||
old = next((d for d in data if d.date_ == date_), None)
|
old = next((d for d in data if d.date_ == date_), None)
|
||||||
if old:
|
if old:
|
||||||
old[name] = quantity
|
old[f"{name} ({units})"] = quantity
|
||||||
else:
|
else:
|
||||||
item = BeerConsumptionReportItem(date_=date_)
|
item = BeerConsumptionReportItem(date_=date_)
|
||||||
item[name] = quantity
|
item[f"{name} ({units})"] = quantity
|
||||||
data.append(item)
|
data.append(item)
|
||||||
return BeerConsumptionReport(
|
return BeerConsumptionReport(
|
||||||
start_date=start_date,
|
start_date=start_date,
|
||||||
|
|||||||
@ -6,6 +6,9 @@ from fastapi import APIRouter, Cookie, Depends, Security
|
|||||||
from sqlalchemy import Date, and_, func, or_, select
|
from sqlalchemy import Date, and_, func, or_, select
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
|
|
||||||
|
from barker.models.sku_version import SkuVersion
|
||||||
|
from barker.models.stock_keeping_unit import StockKeepingUnit
|
||||||
|
|
||||||
from ...core.config import settings
|
from ...core.config import settings
|
||||||
from ...core.security import get_current_active_user as get_user
|
from ...core.security import get_current_active_user as get_user
|
||||||
from ...db.session import SessionFuture
|
from ...db.session import SessionFuture
|
||||||
@ -56,11 +59,18 @@ def get_discount_report(start_date: date, finish_date: date, db: Session) -> lis
|
|||||||
ProductVersion.valid_till >= day,
|
ProductVersion.valid_till >= day,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
sku_version_onclause = and_(
|
||||||
|
SkuVersion.sku_id == StockKeepingUnit.id,
|
||||||
|
or_(SkuVersion.valid_from == None, SkuVersion.valid_from <= day), # noqa: E711
|
||||||
|
or_(SkuVersion.valid_till == None, SkuVersion.valid_till >= day), # noqa: E711
|
||||||
|
)
|
||||||
list_ = db.execute(
|
list_ = db.execute(
|
||||||
select(SaleCategory.name, amount)
|
select(SaleCategory.name, amount)
|
||||||
.join(Voucher.kots)
|
.join(Voucher.kots)
|
||||||
.join(Kot.inventories)
|
.join(Kot.inventories)
|
||||||
.join(Inventory.product)
|
.join(Inventory.sku)
|
||||||
|
.join(SkuVersion, onclause=sku_version_onclause)
|
||||||
|
.join(StockKeepingUnit.product)
|
||||||
.join(ProductVersion, onclause=product_version_onclause)
|
.join(ProductVersion, onclause=product_version_onclause)
|
||||||
.join(ProductVersion.sale_category)
|
.join(ProductVersion.sale_category)
|
||||||
.where(
|
.where(
|
||||||
|
|||||||
@ -3,10 +3,13 @@ import uuid
|
|||||||
from datetime import date, timedelta
|
from datetime import date, timedelta
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
|
|
||||||
from fastapi import APIRouter, Cookie, Depends, Security
|
from fastapi import APIRouter, Depends, Security
|
||||||
from sqlalchemy import Date, and_, func, nulls_last, or_, select
|
from sqlalchemy import Date, and_, func, nulls_last, or_, select
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
|
|
||||||
|
from barker.models.sku_version import SkuVersion
|
||||||
|
from barker.models.stock_keeping_unit import StockKeepingUnit
|
||||||
|
|
||||||
from ...core.config import settings
|
from ...core.config import settings
|
||||||
from ...core.security import get_current_active_user as get_user
|
from ...core.security import get_current_active_user as get_user
|
||||||
from ...db.session import SessionFuture
|
from ...db.session import SessionFuture
|
||||||
@ -18,7 +21,6 @@ from ...models.product_version import ProductVersion
|
|||||||
from ...models.sale_category import SaleCategory
|
from ...models.sale_category import SaleCategory
|
||||||
from ...models.voucher import Voucher
|
from ...models.voucher import Voucher
|
||||||
from ...models.voucher_type import VoucherType
|
from ...models.voucher_type import VoucherType
|
||||||
from ...printing.product_sale_report import print_product_sale_report
|
|
||||||
from ...schemas.menu_engineering_report import MeItem, MeReport
|
from ...schemas.menu_engineering_report import MeItem, MeReport
|
||||||
from ...schemas.user_token import UserToken
|
from ...schemas.user_token import UserToken
|
||||||
from . import check_audit_permission, report_finish_date, report_start_date
|
from . import check_audit_permission, report_finish_date, report_start_date
|
||||||
@ -47,8 +49,7 @@ def menu_engineering_report(start_date: date, finish_date: date, db: Session) ->
|
|||||||
day = func.cast(
|
day = func.cast(
|
||||||
Voucher.date + timedelta(minutes=settings.TIMEZONE_OFFSET_MINUTES - settings.NEW_DAY_OFFSET_MINUTES), Date
|
Voucher.date + timedelta(minutes=settings.TIMEZONE_OFFSET_MINUTES - settings.NEW_DAY_OFFSET_MINUTES), Date
|
||||||
).label("day")
|
).label("day")
|
||||||
product_version_onclause = and_(
|
product_version_valid = and_(
|
||||||
ProductVersion.product_id == Product.id,
|
|
||||||
or_(
|
or_(
|
||||||
ProductVersion.valid_from == None, # noqa: E711
|
ProductVersion.valid_from == None, # noqa: E711
|
||||||
ProductVersion.valid_from <= day,
|
ProductVersion.valid_from <= day,
|
||||||
@ -58,45 +59,53 @@ def menu_engineering_report(start_date: date, finish_date: date, db: Session) ->
|
|||||||
ProductVersion.valid_till >= day,
|
ProductVersion.valid_till >= day,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
sku_version_valid = and_(
|
||||||
|
or_(SkuVersion.valid_from == None, SkuVersion.valid_from <= day), # noqa: E711
|
||||||
|
or_(SkuVersion.valid_till == None, SkuVersion.valid_till >= day), # noqa: E711
|
||||||
|
)
|
||||||
list_ = db.execute(
|
list_ = db.execute(
|
||||||
select(
|
select(
|
||||||
SaleCategory.name,
|
SaleCategory.name,
|
||||||
MenuCategory.name,
|
MenuCategory.name,
|
||||||
ProductVersion.id,
|
ProductVersion.id,
|
||||||
ProductVersion.full_name,
|
ProductVersion.name,
|
||||||
ProductVersion.price,
|
SkuVersion.id,
|
||||||
|
SkuVersion.units,
|
||||||
|
SkuVersion.sale_price,
|
||||||
func.sum(Inventory.quantity),
|
func.sum(Inventory.quantity),
|
||||||
func.sum(Inventory.net),
|
func.sum(Inventory.net),
|
||||||
)
|
)
|
||||||
.select_from(ProductVersion)
|
.select_from(ProductVersion)
|
||||||
.join(ProductVersion.sale_category)
|
.join(ProductVersion.sale_category)
|
||||||
.join(ProductVersion.menu_category)
|
.join(ProductVersion.product)
|
||||||
.join(Product, onclause=product_version_onclause)
|
.join(Product.skus)
|
||||||
.join(Product.inventories, isouter=True)
|
.join(StockKeepingUnit.versions)
|
||||||
|
.join(SkuVersion.menu_category)
|
||||||
|
.join(StockKeepingUnit.inventories, isouter=True)
|
||||||
.join(Inventory.kot, isouter=True)
|
.join(Inventory.kot, isouter=True)
|
||||||
.join(Kot.voucher, isouter=True)
|
.join(Kot.voucher, isouter=True)
|
||||||
.where(
|
.where(
|
||||||
or_(
|
day >= start_date,
|
||||||
day == None, # noqa: E711
|
day <= finish_date,
|
||||||
and_(
|
Voucher.voucher_type == VoucherType.REGULAR_BILL,
|
||||||
day >= start_date,
|
product_version_valid,
|
||||||
day <= finish_date,
|
sku_version_valid,
|
||||||
Voucher.voucher_type == VoucherType.REGULAR_BILL,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
.group_by(
|
.group_by(
|
||||||
SaleCategory.name,
|
SaleCategory.name,
|
||||||
MenuCategory.name,
|
MenuCategory.name,
|
||||||
ProductVersion.id,
|
ProductVersion.id,
|
||||||
ProductVersion.full_name,
|
ProductVersion.name,
|
||||||
|
SkuVersion.id,
|
||||||
|
SkuVersion.units,
|
||||||
|
SkuVersion.sale_price,
|
||||||
)
|
)
|
||||||
.order_by(SaleCategory.name, nulls_last(func.sum(Inventory.net).desc()))
|
.order_by(SaleCategory.name, nulls_last(func.sum(Inventory.net).desc()))
|
||||||
).all()
|
).all()
|
||||||
info: list[MeItem] = []
|
info: list[MeItem] = []
|
||||||
sc_sales: dict[str, Decimal] = {}
|
sc_sales: dict[str, Decimal] = {}
|
||||||
sc_quantity: dict[str, Decimal] = {}
|
sc_quantity: dict[str, Decimal] = {}
|
||||||
for sc, mc, id_, name, price, quantity, sales in list_:
|
for sc, mc, p_id, name, s_id, units, price, quantity, sales in list_:
|
||||||
if sc not in sc_sales:
|
if sc not in sc_sales:
|
||||||
sc_sales[sc] = Decimal(0)
|
sc_sales[sc] = Decimal(0)
|
||||||
if sales is not None:
|
if sales is not None:
|
||||||
@ -107,8 +116,8 @@ def menu_engineering_report(start_date: date, finish_date: date, db: Session) ->
|
|||||||
sc_quantity[sc] += quantity
|
sc_quantity[sc] += quantity
|
||||||
info.append(
|
info.append(
|
||||||
MeItem(
|
MeItem(
|
||||||
id_=id_,
|
id_=uuid.uuid5(uuid.NAMESPACE_DNS, f"{p_id}_{s_id}"),
|
||||||
name=name,
|
name=f"{name} ({units})",
|
||||||
price=price,
|
price=price,
|
||||||
average=round(sales / quantity) if sales and quantity else price,
|
average=round(sales / quantity) if sales and quantity else price,
|
||||||
sale_category=sc,
|
sale_category=sc,
|
||||||
@ -126,20 +135,20 @@ def menu_engineering_report(start_date: date, finish_date: date, db: Session) ->
|
|||||||
return info
|
return info
|
||||||
|
|
||||||
|
|
||||||
@router.get("/print", response_model=bool)
|
# @router.get("/print", response_model=bool)
|
||||||
def print_report(
|
# def print_report(
|
||||||
start_date: date = Depends(report_start_date),
|
# start_date: date = Depends(report_start_date),
|
||||||
finish_date: date = Depends(report_finish_date),
|
# finish_date: date = Depends(report_finish_date),
|
||||||
device_id: uuid.UUID = Cookie(None),
|
# device_id: uuid.UUID = Cookie(None),
|
||||||
user: UserToken = Security(get_user, scopes=["product-sale-report"]),
|
# user: UserToken = Security(get_user, scopes=["product-sale-report"]),
|
||||||
) -> bool:
|
# ) -> bool:
|
||||||
check_audit_permission(start_date, user.permissions)
|
# check_audit_permission(start_date, user.permissions)
|
||||||
with SessionFuture() as db:
|
# with SessionFuture() as db:
|
||||||
report = MeReport(
|
# report = MeReport(
|
||||||
user_name=user.name,
|
# user_name=user.name,
|
||||||
start_date=start_date,
|
# start_date=start_date,
|
||||||
finish_date=finish_date,
|
# finish_date=finish_date,
|
||||||
amounts=menu_engineering_report(start_date, finish_date, db),
|
# amounts=menu_engineering_report(start_date, finish_date, db),
|
||||||
)
|
# )
|
||||||
print_product_sale_report(report, device_id, db)
|
# print_product_sale_report(report, device_id, db)
|
||||||
return True
|
# return True
|
||||||
|
|||||||
@ -7,6 +7,9 @@ from fastapi import APIRouter, Cookie, Depends, Query, Security
|
|||||||
from sqlalchemy import Date, and_, func, or_, select
|
from sqlalchemy import Date, and_, func, or_, select
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
|
|
||||||
|
from barker.models.sku_version import SkuVersion
|
||||||
|
from barker.models.stock_keeping_unit import StockKeepingUnit
|
||||||
|
|
||||||
from ...core.config import settings
|
from ...core.config import settings
|
||||||
from ...core.security import get_current_active_user as get_user
|
from ...core.security import get_current_active_user as get_user
|
||||||
from ...db.session import SessionFuture
|
from ...db.session import SessionFuture
|
||||||
@ -63,20 +66,28 @@ def product_sale_report(
|
|||||||
ProductVersion.valid_till >= day,
|
ProductVersion.valid_till >= day,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
sku_version_onclause = and_(
|
||||||
|
SkuVersion.sku_id == StockKeepingUnit.id,
|
||||||
|
or_(SkuVersion.valid_from == None, SkuVersion.valid_from <= day), # noqa: E711
|
||||||
|
or_(SkuVersion.valid_till == None, SkuVersion.valid_till >= day), # noqa: E711
|
||||||
|
)
|
||||||
query = (
|
query = (
|
||||||
select(
|
select(
|
||||||
ProductVersion.id,
|
ProductVersion.id,
|
||||||
ProductVersion.full_name,
|
ProductVersion.name,
|
||||||
|
SkuVersion.units,
|
||||||
Voucher.voucher_type,
|
Voucher.voucher_type,
|
||||||
Inventory.is_happy_hour,
|
Inventory.is_happy_hour,
|
||||||
func.sum(Inventory.quantity),
|
func.sum(Inventory.quantity),
|
||||||
)
|
)
|
||||||
.join(Inventory.kot)
|
.join(Inventory.kot)
|
||||||
.join(Kot.voucher)
|
.join(Kot.voucher)
|
||||||
.join(Inventory.product)
|
.join(Inventory.sku)
|
||||||
|
.join(SkuVersion, onclause=sku_version_onclause)
|
||||||
|
.join(SkuVersion.menu_category)
|
||||||
|
.join(StockKeepingUnit.product)
|
||||||
.join(ProductVersion, onclause=product_version_onclause)
|
.join(ProductVersion, onclause=product_version_onclause)
|
||||||
.join(ProductVersion.sale_category)
|
.join(ProductVersion.sale_category)
|
||||||
.join(ProductVersion.menu_category)
|
|
||||||
.join(Voucher.food_table)
|
.join(Voucher.food_table)
|
||||||
.where(
|
.where(
|
||||||
day >= start_date,
|
day >= start_date,
|
||||||
@ -89,20 +100,21 @@ def product_sale_report(
|
|||||||
SaleCategory.name,
|
SaleCategory.name,
|
||||||
MenuCategory.name,
|
MenuCategory.name,
|
||||||
ProductVersion.id,
|
ProductVersion.id,
|
||||||
ProductVersion.full_name,
|
ProductVersion.name,
|
||||||
|
SkuVersion.units,
|
||||||
Voucher.voucher_type,
|
Voucher.voucher_type,
|
||||||
Inventory.is_happy_hour,
|
Inventory.is_happy_hour,
|
||||||
).order_by(SaleCategory.name, MenuCategory.name, ProductVersion.full_name)
|
).order_by(SaleCategory.name, MenuCategory.name, ProductVersion.name, SkuVersion.units)
|
||||||
list_ = db.execute(query).all()
|
list_ = db.execute(query).all()
|
||||||
info: list[ProductSaleReportItem] = []
|
info: list[ProductSaleReportItem] = []
|
||||||
for product_version_id, name, v_type, hh, quantity in list_:
|
for product_version_id, name, units, v_type, hh, quantity in list_:
|
||||||
type_ = VoucherType(v_type).name
|
type_ = VoucherType(v_type).name
|
||||||
old = next((i for i in info if i.product_version_id == product_version_id and i.is_happy_hour == hh), None)
|
old = next((i for i in info if i.product_version_id == product_version_id and i.is_happy_hour == hh), None)
|
||||||
if old:
|
if old:
|
||||||
old[type_] = old[type_] + quantity
|
old[type_] = old[type_] + quantity
|
||||||
else:
|
else:
|
||||||
item = ProductSaleReportItem(
|
item = ProductSaleReportItem(
|
||||||
product_version_id=product_version_id, name=f"{'H H ' if hh else ''}{name}", is_happy_hour=hh
|
product_version_id=product_version_id, name=f"{'H H ' if hh else ''}{name} ({units})", is_happy_hour=hh
|
||||||
)
|
)
|
||||||
item[type_] = quantity
|
item[type_] = quantity
|
||||||
info.append(item)
|
info.append(item)
|
||||||
|
|||||||
@ -1,9 +1,13 @@
|
|||||||
from datetime import date, timedelta
|
from datetime import date, timedelta
|
||||||
|
|
||||||
from fastapi import APIRouter, Depends, Security
|
from fastapi import APIRouter, Depends, Security
|
||||||
from sqlalchemy import and_, or_, select
|
from sqlalchemy import and_, nullsfirst, or_, select
|
||||||
from sqlalchemy.orm import Session, contains_eager
|
from sqlalchemy.orm import Session, contains_eager
|
||||||
|
|
||||||
|
from barker.models.product import Product
|
||||||
|
from barker.models.sku_version import SkuVersion
|
||||||
|
from barker.models.stock_keeping_unit import StockKeepingUnit
|
||||||
|
|
||||||
from ...core.security import get_current_active_user as get_user
|
from ...core.security import get_current_active_user as get_user
|
||||||
from ...db.session import SessionFuture
|
from ...db.session import SessionFuture
|
||||||
from ...models.menu_category import MenuCategory
|
from ...models.menu_category import MenuCategory
|
||||||
@ -21,19 +25,32 @@ def product_updates_report_view(
|
|||||||
finish_date: date = Depends(report_finish_date),
|
finish_date: date = Depends(report_finish_date),
|
||||||
user: UserToken = Security(get_user, scopes=["product-sale-report"]),
|
user: UserToken = Security(get_user, scopes=["product-sale-report"]),
|
||||||
):
|
):
|
||||||
with SessionFuture() as db:
|
try:
|
||||||
return {
|
with SessionFuture() as db:
|
||||||
"startDate": start_date.strftime("%d-%b-%Y"),
|
return {
|
||||||
"finishDate": finish_date.strftime("%d-%b-%Y"),
|
"startDate": start_date.strftime("%d-%b-%Y"),
|
||||||
"report": product_updates_report(start_date, finish_date, db),
|
"finishDate": finish_date.strftime("%d-%b-%Y"),
|
||||||
}
|
"report": product_updates_report(start_date, finish_date, db),
|
||||||
|
}
|
||||||
|
except Exception as e:
|
||||||
|
return {"error": str(e)}
|
||||||
|
|
||||||
|
|
||||||
def product_updates_report(start_date: date, finish_date: date, db: Session) -> list[str]:
|
def product_updates_report(start_date: date, finish_date: date, db: Session) -> list[str]:
|
||||||
list_ = (
|
list_ = (
|
||||||
db.execute(
|
db.execute(
|
||||||
select(ProductVersion)
|
select(Product)
|
||||||
.join(ProductVersion.menu_category)
|
.join(Product.versions)
|
||||||
|
.join(ProductVersion.sale_category)
|
||||||
|
.join(Product.skus)
|
||||||
|
.join(StockKeepingUnit.versions)
|
||||||
|
.join(SkuVersion.menu_category)
|
||||||
|
.order_by(MenuCategory.sort_order)
|
||||||
|
.order_by(MenuCategory.name)
|
||||||
|
.order_by(Product.sort_order)
|
||||||
|
.order_by(ProductVersion.name)
|
||||||
|
.order_by(nullsfirst(ProductVersion.valid_from))
|
||||||
|
.order_by(nullsfirst(SkuVersion.valid_from))
|
||||||
.where(
|
.where(
|
||||||
or_(
|
or_(
|
||||||
and_(
|
and_(
|
||||||
@ -44,33 +61,76 @@ def product_updates_report(start_date: date, finish_date: date, db: Session) ->
|
|||||||
ProductVersion.valid_till >= start_date - timedelta(days=1),
|
ProductVersion.valid_till >= start_date - timedelta(days=1),
|
||||||
ProductVersion.valid_till <= finish_date,
|
ProductVersion.valid_till <= finish_date,
|
||||||
),
|
),
|
||||||
|
and_(
|
||||||
|
SkuVersion.valid_from >= start_date,
|
||||||
|
SkuVersion.valid_from <= finish_date,
|
||||||
|
),
|
||||||
|
and_(
|
||||||
|
SkuVersion.valid_till >= start_date - timedelta(days=1),
|
||||||
|
SkuVersion.valid_till <= finish_date,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
.order_by(
|
|
||||||
MenuCategory.sort_order,
|
|
||||||
MenuCategory.name,
|
|
||||||
ProductVersion.sort_order,
|
|
||||||
ProductVersion.name,
|
|
||||||
ProductVersion.valid_from.nullsfirst(),
|
|
||||||
)
|
|
||||||
.options(
|
.options(
|
||||||
contains_eager(ProductVersion.menu_category),
|
contains_eager(Product.versions).contains_eager(ProductVersion.sale_category),
|
||||||
|
contains_eager(Product.skus)
|
||||||
|
.contains_eager(StockKeepingUnit.versions)
|
||||||
|
.contains_eager(SkuVersion.menu_category),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
.unique()
|
||||||
.scalars()
|
.scalars()
|
||||||
.all()
|
.all()
|
||||||
)
|
)
|
||||||
|
|
||||||
report = {}
|
report = {}
|
||||||
for item in list_:
|
for product in list_:
|
||||||
if item.product_id not in report:
|
dates: set[date] = set()
|
||||||
report[item.product_id] = ""
|
for p in product.versions:
|
||||||
report[item.product_id] += (
|
dates.add(p.valid_from or date.min)
|
||||||
"From: "
|
for sk in product.skus:
|
||||||
+ (f"{item.valid_from:%d-%b-%Y}" if item.valid_from is not None else "\u221e")
|
for s in sk.versions:
|
||||||
+ " "
|
dates.add(s.valid_from or date.min)
|
||||||
+ "Till: "
|
sku_versions = [s for sk in product.skus for s in sk.versions]
|
||||||
+ (f"{item.valid_till:%d-%b-%Y}" if item.valid_till is not None else "\u221e")
|
if product.id not in report:
|
||||||
+ "\n"
|
report[product.id] = ""
|
||||||
+ f"{item.full_name} @ {item.price: .2f} - {'Happy Hour' if item.has_happy_hour else 'No Happy Hour'}\n"
|
for d in sorted(dates):
|
||||||
)
|
ap = active_at_p(product.versions, d)
|
||||||
|
ass = active_at_s(sku_versions, d)
|
||||||
|
if ap is None or ass is None:
|
||||||
|
continue
|
||||||
|
v_from: date | None = (
|
||||||
|
max(ap.valid_from, ass.valid_from)
|
||||||
|
if ap.valid_from and ass.valid_from
|
||||||
|
else ap.valid_from or ass.valid_from
|
||||||
|
)
|
||||||
|
v_till: date | None = (
|
||||||
|
min(ap.valid_till, ass.valid_till)
|
||||||
|
if ap.valid_till and ass.valid_till
|
||||||
|
else ap.valid_till or ass.valid_till
|
||||||
|
)
|
||||||
|
report[product.id] += (
|
||||||
|
"From: "
|
||||||
|
+ (f"{v_from:%d-%b-%Y}" if v_from is not None else "\u221e")
|
||||||
|
+ " "
|
||||||
|
+ "Till: "
|
||||||
|
+ (f"{v_till:%d-%b-%Y}" if v_till is not None else "\u221e")
|
||||||
|
+ "\n"
|
||||||
|
+ f"{ap.name} ({ass.units}) @ {ass.sale_price: .2f} - {'Happy Hour' if ass.has_happy_hour else 'No Happy Hour'}\n"
|
||||||
|
)
|
||||||
|
|
||||||
return list(report.values())
|
return list(report.values())
|
||||||
|
|
||||||
|
|
||||||
|
def active_at_p(items: list[ProductVersion], t: date) -> ProductVersion | None:
|
||||||
|
for it in items:
|
||||||
|
if (it.valid_from or date.min) <= t and (it.valid_till is None or t < it.valid_till):
|
||||||
|
return it
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def active_at_s(items: list[SkuVersion], t: date) -> SkuVersion | None:
|
||||||
|
for it in items:
|
||||||
|
if (it.valid_from or date.min) <= t and (it.valid_till is None or t < it.valid_till):
|
||||||
|
return it
|
||||||
|
return None
|
||||||
|
|||||||
@ -51,8 +51,8 @@ class Product(BaseModel):
|
|||||||
|
|
||||||
|
|
||||||
class StockKeepingUnit(BaseModel):
|
class StockKeepingUnit(BaseModel):
|
||||||
id_: uuid.UUID = None
|
id_: uuid.UUID
|
||||||
version_id: uuid.UUID = None
|
version_id: uuid.UUID
|
||||||
units: str = Field(..., min_length=1)
|
units: str = Field(..., min_length=1)
|
||||||
fraction: Daf = Field(ge=Decimal(1), default=Decimal(1))
|
fraction: Daf = Field(ge=Decimal(1), default=Decimal(1))
|
||||||
product_yield: Daf = Field(gt=Decimal(0), le=Decimal(1), default=Decimal(1))
|
product_yield: Daf = Field(gt=Decimal(0), le=Decimal(1), default=Decimal(1))
|
||||||
|
|||||||
@ -98,7 +98,7 @@ export class ProductListComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
updateSortOrder() {
|
updateSortOrder() {
|
||||||
this.ser.updateSortOrder(this.dataSource.filteredData).subscribe({
|
this.ser.updateSortOrder(this.menuCategoryFilter.value, this.dataSource.filteredData).subscribe({
|
||||||
next: (result: Product[]) => {
|
next: (result: Product[]) => {
|
||||||
this.snackBar.open('', 'Success');
|
this.snackBar.open('', 'Success');
|
||||||
this.loadData(result, this.menuCategories);
|
this.loadData(result, this.menuCategories);
|
||||||
|
|||||||
@ -57,9 +57,9 @@ export class ProductService {
|
|||||||
.pipe(catchError(this.log.handleError(serviceName, 'update'))) as Observable<void>;
|
.pipe(catchError(this.log.handleError(serviceName, 'update'))) as Observable<void>;
|
||||||
}
|
}
|
||||||
|
|
||||||
updateSortOrder(list: Product[]): Observable<Product[]> {
|
updateSortOrder(menuCategoryId: string, list: Product[]): Observable<Product[]> {
|
||||||
return this.http
|
return this.http
|
||||||
.post<Product[]>(`${url}/list`, list, httpOptions)
|
.post<Product[]>(`${url}/list/${menuCategoryId}`, list, httpOptions)
|
||||||
.pipe(catchError(this.log.handleError(serviceName, 'updateSortOrder'))) as Observable<Product[]>;
|
.pipe(catchError(this.log.handleError(serviceName, 'updateSortOrder'))) as Observable<Product[]>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user