Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Chapter 9, Coupons impleted #11

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions cart/cart.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from django.conf import settings

from shop.models import Product
from coupons.models import Coupon
from common.constants import (KEY_PRICE, KEY_QUANTITY, KEY_PRODUCT, KEY_TOTAL_PRICE)


Expand All @@ -15,11 +16,29 @@ def __init__(self, request):
"""

self.session = request.session
self.coupon_id = self.session.get('coupon_id')
cart = self.session.get(settings.SESSION_CART_ID)
if not cart:
cart = self.session[settings.SESSION_CART_ID] = {}
self.cart = cart

@property
def coupon(self):
if self.coupon_id:
try:
return Coupon.objects.get(id=self.coupon_id)
except Coupon.DoesNotExist:
pass
return None

def get_discount(self):
if self.coupon:
return (self.coupon.discount / Decimal(100)) * self.get_total_price()
return Decimal(0)

def get_price_after_coupon(self):
return self.get_total_price() - self.get_discount()

def add(self, product, quantity=1, override_quantity=False):
"""
Add Item in cart or update its quantity
Expand Down
4 changes: 3 additions & 1 deletion cart/forms.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
from django import forms
from django.utils.translation import gettext_lazy as _

PRODUCT_QUANTITY_CHOICES = [(i, str(i)) for i in range(1, 21)]


class CartAddProductForm(forms.Form):
quantity = forms.TypedChoiceField(choices=PRODUCT_QUANTITY_CHOICES, coerce=int)
quantity = forms.TypedChoiceField(choices=PRODUCT_QUANTITY_CHOICES, coerce=int,
label=_('Quantity'))
override = forms.BooleanField(required=False, initial=False, widget=forms.HiddenInput)

53 changes: 40 additions & 13 deletions cart/templates/cart/cart_details.html
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
{% extends 'shop/base.html' %}
{% load static %}
{% load i18n %}

{% block title %} Your shopping Cart {% endblock %}
{% block title %} {% trans 'Your shopping Cart' %} {% endblock %}

{% block content %}
<h1>Your shopping cart</h1>
<h1>{% trans 'Your shopping cart' %}</h1>
<table class="cart">
<thead>
<tr>
<th>Image</th>
<th>Product</th>
<th>Quantity</th>
<th>Remove</th>
<th>Unit Price</th>
<th>Price</th>
<th>{% trans 'Image' %}</th>
<th>{% trans 'Product' %}</th>
<th>{% trans 'Quantity' %}</th>
<th>{% trans 'Remove' %}</th>
<th>{% trans 'Unit Price' %}</th>
<th>{% trans 'Price' %}</th>
</tr>
</thead>
<tbody>
Expand All @@ -36,24 +37,50 @@ <h1>Your shopping cart</h1>
</td>
<td>
<form action='{% url "cart:cart_remove" product.id %}' method="post">
<input type="submit" value="Remove Item">
<input type="submit" value="{% trans 'Remove Item' %}">
{% csrf_token %}
</form>
</td>
<td>{{item.price}}</td>
<td>{{item.total_price}}</td>
</tr>
{% if cart.coupon %}
<tr class="subtotal">
<td>{% trans 'Subtotal' %}</td>
<td colspan="4"></td>
<td class="num">${{cart.get_total_price|floatformat:2}}</td>
</tr>
<tr>
<td>
"{{cart.coupon.code}}" coupon
({{cart.coupon.discount}} off)
</td>
<td colspan="4"></td>
<td class="num neg">
- ${{cart.get_discount|floatformat:2}}
</td>
</tr>
{% endif %}
{% endwith %}
{% endfor %}
<tr>
<td>Total</td>
<td>{% trans 'Total' %}</td>
<td colspan="4"></td>
<td class="num">${{cart.get_total_price}}</td>
<td class="num">${{cart.get_price_after_coupon|floatformat:2}}</td>
</tr>
</tbody>
</table>
<div class="coupon">
<p>{% trans 'Apply a Coupon' %}</p>
<form action="{% url 'coupons:apply' %}" method="post">
{{coupon_apply_form}}
{% csrf_token %}
<input type="submit" value="{% trans 'Apply' %}">
</form>
</div>

<p class="text-right">
<a href="{% url 'shop:product_list' %}" class="button light">Continue Shopping</a>
<a href="{% url 'orders:order_create' %}" class="button">Checkout</a>
<a href="{% url 'shop:product_list' %}" class="button light">{% trans 'Continue Shopping' %}</a>
<a href="{% url 'orders:order_create' %}" class="button">{% trans 'Checkout' %}</a>
</p>
{% endblock %}
5 changes: 4 additions & 1 deletion cart/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from .cart import Cart
from .forms import CartAddProductForm
from shop.models import Product
from coupons.forms import CouponApplyForm
from common.constants import KEY_QUANTITY


Expand All @@ -30,8 +31,10 @@ def cart_remove(request, product_id):

def cart_details(request):
cart = Cart(request)
coupon_apply_form = CouponApplyForm()
for item in cart:
item['product_update_form'] = CartAddProductForm(initial={
KEY_QUANTITY: item.get(KEY_QUANTITY), 'override': True
})
return render(request, 'cart/cart_details.html', {'cart': cart})
return render(request, 'cart/cart_details.html', {'cart': cart,
'coupon_apply_form': coupon_apply_form})
Empty file added coupons/__init__.py
Empty file.
10 changes: 10 additions & 0 deletions coupons/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from django.contrib import admin

from .models import Coupon


@admin.register(Coupon)
class CouponAdmin(admin.ModelAdmin):
list_display = ['code', 'valid_from', 'valid_to', 'discount', 'active', 'total_coupons', 'availed_coupons']
list_filter = ['active', 'valid_to', 'valid_from']
search_fields = ['code']
5 changes: 5 additions & 0 deletions coupons/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from django.apps import AppConfig


class CouponsConfig(AppConfig):
name = 'coupons'
6 changes: 6 additions & 0 deletions coupons/forms.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from django import forms
from django.utils.translation import gettext_lazy as _


class CouponApplyForm(forms.Form):
code = forms.CharField(label=_('Code'))
28 changes: 28 additions & 0 deletions coupons/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Generated by Django 3.1.3 on 2021-12-31 18:47

import django.core.validators
from django.db import migrations, models


class Migration(migrations.Migration):

initial = True

dependencies = [
]

operations = [
migrations.CreateModel(
name='Coupon',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('code', models.CharField(db_index=True, max_length=32, unique=True)),
('valid_from', models.DateTimeField()),
('valid_to', models.DateTimeField()),
('discount', models.IntegerField(validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(100)])),
('active', models.BooleanField()),
('total_coupons', models.PositiveIntegerField(default=3)),
('availed_coupons', models.PositiveIntegerField(default=0)),
],
),
]
Empty file added coupons/migrations/__init__.py
Empty file.
15 changes: 15 additions & 0 deletions coupons/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from django.db import models
from django.core.validators import MinValueValidator, MaxValueValidator


class Coupon(models.Model):
code = models.CharField(max_length=32, unique=True, db_index=True)
valid_from = models.DateTimeField()
valid_to = models.DateTimeField()
discount = models.IntegerField(validators=[MinValueValidator(0), MaxValueValidator(100)])
active = models.BooleanField()
total_coupons = models.PositiveIntegerField(default=3)
availed_coupons = models.PositiveIntegerField(default=0)

def __str__(self):
return f"Coupon code:{self.code}"
3 changes: 3 additions & 0 deletions coupons/tests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from django.test import TestCase

# Create your tests here.
10 changes: 10 additions & 0 deletions coupons/urls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from django.urls import path

from .views import coupon_apply

app_name = 'coupons'


urlpatterns = [
path('apply/', coupon_apply, name='apply')
]
21 changes: 21 additions & 0 deletions coupons/views.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
from django.utils import timezone
from django.shortcuts import render, redirect
from django.views.decorators.http import require_POST

from .models import Coupon
from .forms import CouponApplyForm


@require_POST
def coupon_apply(request):
now = timezone.now()
form = CouponApplyForm(request.POST)
if form.is_valid():
code = form.cleaned_data['code']
try:
coupon = Coupon.objects.get(code=code, valid_from__lte=now, valid_to__gte=now, active=True)
request.session['coupon_id'] = coupon.id
except Coupon.DoesNotExist:
request.session['coupon_id'] = None

return redirect('cart:cart_details')
17 changes: 16 additions & 1 deletion eShop/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

import os
import braintree
from django.utils.translation import gettext_lazy as _
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)

BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
Expand Down Expand Up @@ -39,11 +40,13 @@
'cart.apps.CartConfig',
'orders.apps.OrdersConfig',
'payment.apps.PaymentConfig',
'coupons.apps.CouponsConfig'
]

MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.locale.LocaleMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
Expand Down Expand Up @@ -106,7 +109,12 @@
# Internationalization
# https://docs.djangoproject.com/en/2.2/topics/i18n/

LANGUAGE_CODE = 'en-us'
LANGUAGE_CODE = 'en'
LANGUAGES = (
('en', _('English')),
('es', _('Spanish')),
('ur', _('Urdu')),
)

TIME_ZONE = 'UTC'

Expand All @@ -119,6 +127,9 @@
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media/')
STATIC_ROOT = os.path.join(BASE_DIR, 'static/')
LOCALE_PATHS = (
os.path.join(BASE_DIR, 'locale/'),
)

# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/2.2/howto/static-files/
Expand All @@ -133,6 +144,10 @@
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
# EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'

REDIS_HOST = 'localhost'
REDIS_PORT = '6379'
REDIS_DB = 1

try:
from .local_settings import * # pylint: disable=all
except: # pylint: disable=bare-except
Expand Down
6 changes: 4 additions & 2 deletions eShop/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,15 @@
from django.conf import settings
from django.urls import path, include
from django.conf.urls.static import static
from django.conf.urls.i18n import i18n_patterns

urlpatterns = [
urlpatterns = i18n_patterns(
path('admin/', admin.site.urls),
path('payment/', include('payment.urls', namespace='payment')),
path('cart/', include('cart.urls', namespace='cart')),
path('orders/', include('orders.urls', namespace='orders')),
path('coupons/', include('coupons.urls', namespace='coupons')),
path('', include('shop.urls', namespace='shop')),
]
)
if settings.DEBUG:
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
Loading