Aula 63 – Loja Online – Finalizando o Checkout

Aula 63 – Loja Online – Checkout

Loja Online - Django - Checkout

Loja Online – Django – Checkout

Voltar para página principal do blog

Todas as aulas desse curso

Aula 62               Aula 64

Se gostarem do conteúdo dêem um joinha 👍 na página do Código Fluente no
Facebook

Esse é o link do código fluente no Pinterest

Meus links de afiliados:

Hostinger

Digital Ocean

One.com

Melhore seu NETWORKING

https://digitalinnovation.one/

Participe de comunidades de desenvolvedores:

Fiquem a vontade para me adicionar ao linkedin.

E também para me seguir no https://github.com/toticavalcanti.

Código final da aula:

https://github.com/toticavalcanti

Quer aprender python3 de graça e com certificado? Acesse então:

https://workover.com.br/python-codigo-fluente

Toti:

https://www.youtube.com/channel/UCUEtjLuDpcOvR3mIUr-viOA

Backing track / Play-along:

https://www.youtube.com/channel/UCT3TryVMqTqYBjf5g5WAHfA

Código Fluente

https://www.youtube.com/channel/UCgn-O-88XBAwdG9gUWkkb0w

Putz!

https://www.youtube.com/channel/UCZXop2-CECwyFYmHbhnAkAw

Aula 63 – Loja Online – Finalizando o Checkout

Criamos um objeto carrinho no views.py do carts:


def cart_home(request):
    cart_obj, new_obj = Cart.objects.new_or_get(request)
    return render(request, "carts/home.html", {"cart": cart_obj}) 

Temos o método de atualização do carrinho:


def cart_update(request):
    product_id = request.POST.get('product_id')
    if product_id is not None:
        try:
            product_obj = Product.objects.get(id=product_id)
        except Product.DoesNotExist:
            print("Mostrar mensagem ao usuário, esse produto acabou!")
            return redirect("cart:home")
        cart_obj, new_obj = Cart.objects.new_or_get(request)
        if product_obj in cart_obj.products.all():
            cart_obj.products.remove(product_obj)
        else:
            cart_obj.products.add(product_obj) # cart_obj.products.add(product_id)
        request.session['cart_items'] = cart_obj.products.count()
        # return redirect(product_obj.get_absolute_url())
    return redirect("cart:home")

No model.py do carts na classe CartManager função new_or_get() na linha 25 temos:


request.session['cart_id'] = cart_obj.id

Precisamos trazer esse objeto carrinho para o views.py do carts, onde teremos um método para finalizar o pedido, e nele iremos deletar o carrinho com aquele id específico, uma vez que o pedido foi concluído.

Precisaremos fazer o update do status do order para:  done (concluído) ou paid (pago).

E também precisaremos redirecionar o usuário para uma página de sucesso.

Então vamos começar a implementar isso.

Abra o views.py do cart e faça as modificações indicadas em azul no código abaixo.

django_ecommerce/e_commerce/carts/views.py 


from django.shortcuts import render, redirect

from accounts.forms import LoginForm, GuestForm
from accounts.models import GuestEmail

from addresses.forms import AddressForm
from addresses.models import Address

from billing.models import BillingProfile
from orders.models import Order
from products.models import Product
from .models import Cart

def cart_home(request):
    cart_obj, new_obj = Cart.objects.new_or_get(request)
    return render(request, "carts/home.html", {"cart": cart_obj})

def cart_update(request):
    product_id = request.POST.get('product_id')
    if product_id is not None:
        try:
            product_obj = Product.objects.get(id=product_id)
        except Product.DoesNotExist:
            print("Mostrar mensagem ao usuário, esse produto acabou!")
            return redirect("cart:home")
        cart_obj, new_obj = Cart.objects.new_or_get(request)
        if product_obj in cart_obj.products.all():
            cart_obj.products.remove(product_obj)
        else:
            cart_obj.products.add(product_obj) # cart_obj.products.add(product_id)
        request.session['cart_items'] = cart_obj.products.count()
        # return redirect(product_obj.get_absolute_url())
    return redirect("cart:home")

def checkout_home(request):
    #aqui a gente pega o carrinho
    cart_obj, cart_created = Cart.objects.new_or_get(request)
    order_obj = None
    #se o carrinho acabou de ser criado, ele tá zerado
    #ou se o carrinho já existir mas não tiver nada dentro
    if cart_created or cart_obj.products.count() == 0:
        return redirect("cart:home")  
    
    login_form = LoginForm()
    guest_form = GuestForm()
    address_form = AddressForm()
    billing_address_id = request.session.get("billing_address_id", None)
    shipping_address_id = request.session.get("shipping_address_id", None)
    billing_profile, billing_profile_created = BillingProfile.objects.new_or_get(request)
    if billing_profile is not None:
        order_obj, order_obj_created = Order.objects.new_or_get(billing_profile, cart_obj)
        if shipping_address_id:
            order_obj.shipping_address = Address.objects.get(id = shipping_address_id)
            del request.session["shipping_address_id"]
        if billing_address_id:
            order_obj.billing_address = Address.objects.get(id = billing_address_id) 
            del request.session["billing_address_id"]
        if billing_address_id or shipping_address_id:
            order_obj.save()
    if request.method == "POST":
        #verifica se o pedido foi feito
        is_done = order_obj.check_done()
        if is_done:
            order_obj.mark_paid()
            request.session['cart_items'] = 0
            del request.session['cart_id']
            return redirect("/cart/success")
    context = {
        "object": order_obj,
        "billing_profile": billing_profile,
        "login_form": login_form,
        "guest_form": guest_form,
        "address_form": address_form,
    }
    return render(request, "carts/checkout.html", context)

Checando se o pedido está concluído

Para checar se o Order está done, vamos abrir o models.py do orders e fazer as  modificações indicadas em azul abaixo.

django_ecommerce/e_commerce/orders/models.py


import math
from django.db import models
from django.db.models.signals import pre_save, post_save
from addresses.models import Address
from billing.models import BillingProfile
from carts.models import Cart
from e_commerce.utils import unique_order_id_generator

ORDER_STATUS_CHOICES = (
    ('created', 'Criado'),
    ('paid', 'Pago'),
    ('shipped', 'Enviado'),
    ('refunded', 'Devolvido'),
)
class OrderManager(models.Manager):
    def new_or_get(self, billing_profile, cart_obj):
        created = False
        qs = self.get_queryset().filter(
                billing_profile=billing_profile, 
                cart=cart_obj, 
                active=True, 
                status='created'
            )
        if qs.count() == 1:
            obj = qs.first()
        else:
            obj = self.model.objects.create(
                    billing_profile=billing_profile, 
                    cart=cart_obj)
            created = True
        return obj, created

class Order(models.Model):
    billing_profile = models.ForeignKey(BillingProfile, on_delete=models.CASCADE, null = True, blank = True)
    order_id = models.CharField(max_length = 120, blank = True)
    # billing_profile = ?
    shipping_address = models.ForeignKey(Address, related_name="shipping_address", on_delete=models.CASCADE, null=True, blank=True)
    billing_address = models.ForeignKey(Address, related_name="billing_address", on_delete=models.CASCADE, null=True, blank=True)
    cart = models.ForeignKey(Cart, on_delete=models.CASCADE, null = True)
    status = models.CharField(max_length = 120, default = 'created', choices = ORDER_STATUS_CHOICES )
    shipping_total = models.DecimalField(default = 5.99, max_digits = 100, decimal_places = 2)
    total = models.DecimalField(default = 0.00, max_digits = 100, decimal_places = 2)
    active = models.BooleanField(default=True)

    def __str__(self):
        return self.order_id

    objects = OrderManager()

    def update_total(self):
        cart_total = self.cart.total
        shipping_total = self.shipping_total
        new_total = math.fsum([cart_total, shipping_total])
        formatted_total = format(new_total, '.2f')
        self.total = formatted_total 
        self.save()
        return new_total
    def check_done(self):
        billing_profile = self.billing_profile
        shipping_address = self.shipping_address
        billing_address = self.billing_address
        total = self.total
        if billing_profile and shipping_address and billing_address and total > 0:
            return True
        return False

    def mark_paid(self):
        if self.check_done():
            self.status = "paid"
            self.save()
        return self.status
def pre_save_create_order_id(sender, instance, *args, **kwargs):
    if not instance.order_id:
        instance.order_id = unique_order_id_generator(instance)
    # return all orders with this Cart instance, excluding those  
    # that have the same billing profile instance
    qs = Order.objects.filter(cart = instance.cart).exclude(billing_profile = instance.billing_profile)
    print("QuerySet: ", qs)
    if qs.exists():
        qs.update = False

pre_save.connect(pre_save_create_order_id, sender = Order)

def post_save_cart_total(sender, instance, created, *args, **kwargs):
    if not created:
        cart_obj = instance
        cart_total = cart_obj.total
        cart_id = cart_obj.id
        qs = Order.objects.filter(cart__id=cart_id)
        if qs.count() == 1:
            order_obj = qs.first()
            order_obj.update_total()

post_save.connect(post_save_cart_total, sender=Cart)

def post_save_order(sender, instance, created, *args, **kwargs):
    print("Executando")
    if created:
        print("Atualizando")
        instance.update_total()

post_save.connect(post_save_order, sender=Order) 

Agora vamos alterar o checkout.html do carts.

django_ecommerce/e_commerce/carts/templates/carts/checkout.html 


{% extends "base.html" %}
{% block content %}
  {{ object.order_id }} -- {{ object.cart }}
  {% if not billing_profile %}
    <div class='row text-center'>
      <div class='col-12 col-md-6'>
        <p class='lead'>Login</p>
        {% include 'accounts/snippets/form.html' with form=login_form next_url=request.build_absolute_uri %}
      </div>
      <div class='col-12 col-md-6'> 
        Continuar como Convidado
        {% url "guest_register" as guest_register_url %}
        {% include 'accounts/snippets/form.html' with form=guest_form next_url=request.build_absolute_uri action_url=guest_register_url %}
      </div>
    </div>
  {% else %} 
  {% if not object.shipping_address %}
    <div class='row'>
      <div class='col-md-6 mx-auto col-10'>
        <p class='lead'>Endereço de envio</p>
        <hr/>
        {% url "checkout_address_create" as checkout_address_create %}
        {% include 'addresses/form.html' with form=address_form next_url=request.build_absolute_uri action_url=checkout_address_create address_type='shipping' %}
      </div>
    </div>
  {% elif not object.billing_address %}
    <div class='row'>
      <div class='col-md-6 mx-auto col-10'>
        <p class='lead'>Endereço de Cobrança</p>
        <hr/>
        {% url "checkout_address_create" as checkout_address_create %}
        {% include 'addresses/form.html' with form=address_form next_url=request.build_absolute_uri action_url=checkout_address_create address_type='billing' %}
      </div>
    </div>
  {% else %}
    <h1>Finalizar Checkout</h1>
    <p>Total do Carrinho: {{ object.cart.total }}</p>
    <p>Total do Frete: {{ object.shipping_total }}</p>
    <p>Total do Pedido: {{ object.total }}</p>
    <form class='form' method='POST' action="">{% csrf_token %}
      <button type='submit'>Checkout</button>
    </form>
  {% endif %}
{% endif %}
{% endblock %}

Na próxima aula a gente vai construir a funcionalidade que dará a opção ao usuário de escolher o endereço ao invés de ter que escrever toda vez que fizer uma compra.

É isso, ficamos por aqui e até a próxima. 😉

Voltar para página principal do blog

Todas as aulas desse curso

Aula 62               Aula 64

Código final da aula:

https://github.com/toticavalcanti

Outros canais

Toti:

https://www.youtube.com/channel/UCUEtjLuDpcOvR3mIUr-viOA

Backing track / Play-along:

https://www.youtube.com/channel/UCT3TryVMqTqYBjf5g5WAHfA

Código Fluente

https://www.youtube.com/channel/UCgn-O-88XBAwdG9gUWkkb0w

Putz!

https://www.youtube.com/channel/UCZXop2-CECwyFYmHbhnAkAw

Se gostarem do conteúdo dêem um joinha 👍 na página do Código Fluente no
Facebook

Esse é o link do código fluente no Pinterest

Meus links de afiliados:

Hostinger

Digital Ocean

One.com

Nos vemos na próxima então, \o/  😉 Bons Estudos!

 

About The Author
-

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>