Skip to content
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
23 changes: 23 additions & 0 deletions NOTES.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Notes for the user flow

Product list

|

Product detail (add the item to cart)

|

Cart view (order detail view) (all the items in our cart)

|

Checkout (addresses)

|

Payment

|

Order confirmation
Empty file added cart/__init__.py
Empty file.
26 changes: 26 additions & 0 deletions cart/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
from django.contrib import admin
from .models import (
Product, OrderItem, Order, ColourVariation,
SizeVariation, Address, Payment, Category, StripePayment
)


class AddressAdmin(admin.ModelAdmin):
list_display = [
'address_line_1',
'address_line_2',
'city',
'zip_code',
'address_type',
]


admin.site.register(Category)
admin.site.register(Address, AddressAdmin)
admin.site.register(ColourVariation)
admin.site.register(Product)
admin.site.register(OrderItem)
admin.site.register(Order)
admin.site.register(SizeVariation)
admin.site.register(Payment)
admin.site.register(StripePayment)
5 changes: 5 additions & 0 deletions cart/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from django.apps import AppConfig


class CartConfig(AppConfig):
name = 'cart'
107 changes: 107 additions & 0 deletions cart/forms.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
from django.contrib.auth import get_user_model
from django import forms
from .models import (
OrderItem, ColourVariation, Product, SizeVariation,
Address
)

User = get_user_model()


class AddToCartForm(forms.ModelForm):
colour = forms.ModelChoiceField(queryset=ColourVariation.objects.none())
size = forms.ModelChoiceField(queryset=SizeVariation.objects.none())
quantity = forms.IntegerField(min_value=1)

class Meta:
model = OrderItem
fields = ['quantity', 'colour', 'size']

def __init__(self, *args, **kwargs):
self.product_id = kwargs.pop('product_id')
product = Product.objects.get(id=self.product_id)
super().__init__(*args, **kwargs)

self.fields['colour'].queryset = product.available_colours.all()
self.fields['size'].queryset = product.available_sizes.all()

def clean(self):
product_id = self.product_id
product = Product.objects.get(id=self.product_id)
quantity = self.cleaned_data['quantity']
if product.stock < quantity:
raise forms.ValidationError(
f"The maximum stock available is {product.stock}")


class AddressForm(forms.Form):

shipping_address_line_1 = forms.CharField(required=False)
shipping_address_line_2 = forms.CharField(required=False)
shipping_zip_code = forms.CharField(required=False)
shipping_city = forms.CharField(required=False)

billing_address_line_1 = forms.CharField(required=False)
billing_address_line_2 = forms.CharField(required=False)
billing_zip_code = forms.CharField(required=False)
billing_city = forms.CharField(required=False)

selected_shipping_address = forms.ModelChoiceField(
Address.objects.none(), required=False
)
selected_billing_address = forms.ModelChoiceField(
Address.objects.none(), required=False
)

def __init__(self, *args, **kwargs):
user_id = kwargs.pop('user_id')
super().__init__(*args, **kwargs)

user = User.objects.get(id=user_id)

shipping_address_qs = Address.objects.filter(
user=user,
address_type='S'
)
billing_address_qs = Address.objects.filter(
user=user,
address_type='B'
)

self.fields['selected_shipping_address'].queryset = shipping_address_qs
self.fields['selected_billing_address'].queryset = billing_address_qs

def clean(self):
data = self.cleaned_data

selected_shipping_address = data.get('selected_shipping_address', None)
if selected_shipping_address is None:
if not data.get('shipping_address_line_1', None):
self.add_error("shipping_address_line_1",
"Please fill in this field")
if not data.get('shipping_address_line_2', None):
self.add_error("shipping_address_line_2",
"Please fill in this field")
if not data.get('shipping_zip_code', None):
self.add_error("shipping_zip_code",
"Please fill in this field")
if not data.get('shipping_city', None):
self.add_error("shipping_city", "Please fill in this field")

selected_billing_address = data.get('selected_billing_address', None)
if selected_billing_address is None:
if not data.get('billing_address_line_1', None):
self.add_error("billing_address_line_1",
"Please fill in this field")
if not data.get('billing_address_line_2', None):
self.add_error("billing_address_line_2",
"Please fill in this field")
if not data.get('billing_zip_code', None):
self.add_error("billing_zip_code",
"Please fill in this field")
if not data.get('billing_city', None):
self.add_error("billing_city", "Please fill in this field")


class StripePaymentForm(forms.Form):
selectedCard = forms.CharField()
107 changes: 107 additions & 0 deletions cart/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
# Generated by Django 3.0.6 on 2020-05-19 12:07

from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

initial = True

dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]

operations = [
migrations.CreateModel(
name='Address',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('address_line_1', models.CharField(max_length=150)),
('address_line_2', models.CharField(max_length=150)),
('city', models.CharField(max_length=100)),
('zip_code', models.CharField(max_length=20)),
('address_type', models.CharField(choices=[('B', 'Billing'), ('S', 'Shipping')], max_length=1)),
('default', models.BooleanField(default=False)),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
options={
'verbose_name_plural': 'Addresses',
},
),
migrations.CreateModel(
name='Category',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=100)),
],
),
migrations.CreateModel(
name='ColourVariation',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=50)),
],
),
migrations.CreateModel(
name='Order',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('start_date', models.DateTimeField(auto_now_add=True)),
('ordered_date', models.DateTimeField(blank=True, null=True)),
('ordered', models.BooleanField(default=False)),
('billing_address', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='billing_address', to='cart.Address')),
('shipping_address', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='shipping_address', to='cart.Address')),
('user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
),
migrations.CreateModel(
name='SizeVariation',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=50)),
],
),
migrations.CreateModel(
name='Product',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('title', models.CharField(max_length=150)),
('slug', models.SlugField(unique=True)),
('image', models.ImageField(upload_to='product_images')),
('description', models.TextField()),
('price', models.IntegerField(default=0)),
('created', models.DateTimeField(auto_now_add=True)),
('updated', models.DateTimeField(auto_now=True)),
('active', models.BooleanField(default=False)),
('available_colours', models.ManyToManyField(to='cart.ColourVariation')),
('available_sizes', models.ManyToManyField(to='cart.SizeVariation')),
('primary_category', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='primary_products', to='cart.Category')),
('secondary_categories', models.ManyToManyField(blank=True, to='cart.Category')),
],
),
migrations.CreateModel(
name='Payment',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('payment_method', models.CharField(choices=[('PayPal', 'PayPal')], max_length=20)),
('timestamp', models.DateTimeField(auto_now_add=True)),
('successful', models.BooleanField(default=False)),
('amount', models.FloatField()),
('raw_response', models.TextField()),
('order', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='payments', to='cart.Order')),
],
),
migrations.CreateModel(
name='OrderItem',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('quantity', models.PositiveIntegerField(default=1)),
('colour', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='cart.ColourVariation')),
('order', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='items', to='cart.Order')),
('product', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='cart.Product')),
('size', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='cart.SizeVariation')),
],
),
]
17 changes: 17 additions & 0 deletions cart/migrations/0002_auto_20200519_1208.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Generated by Django 3.0.6 on 2020-05-19 12:08

from django.db import migrations


class Migration(migrations.Migration):

dependencies = [
('cart', '0001_initial'),
]

operations = [
migrations.AlterModelOptions(
name='category',
options={'verbose_name_plural': 'Categories'},
),
]
18 changes: 18 additions & 0 deletions cart/migrations/0003_product_stock.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 3.0.6 on 2020-05-19 12:34

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('cart', '0002_auto_20200519_1208'),
]

operations = [
migrations.AddField(
model_name='product',
name='stock',
field=models.IntegerField(default=0),
),
]
25 changes: 25 additions & 0 deletions cart/migrations/0004_stripepayment.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Generated by Django 3.0.6 on 2020-05-19 16:45

from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
('cart', '0003_product_stock'),
]

operations = [
migrations.CreateModel(
name='StripePayment',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('payment_intent_id', models.CharField(max_length=100)),
('timestamp', models.DateTimeField(auto_now_add=True)),
('successful', models.BooleanField(default=False)),
('amount', models.FloatField()),
('order', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='stripe_payments', to='cart.Order')),
],
),
]
18 changes: 18 additions & 0 deletions cart/migrations/0005_auto_20200519_1650.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 3.0.6 on 2020-05-19 16:50

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('cart', '0004_stripepayment'),
]

operations = [
migrations.AlterField(
model_name='stripepayment',
name='amount',
field=models.FloatField(default=0),
),
]
19 changes: 19 additions & 0 deletions cart/migrations/0006_auto_20200529_1313.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Generated by Django 3.0.6 on 2020-05-29 13:13

from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
('cart', '0005_auto_20200519_1650'),
]

operations = [
migrations.AlterField(
model_name='product',
name='primary_category',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='primary_products', to='cart.Category'),
),
]
Empty file added cart/migrations/__init__.py
Empty file.
Loading