Fix: Due to matching of timestamp vs date, on the day that a product valid_till expired,

comparison with voucher.date would wrongly fail.
This commit is contained in:
2025-07-17 03:28:22 +00:00
parent 01b0cbfadf
commit 2fbfe96c40
9 changed files with 58 additions and 45 deletions

View File

@ -4,7 +4,7 @@ from datetime import date, timedelta
from decimal import Decimal
from fastapi import APIRouter, Depends, HTTPException, Security, status
from sqlalchemy import and_, insert, or_, select, update
from sqlalchemy import Date, and_, insert, or_, select, update
from sqlalchemy.exc import SQLAlchemyError
from sqlalchemy.orm import Session, contains_eager
from sqlalchemy.sql.functions import count, func
@ -250,8 +250,8 @@ def delete_route(
)
)
).scalar_one()
day = func.date_trunc(
"day", Voucher.date + timedelta(minutes=settings.NEW_DAY_OFFSET_MINUTES - settings.TIMEZONE_OFFSET_MINUTES)
day = func.cast(
Voucher.date + timedelta(minutes=settings.TIMEZONE_OFFSET_MINUTES - settings.NEW_DAY_OFFSET_MINUTES), Date
).label("day")
billed = db.execute(
select(count(Inventory.id))

View File

@ -2,7 +2,7 @@ from datetime import date, timedelta
from operator import or_
from fastapi import APIRouter, Depends, Security
from sqlalchemy import and_
from sqlalchemy import Date, and_
from sqlalchemy.sql.expression import func, select
from ...core.config import settings
@ -33,19 +33,19 @@ def beer_consumption(
user: UserToken = Security(get_user, scopes=["beer-sale-report"]),
) -> BeerConsumptionReport:
check_audit_permission(start_date, user.permissions)
day = func.date_trunc(
"day", Voucher.date + timedelta(minutes=settings.NEW_DAY_OFFSET_MINUTES - settings.TIMEZONE_OFFSET_MINUTES)
day = func.cast(
Voucher.date + timedelta(minutes=settings.TIMEZONE_OFFSET_MINUTES - settings.NEW_DAY_OFFSET_MINUTES), Date
).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,
ProductVersion.valid_from <= day,
),
or_(
ProductVersion.valid_till == None, # noqa: E711
ProductVersion.valid_till >= Voucher.date,
ProductVersion.valid_till >= day,
),
)
query = (

View File

@ -3,7 +3,7 @@ import uuid
from datetime import date, timedelta
from fastapi import APIRouter, Cookie, Depends, Security
from sqlalchemy import and_, func, or_, select
from sqlalchemy import Date, and_, func, or_, select
from sqlalchemy.orm import Session
from ...core.config import settings
@ -41,19 +41,19 @@ def discount_report(
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)
day = func.cast(
Voucher.date + timedelta(minutes=settings.TIMEZONE_OFFSET_MINUTES - settings.NEW_DAY_OFFSET_MINUTES), Date
).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,
ProductVersion.valid_from <= day,
),
or_(
ProductVersion.valid_till == None, # noqa: E711
ProductVersion.valid_till >= Voucher.date,
ProductVersion.valid_till >= day,
),
)
list_ = db.execute(

View File

@ -4,7 +4,7 @@ from datetime import date, timedelta
from decimal import Decimal
from fastapi import APIRouter, Cookie, Depends, Security
from sqlalchemy import and_, func, nulls_last, or_, select
from sqlalchemy import Date, and_, func, nulls_last, or_, select
from sqlalchemy.orm import Session
from ...core.config import settings
@ -44,18 +44,18 @@ def menu_engineering_report_view(
def menu_engineering_report(start_date: date, finish_date: date, db: Session) -> list[MeItem]:
day = func.date_trunc(
"day", Voucher.date + timedelta(minutes=settings.NEW_DAY_OFFSET_MINUTES - settings.TIMEZONE_OFFSET_MINUTES)
day = func.cast(
Voucher.date + timedelta(minutes=settings.TIMEZONE_OFFSET_MINUTES - settings.NEW_DAY_OFFSET_MINUTES), Date
).label("day")
product_version_onclause = and_(
ProductVersion.product_id == Product.id,
or_(
ProductVersion.valid_from == None, # noqa: E711
ProductVersion.valid_from <= start_date,
ProductVersion.valid_from <= day,
),
or_(
ProductVersion.valid_till == None, # noqa: E711
ProductVersion.valid_till >= finish_date,
ProductVersion.valid_till >= day,
),
)
list_ = db.execute(

View File

@ -4,7 +4,7 @@ from datetime import date, timedelta
from typing import Annotated
from fastapi import APIRouter, Cookie, Depends, Query, Security
from sqlalchemy import and_, func, or_, select
from sqlalchemy import Date, and_, func, or_, select
from sqlalchemy.orm import Session
from ...core.config import settings
@ -49,18 +49,18 @@ def product_sale_report_view(
def product_sale_report(
start_date: date, finish_date: date, id_: uuid.UUID | None, db: Session
) -> list[ProductSaleReportItem]:
day = func.date_trunc(
"day", Voucher.date + timedelta(minutes=settings.NEW_DAY_OFFSET_MINUTES - settings.TIMEZONE_OFFSET_MINUTES)
day = func.cast(
Voucher.date + timedelta(minutes=settings.TIMEZONE_OFFSET_MINUTES - settings.NEW_DAY_OFFSET_MINUTES), Date
).label("day")
product_version_onclause = and_(
ProductVersion.product_id == Product.id,
or_(
ProductVersion.valid_from == None, # noqa: E711
ProductVersion.valid_from <= Voucher.date,
ProductVersion.valid_from <= day,
),
or_(
ProductVersion.valid_till == None, # noqa: E711
ProductVersion.valid_till >= Voucher.date,
ProductVersion.valid_till >= day,
),
)
query = (

View File

@ -5,7 +5,7 @@ from decimal import Decimal
from typing import Annotated
from fastapi import APIRouter, Cookie, Depends, Query, Security
from sqlalchemy import and_, func, or_, select
from sqlalchemy import Date, and_, func, or_, select
from sqlalchemy.orm import Session
from ...core.config import settings
@ -57,18 +57,18 @@ def get_sale_report(
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)
day = func.cast(
Voucher.date + timedelta(minutes=settings.TIMEZONE_OFFSET_MINUTES - settings.NEW_DAY_OFFSET_MINUTES), Date
).label("day")
product_version_onclause = and_(
ProductVersion.product_id == Product.id,
or_(
ProductVersion.valid_from == None, # noqa: E711
ProductVersion.valid_from <= Voucher.date,
ProductVersion.valid_from <= day,
),
or_(
ProductVersion.valid_till == None, # noqa: E711
ProductVersion.valid_till >= Voucher.date,
ProductVersion.valid_till >= day,
),
)
query = (
@ -89,6 +89,7 @@ def get_sale(start_date: date, finish_date: date, id_: uuid.UUID | None, db: Ses
if id_:
query = query.where(FoodTable.section_id == id_)
query = query.group_by(SaleCategory.name).order_by(SaleCategory.name)
print(query)
list_ = db.execute(query).all()
total = Decimal(0)
info = []
@ -99,8 +100,8 @@ def get_sale(start_date: date, finish_date: date, id_: uuid.UUID | None, db: Ses
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)
day = func.cast(
Voucher.date + timedelta(minutes=settings.TIMEZONE_OFFSET_MINUTES - settings.NEW_DAY_OFFSET_MINUTES), Date
).label("day")
query = (
select(SettleOption.name, func.sum(Settlement.amount))

View File

@ -3,7 +3,7 @@ import uuid
from datetime import date, timedelta
from fastapi import APIRouter, HTTPException, Security, status
from sqlalchemy import and_, delete, distinct, or_, select, update
from sqlalchemy import Date, and_, delete, distinct, or_, select, update
from sqlalchemy.exc import SQLAlchemyError
from sqlalchemy.orm import Session, contains_eager
from sqlalchemy.sql.functions import count, func
@ -96,8 +96,8 @@ def check_product(old: ProductVersion, data: schemas.Product, db: Session) -> No
def check_inventories(old: ProductVersion, data: schemas.Product, db: Session) -> None:
day = func.date_trunc(
"day", Voucher.date + timedelta(minutes=settings.NEW_DAY_OFFSET_MINUTES - settings.TIMEZONE_OFFSET_MINUTES)
day = func.cast(
Voucher.date + timedelta(minutes=settings.TIMEZONE_OFFSET_MINUTES - settings.NEW_DAY_OFFSET_MINUTES), Date
).label("day")
if data.valid_from is not None and (old.valid_from or date.min) < data.valid_from:
@ -125,8 +125,8 @@ def check_inventories(old: ProductVersion, data: schemas.Product, db: Session) -
def update_inventories(old: ProductVersion, data: schemas.Product, db: Session) -> None:
if old.product_id != data.id_:
day = func.date_trunc(
"day", Voucher.date + timedelta(minutes=settings.NEW_DAY_OFFSET_MINUTES - settings.TIMEZONE_OFFSET_MINUTES)
day = func.cast(
Voucher.date + timedelta(minutes=settings.TIMEZONE_OFFSET_MINUTES - settings.NEW_DAY_OFFSET_MINUTES), Date
).label("day")
invs = select(Inventory.id).join(Inventory.kot).join(Kot.voucher).where(Inventory.product_id == old.product_id)
if old.valid_from is not None:
@ -153,8 +153,8 @@ def delete_route(
valid_from, valid_till = db.execute(
select(ProductVersion.valid_from, ProductVersion.valid_till).where(ProductVersion.id == version_id)
).one()
day = func.date_trunc(
"day", Voucher.date + timedelta(minutes=settings.NEW_DAY_OFFSET_MINUTES - settings.TIMEZONE_OFFSET_MINUTES)
day = func.cast(
Voucher.date + timedelta(minutes=settings.TIMEZONE_OFFSET_MINUTES - settings.NEW_DAY_OFFSET_MINUTES), Date
).label("day")
query = select(count(Inventory.id)).join(Inventory.kot).join(Kot.voucher).where(Inventory.product_id == id_)
if valid_from is not None:

View File

@ -1,10 +1,13 @@
import re
import uuid
from datetime import timedelta
from fastapi import APIRouter, HTTPException, Security, status
from sqlalchemy import and_, or_, select
from sqlalchemy import Date, and_, func, or_, select
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
@ -48,15 +51,18 @@ def from_id(
user: UserToken = Security(get_user),
) -> VoucherOut:
with SessionFuture() as db:
day = func.cast(
Voucher.date + timedelta(minutes=settings.TIMEZONE_OFFSET_MINUTES - settings.NEW_DAY_OFFSET_MINUTES), Date
).label("day")
product_version_onclause = and_(
ProductVersion.product_id == Product.id,
or_(
ProductVersion.valid_from == None, # noqa: E711
ProductVersion.valid_from <= Voucher.date,
ProductVersion.valid_from <= day,
),
or_(
ProductVersion.valid_till == None, # noqa: E711
ProductVersion.valid_till >= Voucher.date,
ProductVersion.valid_till >= day,
),
)
item: Voucher = (
@ -91,15 +97,18 @@ def from_bill(
user: UserToken = Security(get_user),
) -> VoucherOut:
with SessionFuture() as db:
day = func.cast(
Voucher.date + timedelta(minutes=settings.TIMEZONE_OFFSET_MINUTES - settings.NEW_DAY_OFFSET_MINUTES), Date
).label("day")
product_version_onclause = and_(
ProductVersion.product_id == Product.id,
or_(
ProductVersion.valid_from == None, # noqa: E711
ProductVersion.valid_from <= Voucher.date,
ProductVersion.valid_from <= day,
),
or_(
ProductVersion.valid_till == None, # noqa: E711
ProductVersion.valid_till >= Voucher.date,
ProductVersion.valid_till >= day,
),
)
match = re.compile(r"^(\w+)-(\d+)$").match(id_)
@ -150,15 +159,18 @@ def from_table(
user: UserToken = Security(get_user),
) -> VoucherOut | VoucherBlank:
with SessionFuture() as db:
day = func.cast(
Voucher.date + timedelta(minutes=settings.TIMEZONE_OFFSET_MINUTES - settings.NEW_DAY_OFFSET_MINUTES), Date
).label("day")
product_version_onclause = and_(
ProductVersion.product_id == Product.id,
or_(
ProductVersion.valid_from == None, # noqa: E711
ProductVersion.valid_from <= Voucher.date,
ProductVersion.valid_from <= day,
),
or_(
ProductVersion.valid_till == None, # noqa: E711
ProductVersion.valid_till >= Voucher.date,
ProductVersion.valid_till >= day,
),
)
guest = None if g is None else db.execute(select(GuestBook).where(GuestBook.id == g)).scalar_one()

View File

@ -13,8 +13,8 @@
<mat-icon matSuffix (click)="hide = !hide">{{ hide ? 'visibility' : 'visibility_off' }}</mat-icon>
</mat-form-field>
</div>
<mat-divider></mat-divider>
@if (unregisteredDevice) {
<mat-divider></mat-divider>
<h2>Sorry, device {{ deviceName }} is not enabled.</h2>
}
</form>