[WIP] added offer request

feature/product_models_refactor
mtyton 2023-07-20 17:49:12 +02:00
rodzic c7aa69448c
commit 10b9527261
9 zmienionych plików z 129 dodań i 94 usunięć

Wyświetl plik

@ -2,9 +2,9 @@ from django import forms
from phonenumber_field.formfields import PhoneNumberField
from store.models import (
ProductTemplate,
ProductCategoryParamValue,
ProductCategoryParam,
ProductCategory
Product
)
@ -40,39 +40,27 @@ class CustomerDataForm(forms.Form):
)
class ProductCategoryParamFormset(forms.BaseModelFormSet):
...
class ButtonToggleSelect(forms.RadioSelect):
template_name = "store/forms/button_toggle_select.html"
class ProductCategoryParamValueForm(forms.ModelForm):
class Meta:
model = ProductCategoryParamValue
fields = ("key", "value")
class ProductTemplateConfigForm(forms.Form):
key = forms.CharField(required=True)
value = forms.ModelChoiceField(
queryset=ProductCategoryParamValue.objects.none(),
widget=forms.RadioSelect(attrs={"class": "btn-check", "type": "radio", "autocomplete": "off"}),
required=True
)
def _get_instace(self, key: str):
return ProductCategoryParam.objects.get(key=key)
def __init__(self, *args, **kwargs):
def _create_dynamic_fields(self, template: ProductTemplate):
category_params = template.category.category_params.all()
for param in category_params:
self.fields[param.key] = forms.ModelChoiceField(
queryset=ProductCategoryParamValue.objects.filter(param=param),
widget=ButtonToggleSelect(attrs={"class": "btn-group btn-group-toggle"}),
)
def __init__(
self, template: ProductTemplate, *args, **kwargs
):
self.template = template
super().__init__(*args, **kwargs)
key = self.initial.get("key")
if not key:
return
self.cat_param = self._get_instace(key)
self.fields["value"].choices = [
(param_value.pk, param_value.value) for param_value in self.cat_param.param_values.all()
]
self._create_dynamic_fields(template)
def save(self, *args, **kwargs):
param_value = ProductCategoryParamValue.objects.get(
param__key=str(self.cleaned_data["key"]),
value=str(self.cleaned_data["value"])
)
print(param_value or "DUPSKo")
return param_value
def get_product(self):
params = list(self.cleaned_data.values())
return Product.objects.get_or_create_by_params(template=self.template, params=params)

Wyświetl plik

@ -172,8 +172,7 @@ class ProductManager(models.Manager):
def get_or_create_by_params(self, params: list[ProductCategoryParamValue], template: ProductTemplate):
products = self.filter(template=template)
# TODO - other price
price_proposal = products.first().price if products.first() else 4.0
for param in params:
products = products.filter(params__pk=param.pk)
@ -186,7 +185,7 @@ class ProductManager(models.Manager):
product = self.create(
name=f"{template.title} - AUTOGENERATED",
template=template,
price=price_proposal,
price=0,
available=False
)
for param in params:
@ -220,6 +219,8 @@ class Product(ClusterableModel):
try:
return self.product_images.get(is_main=True)
except ProductImage.DoesNotExist:
if main_image := self.template.main_image:
return main_image
return self.product_images.first()

Wyświetl plik

@ -23,21 +23,11 @@
<div class="container">
<form action="" method="POST" class="mt-5">
{% csrf_token %}
{{ formset.management_form }}
<div class="row mt-5">
{% for form in formset.forms %}
{% for field in form %}
<div class="col-6 text-center">
<h3>{{form.cat_param}}</h3>
{% if form.value.field.choices %}
<input type="hidden" name="{{form.prefix}}-key" value="{{form.key.value}}">
{% for value, label in form.value.field.choices %}
<div class="btn-group btn-group-toggle" role="group">
<input type="radio" class="btn-check" name="{{form.prefix}}-value" id="{{value}}" autocomplete="off" value="{{value}}" required>
<label class="btn btn-outline-primary" for="{{value}}">{{label}}</label>
</div>
{% endfor %}
{% endif %}
<h3>{{field.label}}</h3>
{{field}}
</div>
{% if forloop.counter|divisibleby:"2" %} </div><div class="row mt-5">{% endif %}
{% endfor %}

Wyświetl plik

@ -0,0 +1,52 @@
{% extends 'base.html' %}
{% block content %}
<section class="h-100">
<div class="container">
<div class="row">
<div class="col-6">
<img src="{{variant.main_image.image.url}}"
class="img-fluid img-thumbnail h-80" alt="Responsive image">
</div>
<div class="col-6">
<div class="card mb-2 py-5">
<div class="card-body">
{% for value in params_values %}
<div class="row">
<div class="col-sm-6">
<p class="text-muted mb-0">{{value}}</p>
</div>
</div>
<hr>
{% endfor %}
</div>
</div>
</div>
</div>
{% if not variant.available %}
<div class="row mt-3">
<div class="col-12 card alert-danger text-center">
Niestety skonfigurowany przez Ciebie wariant produktu nie jest jeszcze dostępny.
Jeżeli jesteś zainteresowany tą konfiguracją złóż zapytanie ofertowe.
</div>
</div>
{% endif %}
<div class="row mt-3">
<div class="col-3 ">
{% if variant.available %}
<h3>Cena: {{variant.price}} zł</h3>
{% endif %}
</div>
<div class="col-9 text-end">
<a class="btn btn-outline-primary btn-lg" href="{% url 'product-configure' variant.template.pk %}">Wróć do konfiguratora</a>
{% if variant.available %}
<button class="btn btn-outline-success btn-lg">Zamów produkt</button>
{% else %}
<button class="btn btn-outline-success btn-lg">Złóż zapytanie ofertowe</button>
{% endif %}
</div>
</div>
</div>
</section>
{% endblock %}

Wyświetl plik

@ -0,0 +1,28 @@
{% with id=widget.attrs.id %}
<div class="btn-group btn-group-toggle" role="group">
{% for group, options, index in widget.optgroups %}
{% for option in options %}
<input type="radio" class="btn-check" name="{{option.name}}" id="{{id}}_{{option.index}}" autocomplete="off"
value="{{option.value}}" required>
<label class="btn btn-outline-primary" for="{{id}}_{{option.index}}">{{option.label}}</label>
{% endfor %}
{% endfor %}
</div>
{% endwith %}
<!--
{% with id=widget.attrs.id %}
<div{% if id %} id="{{ id }}"{% endif %}
{% if widget.attrs.class %} class="{{ widget.attrs.class }}"{% endif %}
>
{% for group, options, index in widget.optgroups %}
{% if group %}
<div><label>{{ group }}</label>{% endif %}{% for option in options %}<div>
{% include option.template_name with widget=option %}</div>{% endfor %}{% if group %}
</div>
{% endif %}
{% endfor %}
</div>
{% endwith %}
-->

Wyświetl plik

@ -138,7 +138,7 @@ class ProductTestCase(TestCase):
)
self.assertIsNotNone(prod)
self.assertFalse(prod.available)
self.assertEqual(prod.price, 4.0)
self.assertEqual(prod.price, 0)
class OrderProductTestCase(TestCase):

Wyświetl plik

@ -0,0 +1,11 @@
from django.test import TestCase
class ConfigureProductViewTestCase(TestCase):
def setUp(self):
...
def test_sdkfsdf(self):
...

Wyświetl plik

@ -10,7 +10,7 @@ router.register("cart-action", store_views.CartActionView, "cart-action")
urlpatterns = [
path('product-configure/<int:pk>/', store_views.ConfigureProductView.as_view(), name='product-configure'),
path('product-configure/summary/<int:variant_pk>/', store_views.ConfigureProductSummaryView.as_view(), name='product-configure-summary'),
path('product-configure/summary/<int:variant_pk>/', store_views.ConfigureProductSummaryView.as_view(), name='configure-product-summary'),
path('cart/', store_views.CartView.as_view(), name='cart'),
path("order/", store_views.OrderView.as_view(), name="order"),
path("order/confirm/", store_views.OrderConfirmView.as_view(), name="order-confirm")

Wyświetl plik

@ -20,8 +20,7 @@ from store.serializers import (
)
from store.forms import (
CustomerDataForm,
ProductCategoryParamValueForm,
ProductCategoryParamFormset
ProductTemplateConfigForm
)
from store.models import (
Order,
@ -98,18 +97,11 @@ class ConfigureProductView(View):
def get_context_data(self, pk: int, **kwargs: Any) -> Dict[str, Any]:
template = ProductTemplate.objects.get(pk=pk)
category_params = template.category.category_params.all()
formset_class = modelformset_factory(
ProductCategoryParamValue,
form=ProductCategoryParamValueForm,
formset=ProductCategoryParamFormset
)
formset = formset_class(queryset=category_params)
form = ProductTemplateConfigForm(template=template)
context = {
"template": template,
"available_variants": Product.objects.filter(template__pk=pk),
"category_params": category_params,
"formset": formset
"form": form
}
return context
@ -121,41 +113,14 @@ class ConfigureProductView(View):
def post(self, request, pk: int, *args, **kwargs):
# first select template
template = ProductTemplate.objects.get(pk=pk)
category_params = template.category.category_params.all()
params_values = []
formset_class = modelformset_factory(
ProductCategoryParamValue,
form=ProductCategoryParamValueForm,
formset=ProductCategoryParamFormset
)
formset = formset_class(queryset=category_params, data=request.POST)
print(request.POST)
if not formset.is_valid():
print(formset.errors)
messages.error(request, "Niepoprawne dane")
form = ProductTemplateConfigForm(template=template, data=request.POST)
if not form.is_valid():
context = self.get_context_data(pk)
context["formset"] = formset
context["form"] = form
return render(request, self.template_name, context)
for form in formset.forms:
if not form.is_valid():
messages.error(request, "Niepoprawne dane")
context = self.get_context_data(pk)
context["formset"] = formset
return render(request, self.template_name, context)
params_values.append(form.save())
product_variant = Product.objects.get_or_create_by_params(
template=ProductTemplate.objects.get(pk=pk), params=params_values
)
if not product_variant:
messages.error(request, "Nie udało się utworzyć wariantu produktu")
return HttpResponseRedirect(reverse("product-configure", kwargs={"pk": pk}))
return HttpResponseRedirect(
reverse("product-configure-summary", kwargs={"variant_pk": product_variant.pk})
)
product_variant = form.get_product()
return HttpResponseRedirect(reverse("configure-product-summary", args=[product_variant.pk]))
class ConfigureProductSummaryView(View):
template_name = "store/configure_product_summary.html"