as I stated in the title, I have a problem where an order is saved multiple times in my online store, the more I go into the problem, the more I mess it up.
I am pretty new at this and I need some assistance, thank you for taking the time to look at this hot mess.
models.py:
from django.db import models
from django.contrib.auth.models import User
class Customer(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, null=True, blank=True)
first_name = models.CharField(max_length=200, null=True)
last_name = models.CharField(max_length=200, null=True)
email = models.EmailField(max_length=200, null=True)
phone = models.IntegerField(null=True)
def __str__(self):
return str(self.first_name) + ' ' + str(self.last_name)
class Product(models.Model):
name = models.CharField(max_length=200)
category = models.CharField(max_length=200, null=True, blank=True)
price = models.IntegerField(default=1000, null=True, blank=True)
image = models.ImageField(null=True, blank=True)
def __str__(self):
return str(self.id) + '.' + str(self.name)
def display_in_euro(self):
cents = str(self.price)
return round(int(cents) / 100)
@property
def imageURL(self):
try:
url = self.image.url
except:
url = ''
return url
class Order(models.Model):
customer = models.ForeignKey(Customer, on_delete=models.SET_NULL, null=True, blank=True)
date_ordered = models.DateTimeField(auto_now_add=True)
complete = models.BooleanField(default=False)
transaction_id = models.CharField(max_length=100, null=True)
def __str__(self):
return str(self.id) + '. ' + str(self.customer)
@property
def get_cart_total(self):
order_items = self.orderitem_set.all()
total = sum([item.get_total for item in order_items]) / 100
return total
@property
def get_cart_items(self):
order_items = self.orderitem_set.all()
quantity = sum([item.quantity for item in order_items])
return quantity
class OrderItem(models.Model):
customer = models.ForeignKey(Customer, on_delete=models.SET_NULL, null=True, blank=True)
product = models.ForeignKey(Product, on_delete=models.SET_NULL, null=True, blank=True)
order = models.ForeignKey(Order, on_delete=models.SET_NULL, null=True, blank=True)
quantity = models.IntegerField(default=0, null=True, blank=True)
date_added = models.DateTimeField(auto_now_add=True)
def __str__(self):
return str(self.customer) + ' - ' + str(self.product) + '*' + str(self.quantity)
@property
def get_total(self):
total = self.quantity * self.product.price
return total
@property
def get_stripe_total(self):
total = round(self.quantity * self.product.price)
return total
class ShippingAddress(models.Model):
customer = models.ForeignKey(Customer, on_delete=models.SET_NULL, null=True, blank=True)
order = models.ForeignKey(Order, on_delete=models.SET_NULL, null=True, blank=True)
country = models.CharField(max_length=200)
county = models.CharField(max_length=200)
city = models.CharField(max_length=200)
address = models.CharField(max_length=200)
zipcode = models.CharField(max_length=200)
date_added = models.DateTimeField(auto_now_add=True)
def __str__(self):
return str(self.id) + ' | ' + str(self.customer) + ' | ' + str(self.city) + ' | ' + str(self.address)
utils.py:
import json
import os
import ssl
import smtplib
from os.path import basename
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.mime.application import MIMEApplication
from .models import *
from fpdf import FPDF
def cookie_cart(request):
try:
cart_lc = json.loads(request.COOKIES['cart'])
except:
cart_lc = {}
items = []
order = {'get_cart_total': 0, 'get_cart_items': 0}
cart_items = order['get_cart_items']
for i in cart_lc:
try:
cart_items += cart_lc[i]['quantity']
product = Product.objects.get(id=i)
total = (product.price * cart_lc[i]['quantity'])
order['get_cart_total'] += total
order['get_cart_items'] += cart_lc[i]['quantity']
item = {
'product': {
'id': product.id,
'name': product.name,
'price': product.price,
'imageURL': product.imageURL,
},
'quantity': cart_lc[i]['quantity'],
'get_total': total,
}
items.append(item)
except:
pass
return {'items': items, 'order': order, 'cart_items': cart_items}
def cart_data(request):
if request.user.is_authenticated:
customer = request.user.customer
order, created = Order.objects.get_or_create(customer=customer, complete=False)
items = order.orderitem_set.all()
cart_items = order.get_cart_items
else:
cookie_data = cookie_cart(request)
cart_items = cookie_data['cart_items']
items = cookie_data['items']
order = cookie_data['order']
return {'order': order, 'items': items, 'cart_items': cart_items}
def guest_order(request, data):
cookie_data = cookie_cart(request)
form = data['form']
first_name = form['first_name']
last_name = form['last_name']
email = form['email']
phone = form['phone']
items = cookie_data['items']
customer, created = Customer.objects.get_or_create(email=email)
customer.first_name = first_name
customer.last_name = last_name
customer.phone = phone
customer.save()
order = Order.objects.create(customer=customer, complete=False)
for item in items:
product = Product.objects.get(id=item['product']['id'])
order_item = OrderItem.objects.create(
product=product,
order=order,
customer=customer,
quantity=item['quantity'],
)
return {'customer': customer, 'order': order}
class GetUserData:
def __init__(self):
self.first_name = None
self.last_name = None
self.email = None
self.phone = None
self.cart_items = {
'item_id': [],
'item_name': [],
'item_price': [],
'item_quantity': [],
'item_total': []
}
@staticmethod
def get_shipping_info(data):
shipping = data.get('shipping', {})
return {
'country': shipping.get('country', ''),
'county': shipping.get('county', ''),
'city': shipping.get('city', ''),
'address': shipping.get('address', ''),
'zipcode': shipping.get('zipcode', ''),
}
def get_user_details(self, data, request):
if request.user.is_anonymous:
self.first_name = data['form']['first_name']
self.last_name = data['form']['last_name']
self.email = data['form']['email']
self.phone = data['form']['phone']
else:
customer_lc = request.user.customer
self.first_name = customer_lc.first_name
self.last_name = customer_lc.last_name
self.email = customer_lc.email
self.phone = customer_lc.phone
return {
'first_name': self.first_name,
"last_name": self.last_name,
'email': self.email,
'phone': self.phone,
}
def get_user_order(self, request):
user = request.user
if user.is_anonymous:
cart = cookie_cart(request)['items']
else:
customer = user.customer
order = Order.objects.filter(customer=customer, complete=False).first()
if not order:
order = Order.objects.create(customer=customer, complete=False)
cart = OrderItem.objects.filter(order=order)
for item in cart:
if request.user.is_anonymous:
item_id = item['product']['id']
item_name = item['product']['name']
item_price = item['product']['price']
item_quantity = item['quantity']
else:
item_id = item.product.id
item_name = item.product.name
item_price = item.product.price
item_quantity = item.quantity
item_total = item_price * item_quantity
self.cart_items['item_id'].append(item_id)
self.cart_items['item_name'].append(item_name)
self.cart_items['item_price'].append(item_price)
self.cart_items['item_quantity'].append(item_quantity)
self.cart_items['item_total'].append(item_total)
return self.cart_items
class PDF(FPDF):
def header(self):
self.set_font('helvetica', 'B', 25)
title = 'Magic Home Decors'
title_w = self.get_string_width(title) + 6
doc_w = self.w
self.set_x((doc_w - title_w) / 2)
# Title Background Color
self.set_fill_color(230, 230, 0)
# Text Color
# self.set_text_color(255, 255, 255)
# Border Width
self.set_line_width(1)
self.cell(title_w, 10, title, border='B', align='C', ln=True)
self.ln(10)
def footer(self):
self.set_y(-15)
self.set_font('helvetica', 'I', 15)
self.set_text_color(169, 169, 169)
self.cell(0, 10, f'Page {self.page_no()}/{{nb}}', align='C')
def make_invoice(data, request, user_order):
user_data = GetUserData()
user_address = user_data.get_shipping_info(data=data)
user_info = user_data.get_user_details(data=data, request=request)
user_data.get_user_order(request=request)
user_cart_items = user_order
pdf = PDF(orientation='P', unit='mm', format='A4')
pdf.alias_nb_pages()
pdf.set_auto_page_break(auto=True, margin=15)
pdf.add_page()
pdf.set_font('helvetica', 'B', 20)
# Content
pdf.cell(0, 15, 'User Address:', align='L', ln=False)
pdf.cell(0, 15, 'User Info:', align='R', ln=True)
pdf.set_font('helvetica', '', 15)
start_y = pdf.get_y() # Get the starting Y position before the first multi_cell
pdf.multi_cell(0, 15, txt=f"""
Country: {user_address['country']},
County: {user_address['county']},
City: {user_address['city']},
Address: {user_address['address']},
ZipCode: {user_address['zipcode']},
""", align='L', ln=False)
pdf.set_y(start_y) # Reset the Y position for the second multi_cell
pdf.set_x(105) # Adjust the X position for the second multi_cell
pdf.multi_cell(0, 15, txt=f"""
First Name: {user_info['first_name']},
Last Name: {user_info['last_name']},
Email Address: {user_info['email']},
Phone Number: {user_info['phone']},
""", align='R', ln=True)
# Draw border
pdf.cell(0, 10, '', border='B', ln=True, align='C')
# Items:
pdf.set_draw_color(169, 169, 169)
pdf.set_font('helvetica', 'I', 12)
pdf.cell(0, 10, 'Ordered Items: ', border='B', ln=True, align='C')
item_name = user_cart_items['item_name']
item_price = user_cart_items['item_price']
item_quantity = user_cart_items['item_quantity']
item_total = user_cart_items['item_total']
for i in user_cart_items['item_id']:
i -= 1
pdf.cell(0, 10, f"""
{i + 1}. {item_name[i]}: {int(item_price[i]) / 100} * {item_quantity[i]} = {int(item_total[i]) / 100}
""", border='B', ln=True, align='C')
pdf.cell(0, 10, f"Invoice Total: {sum(user_cart_items['item_total']) / 100}", align='C', border='B')
return pdf.output(f"static/invoices/InvoiceFor{user_info['first_name']}{user_info['last_name']}.pdf")
class SendEmail:
def __init__(self):
self.user_data = GetUserData()
self.sender = '[email protected]'
self.password = os.getenv('PASS')
self.seller = '[email protected]'
self.subject = 'Magic Home Decors'
self.context = ssl.create_default_context()
self.server = smtplib.SMTP_SSL(host='smtp.gmail.com', port=465, context=self.context)
self.server.login(self.sender, self.password)
def send_email(self, data, request, receiver):
msg = MIMEMultipart()
msg['From'] = self.sender
msg['Subject'] = self.subject
user_details = self.user_data.get_user_details(data=data, request=request)
fname = user_details['first_name']
lname = user_details['last_name']
filename = f"static/invoices/InvoiceFor{fname}{lname}.pdf"
with open(filename, 'rb') as f:
attachment = MIMEApplication(f.read(), Name=basename(filename))
attachment['Content-Disposition'] = 'attachment; filename="{}"'.format(basename(filename))
msg.attach(attachment)
customer_body = f"""
Thank you {fname} for choosing Magic Home Decors! n
You have successfully placed an order! n
Below you can find an invoice with your details and your order:
"""
body = MIMEText(customer_body, 'plain')
msg['To'] = receiver
msg.attach(body)
return self.server.send_message(msg, from_addr=self.sender, to_addrs=[receiver])
def send_seller_email(self, data, request):
msg = MIMEMultipart()
msg['From'] = self.sender
msg['Subject'] = self.subject
user_details = self.user_data.get_user_details(data=data, request=request)
fname = user_details['first_name']
lname = user_details['last_name']
filename = f"static/invoices/InvoiceFor{fname}{lname}.pdf"
with open(filename, 'rb') as f:
attachment = MIMEApplication(f.read(), Name=basename(filename))
attachment['Content-Disposition'] = 'attachment; filename="{}"'.format(basename(filename))
msg.attach(attachment)
seller_body = f"""
An order has been placed by {fname} {lname} n
You can find the invoice with {fname}'s data attached below!
"""
body = MIMEText(seller_body, 'plain')
msg.add_header('To', self.seller)
msg.attach(body) # Attach the seller's email body here
return self.server.send_message(msg, from_addr=self.sender, to_addrs=[self.seller])
views.py:
import os
import time
from django.http import JsonResponse, HttpResponse
from django.shortcuts import render
from django.views import View
from django.views.decorators.csrf import csrf_exempt
from django.conf import settings
from .utils import cart_data, guest_order, SendEmail, make_invoice, GetUserData
from .models import *
import json
import datetime
import stripe
import pickle
stripe.api_key = settings.STRIPE_SECRET_KEY
def store(request):
products = Product.objects.all()
cart_items = cart_data(request)['cart_items']
context = {'products': products, 'cartItems': cart_items}
return render(request, 'store/store.html', context)
def cart(request):
data = cart_data(request)
items = data['items']
order = data['order']
cart_items = data['cart_items']
if request.user.is_anonymous:
order['get_cart_total'] = order['get_cart_total'] / 100
for item in items:
item['product']['price'] = item['product']['price'] / 100
item['get_total'] = item['get_total'] / 100
else:
for item in items:
item.product.price = item.product.display_in_euro()
context = {'basket': items,
'order': order,
'cartItems': cart_items,
'logged': request.user.is_authenticated}
return render(request, 'store/cart.html', context)
def checkout(request):
data = cart_data(request)
items = data['items']
order = data['order']
cart_items = data['cart_items']
item_id = None
user_order = []
if request.user.is_anonymous:
order['get_cart_total'] = order['get_cart_total'] / 100
for item in items:
if request.user.is_anonymous:
item['product']['price'] = item['product']['price'] / 100
item['get_total'] = item['get_total'] / 100
else:
item.product.price = item.product.display_in_euro()
try:
item_id = item.product.id
except Exception as e:
item_id = item['product']['id']
user_data = GetUserData()
user_order = user_data.get_user_order(request)
pickle_out = open("user_order.pickle", "wb")
pickle.dump(obj=user_order, file=pickle_out)
pickle_out.close()
product = Product.objects.get(id=item_id)
context = {'basket': items,
'order': order,
'cartItems': cart_items,
'STRIPE_PUBLIC_KEY': settings.STRIPE_PUBLIC_KEY,
'product': product}
return render(request, 'store/checkout.html', context)
def update_item(request):
data = json.loads(request.body)
product_id = data['productId']
action = data['action']
customer = request.user.customer
product = Product.objects.get(id=product_id)
order, created = Order.objects.get_or_create(customer=customer, complete=False)
order_item, created = OrderItem.objects.get_or_create(order=order, product=product, customer=customer)
if action == 'add':
order_item.quantity = (order_item.quantity + 1)
elif action == 'remove':
order_item.quantity = (order_item.quantity - 1)
if order_item.quantity <= 0:
order_item.quantity = 0
order_item.delete()
else:
order_item.save()
return JsonResponse('item Was Added!', safe=False)
def success(request):
return render(request, 'store/success.html')
def cancel(request):
return render(request, 'store/cancel.html')
def process_order(request):
transaction_id = datetime.datetime.now().timestamp()
data = json.loads(request.body)
pickle_out = open("user_data.pickle", "wb")
pickle.dump(obj=data, file=pickle_out)
pickle_out.close()
if request.user.is_authenticated:
customer = request.user.customer
order, created = Order.objects.get_or_create(customer=customer, complete=False)
else:
customer = guest_order(request=request, data=data)['customer']
order = guest_order(request=request, data=data)['order']
total = float(data['form']['total'])
order.transaction_id = transaction_id
if total == order.get_cart_total:
order.complete = True
order.save()
ShippingAddress.objects.create(
customer=customer,
order=order,
country=data['shipping']['country'],
county=data['shipping']['county'],
city=data['shipping']['city'],
address=data['shipping']['address'],
zipcode=data['shipping']['zipcode'],
)
return JsonResponse('Payment Complete', safe=False)
class CreateCheckoutSessionView(View):
def post(self, request, *args, **kwargs):
data = cart_data(request)
items = data['items']
line_items = []
prod_id_list = ''
for item in items:
try:
quantity = item.quantity
item_price = item.product.price
item_name = item.product.name
prod_id_list += str(item.product.id)
except Exception as e:
quantity = item['quantity']
item_price = item['product']['price']
item_name = item['product']['name']
prod_id_list += str(item['product']['id'])
line_items.append(
{
'price_data': {
'currency': 'eur',
'unit_amount': item_price,
'product_data': {
'name': item_name,
},
},
'quantity': quantity
},
)
YOUR_DOMAIN = 'http://127.0.0.1:8000'
product_id = self.kwargs["pk"]
product = Product.objects.get(id=product_id)
checkout_session = stripe.checkout.Session.create(
payment_method_types=['card'],
line_items=line_items,
metadata={
"product_id": prod_id_list
},
mode='payment',
success_url=YOUR_DOMAIN + '/success/',
cancel_url=YOUR_DOMAIN + '/cancel/',
)
return JsonResponse({
'id': checkout_session.id
})
@csrf_exempt
def stripe_web_hook(request):
payload = request.body
sig_header = request.META['HTTP_STRIPE_SIGNATURE']
event = None
try:
event = stripe.Webhook.construct_event(
payload, sig_header, settings.STRIPE_WEBHOOK_SECRET
)
except ValueError as e:
return HttpResponse(status=400)
except stripe.error.SignatureVerificationError as e:
return HttpResponse(status=400)
if event['type'] == 'checkout.session.completed':
session = event['data']['object']
pickle_in = open("user_data.pickle", "rb")
pickle_in_order = open("user_order.pickle", "rb")
user_data = pickle.load(pickle_in)
user_order = pickle.load(pickle_in_order)
print("USER_DATA: ", user_data)
make_invoice(data=user_data, request=request, user_order=user_order)
mail = SendEmail()
receiver = session["customer_details"]["email"]
print("RECEIVER: ", receiver)
mail.send_email(data=user_data, request=request, receiver=receiver)
mail.send_seller_email(data=user_data, request=request)
return HttpResponse(status=200)
I tried everything I could possibly think of