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 dateutil.relativedelta import relativedelta
from django import forms from django import forms
from django.core.exceptions import ValidationError
from django.db.models import Exists, OuterRef, Q from django.db.models import Exists, OuterRef, Q
from django.forms import BaseModelFormSet from django.forms import BaseModelFormSet
from django.utils.timezone import now from django.utils.timezone import now
@@ -34,6 +35,7 @@ from counter.models import (
Eticket, Eticket,
InvoiceCall, InvoiceCall,
Product, Product,
ProductFormula,
Refilling, Refilling,
ReturnableProduct, ReturnableProduct,
ScheduledProductAction, ScheduledProductAction,
@@ -349,13 +351,33 @@ class ProductForm(forms.ModelForm):
return product 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 ReturnableProductForm(forms.ModelForm):
class Meta: class Meta:
model = ReturnableProduct model = ReturnableProduct
fields = ["product", "returned_product", "max_return"] fields = ["product", "returned_product", "max_return"]
widgets = { widgets = {
"product": AutoCompleteSelectProduct(), "product": AutoCompleteSelectProduct,
"returned_product": AutoCompleteSelectProduct(), "returned_product": AutoCompleteSelectProduct,
} }
def save(self, commit: bool = True) -> ReturnableProduct: # noqa FBT 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, CounterStatView,
ProductCreateView, ProductCreateView,
ProductEditView, ProductEditView,
ProductFormulaCreateView,
ProductFormulaDeleteView,
ProductFormulaEditView,
ProductFormulaListView,
ProductListView, ProductListView,
ProductTypeCreateView, ProductTypeCreateView,
ProductTypeEditView, ProductTypeEditView,
@@ -116,6 +120,24 @@ urlpatterns = [
ProductEditView.as_view(), ProductEditView.as_view(),
name="product_edit", 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( path(
"admin/product-type/list/", "admin/product-type/list/",
ProductTypeListView.as_view(), ProductTypeListView.as_view(),

View File

@@ -34,11 +34,13 @@ from counter.forms import (
CloseCustomerAccountForm, CloseCustomerAccountForm,
CounterEditForm, CounterEditForm,
ProductForm, ProductForm,
ProductFormulaForm,
ReturnableProductForm, ReturnableProductForm,
) )
from counter.models import ( from counter.models import (
Counter, Counter,
Product, Product,
ProductFormula,
ProductType, ProductType,
Refilling, Refilling,
ReturnableProduct, ReturnableProduct,
@@ -162,6 +164,49 @@ class ProductEditView(CounterAdminTabsMixin, CounterAdminMixin, UpdateView):
current_tab = "products" 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( class ReturnableProductListView(
CounterAdminTabsMixin, PermissionRequiredMixin, ListView CounterAdminTabsMixin, PermissionRequiredMixin, ListView
): ):

View File

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