From 5f7a9fc6004ffa7da1ac3fb66e17de14a93fa9ec Mon Sep 17 00:00:00 2001
From: imperosol
Date: Thu, 5 Mar 2026 19:07:42 +0100
Subject: [PATCH] Price formset in the Product edit page
---
core/api.py | 2 +-
counter/forms.py | 50 ++++++++---
counter/templates/counter/product_form.jinja | 92 +++++++++++++++++++-
3 files changed, 126 insertions(+), 18 deletions(-)
diff --git a/core/api.py b/core/api.py
index 2f2c0fb1..08aefa6f 100644
--- a/core/api.py
+++ b/core/api.py
@@ -123,7 +123,7 @@ class GroupController(ControllerBase):
)
@paginate(PageNumberPaginationExtra, page_size=50)
def search_group(self, search: Annotated[str, MinLen(1)]):
- return Group.objects.filter(name__icontains=search).values()
+ return Group.objects.filter(name__icontains=search).order_by("name").values()
DepthValue = Annotated[int, Ge(0), Le(10)]
diff --git a/counter/forms.py b/counter/forms.py
index a0ba39e5..86862e10 100644
--- a/counter/forms.py
+++ b/counter/forms.py
@@ -1,6 +1,7 @@
import json
import math
import uuid
+from collections import defaultdict
from datetime import date, datetime, timezone
from dateutil.relativedelta import relativedelta
@@ -35,6 +36,7 @@ from counter.models import (
Customer,
Eticket,
InvoiceCall,
+ Price,
Product,
ProductFormula,
Refilling,
@@ -292,7 +294,21 @@ ScheduledProductActionFormSet = forms.modelformset_factory(
can_delete=True,
can_delete_extra=False,
extra=0,
+)
+
+
+ProductPriceFormSet = forms.inlineformset_factory(
+ parent_model=Product,
+ model=Price,
+ fields=["amount", "label", "groups", "is_always_shown"],
+ widgets={
+ "groups": AutoCompleteSelectMultipleGroup,
+ "is_always_shown": forms.CheckboxInput(attrs={"class": "switch"}),
+ },
+ absolute_max=None,
+ can_delete_extra=False,
min_num=1,
+ extra=0,
)
@@ -307,10 +323,7 @@ class ProductForm(forms.ModelForm):
"description",
"product_type",
"code",
- "buying_groups",
"purchase_price",
- "selling_price",
- "special_selling_price",
"icon",
"club",
"limit_age",
@@ -325,8 +338,8 @@ class ProductForm(forms.ModelForm):
}
widgets = {
"product_type": AutoCompleteSelect,
- "buying_groups": AutoCompleteSelectMultipleGroup,
"club": AutoCompleteSelectClub,
+ "tray": forms.CheckboxInput(attrs={"class": "switch"}),
}
counters = forms.ModelMultipleChoiceField(
@@ -336,14 +349,18 @@ class ProductForm(forms.ModelForm):
queryset=Counter.objects.all(),
)
- def __init__(self, *args, instance=None, **kwargs):
- super().__init__(*args, instance=instance, **kwargs)
+ def __init__(self, *args, prefix: str | None = None, instance=None, **kwargs):
+ super().__init__(*args, prefix=prefix, instance=instance, **kwargs)
+ self.fields["name"].widget.attrs["autofocus"] = "autofocus"
if self.instance.id:
self.fields["counters"].initial = self.instance.counters.all()
if hasattr(self.instance, "formula"):
self.formula_init(self.instance.formula)
+ self.price_formset = ProductPriceFormSet(
+ *args, instance=self.instance, prefix="price", **kwargs
+ )
self.action_formset = ScheduledProductActionFormSet(
- *args, product=self.instance, **kwargs
+ *args, product=self.instance, prefix="action", **kwargs
)
def formula_init(self, formula: ProductFormula):
@@ -366,20 +383,25 @@ class ProductForm(forms.ModelForm):
self.fields[key].validators.append(MaxValueValidator(price))
def is_valid(self):
- return super().is_valid() and self.action_formset.is_valid()
+ return (
+ super().is_valid()
+ and self.price_formset.is_valid()
+ and self.action_formset.is_valid()
+ )
def save(self, *args, **kwargs) -> Product:
product = super().save(*args, **kwargs)
product.counters.set(self.cleaned_data["counters"])
+ # if it's a creation, the product given in the formset
+ # wasn't a persisted instance.
+ # So if we tried to persist the related objects in the current state,
+ # they would be linked to no product, thus be completely useless
+ # To make it work, we have to replace
+ # the initial product with a persisted one
for form in self.action_formset:
- # if it's a creation, the product given in the formset
- # wasn't a persisted instance.
- # So if we tried to persist the scheduled actions in the current state,
- # they would be linked to no product, thus be completely useless
- # To make it work, we have to replace
- # the initial product with a persisted one
form.set_product(product)
self.action_formset.save()
+ self.price_formset.save()
return product
diff --git a/counter/templates/counter/product_form.jinja b/counter/templates/counter/product_form.jinja
index ffd751c0..190670ad 100644
--- a/counter/templates/counter/product_form.jinja
+++ b/counter/templates/counter/product_form.jinja
@@ -39,6 +39,44 @@
{% endmacro %}
+{% macro price_form(form) %}
+
+
+{% endmacro %}
+
{% block content %}
{% if object %}
{% trans name=object %}Edit product {{ name }}{% endtrans %}
@@ -49,7 +87,54 @@
{% endif %}
-
+
{{ form.action_formset.management_form }}
{%- for f in form.action_formset.forms -%}
@@ -78,6 +163,7 @@
{% trans %}Add action{% endtrans %}
+
{{ form.archived.as_field_group() }}
-{% endblock %}
\ No newline at end of file
+{% endblock %}