├── .gitignore ├── LICENSE ├── README.md ├── apps ├── cart │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── cart.py │ ├── context_processors.py │ ├── forms.py │ ├── migrations │ │ └── __init__.py │ ├── models.py │ ├── templates │ │ └── cart │ │ │ ├── cart.html │ │ │ └── success.html │ ├── tests.py │ ├── urls.py │ └── views.py ├── core │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── migrations │ │ └── __init__.py │ ├── models.py │ ├── templates │ │ └── core │ │ │ ├── base.html │ │ │ ├── contact.html │ │ │ └── frontpage.html │ ├── tests.py │ ├── urls.py │ └── views.py ├── order │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── migrations │ │ ├── 0001_initial.py │ │ └── __init__.py │ ├── models.py │ ├── templates │ │ └── order │ │ │ ├── email_notify_customer.html │ │ │ └── email_notify_vendor.html │ ├── tests.py │ ├── utilities.py │ └── views.py ├── product │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── context_processors.py │ ├── forms.py │ ├── migrations │ │ ├── 0001_initial.py │ │ ├── 0002_product.py │ │ ├── 0003_productimage.py │ │ └── __init__.py │ ├── models.py │ ├── templates │ │ └── product │ │ │ ├── category.html │ │ │ ├── parts │ │ │ └── list_item.html │ │ │ ├── product.html │ │ │ └── search.html │ ├── tests.py │ ├── urls.py │ └── views.py └── vendor │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── forms.py │ ├── migrations │ ├── 0001_initial.py │ └── __init__.py │ ├── models.py │ ├── templates │ └── vendor │ │ ├── add_product.html │ │ ├── become_vendor.html │ │ ├── edit_product.html │ │ ├── edit_vendor.html │ │ ├── login.html │ │ ├── vendor.html │ │ ├── vendor_admin.html │ │ └── vendors.html │ ├── tests.py │ ├── urls.py │ └── views.py ├── codesnippets ├── part1.txt ├── part2.txt └── part3.txt ├── interiorshop ├── __init__.py ├── asgi.py ├── settings.py ├── urls.py └── wsgi.py ├── manage.py ├── media └── uploads │ ├── photo-1530018607912-eff2daa1bac4.jpeg │ ├── photo-1530018607912-eff2daa1bac4_1LQQNWA.jpeg │ ├── photo-1530018607912-eff2daa1bac4_RUz4lhj.jpeg │ ├── photo-1530018607912-eff2daa1bac4_VymE1fy.jpeg │ ├── photo-1540574163026-643ea20ade25.jpeg │ ├── photo-1555041469-a586c61ea9bc.jpeg │ └── uploads │ ├── photo-1530018607912-eff2daa1bac4.jpeg │ ├── photo-1530018607912-eff2daa1bac4_1LQQNWA.jpeg │ ├── photo-1530018607912-eff2daa1bac4_RUz4lhj.jpeg │ ├── photo-1530018607912-eff2daa1bac4_VymE1fy.jpeg │ ├── photo-1540574163026-643ea20ade25.jpeg │ └── photo-1555041469-a586c61ea9bc.jpeg └── static ├── scripts └── main.js └── styles └── main.css /.gitignore: -------------------------------------------------------------------------------- 1 | db.sqlite3 2 | *.pyc 3 | */*.pyc 4 | */*/*.pyc 5 | */*/*/*.pyc 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 SteinOveHelset 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # interiorshop 2 | 3 | This repository is a part of a video tutorial on my YouTube channel: Code With Stein 4 | 5 | ## YouTube 6 | 7 | [YouTube playlist](https://www.youtube.com/watch?v=jmc0gV6_NE0&list=PLpyspNLjzwBkeyP_4_bZBdtRjZQreDR_H) 8 | 9 | ## Website 10 | 11 | [Code With Stein - Website](https://codewithstein.com) 12 | -------------------------------------------------------------------------------- /apps/cart/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SteinOveHelset/interiorshop/73749bbe4c6ad08adc495409a8d00849c1bd0a78/apps/cart/__init__.py -------------------------------------------------------------------------------- /apps/cart/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | -------------------------------------------------------------------------------- /apps/cart/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class CartConfig(AppConfig): 5 | name = 'apps.cart' 6 | -------------------------------------------------------------------------------- /apps/cart/cart.py: -------------------------------------------------------------------------------- 1 | from django.conf import settings 2 | 3 | from apps.product.models import Product 4 | 5 | class Cart(object): 6 | def __init__(self, request): 7 | self.session = request.session 8 | cart = self.session.get(settings.CART_SESSION_ID) 9 | 10 | if not cart: 11 | cart = self.session[settings.CART_SESSION_ID] = {} 12 | 13 | self.cart = cart 14 | 15 | def __iter__(self): 16 | for p in self.cart.keys(): 17 | self.cart[str(p)]['product'] = Product.objects.get(pk=p) 18 | 19 | for item in self.cart.values(): 20 | item['total_price'] = item['product'].price * item['quantity'] 21 | 22 | yield item 23 | 24 | def __len__(self): 25 | return sum(item['quantity'] for item in self.cart.values()) 26 | 27 | def add(self, product_id, quantity=1, update_quantity=False): 28 | product_id = str(product_id) 29 | 30 | if product_id not in self.cart: 31 | self.cart[product_id] = {'quantity': 1, 'id': product_id} 32 | 33 | if update_quantity: 34 | self.cart[product_id]['quantity'] += int(quantity) 35 | 36 | if self.cart[product_id]['quantity'] == 0: 37 | self.remove(product_id) 38 | 39 | self.save() 40 | 41 | def remove(self, product_id): 42 | if product_id in self.cart: 43 | del self.cart[product_id] 44 | self.save() 45 | 46 | def save(self): 47 | self.session[settings.CART_SESSION_ID] = self.cart 48 | self.session.modified = True 49 | 50 | def clear(self): 51 | del self.session[settings.CART_SESSION_ID] 52 | self.session.modified = True 53 | 54 | def get_total_cost(self): 55 | for p in self.cart.keys(): 56 | self.cart[str(p)]['product'] = Product.objects.get(pk=p) 57 | 58 | return sum(item['quantity'] * item['product'].price for item in self.cart.values()) -------------------------------------------------------------------------------- /apps/cart/context_processors.py: -------------------------------------------------------------------------------- 1 | from .cart import Cart 2 | 3 | def cart(request): 4 | return {'cart': Cart(request)} -------------------------------------------------------------------------------- /apps/cart/forms.py: -------------------------------------------------------------------------------- 1 | from django import forms 2 | 3 | class CheckoutForm(forms.Form): 4 | first_name = forms.CharField(max_length=255) 5 | last_name = forms.CharField(max_length=255) 6 | email = forms.EmailField(max_length=255) 7 | phone = forms.CharField(max_length=255) 8 | address = forms.CharField(max_length=255) 9 | zipcode = forms.CharField(max_length=255) 10 | place = forms.CharField(max_length=255) 11 | stripe_token = forms.CharField(max_length=255) -------------------------------------------------------------------------------- /apps/cart/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SteinOveHelset/interiorshop/73749bbe4c6ad08adc495409a8d00849c1bd0a78/apps/cart/migrations/__init__.py -------------------------------------------------------------------------------- /apps/cart/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | # Create your models here. 4 | -------------------------------------------------------------------------------- /apps/cart/templates/cart/cart.html: -------------------------------------------------------------------------------- 1 | {% extends 'core/base.html' %} 2 | 3 | {% block title %}Cart | {% endblock %} 4 | 5 | {% block content %} 6 |

Cart

7 | 8 | {% if cart %} 9 |
10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | {% for item in cart %} 22 | 23 | 28 | 31 | 37 | 38 | 39 | 40 | {% endfor %} 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 |
ProductQuantityPrice
24 |
25 | 26 |
27 |
29 | {{ item.product.title }} 30 | 32 | {{ item.quantity }} 33 | 34 | - 35 | + 36 | ${{ item.total_price }}Remove
Total cost{{ cart|length}}${{ cart.get_total_cost }}
52 |
53 |
54 | 55 |

Contact information

56 | 57 |
58 | {% csrf_token %} 59 | 60 | {% if form.non_field_errors %} 61 |
62 | {{ form.non_field_errors}} 63 |
64 | {% endif %} 65 | 66 | {% if form.errors %} 67 |
68 | 75 |
76 | {% endif %} 77 | 78 |
79 |
80 |
81 | 82 | 83 |
84 | 85 |
86 |
87 | 88 |
89 | 90 | 91 |
92 | 93 |
94 |
95 | 96 |
97 | 98 | 99 |
100 | 101 |
102 |
103 | 104 |
105 | 106 | 107 |
108 | 109 |
110 |
111 |
112 | 113 |
114 |
115 | 116 | 117 |
118 | 119 |
120 |
121 | 122 |
123 | 124 | 125 |
126 | 127 |
128 |
129 | 130 |
131 | 132 | 133 |
134 | 135 |
136 |
137 |
138 |
139 | 140 |

Payment information

141 | 142 |
143 | 144 |
145 | 146 | {% if messages %} 147 | {% for message in messages %} 148 |
{{ message }}
149 | {% endfor %} 150 | {% endif %} 151 | 152 |
153 |
154 | 155 |
156 |
157 | 158 | {% else %} 159 |

You don't have any products in your cart!

160 | {% endif %} 161 | {% endblock %} 162 | 163 | {% block scripts %} 164 | 165 | 198 | {% endblock %} -------------------------------------------------------------------------------- /apps/cart/templates/cart/success.html: -------------------------------------------------------------------------------- 1 | {% extends 'core/base.html' %} 2 | 3 | {% block title %}Success | {% endblock %} 4 | 5 | {% block content %} 6 |

Thanks for the order

7 | {% endblock %} -------------------------------------------------------------------------------- /apps/cart/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /apps/cart/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | 3 | from . import views 4 | 5 | urlpatterns = [ 6 | path('', views.cart_detail, name='cart'), 7 | path('success/', views.success, name='success') 8 | ] -------------------------------------------------------------------------------- /apps/cart/views.py: -------------------------------------------------------------------------------- 1 | import stripe 2 | 3 | from django.conf import settings 4 | from django.contrib import messages 5 | from django.shortcuts import render, redirect 6 | 7 | from .cart import Cart 8 | from .forms import CheckoutForm 9 | 10 | from apps.order.utilities import checkout, notify_customer, notify_vendor 11 | 12 | def cart_detail(request): 13 | cart = Cart(request) 14 | 15 | if request.method == 'POST': 16 | form = CheckoutForm(request.POST) 17 | 18 | if form.is_valid(): 19 | stripe.api_key = settings.STRIPE_SECRET_KEY 20 | 21 | stripe_token = form.cleaned_data['stripe_token'] 22 | 23 | try: 24 | charge = stripe.Charge.create( 25 | amount=int(cart.get_total_cost() * 100), 26 | currency='USD', 27 | description='Charge from Interiorshop', 28 | source=stripe_token 29 | ) 30 | 31 | first_name = form.cleaned_data['first_name'] 32 | last_name = form.cleaned_data['last_name'] 33 | email = form.cleaned_data['email'] 34 | phone = form.cleaned_data['phone'] 35 | address = form.cleaned_data['address'] 36 | zipcode = form.cleaned_data['zipcode'] 37 | place = form.cleaned_data['place'] 38 | 39 | order = checkout(request, first_name, last_name, email, address, zipcode, place, phone, cart.get_total_cost()) 40 | 41 | cart.clear() 42 | 43 | notify_customer(order) 44 | notify_vendor(order) 45 | 46 | return redirect('success') 47 | except Exception: 48 | messages.error(request, 'There was something wrong with the payment') 49 | else: 50 | form = CheckoutForm() 51 | 52 | remove_from_cart = request.GET.get('remove_from_cart', '') 53 | change_quantity = request.GET.get('change_quantity', '') 54 | quantity = request.GET.get('quantity', 0) 55 | 56 | if remove_from_cart: 57 | cart.remove(remove_from_cart) 58 | 59 | return redirect('cart') 60 | 61 | if change_quantity: 62 | cart.add(change_quantity, quantity, True) 63 | 64 | return redirect('cart') 65 | 66 | return render(request, 'cart/cart.html', {'form': form, 'stripe_pub_key': settings.STRIPE_PUB_KEY}) 67 | 68 | def success(request): 69 | return render(request, 'cart/success.html') -------------------------------------------------------------------------------- /apps/core/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SteinOveHelset/interiorshop/73749bbe4c6ad08adc495409a8d00849c1bd0a78/apps/core/__init__.py -------------------------------------------------------------------------------- /apps/core/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | -------------------------------------------------------------------------------- /apps/core/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class CoreConfig(AppConfig): 5 | name = 'apps.core' 6 | -------------------------------------------------------------------------------- /apps/core/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SteinOveHelset/interiorshop/73749bbe4c6ad08adc495409a8d00849c1bd0a78/apps/core/migrations/__init__.py -------------------------------------------------------------------------------- /apps/core/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | # Create your models here. 4 | -------------------------------------------------------------------------------- /apps/core/templates/core/base.html: -------------------------------------------------------------------------------- 1 | {% load static %} 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | {% block title %}{% endblock %}Interiorshop 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
59 | 60 |
61 |
62 | {% block content %} 63 | {% endblock %} 64 |
65 |
66 | 67 | 78 | 79 | {% block scripts %} 80 | {% endblock %} 81 | 82 | -------------------------------------------------------------------------------- /apps/core/templates/core/contact.html: -------------------------------------------------------------------------------- 1 | {% extends 'core/base.html' %} 2 | 3 | {% block title %}Contact | {% endblock %} 4 | 5 | {% block content %} 6 |

Contact

7 | {% endblock %} -------------------------------------------------------------------------------- /apps/core/templates/core/frontpage.html: -------------------------------------------------------------------------------- 1 | {% extends 'core/base.html' %} 2 | 3 | {% block title %}Welcome | {% endblock %} 4 | 5 | {% block content %} 6 |
7 |
8 |

Newest products

9 |
10 | 11 | {% for product in newest_products %} 12 | {% include 'product/parts/list_item.html' %} 13 | {% endfor %} 14 |
15 | {% endblock %} -------------------------------------------------------------------------------- /apps/core/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /apps/core/urls.py: -------------------------------------------------------------------------------- 1 | # 2 | # 3 | 4 | from django.urls import path 5 | 6 | # 7 | # 8 | 9 | from . import views 10 | 11 | # 12 | # 13 | 14 | urlpatterns = [ 15 | path('', views.frontpage, name='frontpage'), 16 | path('contact/', views.contact, name='contact') 17 | ] -------------------------------------------------------------------------------- /apps/core/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render 2 | 3 | from apps.product.models import Product 4 | 5 | def frontpage(request): 6 | newest_products = Product.objects.all()[0:8] 7 | 8 | return render(request, 'core/frontpage.html', {'newest_products': newest_products}) 9 | 10 | def contact(request): 11 | return render(request, 'core/contact.html') -------------------------------------------------------------------------------- /apps/order/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SteinOveHelset/interiorshop/73749bbe4c6ad08adc495409a8d00849c1bd0a78/apps/order/__init__.py -------------------------------------------------------------------------------- /apps/order/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | from .models import Order, OrderItem 4 | 5 | admin.site.register(Order) 6 | admin.site.register(OrderItem) -------------------------------------------------------------------------------- /apps/order/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class OrderConfig(AppConfig): 5 | name = 'apps.order' 6 | -------------------------------------------------------------------------------- /apps/order/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.5 on 2021-01-21 19:39 2 | 3 | from django.db import migrations, models 4 | import django.db.models.deletion 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | initial = True 10 | 11 | dependencies = [ 12 | ('product', '0002_product'), 13 | ('vendor', '0001_initial'), 14 | ] 15 | 16 | operations = [ 17 | migrations.CreateModel( 18 | name='Order', 19 | fields=[ 20 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 21 | ('first_name', models.CharField(max_length=100)), 22 | ('last_name', models.CharField(max_length=100)), 23 | ('email', models.CharField(max_length=100)), 24 | ('address', models.CharField(max_length=100)), 25 | ('zipcode', models.CharField(max_length=100)), 26 | ('place', models.CharField(max_length=100)), 27 | ('phone', models.CharField(max_length=100)), 28 | ('created_at', models.DateTimeField(auto_now_add=True)), 29 | ('paid_amount', models.DecimalField(decimal_places=2, max_digits=8)), 30 | ('vendors', models.ManyToManyField(related_name='orders', to='vendor.Vendor')), 31 | ], 32 | options={ 33 | 'ordering': ['-created_at'], 34 | }, 35 | ), 36 | migrations.CreateModel( 37 | name='OrderItem', 38 | fields=[ 39 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 40 | ('vendor_paid', models.BooleanField(default=False)), 41 | ('price', models.DecimalField(decimal_places=2, max_digits=8)), 42 | ('quantity', models.IntegerField(default=1)), 43 | ('order', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='items', to='order.order')), 44 | ('product', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='items', to='product.product')), 45 | ('vendor', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='items', to='vendor.vendor')), 46 | ], 47 | ), 48 | ] 49 | -------------------------------------------------------------------------------- /apps/order/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SteinOveHelset/interiorshop/73749bbe4c6ad08adc495409a8d00849c1bd0a78/apps/order/migrations/__init__.py -------------------------------------------------------------------------------- /apps/order/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | from apps.product.models import Product 4 | from apps.vendor.models import Vendor 5 | 6 | class Order(models.Model): 7 | first_name = models.CharField(max_length=100) 8 | last_name = models.CharField(max_length=100) 9 | email = models.CharField(max_length=100) 10 | address = models.CharField(max_length=100) 11 | zipcode = models.CharField(max_length=100) 12 | place = models.CharField(max_length=100) 13 | phone = models.CharField(max_length=100) 14 | created_at = models.DateTimeField(auto_now_add=True) 15 | paid_amount = models.DecimalField(max_digits=8, decimal_places=2) 16 | vendors = models.ManyToManyField(Vendor, related_name='orders') 17 | 18 | class Meta: 19 | ordering = ['-created_at'] 20 | 21 | def __str__(self): 22 | return self.first_name 23 | 24 | class OrderItem(models.Model): 25 | order = models.ForeignKey(Order, related_name='items', on_delete=models.CASCADE) 26 | product = models.ForeignKey(Product, related_name='items', on_delete=models.CASCADE) 27 | vendor = models.ForeignKey(Vendor, related_name='items', on_delete=models.CASCADE) 28 | vendor_paid = models.BooleanField(default=False) 29 | price = models.DecimalField(max_digits=8, decimal_places=2) 30 | quantity = models.IntegerField(default=1) 31 | 32 | def __str__(self): 33 | return '%s' % self.id 34 | 35 | def get_total_price(self): 36 | return self.price * self.quantity -------------------------------------------------------------------------------- /apps/order/templates/order/email_notify_customer.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Interiorshop 7 | 8 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 39 | 40 | 41 | 42 | 63 | 64 |

Order confirmation

31 |

Details

32 | 33 | Name: {{ order.first_name }} {{ order.last_name }}
34 | Address: {{ order.address }}
35 | Zip and place: {{ order.zipcode }} {{ order.place }}
36 | E-mail: {{ order.email }}
37 | Phone: {{ order.phone }} 38 |
43 |

Products

44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | {% for item in order.items.all %} 54 | 55 | 56 | 57 | 58 | 59 | 60 | {% endfor %} 61 |
ProductPriceQuantityTotal
{{ item.product.title }}${{ item.product.price }}{{ item.quantity }}${{ item.get_total_price }}
62 |
65 | 66 | -------------------------------------------------------------------------------- /apps/order/templates/order/email_notify_vendor.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Interiorshop 7 | 8 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 39 | 40 | 41 | 42 | 65 | 66 |

New order

31 |

Details

32 | 33 | Name: {{ order.first_name }} {{ order.last_name }}
34 | Address: {{ order.address }}
35 | Zip and place: {{ order.zipcode }} {{ order.place }}
36 | E-mail: {{ order.email }}
37 | Phone: {{ order.phone }} 38 |
43 |

Products

44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | {% for item in order.items.all %} 54 | {% if item.vendor == vendor %} 55 | 56 | 57 | 58 | 59 | 60 | 61 | {% endif %} 62 | {% endfor %} 63 |
ProductPriceQuantityTotal
{{ item.product.title }}${{ item.product.price }}{{ item.quantity }}${{ item.get_total_price }}
64 |
67 | 68 | -------------------------------------------------------------------------------- /apps/order/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /apps/order/utilities.py: -------------------------------------------------------------------------------- 1 | from django.conf import settings 2 | from django.core.mail import EmailMultiAlternatives 3 | from django.template.loader import render_to_string 4 | 5 | from apps.cart.cart import Cart 6 | 7 | from .models import Order, OrderItem 8 | 9 | def checkout(request, first_name, last_name, email, address, zipcode, place, phone, amount): 10 | order = Order.objects.create(first_name=first_name, last_name=last_name, email=email, address=address, zipcode=zipcode, place=place, phone=phone, paid_amount=amount) 11 | 12 | for item in Cart(request): 13 | OrderItem.objects.create(order=order, product=item['product'], vendor=item['product'].vendor, price=item['product'].price, quantity=item['quantity']) 14 | 15 | order.vendors.add(item['product'].vendor) 16 | 17 | return order 18 | 19 | def notify_vendor(order): 20 | from_email = settings.DEFAULT_EMAIL_FROM 21 | 22 | for vendor in order.vendors.all(): 23 | to_email = vendor.created_by.email 24 | subject = 'New order' 25 | text_content = 'You have a new order!' 26 | html_content = render_to_string('order/email_notify_vendor.html', {'order': order, 'vendor': vendor}) 27 | 28 | msg = EmailMultiAlternatives(subject, text_content, from_email, [to_email]) 29 | msg.attach_alternative(html_content, 'text/html') 30 | msg.send() 31 | 32 | def notify_customer(order): 33 | from_email = settings.DEFAULT_EMAIL_FROM 34 | 35 | to_email = order.email 36 | subject = 'Order confirmation' 37 | text_content = 'Thank you for the order!' 38 | html_content = render_to_string('order/email_notify_customer.html', {'order': order}) 39 | 40 | msg = EmailMultiAlternatives(subject, text_content, from_email, [to_email]) 41 | msg.attach_alternative(html_content, 'text/html') 42 | msg.send() -------------------------------------------------------------------------------- /apps/order/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render 2 | 3 | # Create your views here. 4 | -------------------------------------------------------------------------------- /apps/product/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SteinOveHelset/interiorshop/73749bbe4c6ad08adc495409a8d00849c1bd0a78/apps/product/__init__.py -------------------------------------------------------------------------------- /apps/product/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | from .models import Category, Product, ProductImage 4 | 5 | admin.site.register(Category) 6 | admin.site.register(Product) 7 | admin.site.register(ProductImage) -------------------------------------------------------------------------------- /apps/product/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class ProductConfig(AppConfig): 5 | name = 'apps.product' 6 | -------------------------------------------------------------------------------- /apps/product/context_processors.py: -------------------------------------------------------------------------------- 1 | from apps.product.models import Category 2 | 3 | def menu_categories(request): 4 | categories = Category.objects.all() 5 | 6 | return {'menu_categories': categories} -------------------------------------------------------------------------------- /apps/product/forms.py: -------------------------------------------------------------------------------- 1 | from django import forms 2 | 3 | class AddToCartForm(forms.Form): 4 | quantity = forms.IntegerField() -------------------------------------------------------------------------------- /apps/product/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.5 on 2021-01-17 19:26 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | initial = True 9 | 10 | dependencies = [ 11 | ] 12 | 13 | operations = [ 14 | migrations.CreateModel( 15 | name='Category', 16 | fields=[ 17 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 18 | ('title', models.CharField(max_length=255)), 19 | ('slug', models.SlugField(max_length=255)), 20 | ('ordering', models.IntegerField(default=0)), 21 | ], 22 | options={ 23 | 'ordering': ['ordering'], 24 | }, 25 | ), 26 | ] 27 | -------------------------------------------------------------------------------- /apps/product/migrations/0002_product.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.5 on 2021-01-17 19:34 2 | 3 | from django.db import migrations, models 4 | import django.db.models.deletion 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('vendor', '0001_initial'), 11 | ('product', '0001_initial'), 12 | ] 13 | 14 | operations = [ 15 | migrations.CreateModel( 16 | name='Product', 17 | fields=[ 18 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 19 | ('title', models.CharField(max_length=255)), 20 | ('slug', models.SlugField(max_length=255)), 21 | ('description', models.TextField(blank=True, null=True)), 22 | ('price', models.DecimalField(decimal_places=2, max_digits=6)), 23 | ('date_added', models.DateTimeField(auto_now_add=True)), 24 | ('image', models.ImageField(blank=True, null=True, upload_to='uploads/')), 25 | ('thumbnail', models.ImageField(blank=True, null=True, upload_to='uploads/')), 26 | ('category', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='products', to='product.category')), 27 | ('vendor', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='products', to='vendor.vendor')), 28 | ], 29 | options={ 30 | 'ordering': ['-date_added'], 31 | }, 32 | ), 33 | ] 34 | -------------------------------------------------------------------------------- /apps/product/migrations/0003_productimage.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.5 on 2021-05-16 07:03 2 | 3 | from django.db import migrations, models 4 | import django.db.models.deletion 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('product', '0002_product'), 11 | ] 12 | 13 | operations = [ 14 | migrations.CreateModel( 15 | name='ProductImage', 16 | fields=[ 17 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 18 | ('image', models.ImageField(blank=True, null=True, upload_to='uploads/')), 19 | ('thumbnail', models.ImageField(blank=True, null=True, upload_to='uploads/')), 20 | ('product', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='images', to='product.product')), 21 | ], 22 | ), 23 | ] 24 | -------------------------------------------------------------------------------- /apps/product/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SteinOveHelset/interiorshop/73749bbe4c6ad08adc495409a8d00849c1bd0a78/apps/product/migrations/__init__.py -------------------------------------------------------------------------------- /apps/product/models.py: -------------------------------------------------------------------------------- 1 | from io import BytesIO 2 | from PIL import Image 3 | 4 | from django.core.files import File 5 | from django.db import models 6 | 7 | from apps.vendor.models import Vendor 8 | 9 | class Category(models.Model): 10 | title = models.CharField(max_length=255) 11 | slug = models.SlugField(max_length=255) 12 | ordering = models.IntegerField(default=0) 13 | 14 | class Meta: 15 | ordering = ['ordering'] 16 | 17 | def __str__(self): 18 | return self.title 19 | 20 | class Product(models.Model): 21 | category = models.ForeignKey(Category, related_name='products', on_delete=models.CASCADE) 22 | vendor = models.ForeignKey(Vendor, related_name='products', on_delete=models.CASCADE) 23 | title = models.CharField(max_length=255) 24 | slug = models.SlugField(max_length=255) 25 | description = models.TextField(blank=True, null=True) 26 | price = models.DecimalField(max_digits=6, decimal_places=2) 27 | date_added = models.DateTimeField(auto_now_add=True) 28 | image = models.ImageField(upload_to='uploads/', blank=True, null=True) 29 | thumbnail = models.ImageField(upload_to='uploads/', blank=True, null=True) 30 | 31 | class Meta: 32 | ordering = ['-date_added'] 33 | 34 | def __str__(self): 35 | return self.title 36 | 37 | def get_thumbnail(self): 38 | if self.thumbnail: 39 | return self.thumbnail.url 40 | else: 41 | if self.image: 42 | self.thumbnail = self.make_thumbnail(self.image) 43 | self.save() 44 | 45 | return self.thumbnail.url 46 | else: 47 | return 'https://via.placeholder.com/240x180.jpg' 48 | 49 | def make_thumbnail(self, image, size=(300, 200)): 50 | img = Image.open(image) 51 | img.convert('RGB') 52 | img.thumbnail(size) 53 | 54 | thumb_io = BytesIO() 55 | img.save(thumb_io, 'JPEG', quality=85) 56 | 57 | thumbnail = File(thumb_io, name=image.name) 58 | 59 | return thumbnail 60 | 61 | class ProductImage(models.Model): 62 | product = models.ForeignKey(Product, related_name='images', on_delete=models.CASCADE) 63 | image = models.ImageField(upload_to='uploads/', blank=True, null=True) 64 | thumbnail = models.ImageField(upload_to='uploads/', blank=True, null=True) 65 | 66 | def get_thumbnail(self): 67 | if self.thumbnail: 68 | return self.thumbnail.url 69 | else: 70 | if self.image: 71 | self.thumbnail = self.make_thumbnail(self.image) 72 | self.save() 73 | 74 | return self.thumbnail.url 75 | else: 76 | return 'https://via.placeholder.com/240x180.jpg' 77 | 78 | def make_thumbnail(self, image, size=(300, 200)): 79 | img = Image.open(image) 80 | img.convert('RGB') 81 | img.thumbnail(size) 82 | 83 | thumb_io = BytesIO() 84 | img.save(thumb_io, 'JPEG', quality=85) 85 | 86 | thumbnail = File(thumb_io, name=image.name) 87 | 88 | return thumbnail -------------------------------------------------------------------------------- /apps/product/templates/product/category.html: -------------------------------------------------------------------------------- 1 | {% extends 'core/base.html' %} 2 | 3 | {% block title %}{{ category.title }} | {% endblock %} 4 | 5 | {% block content %} 6 |
7 |
8 |

{{ category.title }}

9 |
10 | 11 | {% for product in category.products.all %} 12 | {% include 'product/parts/list_item.html' %} 13 | {% endfor %} 14 |
15 | {% endblock %} -------------------------------------------------------------------------------- /apps/product/templates/product/parts/list_item.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | 5 |
6 | 7 |

{{ product.title }}

8 | 9 |

${{ product.price }}

10 | 11 |
12 | 13 | View 14 |
15 |
-------------------------------------------------------------------------------- /apps/product/templates/product/product.html: -------------------------------------------------------------------------------- 1 | {% extends 'core/base.html' %} 2 | 3 | {% block title %}{{ product.title }} | {% endblock %} 4 | 5 | {% block content %} 6 |
7 | {% if product.image %} 8 |
9 | 10 |
11 | {% endif %} 12 | 13 | {% if product.images.all %} 14 |
15 | 16 |
17 |
18 | 19 |
20 |
21 | {% endif %} 22 | 23 |

{{ product.title }}, ${{ product.price }}

24 | 25 |

{{ product.vendor.name }}

26 | 27 |

{{ product.description }}

28 | 29 |
30 | 31 | {% if messages %} 32 | {% for message in messages %} 33 |
{{ message }}
34 | {% endfor %} 35 | {% endif %} 36 | 37 |
38 | {% csrf_token %} 39 | 40 |
41 |
42 | 43 |
44 | 45 |
46 | 47 |
48 |
49 |
50 | 51 | {% if similar_products %} 52 |
53 | 54 |
55 |
56 |

Similar products

57 |
58 | 59 | {% for product in similar_products %} 60 | {% include 'product/parts/list_item.html' %} 61 | {% endfor %} 62 |
63 | {% endif %} 64 |
65 | {% endblock %} 66 | 67 | {% block scripts %} 68 | {{ imagesstring|json_script:"json-imagesstring" }} 69 | 70 | 91 | {% endblock %} -------------------------------------------------------------------------------- /apps/product/templates/product/search.html: -------------------------------------------------------------------------------- 1 | {% extends 'core/base.html' %} 2 | 3 | {% block title %}Search | {% endblock %} 4 | 5 | {% block content %} 6 |
7 |
8 |

Search

9 |

Search term: "{{ query }}"

10 |
11 | 12 | {% for product in products %} 13 | {% include 'product/parts/list_item.html' %} 14 | {% endfor %} 15 |
16 | {% endblock %} -------------------------------------------------------------------------------- /apps/product/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /apps/product/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | 3 | from . import views 4 | 5 | urlpatterns = [ 6 | path('search/', views.search, name='search'), 7 | path('//', views.product, name='product'), 8 | path('/', views.category, name='category') 9 | ] -------------------------------------------------------------------------------- /apps/product/views.py: -------------------------------------------------------------------------------- 1 | import random 2 | 3 | from django.contrib import messages 4 | from django.db.models import Q 5 | from django.shortcuts import render, get_object_or_404, redirect 6 | 7 | from .forms import AddToCartForm 8 | from .models import Category, Product 9 | 10 | from apps.cart.cart import Cart 11 | 12 | def search(request): 13 | query = request.GET.get('query', '') 14 | products = Product.objects.filter(Q(title__icontains=query) | Q(description__icontains=query)) 15 | 16 | return render(request, 'product/search.html', {'products': products, 'query': query}) 17 | 18 | def product(request, category_slug, product_slug): 19 | cart = Cart(request) 20 | 21 | product = get_object_or_404(Product, category__slug=category_slug, slug=product_slug) 22 | 23 | imagesstring = '{"thumbnail": "%s", "image": "%s", "id": "mainimage"},' % (product.get_thumbnail(), product.image.url) 24 | 25 | for image in product.images.all(): 26 | imagesstring += ('{"thumbnail": "%s", "image": "%s", "id": "%s"},' % (image.get_thumbnail(), image.image.url, image.id)) 27 | 28 | print(imagesstring) 29 | 30 | if request.method == 'POST': 31 | form = AddToCartForm(request.POST) 32 | 33 | if form.is_valid(): 34 | quantity = form.cleaned_data['quantity'] 35 | 36 | cart.add(product_id=product.id, quantity=quantity, update_quantity=False) 37 | 38 | messages.success(request, 'The product was added to the cart') 39 | 40 | return redirect('product', category_slug=category_slug, product_slug=product_slug) 41 | else: 42 | form = AddToCartForm() 43 | 44 | similar_products = list(product.category.products.exclude(id=product.id)) 45 | 46 | if len(similar_products) >= 4: 47 | similar_products = random.sample(similar_products, 4) 48 | 49 | context = { 50 | 'form': form, 51 | 'product': product, 52 | 'similar_products': similar_products, 53 | 'imagesstring': "[" + imagesstring.rstrip(',') + "]" 54 | } 55 | 56 | return render(request, 'product/product.html', context) 57 | 58 | def category(request, category_slug): 59 | category = get_object_or_404(Category, slug=category_slug) 60 | 61 | return render(request, 'product/category.html', {'category': category}) -------------------------------------------------------------------------------- /apps/vendor/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SteinOveHelset/interiorshop/73749bbe4c6ad08adc495409a8d00849c1bd0a78/apps/vendor/__init__.py -------------------------------------------------------------------------------- /apps/vendor/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | from .models import Vendor 4 | 5 | admin.site.register(Vendor) -------------------------------------------------------------------------------- /apps/vendor/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class VendorConfig(AppConfig): 5 | name = 'apps.vendor' 6 | -------------------------------------------------------------------------------- /apps/vendor/forms.py: -------------------------------------------------------------------------------- 1 | from django.forms import ModelForm 2 | 3 | from apps.product.models import Product, ProductImage 4 | 5 | class ProductForm(ModelForm): 6 | class Meta: 7 | model = Product 8 | fields = ['category', 'image', 'title', 'description', 'price'] 9 | 10 | class ProductImageForm(ModelForm): 11 | class Meta: 12 | model = ProductImage 13 | fields = ['image'] 14 | -------------------------------------------------------------------------------- /apps/vendor/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.5 on 2021-01-17 19:03 2 | 3 | from django.conf import settings 4 | from django.db import migrations, models 5 | import django.db.models.deletion 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | initial = True 11 | 12 | dependencies = [ 13 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 14 | ] 15 | 16 | operations = [ 17 | migrations.CreateModel( 18 | name='Vendor', 19 | fields=[ 20 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 21 | ('name', models.CharField(max_length=255)), 22 | ('created_at', models.DateTimeField(auto_now_add=True)), 23 | ('created_by', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='vendor', to=settings.AUTH_USER_MODEL)), 24 | ], 25 | options={ 26 | 'ordering': ['name'], 27 | }, 28 | ), 29 | ] 30 | -------------------------------------------------------------------------------- /apps/vendor/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SteinOveHelset/interiorshop/73749bbe4c6ad08adc495409a8d00849c1bd0a78/apps/vendor/migrations/__init__.py -------------------------------------------------------------------------------- /apps/vendor/models.py: -------------------------------------------------------------------------------- 1 | from django.contrib.auth.models import User 2 | from django.db import models 3 | 4 | class Vendor(models.Model): 5 | name = models.CharField(max_length=255) 6 | created_at = models.DateTimeField(auto_now_add=True) 7 | created_by = models.OneToOneField(User, related_name='vendor', on_delete=models.CASCADE) 8 | 9 | class Meta: 10 | ordering = ['name'] 11 | 12 | def __str__(self): 13 | return self.name 14 | 15 | def get_balance(self): 16 | items = self.items.filter(vendor_paid=False, order__vendors__in=[self.id]) 17 | return sum((item.product.price * item.quantity) for item in items) 18 | 19 | def get_paid_amount(self): 20 | items = self.items.filter(vendor_paid=True, order__vendors__in=[self.id]) 21 | return sum((item.product.price * item.quantity) for item in items) -------------------------------------------------------------------------------- /apps/vendor/templates/vendor/add_product.html: -------------------------------------------------------------------------------- 1 | {% extends 'core/base.html' %} 2 | 3 | {% block title %}Add product | {% endblock %} 4 | 5 | {% block content %} 6 |

Add product

7 | 8 |
9 | {% csrf_token %} 10 | 11 | {{ form.as_p }} 12 | 13 |
14 |
15 | 16 |
17 |
18 |
19 | {% endblock %} -------------------------------------------------------------------------------- /apps/vendor/templates/vendor/become_vendor.html: -------------------------------------------------------------------------------- 1 | {% extends 'core/base.html' %} 2 | 3 | {% block title %}Become vendor | {% endblock %} 4 | 5 | {% block content %} 6 |

Become vendor

7 | 8 |
9 | {% csrf_token %} 10 | 11 | {{ form.as_p }} 12 | 13 |
14 |
15 | 16 |
17 |
18 |
19 | 20 |
21 | 22 |

Already have an account?

23 | 24 | Log in 25 | {% endblock %} -------------------------------------------------------------------------------- /apps/vendor/templates/vendor/edit_product.html: -------------------------------------------------------------------------------- 1 | {% extends 'core/base.html' %} 2 | 3 | {% block title %}Edit product | {% endblock %} 4 | 5 | {% block content %} 6 |

Edit product

7 | 8 |
9 | {% csrf_token %} 10 | 11 | {{ form.as_p }} 12 | 13 |
14 |
15 | 16 |
17 |
18 |
19 | 20 |
21 | 22 |

Images

23 | 24 |
25 | {% csrf_token %} 26 | 27 | {{ image_form.as_p }} 28 | 29 |
30 |
31 | 32 |
33 |
34 |
35 | {% endblock %} -------------------------------------------------------------------------------- /apps/vendor/templates/vendor/edit_vendor.html: -------------------------------------------------------------------------------- 1 | {% extends 'core/base.html' %} 2 | 3 | {% block title %}Edit vendor | {% endblock %} 4 | 5 | {% block content %} 6 |

Edit vendor

7 | 8 |
9 | {% csrf_token %} 10 | 11 |
12 | 13 | 14 |
15 | 16 |
17 | 18 |

This is not the same as the username!

19 |
20 | 21 |
22 | 23 | 24 |
25 | 26 |
27 |
28 | 29 |
30 |
31 | 32 |
33 |
34 |
35 | {% endblock %} -------------------------------------------------------------------------------- /apps/vendor/templates/vendor/login.html: -------------------------------------------------------------------------------- 1 | {% extends 'core/base.html' %} 2 | 3 | {% block title %}Log in | {% endblock %} 4 | 5 | {% block content %} 6 |

Log in

7 | 8 |
9 | {% csrf_token %} 10 | 11 | {{ form.as_p }} 12 | 13 |
14 |
15 | 16 |
17 |
18 |
19 | {% endblock %} -------------------------------------------------------------------------------- /apps/vendor/templates/vendor/vendor.html: -------------------------------------------------------------------------------- 1 | {% extends 'core/base.html' %} 2 | 3 | {% block title %}{{ vendor.name }} | {% endblock %} 4 | 5 | {% block content %} 6 |
7 |
8 |

{{ vendor.name }}

9 |
10 | 11 | {% for product in vendor.products.all %} 12 | {% include 'product/parts/list_item.html' %} 13 | {% endfor %} 14 |
15 | {% endblock %} -------------------------------------------------------------------------------- /apps/vendor/templates/vendor/vendor_admin.html: -------------------------------------------------------------------------------- 1 | {% extends 'core/base.html' %} 2 | 3 | {% block title %}Vendor admin | {% endblock %} 4 | 5 | {% block content %} 6 |
7 |

Vendor admin - {{ vendor.name }}

8 | 9 | My balance: ${{ vendor.get_balance }}
10 | My paid amount: ${{ vendor.get_paid_amount }} 11 | 12 |
13 | 14 | Edit 15 | Log out 16 |
17 | 18 |
19 |

My products

20 | 21 | Add product 22 | 23 | {% if products %} 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | {% for product in products %} 35 | 36 | 37 | 38 | 39 | 40 | {% endfor %} 41 | 42 |
TitlePriceActions
{{ product.title }}${{ product.price }}Edit
43 | {% else %} 44 |

You don't have any products yet...

45 | {% endif %} 46 |
47 | 48 |
49 |

My orders

50 | 51 | {% if orders %} 52 | {% for order in orders %} 53 |
54 |
55 |
56 |

#{{ order.id }} - {{ order.first_name }} {{ order.last_name }}

57 |
58 | 59 |
60 | Name: {{ order.first_name }} {{ order.last_name }}
61 | Address: {{ order.address }}
62 | Zip and place: {{ order.zipcode }} {{ order.place }}
63 | E-mail: {{ order.email }}
64 | Phone: {{ order.phone }} 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | {% for item in order.items.all %} 79 | {% if item.vendor == request.user.vendor %} 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | {% endif %} 88 | {% endfor %} 89 | 90 |
TitlePriceQuantityPaidTotal
{{ item.product.title }}${{ item.product.price }}{{ item.quantity }}{{ item.vendor_paid|yesno:"Yes,No" }}${{ item.get_total_price }}
91 |
92 |
93 |
94 | {% endfor %} 95 | {% endif %} 96 |
97 | {% endblock %} -------------------------------------------------------------------------------- /apps/vendor/templates/vendor/vendors.html: -------------------------------------------------------------------------------- 1 | {% extends 'core/base.html' %} 2 | 3 | {% block title %}Vendors | {% endblock %} 4 | 5 | {% block content %} 6 |

Vendors

7 | 8 |
9 | {% for vendor in vendors %} 10 |
11 |
12 |

{{ vendor.name }}

13 | 14 |
15 | 16 | View 17 |
18 |
19 | {% endfor %} 20 |
21 | {% endblock %} -------------------------------------------------------------------------------- /apps/vendor/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /apps/vendor/urls.py: -------------------------------------------------------------------------------- 1 | from django.contrib.auth import views as auth_views 2 | from django.urls import path 3 | 4 | from . import views 5 | 6 | urlpatterns = [ 7 | path('become-vendor/', views.become_vendor, name='become_vendor'), 8 | path('vendor-admin/', views.vendor_admin, name='vendor_admin'), 9 | path('add-product/', views.add_product, name='add_product'), 10 | path('edit-vendor/', views.edit_vendor, name='edit_vendor'), 11 | path('edit-product//', views.edit_product, name='edit_product'), 12 | 13 | path('logout/', auth_views.LogoutView.as_view(), name='logout'), 14 | path('login/', auth_views.LoginView.as_view(template_name='vendor/login.html'), name='login'), 15 | path('', views.vendors, name='vendors'), 16 | path('/', views.vendor, name='vendor'), 17 | ] -------------------------------------------------------------------------------- /apps/vendor/views.py: -------------------------------------------------------------------------------- 1 | from django.contrib.auth import login 2 | from django.contrib.auth.decorators import login_required 3 | from django.contrib.auth.forms import UserCreationForm 4 | from django.utils.text import slugify 5 | from django.shortcuts import render, redirect, get_object_or_404 6 | 7 | from .models import Vendor 8 | from apps.product.models import Product, ProductImage 9 | 10 | from .forms import ProductForm, ProductImageForm 11 | 12 | def become_vendor(request): 13 | if request.method == 'POST': 14 | form = UserCreationForm(request.POST) 15 | 16 | if form.is_valid(): 17 | user = form.save() 18 | 19 | login(request, user) 20 | 21 | vendor = Vendor.objects.create(name=user.username, created_by=user) 22 | 23 | return redirect('frontpage') 24 | else: 25 | form = UserCreationForm() 26 | 27 | return render(request, 'vendor/become_vendor.html', {'form': form}) 28 | 29 | @login_required 30 | def vendor_admin(request): 31 | vendor = request.user.vendor 32 | products = vendor.products.all() 33 | orders = vendor.orders.all() 34 | 35 | for order in orders: 36 | order.vendor_amount = 0 37 | order.vendor_paid_amount = 0 38 | order.fully_paid = True 39 | 40 | for item in order.items.all(): 41 | if item.vendor == request.user.vendor: 42 | if item.vendor_paid: 43 | order.vendor_paid_amount += item.get_total_price() 44 | else: 45 | order.vendor_amount += item.get_total_price() 46 | order.fully_paid = False 47 | 48 | return render(request, 'vendor/vendor_admin.html', {'vendor': vendor, 'products': products, 'orders': orders}) 49 | 50 | @login_required 51 | def add_product(request): 52 | if request.method == 'POST': 53 | form = ProductForm(request.POST, request.FILES) 54 | 55 | if form.is_valid(): 56 | product = form.save(commit=False) 57 | product.vendor = request.user.vendor 58 | product.slug = slugify(product.title) 59 | product.save() 60 | 61 | return redirect('vendor_admin') 62 | else: 63 | form = ProductForm() 64 | 65 | return render(request, 'vendor/add_product.html', {'form': form}) 66 | 67 | @login_required 68 | def edit_product(request, pk): 69 | vendor = request.user.vendor 70 | product = vendor.products.get(pk=pk) 71 | 72 | if request.method == 'POST': 73 | form = ProductForm(request.POST, request.FILES, instance=product) 74 | image_form = ProductImageForm(request.POST, request.FILES) 75 | 76 | if image_form.is_valid(): 77 | productimage = image_form.save(commit=False) 78 | productimage.product = product 79 | productimage.save() 80 | 81 | return redirect('vendor_admin') 82 | 83 | if form.is_valid(): 84 | form.save() 85 | 86 | return redirect('vendor_admin') 87 | else: 88 | form = ProductForm(instance=product) 89 | image_form = ProductImageForm() 90 | 91 | return render(request, 'vendor/edit_product.html', {'form': form, 'image_form': image_form, 'product': product}) 92 | 93 | @login_required 94 | def edit_vendor(request): 95 | vendor = request.user.vendor 96 | 97 | if request.method == 'POST': 98 | name = request.POST.get('name', '') 99 | email = request.POST.get('email', '') 100 | 101 | if name: 102 | vendor.created_by.email = email 103 | vendor.created_by.save() 104 | 105 | vendor.name = name 106 | vendor.save() 107 | 108 | return redirect('vendor_admin') 109 | 110 | return render(request, 'vendor/edit_vendor.html', {'vendor': vendor}) 111 | 112 | def vendors(request): 113 | vendors = Vendor.objects.all() 114 | 115 | return render(request, 'vendor/vendors.html', {'vendors': vendors}) 116 | 117 | def vendor(request, vendor_id): 118 | vendor = get_object_or_404(Vendor, pk=vendor_id) 119 | 120 | return render(request, 'vendor/vendor.html', {'vendor': vendor}) -------------------------------------------------------------------------------- /codesnippets/part1.txt: -------------------------------------------------------------------------------- 1 | Base.html 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Interiorshop 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 |
18 |
19 | 20 | 21 | 22 | ----- 23 | 24 | Navbar and footer 25 | 26 | 42 | 43 |