I have two models orders/carts. I got braintreee implemented and working fine with sandbox. Now need to get cashondelivery model implemented with minimum iteration. So I did created a form out of field "payment_options" from Order models. I need help to generalize the process to implement COD. I know its a lengthy code set but I would appreciate if someone can take couple minutes to look at it. Secondly its not letting me call two different forms in my carts CheckoutView so some help would be great. For the sake of process I had to provide complete set of code so please don't mind if question is over populated. Following is my code:
Orders/models.py:
from decimal import Decimal
from django.conf import settings
from django.core.urlresolvers import reverse
from django.db import models
from django.db.models.signals import pre_save, post_save
from carts.models import Cart
import braintree
if settings.DEBUG:
braintree.Configuration.configure(braintree.Environment.Sandbox,
merchant_id=settings.BRAINTREE_MERCHANT_ID,
public_key=settings.BRAINTREE_PUBLIC,
private_key=settings.BRAINTREE_PRIVATE)
class UserCheckout(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL, null=True, blank=True) #not required
email = models.EmailField(unique=True) #--> required
braintree_id = models.CharField(max_length=120, null=True, blank=True)
def __unicode__(self): #def __str__(self):
return self.email
@property
def get_braintree_id(self,):
instance = self
if not instance.braintree_id:
result = braintree.Customer.create({
"email": instance.email,
})
if result.is_success:
instance.braintree_id =
result.customer.id instance.save()
return instance.braintree_id
def get_client_token(self):
customer_id = self.get_braintree_id
if customer_id:
client_token = braintree.ClientToken.generate({
"customer_id": customer_id
})
return client_token
return None
def update_braintree_id(sender, instance, *args, **kwargs):
if not instance.braintree_id:
instance.get_braintree_id
post_save.connect(update_braintree_id, sender=UserCheckout)
ADDRESS_TYPE = (
('billing', 'Billing'),
('shipping', 'Shipping'),
)
class UserAddress(models.Model):
user = models.ForeignKey(UserCheckout)
type = models.CharField(max_length=120, choices=ADDRESS_TYPE)
street = models.CharField(max_length=120)
city = models.CharField(max_length=120)
state = models.CharField(max_length=120)
zipcode = models.CharField(max_length=120)
def __unicode__(self):
return self.street
def get_address(self):
return "%s, %s, %s %s" %(self.street, self.city, self.state, self.zipcode)
ORDER_STATUS_CHOICES = (
('created', 'Created'),
('paid', 'Paid'),
('shipped', 'Shipped'),
('refunded', 'Refunded'),
)
PAYMENT_BY =(
('paypal', 'Paypal'),
('creditcard', 'Credit Card'),
('cash on delivery', 'Cash On Delivery'),
)
class Order(models.Model):
status = models.CharField(max_length=120, choices=ORDER_STATUS_CHOICES, default='created')
cart = models.ForeignKey(Cart)
user = models.ForeignKey(UserCheckout, null=True)
billing_address = models.ForeignKey(UserAddress, related_name='billing_address', null=True)
shipping_address = models.ForeignKey(UserAddress, related_name='shipping_address', null=True)
shipping_total_price = models.DecimalField(max_digits=50, decimal_places=2, default=5.99)
order_total = models.DecimalField(max_digits=50, decimal_places=2, )
order_id = models.CharField(max_length=20, null=True, blank=True)
payment_options = models.CharField(max_length=20, choices=PAYMENT_BY)
class Meta:
ordering = ['-id']
def get_absolute_url(self):
return reverse("order_detail", kwargs={"pk":
self.pk})
def mark_completed(self, order_id=None):
self.status = "paid"
if order_id and not self.order_id:
self.order_id = order_id
self.save()
def order_pre_save(sender, instance, *args, **kwargs):
shipping_total_price = instance.shipping_total_price
cart_total = instance.cart.total
order_total = Decimal(shipping_total_price) + Decimal(cart_total)
instance.order_total = order_total
Orders/forms.py:
from django import forms
from django.contrib.auth import get_user_model
from .models import Order
from .models import UserAddress
User = get_user_model()
class GuestCheckoutForm(forms.Form):
email = forms.EmailField()
email2 = forms.EmailField(label='Verify Email')
def clean_email2(self):
email = self.cleaned_data.get("email")
email2 = self.cleaned_data.get("email2")
if email == email2:
user_exists = User.objects.filter(email=email).count()
if user_exists != 0:
raise forms.ValidationError("This User already exists. Please login instead.")
return email2
else:
raise forms.ValidationError("Please confirm emails are the same")
class AddressForm(forms.Form):
billing_address = forms.ModelChoiceField(
queryset=UserAddress.objects.filter(type="billing"),
widget = forms.RadioSelect,
empty_label = None
)
shipping_address = forms.ModelChoiceField(
queryset=UserAddress.objects.filter(type="shipping"),
widget = forms.RadioSelect,
empty_label = None,
)
class UserAddressForm(forms.ModelForm):
class Meta:
model = UserAddress
fields = [
'street',
'city',
'state',
'zipcode',
'type'
]
class OrderForm(forms.ModelForm):
class Meta:
model = Order
fields = [
'payment_options'
]
Orders/views.py:
from django.contrib import messages
from django.http import Http404
from django.shortcuts import redirect
from django.views.generic.edit import CreateView, FormView
from django.views.generic.detail import DetailView
from django.views.generic.list import ListView
from .forms import AddressForm, UserAddressForm
from .mixins import CartOrderMixin, LoginRequiredMixin
from .models import UserAddress, UserCheckout, Order
class OrderDetail(DetailView):
model = Order
def dispatch(self, request, *args, **kwargs):
try:
user_check_id = self.request.session.get("user_checkout_id")
user_checkout = UserCheckout.objects.get(id=user_check_id)
except UserCheckout.DoesNotExist:
user_checkout = UserCheckout.objects.get(user=request.user)
except:
user_checkout = None
obj = self.get_object()
if obj.user == user_checkout and user_checkout is not None:
return super(OrderDetail, self).dispatch(request, *args, **kwargs)
else:
raise Http404
class OrderList(ListView):
queryset = Order.objects.all()
def get_queryset(self):
user_check_id = self.request.user.id
user_checkout = UserCheckout.objects.get(id=user_check_id)
return super(OrderList, self).get_queryset().filter(user=user_checkout)
class UserAddressCreateView(CreateView):
form_class = UserAddressForm
template_name = "forms.html"
success_url = "/checkout/address/"
def get_checkout_user(self):
user_check_id = self.request.session.get("user_checkout_id")
user_checkout = UserCheckout.objects.get(id=user_check_id)
return user_checkout
def form_valid(self, form, *args, **kwargs):
form.instance.user = self.get_checkout_user()
return super(UserAddressCreateView, self).form_valid(form, *args, **kwargs)
class AddressSelectFormView(CartOrderMixin, FormView):
form_class = AddressForm
template_name = "orders/address_select.html"
def dispatch(self, *args, **kwargs):
b_address, s_address = self.get_addresses()
if b_address.count() == 0:
messages.success(self.request, "Please add a billing address before continuing")
return redirect("user_address_create")
elif s_address.count() == 0:
messages.success(self.request, "Please add a shipping address before continuing")
return redirect("user_address_create")
else:
return super(AddressSelectFormView, self).dispatch(*args, **kwargs)
def get_addresses(self, *args, **kwargs):
user_check_id = self.request.session.get("user_checkout_id")
user_checkout = UserCheckout.objects.get(id=user_check_id)
b_address = UserAddress.objects.filter(
user=user_checkout,
type='billing',
)
s_address = UserAddress.objects.filter(
user=user_checkout,
type='shipping',
)
return b_address, s_address
def get_form(self, *args, **kwargs):
form = super(AddressSelectFormView, self).get_form(*args, **kwargs)
b_address, s_address = self.get_addresses()
form.fields["billing_address"].queryset = b_address
form.fields["shipping_address"].queryset = s_address
return form
def form_valid(self, form, *args, **kwargs):
billing_address = form.cleaned_data["billing_address"]
shipping_address = form.cleaned_data["shipping_address"]
order = self.get_order()
order.billing_address = billing_address
order.shipping_address = shipping_address
order.save()
return super(AddressSelectFormView, self).form_valid(form, *args, **kwargs)
def get_success_url(self, *args, **kwargs):
return "/checkout/"
Carts/models.py:
from decimal import Decimal
from django.conf import settings
from django.core.urlresolvers import reverse
from django.db import models
from django.db.models.signals import pre_save, post_save, post_delete
from products.models import Variation
class CartItem(models.Model):
cart = models.ForeignKey("Cart")
item = models.ForeignKey(Variation)
quantity = models.PositiveIntegerField(default=1)
line_item_total = models.DecimalField(max_digits=10, decimal_places=2)
def __unicode__(self):
return self.item.title
def remove(self):
return self.item.remove_from_cart()
def cart_item_pre_save_receiver(sender, instance, *args, **kwargs):
qty = instance.quantity
if qty >= 1:
price = instance.item.get_price()
line_item_total = Decimal(qty) * Decimal(price)
instance.line_item_total = line_item_total
pre_save.connect(cart_item_pre_save_receiver, sender=CartItem)
def cart_item_post_save_receiver(sender, instance, *args, **kwargs):
instance.cart.update_subtotal()
post_save.connect(cart_item_post_save_receiver, sender=CartItem)
post_delete.connect(cart_item_post_save_receiver, sender=CartItem)
class Cart(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, blank=True)
items = models.ManyToManyField(Variation, through=CartItem)
timestamp = models.DateTimeField(auto_now_add=True, auto_now=False)
updated = models.DateTimeField(auto_now_add=False, auto_now=True)
subtotal = models.DecimalField(max_digits=50, decimal_places=2, default=25.00)
tax_percentage = models.DecimalField(max_digits=10, decimal_places=5, default=0.085)
tax_total = models.DecimalField(max_digits=50, decimal_places=2, default=25.00)
total = models.DecimalField(max_digits=50, decimal_places=2, default=25.00)
# discounts
# shipping
def __unicode__(self):
return str(self.id)
def update_subtotal(self):
print "updating..."
subtotal = 0
items = self.cartitem_set.all()
for item in items:
subtotal += item.line_item_total
self.subtotal = "%.2f" %(subtotal)
self.save()
def do_tax_and_total_receiver(sender, instance, *args, **kwargs):
subtotal = Decimal(instance.subtotal)
tax_total = round(subtotal * Decimal(instance.tax_percentage), 2) #8.5%
print instance.tax_percentage
total = round(subtotal + Decimal(tax_total), 2)
instance.tax_total = "%.2f" %(tax_total)
instance.total = "%.2f" %(total)
#instance.save()
pre_save.connect(do_tax_and_total_receiver, sender=Cart)
Carts/views.py:
import braintree
from django.conf import settings
from django.contrib import messages
from django.contrib.auth.forms import AuthenticationForm
from django.core.urlresolvers import reverse
from django.http import HttpResponseRedirect, Http404, JsonResponse
from django.shortcuts import render, get_object_or_404, redirect
from django.views.generic.base import View
from django.views.generic.detail import SingleObjectMixin, DetailView
from django.views.generic.edit import FormMixin
from orders.forms import GuestCheckoutForm, OrderForm
from orders.mixins import CartOrderMixin
from orders.models import UserCheckout, Order, UserAddress
from products.models import Variation
from .models import Cart, CartItem
if settings.DEBUG:
braintree.Configuration.configure(braintree.Environment.Sandbox,
merchant_id=settings.BRAINTREE_MERCHANT_ID,
public_key=settings.BRAINTREE_PUBLIC,
private_key=settings.BRAINTREE_PRIVATE)
class ItemCountView(View):
def get(self, request, *args, **kwargs):
if request.is_ajax():
cart_id = self.request.session.get("cart_id")
if cart_id == None:
count = 0
else:
cart = Cart.objects.get(id=cart_id)
count = cart.items.count()
request.session["cart_item_count"] = count
return JsonResponse({"count": count})
else:
raise Http404
class CartView(SingleObjectMixin, View):
model = Cart
template_name = "carts/view.html"
def get_object(self, *args, **kwargs):
self.request.session.set_expiry(0) #5 minutes
cart_id = self.request.session.get("cart_id")
if cart_id == None:
cart = Cart()
cart.tax_percentage = 0.075
cart.save()
cart_id = cart.id
self.request.session["cart_id"] = cart_id
cart = Cart.objects.get(id=cart_id)
if self.request.user.is_authenticated():
cart.user = self.request.user
cart.save()
return cart
def get(self, request, *args, **kwargs):
cart = self.get_object()
item_id = request.GET.get("item")
delete_item = request.GET.get("delete", False)
flash_message = ""
item_added = False
if item_id:
item_instance = get_object_or_404(Variation, id=item_id)
qty = request.GET.get("qty", 1)
try:
if int(qty) < 1:
delete_item = True
except:
raise Http404
cart_item, created = CartItem.objects.get_or_create(cart=cart, item=item_instance)
if created:
flash_message = "Successfully added to the cart"
item_added = True
if delete_item:
flash_message = "Item removed successfully."
cart_item.delete()
else:
if not created:
flash_message = "Quantity has been updated successfully."
cart_item.quantity = qty
cart_item.save()
if not request.is_ajax():
return HttpResponseRedirect(reverse("cart"))
#return cart_item.cart.get_absolute_url()
if request.is_ajax():
try:
total = cart_item.line_item_total
except:
total = None
try:
subtotal = cart_item.cart.subtotal
except:
subtotal = None
try:
cart_total = cart_item.cart.total
except:
cart_total = None
try:
tax_total = cart_item.cart.tax_total
except:
tax_total = None
try:
total_items = cart_item.cart.items.count()
except:
total_items = 0
data = {
"deleted": delete_item,
"item_added": item_added,
"line_total": total,
"subtotal": subtotal,
"cart_total": cart_total,
"tax_total": tax_total,
"flash_message": flash_message,
"total_items": total_items
}
return JsonResponse(data)
context = {
"object": self.get_object()
}
template = self.template_name
return render(request, template, context)
class CheckoutView(CartOrderMixin, FormMixin, DetailView):
model = Cart
template_name = "carts/checkout_view.html"
form_class = GuestCheckoutForm
def get_object(self, *args, **kwargs):
cart = self.get_cart()
if cart == None:
return None
return cart
def get_context_data(self, *args, **kwargs):
context = super(CheckoutView, self).get_context_data(*args, **kwargs)
user_can_continue = False
user_check_id = self.request.session.get("user_checkout_id")
if self.request.user.is_authenticated():
user_can_continue = True
user_checkout, created = UserCheckout.objects.get_or_create(email=self.request.user.email)
user_checkout.user = self.request.user
user_checkout.save()
context["client_token"] = user_checkout.get_client_token()
self.request.session["user_checkout_id"] =
user_checkout.id elif not self.request.user.is_authenticated() and user_check_id == None:
context["login_form"] = AuthenticationForm()
context["next_url"] = self.request.build_absolute_uri()
else:
pass
if user_check_id != None:
user_can_continue = True
if not self.request.user.is_authenticated(): #GUEST USER
user_checkout_2 = UserCheckout.objects.get(id=user_check_id)
context["client_token"] = user_checkout_2.get_client_token()
#if self.get_cart() is not None:
context["order"] = self.get_order()
context["user_can_continue"] = user_can_continue
context["form"] = self.get_form()
return context
def post(self, request, *args, **kwargs):
self.object = self.get_object()
form = self.get_form()
if form.is_valid():
email = form.cleaned_data.get("email")
user_checkout, created = UserCheckout.objects.get_or_create(email=email)
request.session["user_checkout_id"] =
user_checkout.id return self.form_valid(form)
else:
return self.form_invalid(form)
def get_success_url(self):
return reverse("checkout")
def get(self, request, *args, **kwargs):
get_data = super(CheckoutView, self).get(request, *args, **kwargs)
cart = self.get_object()
if cart == None:
return redirect("cart")
new_order = self.get_order()
user_checkout_id = request.session.get("user_checkout_id")
if user_checkout_id != None:
user_checkout = UserCheckout.objects.get(id=user_checkout_id)
if new_order.billing_address == None or new_order.shipping_address == None:
return redirect("order_address")
new_order.user = user_checkout
new_order.save()
return get_data
class CheckoutFinalView(CartOrderMixin, View):
def post(self, request, *args, **kwargs):
order = self.get_order()
order_total = order.order_total
nonce = request.POST.get("payment_method_nonce")
payment_options = order.payment_options
if nonce:
result = braintree.Transaction.sale({
"amount": order_total,
"payment_method_nonce": nonce,
"billing": {
"postal_code": "%s" %(order.billing_address.zipcode),
},
"options": {
"submit_for_settlement": True
}
})
if result.is_success:
#
result.transaction.id to order
order.mark_completed(order_id=
result.transaction.id)
messages.success(request, "Thank you for your order.")
del request.session["cart_id"]
del request.session["order_id"]
else:
#messages.success(request, "There was a problem with your order.")
messages.success(request, "%s" %(result.message))
return redirect("checkout")
else:
if payment_options == 'Cash On Delivery':
order.mark_completed()
messages.success(request, "Thank you for your order.")
del request.session['cart_id']
del request.session['order_id']
return redirect("checkout")
return redirect("order_detail", pk=
order.pk)
def get(self, request, *args, **kwargs):
return redirect("checkout")
Please advise how to create a cash on delivery process based on above code