fix: 500 on product create view

This commit is contained in:
imperosol
2025-09-29 14:20:44 +02:00
parent ac38ad7861
commit 09d9529f0d
5 changed files with 71 additions and 25 deletions

View File

@@ -29,11 +29,7 @@ from django.contrib.auth.mixins import PermissionRequiredMixin
from django.core.exceptions import NON_FIELD_ERRORS, PermissionDenied, ValidationError from django.core.exceptions import NON_FIELD_ERRORS, PermissionDenied, ValidationError
from django.core.paginator import InvalidPage, Paginator from django.core.paginator import InvalidPage, Paginator
from django.db.models import Sum from django.db.models import Sum
from django.http import ( from django.http import Http404, HttpResponseRedirect, StreamingHttpResponse
Http404,
HttpResponseRedirect,
StreamingHttpResponse,
)
from django.shortcuts import get_object_or_404, redirect from django.shortcuts import get_object_or_404, redirect
from django.urls import reverse, reverse_lazy from django.urls import reverse, reverse_lazy
from django.utils import timezone from django.utils import timezone
@@ -58,10 +54,7 @@ from com.views import (
PosterEditBaseView, PosterEditBaseView,
PosterListBaseView, PosterListBaseView,
) )
from core.auth.mixins import ( from core.auth.mixins import CanEditMixin, CanViewMixin
CanEditMixin,
CanViewMixin,
)
from core.models import PageRev from core.models import PageRev
from core.views import DetailFormView, PageEditViewBase from core.views import DetailFormView, PageEditViewBase
from core.views.mixins import TabedViewMixin from core.views.mixins import TabedViewMixin

View File

@@ -232,13 +232,16 @@ class ScheduledProductActionForm(forms.ModelForm):
class BaseScheduledProductActionFormSet(BaseModelFormSet): class BaseScheduledProductActionFormSet(BaseModelFormSet):
def __init__(self, *args, product: Product, **kwargs): def __init__(self, *args, product: Product, **kwargs):
queryset = ( if product.id:
product.scheduled_actions.filter( queryset = (
enabled=True, clocked__clocked_time__gt=now() product.scheduled_actions.filter(
enabled=True, clocked__clocked_time__gt=now()
)
.order_by("clocked__clocked_time")
.select_related("clocked")
) )
.order_by("clocked__clocked_time") else:
.select_related("clocked") queryset = ScheduledProductAction.objects.none()
)
form_kwargs = {"product": product} form_kwargs = {"product": product}
super().__init__(*args, queryset=queryset, form_kwargs=form_kwargs, **kwargs) super().__init__(*args, queryset=queryset, form_kwargs=form_kwargs, **kwargs)
@@ -260,7 +263,7 @@ ScheduledProductActionFormSet = forms.modelformset_factory(
) )
class ProductEditForm(forms.ModelForm): class ProductForm(forms.ModelForm):
error_css_class = "error" error_css_class = "error"
required_css_class = "required" required_css_class = "required"
@@ -367,7 +370,7 @@ class CloseCustomerAccountForm(forms.Form):
) )
class ProductForm(forms.Form): class BasketProductForm(forms.Form):
quantity = forms.IntegerField(min_value=1, required=True) quantity = forms.IntegerField(min_value=1, required=True)
id = forms.IntegerField(min_value=0, required=True) id = forms.IntegerField(min_value=0, required=True)
@@ -472,5 +475,5 @@ class BaseBasketForm(forms.BaseFormSet):
BasketForm = forms.formset_factory( BasketForm = forms.formset_factory(
ProductForm, formset=BaseBasketForm, absolute_max=None, min_num=1 BasketProductForm, formset=BaseBasketForm, absolute_max=None, min_num=1
) )

View File

@@ -6,14 +6,16 @@ import pytest
from django.conf import settings from django.conf import settings
from django.core.cache import cache from django.core.cache import cache
from django.core.files.uploadedfile import SimpleUploadedFile from django.core.files.uploadedfile import SimpleUploadedFile
from django.test import Client from django.test import Client, TestCase
from django.urls import reverse from django.urls import reverse
from model_bakery import baker from model_bakery import baker
from PIL import Image from PIL import Image
from pytest_django.asserts import assertNumQueries from pytest_django.asserts import assertNumQueries, assertRedirects
from club.models import Club
from core.baker_recipes import board_user, subscriber_user from core.baker_recipes import board_user, subscriber_user
from core.models import Group, User from core.models import Group, User
from counter.forms import ProductForm
from counter.models import Product, ProductType from counter.models import Product, ProductType
@@ -84,3 +86,51 @@ def test_fetch_product_nb_queries(client: Client):
# - 1 for the actual request # - 1 for the actual request
# - 1 to prefetch the related buying_groups # - 1 to prefetch the related buying_groups
client.get(reverse("api:search_products_detailed")) client.get(reverse("api:search_products_detailed"))
class TestCreateProduct(TestCase):
@classmethod
def setUpTestData(cls):
cls.product_type = baker.make(ProductType)
cls.club = baker.make(Club)
cls.data = {
"name": "foo",
"description": "bar",
"product_type": cls.product_type.id,
"club": cls.club.id,
"code": "FOO",
"purchase_price": 1.0,
"selling_price": 1.0,
"special_selling_price": 1.0,
"limit_age": 0,
"form-TOTAL_FORMS": 0,
"form-INITIAL_FORMS": 0,
}
def test_form(self):
form = ProductForm(data=self.data)
print(form.errors)
assert form.is_valid()
instance = form.save()
assert instance.club == self.club
assert instance.product_type == self.product_type
assert instance.name == "foo"
assert instance.selling_price == 1.0
def test_view(self):
self.client.force_login(
baker.make(
User,
groups=[Group.objects.get(id=settings.SITH_GROUP_COUNTER_ADMIN_ID)],
)
)
url = reverse("counter:new_product")
response = self.client.get(url)
assert response.status_code == 200
response = self.client.post(url, data=self.data)
print(response.text)
assertRedirects(response, reverse("counter:product_list"))
product = Product.objects.last()
assert product.name == "foo"
assert product.club == self.club
assert product.product_type == self.product_type

View File

@@ -32,7 +32,7 @@ from core.utils import get_semester_code, get_start_of_semester
from counter.forms import ( from counter.forms import (
CloseCustomerAccountForm, CloseCustomerAccountForm,
CounterEditForm, CounterEditForm,
ProductEditForm, ProductForm,
ReturnableProductForm, ReturnableProductForm,
) )
from counter.models import ( from counter.models import (
@@ -146,7 +146,7 @@ class ProductCreateView(CounterAdminTabsMixin, CounterAdminMixin, CreateView):
"""A create view for the admins.""" """A create view for the admins."""
model = Product model = Product
form_class = ProductEditForm form_class = ProductForm
template_name = "counter/product_form.jinja" template_name = "counter/product_form.jinja"
current_tab = "products" current_tab = "products"
@@ -155,7 +155,7 @@ class ProductEditView(CounterAdminTabsMixin, CounterAdminMixin, UpdateView):
"""An edit view for the admins.""" """An edit view for the admins."""
model = Product model = Product
form_class = ProductEditForm form_class = ProductForm
pk_url_kwarg = "product_id" pk_url_kwarg = "product_id"
template_name = "counter/product_form.jinja" template_name = "counter/product_form.jinja"
current_tab = "products" current_tab = "products"

View File

@@ -48,7 +48,7 @@ from django_countries.fields import Country
from core.auth.mixins import CanViewMixin from core.auth.mixins import CanViewMixin
from core.views.mixins import FragmentMixin, UseFragmentsMixin from core.views.mixins import FragmentMixin, UseFragmentsMixin
from counter.forms import BaseBasketForm, BillingInfoForm, ProductForm from counter.forms import BaseBasketForm, BillingInfoForm, BasketProductForm
from counter.models import ( from counter.models import (
BillingInfo, BillingInfo,
Customer, Customer,
@@ -78,7 +78,7 @@ class BaseEbouticBasketForm(BaseBasketForm):
EbouticBasketForm = forms.formset_factory( EbouticBasketForm = forms.formset_factory(
ProductForm, formset=BaseEbouticBasketForm, absolute_max=None, min_num=1 BasketProductForm, formset=BaseEbouticBasketForm, absolute_max=None, min_num=1
) )