diff --git a/shop/migrations/0005_alter_order_options_order_amount_paid_and_more.py b/shop/migrations/0005_alter_order_options_order_amount_paid_and_more.py new file mode 100644 index 0000000..61b291f --- /dev/null +++ b/shop/migrations/0005_alter_order_options_order_amount_paid_and_more.py @@ -0,0 +1,44 @@ +# Generated by Django 5.1.4 on 2025-01-16 17:00 + +import django.db.models.deletion +from decimal import Decimal +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("shop", "0004_shopsettings_tpv_domain"), + ] + + operations = [ + migrations.AlterModelOptions( + name="order", + options={ + "ordering": ("creation_date",), + "verbose_name": "pedido", + "verbose_name_plural": "pedidos", + }, + ), + migrations.AddField( + model_name="order", + name="amount_paid", + field=models.DecimalField( + decimal_places=2, + default=Decimal("0.00"), + max_digits=11, + verbose_name="cantidad pagada", + ), + ), + migrations.AddField( + model_name="order", + name="from_cart", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to="shop.cart", + verbose_name="carrito de origen de pedido", + ), + ), + ] diff --git a/shop/models.py b/shop/models.py index dd2b953..c673be9 100644 --- a/shop/models.py +++ b/shop/models.py @@ -429,6 +429,13 @@ class Order(TimestampedModel): max_length=32, blank=True, verbose_name=_("teléfono de contacto") ) + amount_paid = models.DecimalField( + default=Decimal("0.00"), + max_digits=11, + decimal_places=2, + verbose_name=_("cantidad pagada"), + ) + shipping_method = models.ForeignKey( "shop.ShippingMethod", blank=True, @@ -437,6 +444,14 @@ class Order(TimestampedModel): verbose_name=_("método de envío"), ) + from_cart = models.ForeignKey( + "shop.Cart", + on_delete=models.SET_NULL, + blank=True, + null=True, + verbose_name=_("carrito de origen de pedido"), + ) + def calculate_total_from_lines(self): self.base_total = self.lines.aggregate(base_total=models.Sum("base_total")).get( "base_total" diff --git a/shop/utils.py b/shop/utils.py index ab97de2..13bea48 100644 --- a/shop/utils.py +++ b/shop/utils.py @@ -7,6 +7,7 @@ from decimal import Decimal import pyDes from django.contrib.auth import get_user_model from django.db import transaction +from django.db.models import Sum from django.utils import timezone from django.utils.text import gettext_lazy as _ @@ -20,8 +21,10 @@ from shop.models import ( ProductBatch, ProductPrice, ShippingMethod, + CartItem, ) from shop.settings import ERROR_CODES +from shop.signals import clear_cart User = get_user_model() @@ -114,6 +117,7 @@ def create_order_from_cart( shipping_method=shipping_method, user=cart.user, email=email, + from_cart=cart, ) for item in cart.items.all(): @@ -261,15 +265,34 @@ def validate_expiry_date(expiry_date: str): return True -def pay_order(order: Order, amount_paid: Decimal): - with transaction.atomic() as tx: +def update_order_payment_status(order: Order): + if order.amount_paid >= order.total: + order.status = Order.Statuses.STATUS_PAID + elif order.status == Order.Statuses.STATUS_PAID and order.amount_paid == Decimal( + "0.00" + ): + order.status = Order.Statuses.STATUS_RETURNED + else: + order.status = Order.Statuses.STATUS_PENDING + + order.save() + + +def delete_cart_items_from_order(order): + if order.status == Order.Statuses.STATUS_PAID and order.from_cart: + CartItem.objects.filter(cart=order.from_cart).delete() + + +def add_payment_to_order(order: Order, amount): + with transaction.atomic(): payment = Payment.objects.create( - amount=amount_paid, + amount=amount, order=order, user=order.user, method=Payment.MethodChoices.REDSYS, ) + order.amount_paid += payment.amount - if amount_paid >= order.total: - order.status = Order.Statuses.STATUS_PAID - order.save() + update_order_payment_status(order) + + delete_cart_items_from_order(order) diff --git a/shop/views.py b/shop/views.py index 76c2158..b48ecf8 100644 --- a/shop/views.py +++ b/shop/views.py @@ -3,7 +3,7 @@ from django.shortcuts import get_object_or_404 from django.views.decorators.csrf import csrf_exempt from shop.models import Order -from shop.utils import pay_order, validate_payment_for_order +from shop.utils import validate_payment_for_order, add_payment_to_order @csrf_exempt @@ -12,7 +12,7 @@ def webhook(request, uuid): try: amount_paid = validate_payment_for_order(request, order) - pay_order(order, amount_paid) + add_payment_to_order(order, amount_paid) return HttpResponse(status=200) except Exception as e: diff --git a/web/forms.py b/web/forms.py index ee9ba4f..66cc568 100644 --- a/web/forms.py +++ b/web/forms.py @@ -8,6 +8,7 @@ class CartItemForm(forms.Form): """ + product = forms.IntegerField(widget=widgets.HiddenInput) quantity = forms.IntegerField() diff --git a/web/tests/test_manifest.py b/web/tests/test_manifest.py index 93db4a5..b7bf3cb 100644 --- a/web/tests/test_manifest.py +++ b/web/tests/test_manifest.py @@ -11,10 +11,13 @@ class TestManifest(TestCase): def setUp(self): self.settings = WebSettings.load() - with open(os.path.join(settings.BASE_DIR, 'web', 'tests', 'images', 'trolley.png'), 'rb') as f: - file_128 = SimpleUploadedFile('logo_128.png', f.read()) + with open( + os.path.join(settings.BASE_DIR, "web", "tests", "images", "trolley.png"), + "rb", + ) as f: + file_128 = SimpleUploadedFile("logo_128.png", f.read()) self.settings.logo_128 = file_128 - file_240 = SimpleUploadedFile('logo_240.png', f.read()) + file_240 = SimpleUploadedFile("logo_240.png", f.read()) self.settings.logo_240 = file_240 self.settings.logo = file_240 self.settings.save() diff --git a/web/tests/test_orders.py b/web/tests/test_orders.py index e23a008..adbeca3 100644 --- a/web/tests/test_orders.py +++ b/web/tests/test_orders.py @@ -4,14 +4,23 @@ from django.contrib.auth.models import User from django.test import TestCase from django.urls import reverse -from shop.models import Cart, CartItem, Order, CustomerAddress, ShippingMethod, ShopSettings +from shop.models import ( + Cart, + CartItem, + Order, + CustomerAddress, + ShippingMethod, + ShopSettings, +) from shop.tests.mixins import CreateProductsMixin class TestOrders(TestCase, CreateProductsMixin): def setUp(self): self.product = self.create_product() - self.user = User.objects.create_user("anakin", "anakin@skywalker.com", "ihatesand") + self.user = User.objects.create_user( + "anakin", "anakin@skywalker.com", "ihatesand" + ) self.cart_for_user = self.create_cart(user=self.user) self.anonymous_cart = self.create_cart() self.address = CustomerAddress.objects.create(