Sort order is no longer versioned. Also, data moved is not normalized to add the right skus to the products and coalesce contiguous products and skus with no changes.

This commit is contained in:
2026-01-23 16:20:08 +00:00
parent 1b8ab2857f
commit e4072d0872
8 changed files with 476 additions and 52 deletions

View File

@ -0,0 +1,286 @@
"""combine skus
Revision ID: 1e0daf6bc1ae
Revises: c116517977af
Create Date: 2026-01-23 15:56:01.354574
"""
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision = "1e0daf6bc1ae"
down_revision = "c116517977af"
branch_labels = None
depends_on = None
def upgrade():
op.execute(
sa.text(
"""
-- Merge contiguous identical sku_versions (same business fields) into single rows.
-- 0) Work table: compute vf/vt sentinels + run_id (islands)
CREATE TEMP TABLE tmp_sku_work ON COMMIT DROP AS
WITH base AS (
SELECT
sv.id,
sv.sku_id,
sv.units,
sv.fraction,
sv.product_yield,
sv.cost_price,
sv.sale_price,
sv.has_happy_hour,
sv.menu_category_id,
sv.valid_from,
sv.valid_till,
COALESCE(sv.valid_from, DATE '1900-01-01') AS vf,
COALESCE(sv.valid_till, DATE '9999-12-31') AS vt
FROM sku_versions sv
),
ordered AS (
SELECT
b.*,
LAG(vt) OVER (
PARTITION BY sku_id, units, fraction, product_yield, cost_price, sale_price, has_happy_hour, menu_category_id
ORDER BY vf, vt, id
) AS prev_vt
FROM base b
),
flagged AS (
SELECT
o.*,
CASE
WHEN o.prev_vt IS NULL THEN 1
WHEN o.vf = (o.prev_vt + INTERVAL '1 day')::date THEN 0
ELSE 1
END AS is_new_run
FROM ordered o
),
runs AS (
SELECT
f.*,
SUM(is_new_run) OVER (
PARTITION BY sku_id, units, fraction, product_yield, cost_price, sale_price, has_happy_hour, menu_category_id
ORDER BY vf, vt, id
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
) AS run_id
FROM flagged f
)
SELECT
id,
sku_id, units, fraction, product_yield, cost_price, sale_price, has_happy_hour, menu_category_id,
valid_from, valid_till,
vf, vt,
run_id
FROM runs;
-- 1) Survivors + merged validity (only runs with >1 row)
CREATE TEMP TABLE tmp_sku_merge ON COMMIT DROP AS
WITH run_stats AS (
SELECT
sku_id, units, fraction, product_yield, cost_price, sale_price, has_happy_hour, menu_category_id,
run_id,
MIN(vf) AS min_vf,
MAX(vt) AS max_vt,
COUNT(*) AS cnt
FROM tmp_sku_work
GROUP BY
sku_id, units, fraction, product_yield, cost_price, sale_price, has_happy_hour, menu_category_id, run_id
HAVING COUNT(*) > 1
),
survivors AS (
SELECT DISTINCT ON (
w.sku_id, w.units, w.fraction, w.product_yield, w.cost_price, w.sale_price, w.has_happy_hour, w.menu_category_id, w.run_id
)
w.sku_id, w.units, w.fraction, w.product_yield, w.cost_price, w.sale_price, w.has_happy_hour, w.menu_category_id, w.run_id,
w.id AS survivor_id
FROM tmp_sku_work w
JOIN run_stats s
ON s.sku_id = w.sku_id
AND s.units = w.units
AND s.fraction = w.fraction
AND s.product_yield = w.product_yield
AND s.cost_price = w.cost_price
AND s.sale_price = w.sale_price
AND s.has_happy_hour = w.has_happy_hour
AND s.menu_category_id = w.menu_category_id
AND s.run_id = w.run_id
ORDER BY
w.sku_id, w.units, w.fraction, w.product_yield, w.cost_price, w.sale_price, w.has_happy_hour, w.menu_category_id, w.run_id,
w.vf, w.vt, w.id
)
SELECT
sv.survivor_id,
sv.sku_id, sv.units, sv.fraction, sv.product_yield, sv.cost_price, sv.sale_price, sv.has_happy_hour, sv.menu_category_id, sv.run_id,
CASE WHEN s.min_vf = DATE '1900-01-01' THEN NULL ELSE s.min_vf END AS new_valid_from,
CASE WHEN s.max_vt = DATE '9999-12-31' THEN NULL ELSE s.max_vt END AS new_valid_till
FROM survivors sv
JOIN run_stats s
ON s.sku_id = sv.sku_id
AND s.units = sv.units
AND s.fraction = sv.fraction
AND s.product_yield = sv.product_yield
AND s.cost_price = sv.cost_price
AND s.sale_price = sv.sale_price
AND s.has_happy_hour = sv.has_happy_hour
AND s.menu_category_id = sv.menu_category_id
AND s.run_id = sv.run_id;
-- 2) Rows to delete = everything in a merged run except the survivor
CREATE TEMP TABLE tmp_sku_delete ON COMMIT DROP AS
SELECT w.id
FROM tmp_sku_work w
JOIN tmp_sku_merge m
ON m.sku_id = w.sku_id
AND m.units = w.units
AND m.fraction = w.fraction
AND m.product_yield = w.product_yield
AND m.cost_price = w.cost_price
AND m.sale_price = w.sale_price
AND m.has_happy_hour = w.has_happy_hour
AND m.menu_category_id = w.menu_category_id
AND m.run_id = w.run_id
WHERE w.id <> m.survivor_id;
-- 3) Delete first (avoid exclusion overlap), then update survivor validity
DELETE FROM sku_versions
WHERE id IN (SELECT id FROM tmp_sku_delete);
UPDATE sku_versions sv
SET valid_from = m.new_valid_from,
valid_till = m.new_valid_till
FROM tmp_sku_merge m
WHERE sv.id = m.survivor_id;
"""
)
)
op.execute(
sa.text(
"""
-- Merge contiguous identical product_versions (ignoring fraction_units)
-- 0) Work table: compute vf/vt sentinels + run_id (islands)
CREATE TEMP TABLE tmp_pv_work ON COMMIT DROP AS
WITH base AS (
SELECT
pv.id,
pv.product_id,
pv.name,
pv.valid_from,
pv.valid_till,
COALESCE(pv.valid_from, DATE '1900-01-01') AS vf,
COALESCE(pv.valid_till, DATE '9999-12-31') AS vt
FROM product_versions pv
),
ordered AS (
SELECT
b.*,
LAG(vt) OVER (
PARTITION BY product_id, name
ORDER BY vf, vt, id
) AS prev_vt
FROM base b
),
flagged AS (
SELECT
o.*,
CASE
WHEN o.prev_vt IS NULL THEN 1
WHEN o.vf = (o.prev_vt + INTERVAL '1 day')::date THEN 0
ELSE 1
END AS is_new_run
FROM ordered o
),
runs AS (
SELECT
f.*,
SUM(is_new_run) OVER (
PARTITION BY product_id, name
ORDER BY vf, vt, id
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
) AS run_id
FROM flagged f
)
SELECT
id,
product_id,
name,
valid_from,
valid_till,
vf, vt,
run_id
FROM runs;
-- 1) Survivors + merged validity (only runs with >1 row)
CREATE TEMP TABLE tmp_pv_merge ON COMMIT DROP AS
WITH run_stats AS (
SELECT
product_id,
name,
run_id,
MIN(vf) AS min_vf,
MAX(vt) AS max_vt,
COUNT(*) AS cnt
FROM tmp_pv_work
GROUP BY product_id, name, run_id
HAVING COUNT(*) > 1
),
survivors AS (
-- deterministic survivor: earliest vf, then earliest vt, then lowest id
SELECT DISTINCT ON (w.product_id, w.name, w.run_id)
w.product_id,
w.name,
w.run_id,
w.id AS survivor_id
FROM tmp_pv_work w
JOIN run_stats s
ON s.product_id = w.product_id
AND s.name = w.name
AND s.run_id = w.run_id
ORDER BY w.product_id, w.name, w.run_id, w.vf, w.vt, w.id
)
SELECT
sv.survivor_id,
sv.product_id,
sv.name,
sv.run_id,
CASE WHEN s.min_vf = DATE '1900-01-01' THEN NULL ELSE s.min_vf END AS new_valid_from,
CASE WHEN s.max_vt = DATE '9999-12-31' THEN NULL ELSE s.max_vt END AS new_valid_till
FROM survivors sv
JOIN run_stats s
ON s.product_id = sv.product_id
AND s.name = sv.name
AND s.run_id = sv.run_id;
-- 2) Rows to delete = everything in a merged run except the survivor
CREATE TEMP TABLE tmp_pv_delete ON COMMIT DROP AS
SELECT w.id
FROM tmp_pv_work w
JOIN tmp_pv_merge m
ON m.product_id = w.product_id
AND m.name = w.name
AND m.run_id = w.run_id
WHERE w.id <> m.survivor_id;
-- 3) Delete first (avoid exclusion overlap), then update survivor validity
DELETE FROM product_versions
WHERE id IN (SELECT id FROM tmp_pv_delete);
UPDATE product_versions pv
SET valid_from = m.new_valid_from,
valid_till = m.new_valid_till
FROM tmp_pv_merge m
WHERE pv.id = m.survivor_id;
"""
)
)
def downgrade():
pass

View File

@ -15,6 +15,14 @@ depends_on = None
def upgrade():
# ---------------------------------------------------------------------
# 0) Add NON-VERSIONED sort_order columns (but do NOT remove versioned ones here)
# ---------------------------------------------------------------------
op.add_column(
"products",
sa.Column("sort_order", sa.Integer(), nullable=False, server_default=sa.text("0")),
)
# ---------------------------------------------------------------------
# 1) stock_keeping_units
# ---------------------------------------------------------------------
@ -23,6 +31,7 @@ def upgrade():
sa.Column("id", sa.Uuid(), server_default=sa.text("gen_random_uuid()"), nullable=False),
sa.Column("product_id", sa.Uuid(), nullable=False),
sa.Column("is_not_available", sa.Boolean(), nullable=False, server_default=sa.text("false")),
sa.Column("sort_order", sa.Integer(), nullable=False),
sa.ForeignKeyConstraint(
["product_id"], ["products.id"], name=op.f("fk_stock_keeping_units_product_id_products")
),
@ -42,7 +51,6 @@ def upgrade():
sa.Column("cost_price", sa.Numeric(precision=15, scale=2), nullable=False),
sa.Column("sale_price", sa.Numeric(precision=15, scale=2), nullable=False),
sa.Column("has_happy_hour", sa.Boolean(), nullable=False),
sa.Column("sort_order", sa.Integer(), nullable=False),
sa.Column("menu_category_id", sa.Uuid(), nullable=False),
sa.Column("valid_from", sa.Date(), nullable=True),
sa.Column("valid_till", sa.Date(), nullable=True),
@ -77,15 +85,17 @@ def upgrade():
sa.column("id", postgresql.UUID(as_uuid=True)),
sa.column("product_id", postgresql.UUID(as_uuid=True)),
sa.column("is_not_available", sa.Boolean()),
sa.column("sort_order", sa.Integer()),
)
op.execute(
stock_keeping_units.insert().from_select(
["id", "product_id", "is_not_available"],
["id", "product_id", "is_not_available", "sort_order"],
sa.select(
sa.func.gen_random_uuid(),
products.c.id,
sa.false(),
sa.literal(0),
),
)
)
@ -141,7 +151,8 @@ def upgrade():
sa.text(
"""
UPDATE stock_keeping_units sku
SET is_not_available = pv.is_not_available
SET is_not_available = pv.is_not_available,
sort_order = pv.sort_order
FROM product_versions pv
WHERE pv.product_id = sku.product_id
AND pv.valid_till IS NULL
@ -160,7 +171,6 @@ def upgrade():
"cost_price",
"sale_price",
"has_happy_hour",
"sort_order",
"menu_category_id",
"valid_from",
"valid_till",
@ -168,13 +178,12 @@ def upgrade():
sa.select(
sa.func.gen_random_uuid(),
stock_keeping_units.c.id,
pv.c.units,
sa.func.coalesce(sa.func.nullif(sa.func.btrim(pv.c.units), ""), sa.literal("pc")),
sa.cast(1.0, sa.Numeric(15, 5)),
sa.cast(1.0, sa.Numeric(15, 5)),
sa.cast(0.0, sa.Numeric(15, 2)),
sa.cast(pv.c.price, sa.Numeric(15, 2)),
pv.c.has_happy_hour,
pv.c.sort_order,
pv.c.menu_category_id,
pv.c.valid_from,
pv.c.valid_till,
@ -206,7 +215,7 @@ def upgrade():
# ---------------------------------------------------------------------
op.alter_column("product_versions", "units", new_column_name="fraction_units")
op.drop_constraint(op.f("fk_products_menu_category_id_menu_categories"), "product_versions", type_="foreignkey")
for col in ["price", "quantity", "has_happy_hour", "is_not_available", "menu_category_id"]:
for col in ["price", "quantity", "has_happy_hour", "is_not_available", "menu_category_id", "sort_order"]:
op.drop_column("product_versions", col)

View File

@ -0,0 +1,146 @@
"""normalize sku
Revision ID: c116517977af
Revises: 32c508eed4df
Create Date: 2026-01-23 15:33:39.682395
"""
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision = "c116517977af"
down_revision = "32c508eed4df"
branch_labels = None
depends_on = None
def upgrade():
# ---------------------------------------------------------------------
# 1) Build mapping: old_product_id -> canonical_product_id
# Canonical = product whose CURRENT pv (valid_till IS NULL) has
# latest valid_from (prefer newer). Tie-break: product_id.
# ---------------------------------------------------------------------
op.execute(
sa.text(
"""
CREATE TEMP TABLE tmp_product_canon (
old_product_id uuid PRIMARY KEY,
canonical_product_id uuid NOT NULL
) ON COMMIT DROP;
WITH current_pv AS (
SELECT
pv.product_id,
pv.name,
pv.valid_from
FROM product_versions pv
WHERE pv.valid_till IS NULL
),
canon AS (
SELECT DISTINCT ON (name)
name,
product_id AS canonical_product_id
FROM current_pv
ORDER BY name, valid_from DESC NULLS LAST, product_id
)
INSERT INTO tmp_product_canon (old_product_id, canonical_product_id)
SELECT
cp.product_id AS old_product_id,
c.canonical_product_id
FROM current_pv cp
JOIN canon c ON c.name = cp.name;
"""
)
)
# ---------------------------------------------------------------------
# 2) Move SKUs to canonical product
# ---------------------------------------------------------------------
op.execute(
sa.text(
"""
UPDATE stock_keeping_units sku
SET product_id = m.canonical_product_id
FROM tmp_product_canon m
WHERE sku.product_id = m.old_product_id
AND m.old_product_id <> m.canonical_product_id;
"""
)
)
# ---------------------------------------------------------------------
# 3) Merge modifier_categories_products:
# (A) delete duplicates that would conflict after update
# (B) update remaining rows to canonical_product_id
# ---------------------------------------------------------------------
op.execute(
sa.text(
"""
DELETE FROM modifier_categories_products mcp
USING tmp_product_canon m
WHERE m.old_product_id <> m.canonical_product_id
AND mcp.product_id = m.old_product_id
AND EXISTS (
SELECT 1
FROM modifier_categories_products mcp2
WHERE mcp2.product_id = m.canonical_product_id
AND mcp2.modifier_category_id = mcp.modifier_category_id
);
"""
)
)
op.execute(
sa.text(
"""
UPDATE modifier_categories_products mcp
SET product_id = m.canonical_product_id
FROM tmp_product_canon m
WHERE m.old_product_id <> m.canonical_product_id
AND mcp.product_id = m.old_product_id;
"""
)
)
# ---------------------------------------------------------------------
# 4) Delete duplicate product_versions and products.
# Since you said "newer products mean more than old ones",
# we keep canonical product and delete other products in the group.
#
# IMPORTANT:
# - inventories already point to SKUs, so safe
# - SKUs already moved to canonical product
# - modifier categories already moved
# ---------------------------------------------------------------------
# Delete product_versions for old (non-canonical) products
op.execute(
sa.text(
"""
DELETE FROM product_versions pv
USING tmp_product_canon m
WHERE pv.product_id = m.old_product_id
AND m.old_product_id <> m.canonical_product_id;
"""
)
)
# Delete products for old (non-canonical) products
op.execute(
sa.text(
"""
DELETE FROM products p
USING tmp_product_canon m
WHERE p.id = m.old_product_id
AND m.old_product_id <> m.canonical_product_id;
"""
)
)
def downgrade():
pass

View File

@ -4,7 +4,7 @@ import uuid
from typing import TYPE_CHECKING
from sqlalchemy import Uuid, text
from sqlalchemy import Integer, Uuid, text
from sqlalchemy.orm import Mapped, mapped_column, relationship
from ..db.base_class import reg
@ -21,6 +21,7 @@ if TYPE_CHECKING:
class Product:
__tablename__ = "products"
id: Mapped[uuid.UUID] = mapped_column(Uuid, primary_key=True, server_default=text("gen_random_uuid()"))
sort_order: Mapped[int] = mapped_column(Integer, nullable=False)
versions: Mapped[list[ProductVersion]] = relationship(back_populates="product")
skus: Mapped[list[StockKeepingUnit]] = relationship("StockKeepingUnit", back_populates="product")
modifier_categories: Mapped[list[ModifierCategory]] = relationship(
@ -30,6 +31,7 @@ class Product:
back_populates="products",
)
def __init__(self, id_: uuid.UUID | None = None):
def __init__(self, sort_order: int = 0, id_: uuid.UUID | None = None):
self.sort_order = sort_order
if id_ is not None:
self.id = id_

View File

@ -8,7 +8,6 @@ from typing import TYPE_CHECKING
from sqlalchemy import (
Date,
ForeignKey,
Integer,
Text,
Uuid,
func,
@ -37,7 +36,6 @@ class ProductVersion:
ForeignKey("sale_categories.id"),
nullable=False,
)
sort_order: Mapped[int] = mapped_column(Integer, nullable=False)
valid_from: Mapped[date | None] = mapped_column(Date(), nullable=True)
valid_till: Mapped[date | None] = mapped_column(Date(), nullable=True)
@ -65,7 +63,6 @@ class ProductVersion:
sale_category_id: uuid.UUID | None = None,
valid_from: date | None = None,
valid_till: date | None = None,
sort_order: int = 0,
id_: uuid.UUID | None = None,
):
self.product_id = product_id
@ -75,6 +72,5 @@ class ProductVersion:
self.sale_category_id = sale_category_id
self.valid_from = valid_from
self.valid_till = valid_till
self.sort_order = sort_order
if id_ is not None:
self.id = id_

View File

@ -6,7 +6,7 @@ from datetime import date
from decimal import Decimal
from typing import TYPE_CHECKING
from sqlalchemy import Boolean, Date, ForeignKey, Integer, Numeric, Unicode, Uuid, func, text
from sqlalchemy import Boolean, Date, ForeignKey, Numeric, Unicode, Uuid, func, text
from sqlalchemy.dialects import postgresql
from sqlalchemy.orm import Mapped, mapped_column, relationship
@ -34,7 +34,6 @@ class SkuVersion:
cost_price: Mapped[Decimal] = mapped_column(Numeric(precision=15, scale=2), nullable=False)
sale_price: Mapped[Decimal] = mapped_column(Numeric(precision=15, scale=2), nullable=False)
has_happy_hour: Mapped[bool] = mapped_column(Boolean, nullable=False)
sort_order: Mapped[int] = mapped_column(Integer, nullable=False)
menu_category_id: Mapped[uuid.UUID] = mapped_column(
Uuid,
ForeignKey("menu_categories.id"),
@ -66,7 +65,6 @@ class SkuVersion:
cost_price: Decimal = Decimal("0.0"),
sale_price: Decimal = Decimal("0.0"),
has_happy_hour: bool = False,
sort_order: int = 0,
menu_category_id: uuid.UUID | None = None,
sku_id: uuid.UUID | None = None,
sku: StockKeepingUnit | None = None,
@ -82,7 +80,6 @@ class SkuVersion:
self.cost_price = cost_price
self.sale_price = sale_price
self.has_happy_hour = has_happy_hour
self.sort_order = sort_order
if menu_category_id is not None:
self.menu_category_id = menu_category_id
if id_ is not None:

View File

@ -4,7 +4,7 @@ import uuid
from typing import TYPE_CHECKING
from sqlalchemy import Boolean, ForeignKey, Uuid, text
from sqlalchemy import Boolean, ForeignKey, Integer, Uuid, text
from sqlalchemy.orm import Mapped, mapped_column, relationship
from ..db.base_class import reg
@ -25,6 +25,7 @@ class StockKeepingUnit:
)
product_id: Mapped[uuid.UUID] = mapped_column(Uuid, ForeignKey("products.id"), nullable=False)
is_not_available: Mapped[bool] = mapped_column(Boolean, nullable=False)
sort_order: Mapped[int] = mapped_column(Integer, nullable=False)
product: Mapped[Product] = relationship("Product", back_populates="skus")
inventories: Mapped[list[Inventory]] = relationship(back_populates="sku")
@ -33,11 +34,13 @@ class StockKeepingUnit:
def __init__(
self,
is_not_available: bool = False,
sort_order: int = 0,
product_id: uuid.UUID | None = None,
product: Product | None = None,
id_: uuid.UUID | None = None,
) -> None:
self.is_not_available = is_not_available
self.sort_order = sort_order
if product_id is not None:
self.product_id = product_id
if id_ is not None:

View File

@ -83,7 +83,7 @@ def save(
) -> None:
try:
with SessionFuture() as db:
product = Product()
product = Product(sort_order=data.sort_order)
db.add(product)
db.flush() # so product.id is available
version = ProductVersion(
@ -91,7 +91,6 @@ def save(
name=data.name,
fraction_units=data.fraction_units,
sale_category_id=data.sale_category.id_,
sort_order=data.sort_order,
valid_from=date_,
valid_till=None,
)
@ -216,13 +215,13 @@ def update_route(
or version.fraction_units != data.fraction_units
or version.sale_category_id != data.sale_category.id_
)
version.product.sort_order = data.sort_order
if pv_changed:
if version.valid_from == date_:
# Safe to mutate current version
version.name = data.name
version.fraction_units = data.fraction_units
version.sale_category_id = data.sale_category.id_
version.sort_order = data.sort_order
else:
# Close current and create a new version effective today
version.valid_till = date_ - timedelta(days=1)
@ -231,7 +230,6 @@ def update_route(
name=data.name,
fraction_units=data.fraction_units,
sale_category_id=data.sale_category.id_,
sort_order=data.sort_order,
valid_from=date_,
valid_till=None,
)
@ -251,7 +249,7 @@ def update_route(
contains_eager(SkuVersion.menu_category),
)
.where(StockKeepingUnit.product_id == id_)
.order_by(SkuVersion.sort_order, SkuVersion.units)
.order_by(StockKeepingUnit.sort_order, SkuVersion.units)
)
.scalars()
.all()
@ -274,6 +272,8 @@ def update_route(
or new_data_sku.menu_category.id_ != sku.menu_category_id
)
print(f"SKU Changed: {sku_changed} for {sku.id}")
sku.sku.sort_order = new_data_sku.sort_order
sku.sku.is_not_available = new_data_sku.is_not_available
if sku_changed:
if sku.valid_from == date_:
# Safe to mutate current version
@ -295,7 +295,6 @@ def update_route(
cost_price=round(new_data_sku.cost_price, 2),
sale_price=round(new_data_sku.sale_price, 2),
has_happy_hour=new_data_sku.has_happy_hour,
sort_order=sku.sort_order,
menu_category_id=new_data_sku.menu_category.id_,
)
db.add(new_sku)
@ -431,9 +430,9 @@ def product_list(date_: date, db: Session) -> list[schemas.Product]:
.order_by(
MenuCategory.sort_order,
MenuCategory.name,
ProductVersion.sort_order,
Product.sort_order,
ProductVersion.name,
SkuVersion.sort_order,
StockKeepingUnit.sort_order,
SkuVersion.units,
)
.options(
@ -507,10 +506,16 @@ def show_term(
if sc is not None:
query = query.join(SkuVersion.menu_category).join(ProductVersion.sale_category).join(SaleCategory.tax)
if mc is not None:
query = query.where(SkuVersion.menu_category_id == mc).order_by(ProductVersion.sort_order, ProductVersion.name)
query = query.where(SkuVersion.menu_category_id == mc).order_by(
Product.sort_order, ProductVersion.name, StockKeepingUnit.sort_order, SkuVersion.units
)
if sc is not None:
query = query.where(ProductVersion.sale_category_id == sc).order_by(
MenuCategory.sort_order, ProductVersion.sort_order, ProductVersion.name
MenuCategory.sort_order,
Product.sort_order,
ProductVersion.name,
StockKeepingUnit.sort_order,
SkuVersion.units,
)
if mc is not None:
@ -605,7 +610,7 @@ def show_id(
.join(SkuVersion.menu_category)
.where(pv_active)
.order_by(
SkuVersion.sort_order,
StockKeepingUnit.sort_order,
SkuVersion.units,
)
.options(
@ -639,7 +644,7 @@ def query_product_info(item: SkuVersion, happy_hour: bool) -> ProductQuery:
price=item.sale_price,
has_happy_hour=happy_hour,
is_not_available=item.sku.is_not_available,
sort_order=item.sort_order,
sort_order=item.sku.sort_order,
)
@ -666,12 +671,12 @@ def product_info(version: ProductVersion) -> schemas.Product:
menu_category=MenuCategoryLink(
id_=sku_version.menu_category_id, name=sku_version.menu_category.name, products=[]
),
sort_order=sku_version.sort_order,
sort_order=sku_version.sku.sort_order,
)
for sku in version.product.skus
for sku_version in sku.versions
],
sort_order=version.sort_order,
sort_order=version.product.sort_order,
)
@ -683,23 +688,3 @@ def product_blank() -> schemas.ProductBlank:
sort_order=0,
enabled=True,
)
# def product_info(item: ProductVersion) -> schemas.Product:
# return schemas.Product(
# id_=item.product_id,
# units=item.units,
# sale_category=schemas.SaleCategoryLink(
# id_=item.sale_category_id,
# name=item.sale_category.name,
# ),
# price=item.price,
# has_happy_hour=item.has_happy_hour,
# is_not_available=item.is_not_available,
# quantity=item.quantity,
# is_active=True,
# sort_order=item.sort_order,
# valid_from=item.valid_from,
# valid_till=item.valid_till,
# )