Added the funtionality to update product name and units when importing sales.

This commit is contained in:
2026-09-12 14:46:09 +00:00
parent b425b299bf
commit 7be26ad9da
2 changed files with 98 additions and 17 deletions
@@ -16,7 +16,7 @@ from alembic import op
# revision identifiers, used by Alembic.
revision = "51013f52f0fb"
down_revision = "9b4f7c2e81a3"
down_revision = "c7e1a94b2d30"
branch_labels = None
depends_on = None
@@ -3,7 +3,7 @@
The mapping, once made, is standing truth: a Barker SKU whose mapping row exists is served
from it verbatim, forever. The slugified product name is only the resolver for a Barker
product never mapped before. Barker renames and units changes after first mapping are
ignored brewman master data is edited by hand when that matters.
captured by creating a new product version or SKU version.
Resolution for one Barker line on business date ``date_``:
@@ -27,7 +27,7 @@ import re
import uuid
from dataclasses import dataclass
from datetime import date
from datetime import date, timedelta
from decimal import Decimal
from typing import Any, Literal
@@ -87,6 +87,8 @@ def resolve_product(db: Session, line: BarkerSaleLine, date_: date) -> Provision
"""
binding = db.execute(select(BarkerProduct).where(BarkerProduct.barker_sku_id == line.sku_id)).scalar_one_or_none()
if binding is not None:
_update_name_if_needed(db, binding.brewman_product_id, line.name, date_)
_update_units_if_needed(db, binding.brewman_sku_id, line.units, date_)
return ProvisionedProduct(
product_id=binding.brewman_product_id, sku_id=binding.brewman_sku_id, disposition="mapped"
)
@@ -100,7 +102,9 @@ def resolve_product(db: Session, line: BarkerSaleLine, date_: date) -> Provision
.first()
)
if known_product_id is not None:
_update_name_if_needed(db, known_product_id, line.name, date_)
sku_id = _find_or_create_sku(db, known_product_id, line, date_)
_update_units_if_needed(db, sku_id, line.units, date_)
_write_mapping(db, line, known_product_id, sku_id)
return ProvisionedProduct(product_id=known_product_id, sku_id=sku_id, disposition="linked")
@@ -109,7 +113,10 @@ def resolve_product(db: Session, line: BarkerSaleLine, date_: date) -> Provision
if product_id is None:
product_id = _create_product(db, line, date_)
disposition = "created"
else:
_update_name_if_needed(db, product_id, line.name, date_)
sku_id = _find_or_create_sku(db, product_id, line, date_)
_update_units_if_needed(db, sku_id, line.units, date_)
_write_mapping(db, line, product_id, sku_id)
return ProvisionedProduct(product_id=product_id, sku_id=sku_id, disposition=disposition)
@@ -178,24 +185,20 @@ def _find_product_id_by_name(db: Session, name: str, date_: date) -> uuid.UUID |
"""
wanted = _normalize(name)
normalized_name = _pg_normalize(ProductVersion.name)
rows = (
db.execute(
select(ProductVersion.product_id, ProductVersion.name).where(
normalized_name == wanted,
(ProductVersion.valid_from == None) # noqa: E711
| (ProductVersion.valid_from <= date_),
(ProductVersion.valid_till == None) # noqa: E711
| (ProductVersion.valid_till >= date_),
)
rows = db.execute(
select(ProductVersion.product_id, ProductVersion.name).where(
normalized_name == wanted,
(ProductVersion.valid_from == None) # noqa: E711
| (ProductVersion.valid_from <= date_),
(ProductVersion.valid_till == None) # noqa: E711
| (ProductVersion.valid_till >= date_),
)
.scalars()
.all()
)
if rows.count > 1:
).all()
if len(rows) > 1:
raise HTTPException(
status_code=status.HTTP_409_CONFLICT,
detail=f"Cannot import: '{name}' matches multiple products: "
+ ", ".join(f"'{name}'" for (id_, name) in rows),
+ ", ".join(f"'{p_name}'" for (id_, p_name) in rows),
)
return rows[0][0] if rows else None
@@ -323,3 +326,81 @@ def _write_mapping(db: Session, line: BarkerSaleLine, product_id: uuid.UUID, sku
row.brewman_product_id = product_id
row.brewman_sku_id = sku_id
db.flush() # autoflush is off — later lookups must see this row
def _update_name_if_needed(db: Session, product_id: uuid.UUID, new_name: str, date_: date) -> None:
wanted = _normalize(new_name)
active_pv = db.execute(
select(ProductVersion).where(
ProductVersion.product_id == product_id,
(ProductVersion.valid_from == None) | (ProductVersion.valid_from <= date_), # noqa: E711
(ProductVersion.valid_till == None) | (ProductVersion.valid_till >= date_), # noqa: E711
)
).scalar_one_or_none()
if active_pv is not None and _normalize(active_pv.name) != wanted:
if active_pv.valid_from == date_:
active_pv.name = new_name
active_pv.handle = ProductVersion.slugify(new_name)
db.flush()
else:
active_pv.valid_till = date_ - timedelta(days=1)
new_pv = ProductVersion(
product_id=active_pv.product_id,
name=new_name,
description=active_pv.description,
handle=ProductVersion.slugify(new_name),
fraction_units=active_pv.fraction_units,
product_group_id=active_pv.product_group_id,
account_id=active_pv.account_id,
is_purchased=active_pv.is_purchased,
is_sold=active_pv.is_sold,
allergen=active_pv.allergen,
protein=active_pv.protein,
carbohydrate=active_pv.carbohydrate,
total_sugar=active_pv.total_sugar,
added_sugar=active_pv.added_sugar,
total_fat=active_pv.total_fat,
saturated_fat=active_pv.saturated_fat,
trans_fat=active_pv.trans_fat,
cholestrol=active_pv.cholestrol,
sodium=active_pv.sodium,
msnf=active_pv.msnf,
other_solids=active_pv.other_solids,
total_solids=active_pv.total_solids,
water=active_pv.water,
valid_from=date_,
valid_till=None,
)
db.add(new_pv)
db.flush()
def _update_units_if_needed(db: Session, sku_id: uuid.UUID, new_units: str, date_: date) -> None:
wanted = _normalize(new_units)
active_sv = db.execute(
select(SkuVersion).where(
SkuVersion.sku_id == sku_id,
(SkuVersion.valid_from == None) | (SkuVersion.valid_from <= date_), # noqa: E711
(SkuVersion.valid_till == None) | (SkuVersion.valid_till >= date_), # noqa: E711
)
).scalar_one_or_none()
if active_sv is not None and _normalize(active_sv.units) != wanted:
if active_sv.valid_from == date_:
active_sv.units = new_units
db.flush()
else:
active_sv.valid_till = date_ - timedelta(days=1)
new_sv = SkuVersion(
sku_id=active_sv.sku_id,
units=new_units,
fraction=active_sv.fraction,
product_yield=active_sv.product_yield,
cost_price=active_sv.cost_price,
sale_price=active_sv.sale_price,
valid_from=date_,
valid_till=None,
)
db.add(new_sv)
db.flush()