generated from Code-Institute-Org/gitpod-full-template
-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #51 from JoGorska/05-checkout
05 checkout
- Loading branch information
Showing
18 changed files
with
484 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
from django.contrib import admin | ||
from .models import Order, OrderLineItem | ||
|
||
|
||
class OrderLineItemAdminInline(admin.TabularInline): | ||
''' | ||
admin for line item model | ||
admin can add and edit line items from inside the order model | ||
when we look at the order, we will see the list of editable line items | ||
without going to line item interface | ||
''' | ||
model = OrderLineItem | ||
readonly_fields = ('lineitem_total',) | ||
|
||
|
||
class OrderAdmin(admin.ModelAdmin): | ||
''' | ||
admin for order model | ||
''' | ||
# this allows to vied and edit inline items inside each order | ||
inlines = (OrderLineItemAdminInline,) | ||
|
||
readonly_fields = ('order_number', 'date', | ||
'delivery_cost', 'order_total', | ||
'grand_total', 'original_bag', 'stripe_pid',) | ||
# this settings dictates the order in which the columns are displayed | ||
# in admin | ||
fields = ('order_number', 'date', 'full_name', | ||
'email', 'phone_number', 'country', | ||
'postcode', 'town_or_city', 'street_address1', | ||
'street_address2', 'county', 'delivery_cost', | ||
'order_total', 'grand_total', 'original_bag', 'stripe_pid',) | ||
# restrict the columns that show up in order list | ||
list_display = ('order_number', 'date', 'full_name', | ||
'order_total', 'delivery_cost', | ||
'grand_total',) | ||
# most recent orders at the top | ||
ordering = ('-date',) | ||
|
||
|
||
admin.site.register(Order, OrderAdmin) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
from django.apps import AppConfig | ||
|
||
|
||
class CheckoutConfig(AppConfig): | ||
''' | ||
checkout config with method overiding ready method | ||
and imorting signals | ||
''' | ||
default_auto_field = 'django.db.models.BigAutoField' | ||
name = 'checkout' | ||
|
||
def ready(self): | ||
import checkout.signals |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
from django import forms | ||
from .models import Order | ||
|
||
|
||
class OrderForm(forms.ModelForm): | ||
''' | ||
order form | ||
''' | ||
class Meta: | ||
''' | ||
meta data - which model the form reffers to | ||
which fields from the model we want to render | ||
NOT rendering the fields that will be automaticaly calculated | ||
''' | ||
model = Order | ||
fields = ('full_name', 'email', 'phone_number', | ||
'street_address1', 'street_address2', | ||
'town_or_city', 'postcode', 'country', | ||
'county',) | ||
|
||
def __init__(self, *args, **kwargs): | ||
""" | ||
overrides form __init__ method | ||
Add placeholders and classes, remove auto-generated | ||
labels and set autofocus on first field | ||
""" | ||
super().__init__(*args, **kwargs) | ||
# adds nice placeholders | ||
placeholders = { | ||
'full_name': 'Full Name', | ||
'email': 'Email Address', | ||
'phone_number': 'Phone Number', | ||
'postcode': 'Postal Code', | ||
'town_or_city': 'Town or City', | ||
'street_address1': 'Street Address 1', | ||
'street_address2': 'Street Address 2', | ||
'county': 'County', | ||
} | ||
# sets autofocus on full name field | ||
self.fields['full_name'].widget.attrs['autofocus'] = True | ||
# makes country readonly | ||
self.fields['country'].widget.attrs['readonly'] = True | ||
|
||
for field in self.fields: | ||
if field != 'country': | ||
if self.fields[field].required: | ||
# ads a star for fields that are required on the model | ||
placeholder = f'{placeholders[field]} *' | ||
else: | ||
placeholder = placeholders[field] | ||
# sets attributes for place holder and class and removes labels | ||
self.fields[field].widget.attrs['placeholder'] = placeholder | ||
self.fields[field].widget.attrs['class'] = 'stripe-style-input' | ||
self.fields[field].label = False |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
# Generated by Django 3.2 on 2022-04-03 18:05 | ||
|
||
from django.db import migrations, models | ||
import django.db.models.deletion | ||
|
||
|
||
class Migration(migrations.Migration): | ||
|
||
initial = True | ||
|
||
dependencies = [ | ||
('trees', '0009_auto_20220329_0820'), | ||
] | ||
|
||
operations = [ | ||
migrations.CreateModel( | ||
name='Order', | ||
fields=[ | ||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), | ||
('order_number', models.CharField(editable=False, max_length=32)), | ||
('full_name', models.CharField(max_length=50)), | ||
('email', models.EmailField(max_length=254)), | ||
('phone_number', models.CharField(max_length=20)), | ||
('country', models.CharField(default='GB', max_length=2)), | ||
('postcode', models.CharField(blank=True, max_length=20, null=True)), | ||
('town_or_city', models.CharField(max_length=40)), | ||
('street_address1', models.CharField(max_length=80)), | ||
('street_address2', models.CharField(blank=True, max_length=80, null=True)), | ||
('county', models.CharField(blank=True, max_length=80, null=True)), | ||
('date', models.DateTimeField(auto_now_add=True)), | ||
('delivery_cost', models.DecimalField(decimal_places=2, default=0, max_digits=6)), | ||
('order_total', models.DecimalField(decimal_places=2, default=0, max_digits=10)), | ||
('grand_total', models.DecimalField(decimal_places=2, default=0, max_digits=10)), | ||
('original_bag', models.TextField(default='')), | ||
('stripe_pid', models.CharField(default='', max_length=254)), | ||
], | ||
), | ||
migrations.CreateModel( | ||
name='OrderLineItem', | ||
fields=[ | ||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), | ||
('quantity', models.IntegerField(default=0)), | ||
('lineitem_total', models.DecimalField(decimal_places=2, editable=False, max_digits=6)), | ||
('order', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='lineitems', to='checkout.order')), | ||
('tree', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='trees.tree')), | ||
], | ||
), | ||
] |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
import uuid | ||
|
||
from django.db import models | ||
from django.db.models import Sum | ||
from django.conf import settings | ||
|
||
from trees.models import Tree | ||
|
||
|
||
class Order(models.Model): | ||
""" | ||
order model | ||
""" | ||
order_number = models.CharField(max_length=32, null=False, editable=False) | ||
|
||
full_name = models.CharField(max_length=50, null=False, blank=False) | ||
|
||
email = models.EmailField(max_length=254, null=False, blank=False) | ||
phone_number = models.CharField(max_length=20, null=False, blank=False) | ||
# set to GB | ||
country = models.CharField(default='GB', max_length=2, null=False, blank=False) | ||
postcode = models.CharField(max_length=20, null=True, blank=True) | ||
town_or_city = models.CharField(max_length=40, null=False, blank=False) | ||
street_address1 = models.CharField(max_length=80, null=False, blank=False) | ||
street_address2 = models.CharField(max_length=80, null=True, blank=True) | ||
county = models.CharField(max_length=80, null=True, blank=True) | ||
date = models.DateTimeField(auto_now_add=True) | ||
delivery_cost = models.DecimalField(max_digits=6, decimal_places=2, null=False, default=0) | ||
order_total = models.DecimalField(max_digits=10, decimal_places=2, null=False, default=0) | ||
grand_total = models.DecimalField(max_digits=10, decimal_places=2, null=False, default=0) | ||
# in case if customer makes the same purchase twice | ||
original_bag = models.TextField(null=False, blank=False, default='') | ||
stripe_pid = models.CharField(max_length=254, null=False, blank=False, default='') | ||
|
||
def _generate_order_number(self): | ||
""" | ||
Generate a random, unique order number using UUID | ||
_ before name means it is private method to be used only inside this class | ||
""" | ||
return uuid.uuid4().hex.upper() | ||
|
||
def update_total(self): | ||
""" | ||
Update grand total each time a line item is added, | ||
accounting for delivery costs. | ||
""" | ||
# or 0 is to prevent setting order_total to none if we manually | ||
# delete all orders from the database | ||
self.order_total = self.lineitems.aggregate(Sum('lineitem_total'))['lineitem_total__sum'] or 0 | ||
if self.order_total < settings.FREE_DELIVERY_THRESHOLD: | ||
self.delivery_cost = self.order_total * settings.STANDARD_DELIVERY_PERCENTAGE / 100 | ||
else: | ||
self.delivery_cost = 0 | ||
self.grand_total = self.order_total + self.delivery_cost | ||
self.save() | ||
|
||
def save(self, *args, **kwargs): | ||
""" | ||
Override the original save method to set the order number | ||
if it hasn't been set already. | ||
""" | ||
if not self.order_number: | ||
self.order_number = self._generate_order_number() | ||
super().save(*args, **kwargs) | ||
|
||
def __str__(self): | ||
return self.order_number | ||
|
||
|
||
class OrderLineItem(models.Model): | ||
""" | ||
individual shopping bag item, relating to specific order from the order model | ||
total cost for this specific item | ||
the function will iterate through order and get each item as a seperate line item | ||
this one will update delivery cost, order total and grand total | ||
""" | ||
order = models.ForeignKey(Order, null=False, blank=False, on_delete=models.CASCADE, related_name='lineitems') | ||
tree = models.ForeignKey(Tree, null=False, blank=False, on_delete=models.CASCADE) | ||
quantity = models.IntegerField(null=False, blank=False, default=0) | ||
lineitem_total = models.DecimalField(max_digits=6, decimal_places=2, null=False, blank=False, editable=False) | ||
|
||
def save(self, *args, **kwargs): | ||
""" | ||
Override the original save method to set the lineitem total | ||
and update the order total. | ||
""" | ||
self.lineitem_total = self.tree.price * self.quantity | ||
super().save(*args, **kwargs) | ||
|
||
def __str__(self): | ||
return f'Slug {self.tree.slug} on order {self.order.order_number}' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
|
||
from django.db.models.signals import post_save, post_delete | ||
from django.dispatch import receiver | ||
|
||
from .models import OrderLineItem | ||
|
||
@receiver(post_save, sender=OrderLineItem) | ||
def update_on_save(sender, instance, created, **kwargs): | ||
""" | ||
handles signals from post save event | ||
sender of the signal - update/ create order line item | ||
instance of the model - sends it | ||
created - boleon if this is a new instance or update | ||
receiver decorator received post save signal from orderlineitem | ||
""" | ||
instance.order.update_total() | ||
|
||
|
||
@receiver(post_delete, sender=OrderLineItem) | ||
def update_on_delete(sender, instance, **kwargs): | ||
""" | ||
Update order total on lineitem delete | ||
""" | ||
instance.order.update_total() |
Oops, something went wrong.