This commit is contained in:
2026-06-09 21:28:53 +12:00
parent daa6e60a69
commit 349e4a4b5b
61 changed files with 6404 additions and 1382 deletions
+45 -5
View File
@@ -76,6 +76,21 @@ def _fractional_bag_warning(batch_size_kg: float, total_bags: float, unit_of_mea
)
def _mix_calculator_label(product: Product) -> str:
return product.mix.name if product.mix else product.name
def _mix_calculator_option_rank(product: Product) -> tuple[int, int, float, int]:
unit_label = (product.unit_of_measure or "").lower()
unit_size = extract_unit_quantity_kg(product.unit_of_measure)
return (
0 if abs(unit_size - 20) < 1e-9 and "bag" in unit_label and "bulka" not in unit_label else 1,
0 if "bulka" not in unit_label else 1,
unit_size if unit_size > 0 else 999999,
product.id,
)
def calculate_mix_calculator_preview(
db: Session,
*,
@@ -117,12 +132,15 @@ def calculate_mix_calculator_preview(
}
)
mix_label = _mix_calculator_label(product)
return {
"client_name": product.client_name,
"product_id": product.id,
"product_name": product.name,
# The source workbook labels this as Product, but for the calculator
# it is the mix/formula being produced.
"product_name": mix_label,
"mix_id": product.mix_id,
"mix_name": product.mix.name if product.mix else product.name,
"mix_name": mix_label,
"mix_date": values["mix_date"],
"batch_size_kg": round(batch_size_kg, 4),
"total_bags": total_bags,
@@ -156,21 +174,43 @@ def build_mix_calculator_options(db: Session, *, tenant_id: str) -> dict:
).all()
mix_totals: dict[int, float] = {mix_id: round(total or 0.0, 4) for mix_id, total in mix_totals_rows}
product_ids_with_formulas = select(ProductIngredient.product_id).where(ProductIngredient.tenant_id == tenant_id)
products = db.scalars(
select(Product)
.where(Product.tenant_id == tenant_id, Product.visible.is_(True))
.where(
Product.tenant_id == tenant_id,
Product.visible.is_(True),
Product.id.in_(product_ids_with_formulas),
)
.options(joinedload(Product.mix))
.order_by(Product.client_name, Product.name)
).all()
representative_products: dict[tuple[str, str], Product] = {}
for product in products:
mix_label = _mix_calculator_label(product)
key = (product.client_name, mix_label)
current = representative_products.get(key)
if current is None:
representative_products[key] = product
continue
if _mix_calculator_option_rank(product) < _mix_calculator_option_rank(current):
representative_products[key] = product
products = sorted(
representative_products.values(),
key=lambda product: (product.client_name, _mix_calculator_label(product), product.id),
)
clients = sorted({product.client_name for product in products})
product_rows = [
{
"product_id": product.id,
"client_name": product.client_name,
"product_name": product.name,
"product_name": _mix_calculator_label(product),
"mix_id": product.mix_id,
"mix_name": product.mix.name if product.mix else "",
"mix_name": _mix_calculator_label(product),
"unit_of_measure": product.unit_of_measure,
"unit_size_kg": round(extract_unit_quantity_kg(product.unit_of_measure), 4),
"mix_total_kg": product_totals.get(product.id, mix_totals.get(product.mix_id, 0.0)),