mirror of
https://github.com/ae-utbm/sith.git
synced 2026-03-13 15:15:03 +00:00
Price formset in the Product edit page
This commit is contained in:
@@ -123,7 +123,7 @@ class GroupController(ControllerBase):
|
|||||||
)
|
)
|
||||||
@paginate(PageNumberPaginationExtra, page_size=50)
|
@paginate(PageNumberPaginationExtra, page_size=50)
|
||||||
def search_group(self, search: Annotated[str, MinLen(1)]):
|
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)]
|
DepthValue = Annotated[int, Ge(0), Le(10)]
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import json
|
import json
|
||||||
import math
|
import math
|
||||||
import uuid
|
import uuid
|
||||||
|
from collections import defaultdict
|
||||||
from datetime import date, datetime, timezone
|
from datetime import date, datetime, timezone
|
||||||
|
|
||||||
from dateutil.relativedelta import relativedelta
|
from dateutil.relativedelta import relativedelta
|
||||||
@@ -35,6 +36,7 @@ from counter.models import (
|
|||||||
Customer,
|
Customer,
|
||||||
Eticket,
|
Eticket,
|
||||||
InvoiceCall,
|
InvoiceCall,
|
||||||
|
Price,
|
||||||
Product,
|
Product,
|
||||||
ProductFormula,
|
ProductFormula,
|
||||||
Refilling,
|
Refilling,
|
||||||
@@ -292,7 +294,21 @@ ScheduledProductActionFormSet = forms.modelformset_factory(
|
|||||||
can_delete=True,
|
can_delete=True,
|
||||||
can_delete_extra=False,
|
can_delete_extra=False,
|
||||||
extra=0,
|
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,
|
min_num=1,
|
||||||
|
extra=0,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -307,10 +323,7 @@ class ProductForm(forms.ModelForm):
|
|||||||
"description",
|
"description",
|
||||||
"product_type",
|
"product_type",
|
||||||
"code",
|
"code",
|
||||||
"buying_groups",
|
|
||||||
"purchase_price",
|
"purchase_price",
|
||||||
"selling_price",
|
|
||||||
"special_selling_price",
|
|
||||||
"icon",
|
"icon",
|
||||||
"club",
|
"club",
|
||||||
"limit_age",
|
"limit_age",
|
||||||
@@ -325,8 +338,8 @@ class ProductForm(forms.ModelForm):
|
|||||||
}
|
}
|
||||||
widgets = {
|
widgets = {
|
||||||
"product_type": AutoCompleteSelect,
|
"product_type": AutoCompleteSelect,
|
||||||
"buying_groups": AutoCompleteSelectMultipleGroup,
|
|
||||||
"club": AutoCompleteSelectClub,
|
"club": AutoCompleteSelectClub,
|
||||||
|
"tray": forms.CheckboxInput(attrs={"class": "switch"}),
|
||||||
}
|
}
|
||||||
|
|
||||||
counters = forms.ModelMultipleChoiceField(
|
counters = forms.ModelMultipleChoiceField(
|
||||||
@@ -336,14 +349,18 @@ class ProductForm(forms.ModelForm):
|
|||||||
queryset=Counter.objects.all(),
|
queryset=Counter.objects.all(),
|
||||||
)
|
)
|
||||||
|
|
||||||
def __init__(self, *args, instance=None, **kwargs):
|
def __init__(self, *args, prefix: str | None = None, instance=None, **kwargs):
|
||||||
super().__init__(*args, instance=instance, **kwargs)
|
super().__init__(*args, prefix=prefix, instance=instance, **kwargs)
|
||||||
|
self.fields["name"].widget.attrs["autofocus"] = "autofocus"
|
||||||
if self.instance.id:
|
if self.instance.id:
|
||||||
self.fields["counters"].initial = self.instance.counters.all()
|
self.fields["counters"].initial = self.instance.counters.all()
|
||||||
if hasattr(self.instance, "formula"):
|
if hasattr(self.instance, "formula"):
|
||||||
self.formula_init(self.instance.formula)
|
self.formula_init(self.instance.formula)
|
||||||
|
self.price_formset = ProductPriceFormSet(
|
||||||
|
*args, instance=self.instance, prefix="price", **kwargs
|
||||||
|
)
|
||||||
self.action_formset = ScheduledProductActionFormSet(
|
self.action_formset = ScheduledProductActionFormSet(
|
||||||
*args, product=self.instance, **kwargs
|
*args, product=self.instance, prefix="action", **kwargs
|
||||||
)
|
)
|
||||||
|
|
||||||
def formula_init(self, formula: ProductFormula):
|
def formula_init(self, formula: ProductFormula):
|
||||||
@@ -366,20 +383,25 @@ class ProductForm(forms.ModelForm):
|
|||||||
self.fields[key].validators.append(MaxValueValidator(price))
|
self.fields[key].validators.append(MaxValueValidator(price))
|
||||||
|
|
||||||
def is_valid(self):
|
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:
|
def save(self, *args, **kwargs) -> Product:
|
||||||
product = super().save(*args, **kwargs)
|
product = super().save(*args, **kwargs)
|
||||||
product.counters.set(self.cleaned_data["counters"])
|
product.counters.set(self.cleaned_data["counters"])
|
||||||
for form in self.action_formset:
|
|
||||||
# if it's a creation, the product given in the formset
|
# if it's a creation, the product given in the formset
|
||||||
# wasn't a persisted instance.
|
# wasn't a persisted instance.
|
||||||
# So if we tried to persist the scheduled actions in the current state,
|
# So if we tried to persist the related objects in the current state,
|
||||||
# they would be linked to no product, thus be completely useless
|
# they would be linked to no product, thus be completely useless
|
||||||
# To make it work, we have to replace
|
# To make it work, we have to replace
|
||||||
# the initial product with a persisted one
|
# the initial product with a persisted one
|
||||||
|
for form in self.action_formset:
|
||||||
form.set_product(product)
|
form.set_product(product)
|
||||||
self.action_formset.save()
|
self.action_formset.save()
|
||||||
|
self.price_formset.save()
|
||||||
return product
|
return product
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -39,6 +39,44 @@
|
|||||||
{% endmacro %}
|
{% endmacro %}
|
||||||
|
|
||||||
|
|
||||||
|
{% macro price_form(form) %}
|
||||||
|
<fieldset>
|
||||||
|
{{ form.non_field_errors() }}
|
||||||
|
<div class="form-group row gap-2x">
|
||||||
|
<div>{{ form.amount.as_field_group() }}</div>
|
||||||
|
<div>
|
||||||
|
{{ form.label.errors }}
|
||||||
|
<label for="{{ form.label.id_for_label }}">{{ form.label.label }}</label>
|
||||||
|
{{ form.label }}
|
||||||
|
<span class="helptext">{{ form.label.help_text }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="grow">{{ form.groups.as_field_group() }}</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<div>
|
||||||
|
{{ form.is_always_shown.as_field_group() }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{%- if form.DELETE -%}
|
||||||
|
<div class="form-group row gap">
|
||||||
|
{{ form.DELETE.as_field_group() }}
|
||||||
|
</div>
|
||||||
|
{%- else -%}
|
||||||
|
<br>
|
||||||
|
<button
|
||||||
|
class="btn btn-grey"
|
||||||
|
@click.prevent="removeForm($event.target.closest('fieldset').parentElement)"
|
||||||
|
>
|
||||||
|
<i class="fa fa-minus"></i> {% trans %}Remove price{% endtrans %}
|
||||||
|
</button>
|
||||||
|
{%- endif -%}
|
||||||
|
{%- for field in form.hidden_fields() -%}
|
||||||
|
{{ field }}
|
||||||
|
{%- endfor -%}
|
||||||
|
</fieldset>
|
||||||
|
<hr class="margin-bottom">
|
||||||
|
{% endmacro %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
{% if object %}
|
{% if object %}
|
||||||
<h2>{% trans name=object %}Edit product {{ name }}{% endtrans %}</h2>
|
<h2>{% trans name=object %}Edit product {{ name }}{% endtrans %}</h2>
|
||||||
@@ -49,7 +87,54 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
<form method="post" enctype="multipart/form-data">
|
<form method="post" enctype="multipart/form-data">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
{{ form.as_p() }}
|
{{ form.non_field_errors() }}
|
||||||
|
<fieldset class="row gap">
|
||||||
|
<div>{{ form.name.as_field_group() }}</div>
|
||||||
|
<div>{{ form.code.as_field_group() }}</div>
|
||||||
|
</fieldset>
|
||||||
|
<fieldset>
|
||||||
|
<div class="form-group">{{ form.description.as_field_group() }}</div>
|
||||||
|
</fieldset>
|
||||||
|
<fieldset class="row gap">
|
||||||
|
<div>{{ form.club.as_field_group() }}</div>
|
||||||
|
<div>{{ form.product_type.as_field_group() }}</div>
|
||||||
|
</fieldset>
|
||||||
|
<fieldset><div>{{ form.icon.as_field_group() }}</div></fieldset>
|
||||||
|
<fieldset><div>{{ form.purchase_price.as_field_group() }}</div></fieldset>
|
||||||
|
<fieldset>
|
||||||
|
<div>{{ form.limit_age.as_field_group() }}</div>
|
||||||
|
</fieldset>
|
||||||
|
<fieldset>
|
||||||
|
<div class="row gap">
|
||||||
|
{{ form.tray }}
|
||||||
|
<div>
|
||||||
|
{{ form.tray.label_tag() }}
|
||||||
|
<span class="helptext">{{ form.tray.help_text }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
<fieldset><div>{{ form.counters.as_field_group() }}</div></fieldset>
|
||||||
|
|
||||||
|
<h3 class="margin-bottom">{% trans %}Prices{% endtrans %}</h3>
|
||||||
|
|
||||||
|
<div x-data="dynamicFormSet({ prefix: '{{ form.price_formset.prefix }}' })">
|
||||||
|
{{ form.price_formset.management_form }}
|
||||||
|
<div x-ref="formContainer">
|
||||||
|
{%- for form in form.price_formset.forms -%}
|
||||||
|
<div>
|
||||||
|
{{ price_form(form) }}
|
||||||
|
</div>
|
||||||
|
{%- endfor -%}
|
||||||
|
</div>
|
||||||
|
<template x-ref="formTemplate">
|
||||||
|
<div>
|
||||||
|
{{ price_form(form.price_formset.empty_form) }}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<button class="btn btn-grey" @click.prevent="addForm()">
|
||||||
|
<i class="fa fa-plus"></i> {% trans %}Add a price{% endtrans %}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
<br />
|
<br />
|
||||||
|
|
||||||
@@ -64,7 +149,7 @@
|
|||||||
</em>
|
</em>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div x-data="dynamicFormSet" class="margin-bottom">
|
<div x-data="dynamicFormSet({ prefix: '{{ form.action_formset.prefix }}' })" class="margin-bottom">
|
||||||
{{ form.action_formset.management_form }}
|
{{ form.action_formset.management_form }}
|
||||||
<div x-ref="formContainer">
|
<div x-ref="formContainer">
|
||||||
{%- for f in form.action_formset.forms -%}
|
{%- for f in form.action_formset.forms -%}
|
||||||
@@ -78,6 +163,7 @@
|
|||||||
<i class="fa fa-plus"></i>{% trans %}Add action{% endtrans %}
|
<i class="fa fa-plus"></i>{% trans %}Add action{% endtrans %}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="row gap margin-bottom">{{ form.archived.as_field_group() }}</div>
|
||||||
<p><input class="btn btn-blue" type="submit" value="{% trans %}Save{% endtrans %}" /></p>
|
<p><input class="btn btn-blue" type="submit" value="{% trans %}Save{% endtrans %}" /></p>
|
||||||
</form>
|
</form>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
Reference in New Issue
Block a user