[WIP] added offer request
rodzic
c7aa69448c
commit
10b9527261
|
@ -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 _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 _get_instace(self, key: str):
|
||||
return ProductCategoryParam.objects.get(key=key)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
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)
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
||||
|
|
|
@ -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 %}
|
||||
|
|
|
@ -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 %}
|
|
@ -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 %}
|
||||
|
||||
-->
|
|
@ -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):
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
from django.test import TestCase
|
||||
|
||||
|
||||
class ConfigureProductViewTestCase(TestCase):
|
||||
|
||||
def setUp(self):
|
||||
...
|
||||
|
||||
def test_sdkfsdf(self):
|
||||
...
|
||||
|
|
@ -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")
|
||||
|
|
|
@ -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")
|
||||
context = self.get_context_data(pk)
|
||||
context["formset"] = formset
|
||||
return render(request, self.template_name, context)
|
||||
|
||||
for form in formset.forms:
|
||||
form = ProductTemplateConfigForm(template=template, data=request.POST)
|
||||
if not form.is_valid():
|
||||
messages.error(request, "Niepoprawne dane")
|
||||
context = self.get_context_data(pk)
|
||||
context["formset"] = formset
|
||||
context["form"] = form
|
||||
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"
|
||||
|
|
Ładowanie…
Reference in New Issue