product formulas management views

This commit is contained in:
imperosol
2025-11-26 13:22:43 +01:00
parent 9fb3f83df5
commit df8b23a4a1
5 changed files with 131 additions and 2 deletions

View File

@@ -5,6 +5,7 @@ from datetime import date, datetime, timezone
from dateutil.relativedelta import relativedelta
from django import forms
from django.core.exceptions import ValidationError
from django.db.models import Exists, OuterRef, Q
from django.forms import BaseModelFormSet
from django.utils.timezone import now
@@ -34,6 +35,7 @@ from counter.models import (
Eticket,
InvoiceCall,
Product,
ProductFormula,
Refilling,
ReturnableProduct,
ScheduledProductAction,
@@ -349,13 +351,33 @@ class ProductForm(forms.ModelForm):
return product
class ProductFormulaForm(forms.ModelForm):
class Meta:
model = ProductFormula
fields = ["products", "result"]
widgets = {
"products": AutoCompleteSelectMultipleProduct,
"result": AutoCompleteSelectProduct,
}
def clean(self):
cleaned_data = super().clean()
prices = [p.selling_price for p in cleaned_data["products"]]
selling_price = cleaned_data["result"].selling_price
if selling_price > sum(prices):
raise ValidationError(
_("This formula is more expensive than its constituant products.")
)
return cleaned_data
class ReturnableProductForm(forms.ModelForm):
class Meta:
model = ReturnableProduct
fields = ["product", "returned_product", "max_return"]
widgets = {
"product": AutoCompleteSelectProduct(),
"returned_product": AutoCompleteSelectProduct(),
"product": AutoCompleteSelectProduct,
"returned_product": AutoCompleteSelectProduct,
}
def save(self, commit: bool = True) -> ReturnableProduct: # noqa FBT

View File

@@ -0,0 +1,35 @@
{% extends "core/base.jinja" %}
{% block title %}
{% trans %}Product formulas{% endtrans %}
{% endblock %}
{% block additional_css %}
<link rel="stylesheet" href="{{ static("core/components/card.scss") }}">
<link rel="stylesheet" href="{{ static("counter/css/admin.scss") }}">
{% endblock %}
{% block content %}
<main>
<h3 class="margin-bottom">{% trans %}Product formulas{% endtrans %}</h3>
<p>
<a href="{{ url('counter:product_formula_create') }}" class="btn btn-blue">
{% trans %}New formula{% endtrans %}
<i class="fa fa-plus"></i>
</a>
</p>
<ul class="product-group">
{%- for formula in object_list -%}
<li>
<a href="{{ url('counter:product_formula_edit', formula_id=formula.id) }}">
{{ formula.result.name }}
</a>
<a href="{{ url('counter:product_formula_delete', formula_id=formula.id) }}">
<i class="fa fa-trash delete-action"></i>
</a>
</li>
{%- endfor -%}
</ul>
</div>
</main>
{% endblock %}

View File

@@ -25,6 +25,10 @@ from counter.views.admin import (
CounterStatView,
ProductCreateView,
ProductEditView,
ProductFormulaCreateView,
ProductFormulaDeleteView,
ProductFormulaEditView,
ProductFormulaListView,
ProductListView,
ProductTypeCreateView,
ProductTypeEditView,
@@ -116,6 +120,24 @@ urlpatterns = [
ProductEditView.as_view(),
name="product_edit",
),
path(
"admin/formula/", ProductFormulaListView.as_view(), name="product_formula_list"
),
path(
"admin/formula/new/",
ProductFormulaCreateView.as_view(),
name="product_formula_create",
),
path(
"admin/formula/<int:formula_id>/edit",
ProductFormulaEditView.as_view(),
name="product_formula_edit",
),
path(
"admin/formula/<int:formula_id>/delete",
ProductFormulaDeleteView.as_view(),
name="product_formula_delete",
),
path(
"admin/product-type/list/",
ProductTypeListView.as_view(),

View File

@@ -34,11 +34,13 @@ from counter.forms import (
CloseCustomerAccountForm,
CounterEditForm,
ProductForm,
ProductFormulaForm,
ReturnableProductForm,
)
from counter.models import (
Counter,
Product,
ProductFormula,
ProductType,
Refilling,
ReturnableProduct,
@@ -162,6 +164,49 @@ class ProductEditView(CounterAdminTabsMixin, CounterAdminMixin, UpdateView):
current_tab = "products"
class ProductFormulaListView(CounterAdminTabsMixin, PermissionRequiredMixin, ListView):
model = ProductFormula
queryset = ProductFormula.objects.select_related("result")
template_name = "counter/formula_list.jinja"
current_tab = "formulas"
permission_required = "counter.view_productformula"
class ProductFormulaCreateView(
CounterAdminTabsMixin, PermissionRequiredMixin, CreateView
):
model = ProductFormula
form_class = ProductFormulaForm
pk_url_kwarg = "formula_id"
template_name = "core/create.jinja"
current_tab = "formulas"
success_url = reverse_lazy("counter:product_formula_list")
permission_required = "counter.add_productformula"
class ProductFormulaEditView(
CounterAdminTabsMixin, PermissionRequiredMixin, UpdateView
):
model = ProductFormula
form_class = ProductFormulaForm
pk_url_kwarg = "formula_id"
template_name = "core/edit.jinja"
current_tab = "formulas"
success_url = reverse_lazy("counter:product_formula_list")
permission_required = "counter.change_productformula"
class ProductFormulaDeleteView(
CounterAdminTabsMixin, PermissionRequiredMixin, DeleteView
):
model = ProductFormula
pk_url_kwarg = "formula_id"
template_name = "core/delete_confirm.jinja"
current_tab = "formulas"
success_url = reverse_lazy("counter:product_formula_list")
permission_required = "counter.delete_productformula"
class ReturnableProductListView(
CounterAdminTabsMixin, PermissionRequiredMixin, ListView
):

View File

@@ -100,6 +100,11 @@ class CounterAdminTabsMixin(TabedViewMixin):
"slug": "products",
"name": _("Products"),
},
{
"url": reverse_lazy("counter:product_formula_list"),
"slug": "formulas",
"name": _("Formulas"),
},
{
"url": reverse_lazy("counter:product_type_list"),
"slug": "product_types",