feat: cart detail leads to order creation
This commit is contained in:
@@ -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")},
|
||||||
|
},
|
||||||
|
),
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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(
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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"
|
||||||
|
|||||||
Reference in New Issue
Block a user