feat: cart detail leads to order creation

This commit is contained in:
2025-01-02 17:20:03 +01:00
parent ab2c9f4d22
commit 10b411ba33
9 changed files with 290 additions and 187 deletions
+50 -4
View File
@@ -1,10 +1,9 @@
# Generated by Django 5.1.3 on 2024-12-31 09:48 # Generated by Django 5.1.3 on 2025-01-02 13:36
import uuid
from decimal import Decimal
import django.db.models.deletion import django.db.models.deletion
import django.utils.timezone import django.utils.timezone
import uuid
from decimal import Decimal
from django.conf import settings from django.conf import settings
from django.db import migrations, models from django.db import migrations, models
@@ -894,4 +893,51 @@ class Migration(migrations.Migration):
"ordering": ("-date",), "ordering": ("-date",),
}, },
), ),
migrations.CreateModel(
name="WishlistedProduct",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"creation_date",
models.DateTimeField(
auto_now_add=True, verbose_name="fecha de creación"
),
),
(
"last_modification_date",
models.DateTimeField(
auto_now=True, verbose_name="fecha de última modificación"
),
),
(
"product",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
to="shop.product",
verbose_name="producto",
),
),
(
"user",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
to=settings.AUTH_USER_MODEL,
verbose_name="usuario",
),
),
],
options={
"verbose_name": "productos deseados",
"verbose_name_plural": "productos deseados",
"unique_together": {("user", "product")},
},
),
] ]
-62
View File
@@ -1,62 +0,0 @@
# Generated by Django 5.1.3 on 2025-01-02 09:35
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("shop", "0001_initial"),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name="WishlistedProduct",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"creation_date",
models.DateTimeField(
auto_now_add=True, verbose_name="fecha de creación"
),
),
(
"last_modification_date",
models.DateTimeField(
auto_now=True, verbose_name="fecha de última modificación"
),
),
(
"product",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
to="shop.product",
verbose_name="producto",
),
),
(
"user",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
to=settings.AUTH_USER_MODEL,
verbose_name="usuario",
),
),
],
options={
"verbose_name": "productos deseados",
"verbose_name_plural": "productos deseados",
},
),
]
+10 -7
View File
@@ -122,11 +122,7 @@ class Product(TimestampedModel):
def __str__(self): def __str__(self):
return self.name return self.name
def save( def save(self, *args, **kwargs):
self,
*args,
**kwargs,
):
if not self.pk: if not self.pk:
self.slug = slugify(self.name)[:128] self.slug = slugify(self.name)[:128]
super().save(*args, **kwargs) super().save(*args, **kwargs)
@@ -362,7 +358,7 @@ class Order(TimestampedModel):
user = models.ForeignKey(User, on_delete=models.PROTECT, verbose_name=_("cliente")) user = models.ForeignKey(User, on_delete=models.PROTECT, verbose_name=_("cliente"))
# Billing information # Datos de facturación
billing_address = models.CharField( billing_address = models.CharField(
max_length=255, blank=False, verbose_name=_("dirección de facturación") max_length=255, blank=False, verbose_name=_("dirección de facturación")
) )
@@ -379,7 +375,7 @@ class Order(TimestampedModel):
max_length=32, blank=False, verbose_name=_("código postal de facturación") max_length=32, blank=False, verbose_name=_("código postal de facturación")
) )
# Shipping information # Datos de envío
shipping_address = models.CharField( shipping_address = models.CharField(
max_length=255, blank=False, verbose_name=_("dirección") max_length=255, blank=False, verbose_name=_("dirección")
) )
@@ -499,6 +495,9 @@ class CustomerAddress(models.Model):
default = models.BooleanField(default=True, verbose_name=_("por defecto")) default = models.BooleanField(default=True, verbose_name=_("por defecto"))
hidden = models.BooleanField(default=False, db_index=True, verbose_name=_("oculto")) hidden = models.BooleanField(default=False, db_index=True, verbose_name=_("oculto"))
def __str__(self):
return f'{self.full_name} - {self.address}, {self.address_zip}, {self.address_state} - {self.address_country}'
class Meta: class Meta:
verbose_name = _("dirección de cliente") verbose_name = _("dirección de cliente")
verbose_name_plural = _("direcciones de cliente") verbose_name_plural = _("direcciones de cliente")
@@ -529,3 +528,7 @@ class WishlistedProduct(TimestampedModel):
class Meta: class Meta:
verbose_name = _("productos deseados") verbose_name = _("productos deseados")
verbose_name_plural = _("productos deseados") verbose_name_plural = _("productos deseados")
unique_together = (
"user",
"product",
)
+3 -2
View File
@@ -88,6 +88,7 @@ def create_order_from_cart(
shipping_state=shipping_address.address_state, shipping_state=shipping_address.address_state,
shipping_country=shipping_address.address_country, shipping_country=shipping_address.address_country,
shipping_zip=shipping_address.address_zip, shipping_zip=shipping_address.address_zip,
user=cart.user,
) )
for item in cart.items.all(): for item in cart.items.all():
@@ -95,7 +96,7 @@ def create_order_from_cart(
price: ProductPrice = product.price price: ProductPrice = product.price
base_total = price.price * item.quantity base_total = price.price * item.quantity
tax_value = price.tax.value tax_value = price.tax.value
taxes = base_total * (tax_value / 100) taxes = base_total * tax_value / Decimal(100)
total = base_total + taxes total = base_total + taxes
OrderLine.objects.create( OrderLine.objects.create(
@@ -119,7 +120,7 @@ def add_shipping_order_line(order, shipping_method):
shipping_price: ProductPrice = shipping_method.shipping_product.price shipping_price: ProductPrice = shipping_method.shipping_product.price
shipping_base_total = shipping_price.price shipping_base_total = shipping_price.price
shipping_tax_value = shipping_price.tax.value shipping_tax_value = shipping_price.tax.value
shipping_taxes = shipping_base_total * (shipping_tax_value / 100) shipping_taxes = shipping_base_total * (shipping_tax_value / Decimal(100))
shipping_total = shipping_base_total + shipping_taxes shipping_total = shipping_base_total + shipping_taxes
OrderLine.objects.create( OrderLine.objects.create(
+6
View File
@@ -11,3 +11,9 @@ class CartItemForm(forms.Form):
product = forms.IntegerField(widget=widgets.HiddenInput) product = forms.IntegerField(widget=widgets.HiddenInput)
quantity = forms.IntegerField() quantity = forms.IntegerField()
class CreateOrderForm(forms.Form):
shipping_address = forms.IntegerField()
billing_address = forms.IntegerField()
shipping_method = forms.IntegerField()
+160 -106
View File
@@ -1,139 +1,193 @@
{% load i18n %} {% load i18n %}
<section class="bg-white py-2 antialiased dark:bg-gray-900 md:py-8"> <section class="bg-white py-2 antialiased dark:bg-gray-900 md:py-8">
<div class="mx-auto max-w-screen-xl px-4 2xl:px-0"> <form action="{% url 'web:cart_detail' %}" method="post">
<h2 class="text-xl font-semibold text-gray-900 dark:text-white sm:text-2xl">{% translate 'Carrito' %}</h2> {% csrf_token %}
<div class="mx-auto max-w-screen-xl px-4 2xl:px-0">
<h2 class="text-xl font-semibold text-gray-900 dark:text-white sm:text-2xl">{% translate 'Carrito' %}</h2>
<div class="mt-4 sm:mt-6 md:gap-6 lg:flex lg:items-start xl:gap-8"> <div class="mt-4 sm:mt-6 md:gap-6 lg:flex lg:items-start xl:gap-8">
<div class="mx-auto w-full flex-none lg:max-w-2xl xl:max-w-4xl"> <div class="mx-auto w-full flex-none lg:max-w-2xl xl:max-w-4xl">
<div class="space-y-6"> <div class="space-y-6">
{% for item in items %} {% for item in items %}
<div <div
class="rounded-lg border border-gray-200 bg-white p-4 shadow-sm dark:border-gray-700 dark:bg-gray-800 md:p-6"> class="rounded-lg border border-gray-200 bg-white p-4 shadow-sm dark:border-gray-700 dark:bg-gray-800 md:p-6">
<div class="space-y-4 md:flex md:items-center md:justify-between md:gap-6 md:space-y-0"> <div class="space-y-4 md:flex md:items-center md:justify-between md:gap-6 md:space-y-0">
<a href="#" class="shrink-0 md:order-1"> <a href="#" class="shrink-0 md:order-1">
<img class="h-20 w-20 dark:hidden" src="{{ item.product.images.all|first }}" <img class="h-20 w-20 dark:hidden" src="{{ item.product.images.all|first }}"
alt="{{ item.product.name }}"/> alt="{{ item.product.name }}"/>
<img class="hidden h-20 w-20 dark:block" src="{{ item.product.images.all|first }}" <img class="hidden h-20 w-20 dark:block" src="{{ item.product.images.all|first }}"
alt="{{ item.product.name }}"/> alt="{{ item.product.name }}"/>
</a> </a>
<label for="counter-input" class="sr-only">{% translate 'Cantidad' %}</label> <label for="counter-input" class="sr-only">{% translate 'Cantidad' %}</label>
<div class="flex items-center justify-between md:order-3 md:justify-end"> <div class="flex items-center justify-between md:order-3 md:justify-end">
<div class="flex items-center"> <div class="flex items-center">
<span <span
class="w-10 shrink-0 border-0 bg-transparent text-center text-sm font-medium text-gray-900 focus:outline-none focus:ring-0 dark:text-white"> class="w-10 shrink-0 border-0 bg-transparent text-center text-sm font-medium text-gray-900 focus:outline-none focus:ring-0 dark:text-white">
{{ item.quantity }}x {{ item.quantity }}x
</span> </span>
</div>
<div class="text-end md:order-4 md:w-32">
<p
class="text-base font-bold text-gray-900 dark:text-white">{{ item.product.price.price_with_tax }}
</p>
</div>
</div> </div>
<div class="text-end md:order-4 md:w-32">
<p class="text-base font-bold text-gray-900 dark:text-white">{{ item.product.price.price_with_tax }}
</p>
</div>
</div>
<div class="w-full min-w-0 flex-1 space-y-4 md:order-2 md:max-w-md"> <div class="w-full min-w-0 flex-1 space-y-4 md:order-2 md:max-w-md">
<a href="{% url 'web:product_detail' pk=item.product.pk slug=item.product.slug %}" <a href="{% url 'web:product_detail' pk=item.product.pk slug=item.product.slug %}"
class="text-base font-medium text-gray-900 hover:underline dark:text-white" class="text-base font-medium text-gray-900 hover:underline dark:text-white"
> >
{{ item.product.name }} {{ item.product.name }}
</a> </a>
<div class="flex items-center gap-4"> <div class="flex items-center gap-4">
<form class="flex" hx-post="{% url 'web:delete_cart_item' pk=item.pk %}" <form class="flex" hx-post="{% url 'web:delete_cart_item' pk=item.pk %}"
hx-swap="none"> hx-swap="none">
{% csrf_token %} {% csrf_token %}
<button <button
type="submit" type="submit"
class="inline-flex items-center text-sm font-medium text-red-600 hover:underline dark:text-red-500" class="inline-flex items-center text-sm font-medium text-red-600 hover:underline dark:text-red-500"
> >
{% include 'components/icons/x.svg' %} {% include 'components/icons/x.svg' %}
{% translate 'Quitar' %} {% translate 'Quitar' %}
</button> </button>
</form> </form>
</div>
</div> </div>
</div> </div>
</div> </div>
</div> {% endfor %}
{% endfor %}
{% if items.count == 0 %} {% if items.count == 0 %}
<span class="text-base font-normal text-gray-500 dark:text-gray-400"> <p class="text-base font-normal text-gray-500 dark:text-gray-400">
{% translate 'No hay nada en el carrito...' %} {% translate 'No hay nada en el carrito...' %}
</span> </p>
{% endif %} <p>
</div> <a href="{% url 'web:index' %}" title=""
</div> class="inline-flex items-center gap-2 text-sm font-medium text-primary-700 underline hover:no-underline dark:text-primary-500">
{% translate 'Volver a inicio' %}
</a>
</p>
{% endif %}
<div class="mx-auto mt-6 max-w-4xl flex-1 space-y-6 lg:mt-0 lg:w-full"> <section>
<div <h2 class="text-xl font-semibold text-gray-900 dark:text-white sm:text-2xl">
class="space-y-4 rounded-lg border border-gray-200 bg-white p-4 shadow-sm dark:border-gray-700 dark:bg-gray-800 sm:p-6"> {% translate 'Dirección de envío' %}
<p class="text-xl font-semibold text-gray-900 dark:text-white">{% translate 'Resumen del pedido' %}</p> </h2>
<div class="space-y-4"> {% for address in shipping_addresses %}
<div class="space-y-2"> <input type="radio" id="shipping_address_{{ address.pk }}" name="shipping_address"
<dl class="flex items-center justify-between gap-4"> value="{{ address.pk }}">
<dt class="text-base font-normal text-gray-500 dark:text-gray-400">{% translate 'Base imponible' %}</dt> <label for="shipping_address_{{ address.pk }}">
<dd class="text-base font-medium text-gray-900 dark:text-white">{{ base_total }} €</dd> {{ address }}
</dl> </label>
{% endfor %}
</section>
<dl class="flex items-center justify-between gap-4"> <section>
<dt class="text-base font-normal text-gray-500 dark:text-gray-400">{% translate 'Descuentos' %}</dt> <h2 class="text-xl font-semibold text-gray-900 dark:text-white sm:text-2xl">
<dd class="text-base font-medium text-green-600">-{{ savings }} €</dd> {% translate 'Dirección de facturación' %}
</dl> </h2>
<dl class="flex items-center justify-between gap-4"> {% for address in billing_addresses %}
<dt <input type="radio" id="billing_address_{{ address.pk }}" name="billing_address"
class="text-base font-normal text-gray-500 dark:text-gray-400">{% translate 'Gastos de envío' %}</dt> value="{{ address.pk }}">
<dd class="text-base font-medium text-gray-900 dark:text-white">{{ shipping_cost }} €</dd> <label for="billing_address_{{ address.pk }}">
</dl> {{ address }}
</label>
{% endfor %}
</section>
<dl class="flex items-center justify-between gap-4"> <section>
<dt class="text-base font-normal text-gray-500 dark:text-gray-400">{% translate 'Impuestos' %}</dt> <h2 class="text-xl font-semibold text-gray-900 dark:text-white sm:text-2xl">
<dd class="text-base font-medium text-gray-900 dark:text-white">{{ tax_total }} €</dd> {% translate 'Opciones de envío' %}
</dl> </h2>
</div>
<dl class="flex items-center justify-between gap-4 border-t border-gray-200 pt-2 dark:border-gray-700"> {% for shipping_method in shipping_methods %}
<dt class="text-base font-bold text-gray-900 dark:text-white">{% translate 'Total' %}</dt> <input type="radio" id="shipping_method_{{ shipping_method.pk }}" name="shipping_method"
<dd class="text-base font-bold text-gray-900 dark:text-white">{{ total }} €</dd> value="{{ shipping_method.pk }}">
</dl> <label for="shipping_method_{{ shipping_method.pk }}">
{{ shipping_method.name }} - {{ shipping_method.shipping_product.price.price_with_tax }}€
</label>
{% endfor %}
</section>
</div> </div>
<form action=""> </div>
<!-- Resumen del pedido -->
<div class="mx-auto mt-6 max-w-4xl flex-1 space-y-6 lg:mt-0 lg:w-full">
<div
class="space-y-4 rounded-lg border border-gray-200 bg-white p-4 shadow-sm dark:border-gray-700 dark:bg-gray-800 sm:p-6">
<p class="text-xl font-semibold text-gray-900 dark:text-white">{% translate 'Resumen del pedido' %}</p>
<div class="space-y-4">
<div class="space-y-2">
<dl class="flex items-center justify-between gap-4">
<dt
class="text-base font-normal text-gray-500 dark:text-gray-400">{% translate 'Base imponible' %}</dt>
<dd class="text-base font-medium text-gray-900 dark:text-white">{{ base_total }} €</dd>
</dl>
<dl class="flex items-center justify-between gap-4">
<dt class="text-base font-normal text-gray-500 dark:text-gray-400">{% translate 'Descuentos' %}</dt>
<dd class="text-base font-medium text-green-600">-{{ savings }} €</dd>
</dl>
<dl class="flex items-center justify-between gap-4">
<dt
class="text-base font-normal text-gray-500 dark:text-gray-400">{% translate 'Gastos de envío' %}</dt>
<dd class="text-base font-medium text-gray-900 dark:text-white">{{ shipping_cost }} €</dd>
</dl>
<dl class="flex items-center justify-between gap-4">
<dt class="text-base font-normal text-gray-500 dark:text-gray-400">{% translate 'Impuestos' %}</dt>
<dd class="text-base font-medium text-gray-900 dark:text-white">{{ tax_total }} €</dd>
</dl>
</div>
<dl class="flex items-center justify-between gap-4 border-t border-gray-200 pt-2 dark:border-gray-700">
<dt class="text-base font-bold text-gray-900 dark:text-white">{% translate 'Total' %}</dt>
<dd class="text-base font-bold text-gray-900 dark:text-white">{{ total }} €</dd>
</dl>
</div>
<button <button
class="flex w-full items-center justify-center rounded-lg bg-primary-700 px-5 py-2.5 text-sm font-medium text-white hover:bg-primary-800 focus:outline-none focus:ring-4 focus:ring-primary-300 dark:bg-primary-600 dark:hover:bg-primary-700 dark:focus:ring-primary-800"> class="flex w-full items-center justify-center rounded-lg bg-primary-700 px-5 py-2.5 text-sm font-medium text-white hover:bg-primary-800 focus:outline-none focus:ring-4 focus:ring-primary-300 dark:bg-primary-600 dark:hover:bg-primary-700 dark:focus:ring-primary-800">
{% translate 'Ir a pagar' %} {% translate 'Terminar pedido' %}
</button> </button>
</form>
<div class="flex items-center justify-center gap-2"> <div class="flex items-center justify-center gap-2">
<span class="text-sm font-normal text-gray-500 dark:text-gray-400"> {% translate 'o' %} </span> <span class="text-sm font-normal text-gray-500 dark:text-gray-400"> {% translate 'o' %} </span>
<a href="{% url 'web:index' %}" title="" <a href="{% url 'web:index' %}" title=""
class="inline-flex items-center gap-2 text-sm font-medium text-primary-700 underline hover:no-underline dark:text-primary-500"> class="inline-flex items-center gap-2 text-sm font-medium text-primary-700 underline hover:no-underline dark:text-primary-500">
{% translate 'Seguir comprando' %} {% translate 'Seguir comprando' %}
</a> </a>
</div>
</div> </div>
</div>
{# <div#} {# <div#}
{# class="space-y-4 rounded-lg border border-gray-200 bg-white p-4 shadow-sm dark:border-gray-700 dark:bg-gray-800 sm:p-6">#} {# class="space-y-4 rounded-lg border border-gray-200 bg-white p-4 shadow-sm dark:border-gray-700 dark:bg-gray-800 sm:p-6">#}
{# <form class="space-y-4">#} {# <form class="space-y-4">#}
{# <div>#} {# <div>#}
{# <label for="voucher" class="mb-2 block text-sm font-medium text-gray-900 dark:text-white"> Do you have a#} {# <label for="voucher" class="mb-2 block text-sm font-medium text-gray-900 dark:text-white"> Do you have a#}
{# voucher or gift card? </label>#} {# voucher or gift card? </label>#}
{# <input type="text" id="voucher"#} {# <input type="text" id="voucher"#}
{# class="block w-full rounded-lg border border-gray-300 bg-gray-50 p-2.5 text-sm text-gray-900 focus:border-primary-500 focus:ring-primary-500 dark:border-gray-600 dark:bg-gray-700 dark:text-white dark:placeholder:text-gray-400 dark:focus:border-primary-500 dark:focus:ring-primary-500"#} {# class="block w-full rounded-lg border border-gray-300 bg-gray-50 p-2.5 text-sm text-gray-900 focus:border-primary-500 focus:ring-primary-500 dark:border-gray-600 dark:bg-gray-700 dark:text-white dark:placeholder:text-gray-400 dark:focus:border-primary-500 dark:focus:ring-primary-500"#}
{# placeholder="" required/>#} {# placeholder="" required/>#}
{# </div>#} {# </div>#}
{# <button type="submit"#} {# <button type="submit"#}
{# class="flex w-full items-center justify-center rounded-lg bg-primary-700 px-5 py-2.5 text-sm font-medium text-white hover:bg-primary-800 focus:outline-none focus:ring-4 focus:ring-primary-300 dark:bg-primary-600 dark:hover:bg-primary-700 dark:focus:ring-primary-800">#} {# class="flex w-full items-center justify-center rounded-lg bg-primary-700 px-5 py-2.5 text-sm font-medium text-white hover:bg-primary-800 focus:outline-none focus:ring-4 focus:ring-primary-300 dark:bg-primary-600 dark:hover:bg-primary-700 dark:focus:ring-primary-800">#}
{# Apply Code#} {# Apply Code#}
{# </button>#} {# </button>#}
{# </form>#} {# </form>#}
{# </div>#} {# </div>#}
</div>
</div> </div>
</div> </div>
</div> </form>
</section> </section>
@@ -74,7 +74,7 @@
class="mb-2 me-2 inline-flex w-full items-center justify-center rounded-lg bg-primary-700 px-5 py-2.5 text-sm class="mb-2 me-2 inline-flex w-full items-center justify-center rounded-lg bg-primary-700 px-5 py-2.5 text-sm
font-medium text-white hover:bg-primary-800 focus:outline-none focus:ring-4 focus:ring-primary-300 font-medium text-white hover:bg-primary-800 focus:outline-none focus:ring-4 focus:ring-primary-300
dark:bg-primary-600 dark:hover:bg-primary-700 dark:focus:ring-primary-800" dark:bg-primary-600 dark:hover:bg-primary-700 dark:focus:ring-primary-800"
role="button"> {% translate 'Ir a pagar' %} role="button"> {% translate 'Comprar' %}
</a> </a>
{% endif %} {% endif %}
+19 -1
View File
@@ -4,7 +4,14 @@ from django.shortcuts import get_object_or_404, render
from django.views.generic import TemplateView from django.views.generic import TemplateView
from shop.filters import ProductFilter from shop.filters import ProductFilter
from shop.models import CartItem, Product, ProductPrice, WishlistedProduct from shop.models import (
CartItem,
Product,
ProductPrice,
WishlistedProduct,
CustomerAddress,
ShippingMethod,
)
from web.mixins import FilteredQuerysetMixin, PaginatedQuerysetMixin from web.mixins import FilteredQuerysetMixin, PaginatedQuerysetMixin
from web.settings import ANONYMOUS_CART_ID_COOKIE_NAME from web.settings import ANONYMOUS_CART_ID_COOKIE_NAME
from web.utils import get_or_create_cart from web.utils import get_or_create_cart
@@ -84,6 +91,14 @@ def cart(request, *args, **kwargs):
total = base_total + tax_total total = base_total + tax_total
billing_addresses = CustomerAddress.objects.filter(
user=request.user, address_type=CustomerAddress.Types.BILLING
)
shipping_addresses = CustomerAddress.objects.filter(
user=request.user, address_type=CustomerAddress.Types.SHIPPING
)
shipping_methods = ShippingMethod.objects.all()
response = render( response = render(
request, request,
"components/cart/cart.html", "components/cart/cart.html",
@@ -93,6 +108,9 @@ def cart(request, *args, **kwargs):
"total": total, "total": total,
"base_total": base_total, "base_total": base_total,
"tax_total": tax_total, "tax_total": tax_total,
"billing_addresses": billing_addresses,
"shipping_addresses": shipping_addresses,
"shipping_methods": shipping_methods,
}, },
) )
+41 -4
View File
@@ -1,10 +1,19 @@
from django.http.response import HttpResponse, JsonResponse from django.http.response import HttpResponse, JsonResponse
from django.shortcuts import get_object_or_404 from django.shortcuts import get_object_or_404, redirect, reverse
from django.utils.text import gettext_lazy as _ from django.utils.text import gettext_lazy as _
from django.views.decorators.http import require_http_methods from django.views.decorators.http import require_http_methods
from django.views.generic import TemplateView from django.views.generic import TemplateView, CreateView
from shop.models import CartItem, Product, ProductCategory, WishlistedProduct from shop.models import (
CartItem,
Product,
ProductCategory,
WishlistedProduct,
CustomerAddress,
ShippingMethod,
)
from shop.utils import create_order_from_cart
from web.forms import CreateOrderForm
from web.models import WebSettings from web.models import WebSettings
from web.settings import ANONYMOUS_CART_ID_COOKIE_NAME from web.settings import ANONYMOUS_CART_ID_COOKIE_NAME
from web.utils import get_or_create_cart from web.utils import get_or_create_cart
@@ -55,8 +64,9 @@ class ProductDetail(TemplateView):
} }
class CartDetail(TemplateView): class CartDetail(TemplateView, CreateView):
template_name = "web/cart_detail.html" template_name = "web/cart_detail.html"
form_class = CreateOrderForm
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
web_settings = WebSettings.load() web_settings = WebSettings.load()
@@ -70,6 +80,33 @@ class CartDetail(TemplateView):
"description": _("resumen del carrito"), "description": _("resumen del carrito"),
} }
def post(self, request, *args, **kwargs):
form = CreateOrderForm(request.POST)
if not form.is_valid():
context = self.get_context_data(**kwargs)
context.update({"errors": form.errors})
return self.render_to_response(context)
billing_address_id = form.cleaned_data.get("billing_address")
shipping_address_id = form.cleaned_data.get("shipping_address")
shipping_method_id = form.cleaned_data.get("shipping_method")
cart, created = get_or_create_cart(request)
billing_address = get_object_or_404(CustomerAddress, pk=billing_address_id)
shipping_address = get_object_or_404(CustomerAddress, pk=shipping_address_id)
shipping_method = get_object_or_404(ShippingMethod, pk=shipping_method_id)
order = create_order_from_cart(
cart=cart,
billing_address=billing_address,
shipping_address=shipping_address,
shipping_method=shipping_method,
)
return redirect(reverse("web:order", kwargs={"pk": order.pk}))
class WishlistView(TemplateView): class WishlistView(TemplateView):
template_name = "web/wishlist.html" template_name = "web/wishlist.html"