2016-01-22 05:01:30 +00:00
|
|
|
from decimal import Decimal
|
|
|
|
from django.core.exceptions import ObjectDoesNotExist
|
2016-01-22 06:02:07 +00:00
|
|
|
from django.db.models import Sum
|
2016-01-22 05:01:30 +00:00
|
|
|
|
|
|
|
from registrasion import models as rego
|
|
|
|
|
|
|
|
from cart import CartController
|
|
|
|
|
2016-01-22 06:02:07 +00:00
|
|
|
|
2016-01-22 05:01:30 +00:00
|
|
|
class InvoiceController(object):
|
|
|
|
|
|
|
|
def __init__(self, invoice):
|
|
|
|
self.invoice = invoice
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def for_cart(cls, cart):
|
|
|
|
''' Returns an invoice object for a given cart at its current revision.
|
|
|
|
If such an invoice does not exist, the cart is validated, and if valid,
|
|
|
|
an invoice is generated.'''
|
|
|
|
|
|
|
|
try:
|
|
|
|
invoice = rego.Invoice.objects.get(
|
|
|
|
cart=cart, cart_revision=cart.revision)
|
|
|
|
except ObjectDoesNotExist:
|
|
|
|
cart_controller = CartController(cart)
|
2016-01-22 06:02:07 +00:00
|
|
|
cart_controller.validate_cart() # Raises ValidationError on fail.
|
2016-01-22 05:01:30 +00:00
|
|
|
invoice = cls._generate(cart)
|
|
|
|
|
|
|
|
return InvoiceController(invoice)
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def resolve_discount_value(cls, item):
|
|
|
|
try:
|
|
|
|
condition = rego.DiscountForProduct.objects.get(
|
|
|
|
discount=item.discount,
|
|
|
|
product=item.product
|
|
|
|
)
|
|
|
|
except ObjectDoesNotExist:
|
|
|
|
condition = rego.DiscountForCategory.objects.get(
|
|
|
|
discount=item.discount,
|
|
|
|
category=item.product.category
|
|
|
|
)
|
|
|
|
if condition.percentage is not None:
|
|
|
|
value = item.product.price * (condition.percentage / 100)
|
|
|
|
else:
|
|
|
|
value = condition.price
|
|
|
|
return value
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def _generate(cls, cart):
|
|
|
|
''' Generates an invoice for the given cart. '''
|
|
|
|
invoice = rego.Invoice.objects.create(
|
|
|
|
user=cart.user,
|
|
|
|
cart=cart,
|
|
|
|
cart_revision=cart.revision,
|
|
|
|
value=Decimal()
|
|
|
|
)
|
|
|
|
invoice.save()
|
|
|
|
|
|
|
|
# TODO: calculate line items.
|
|
|
|
product_items = rego.ProductItem.objects.filter(cart=cart)
|
2016-03-04 20:22:01 +00:00
|
|
|
product_items = product_items.order_by(
|
|
|
|
"product__category__order", "product__order"
|
|
|
|
)
|
2016-01-22 05:01:30 +00:00
|
|
|
discount_items = rego.DiscountItem.objects.filter(cart=cart)
|
|
|
|
invoice_value = Decimal()
|
|
|
|
for item in product_items:
|
2016-03-04 20:22:01 +00:00
|
|
|
product = item.product
|
2016-01-22 05:01:30 +00:00
|
|
|
line_item = rego.LineItem.objects.create(
|
|
|
|
invoice=invoice,
|
2016-03-04 20:22:01 +00:00
|
|
|
description="%s - %s" % (product.category.name, product.name),
|
2016-01-22 05:01:30 +00:00
|
|
|
quantity=item.quantity,
|
2016-03-04 20:22:01 +00:00
|
|
|
price=product.price,
|
2016-01-22 05:01:30 +00:00
|
|
|
)
|
|
|
|
line_item.save()
|
|
|
|
invoice_value += line_item.quantity * line_item.price
|
|
|
|
|
|
|
|
for item in discount_items:
|
|
|
|
line_item = rego.LineItem.objects.create(
|
|
|
|
invoice=invoice,
|
|
|
|
description=item.discount.description,
|
|
|
|
quantity=item.quantity,
|
|
|
|
price=cls.resolve_discount_value(item) * -1,
|
|
|
|
)
|
|
|
|
line_item.save()
|
|
|
|
invoice_value += line_item.quantity * line_item.price
|
|
|
|
|
|
|
|
# TODO: calculate line items from discounts
|
|
|
|
invoice.value = invoice_value
|
|
|
|
invoice.save()
|
|
|
|
|
|
|
|
return invoice
|
|
|
|
|
|
|
|
def is_valid(self):
|
|
|
|
''' Returns true if the attached invoice is not void and it represents
|
|
|
|
a valid cart. '''
|
|
|
|
if self.invoice.void:
|
|
|
|
return False
|
|
|
|
if self.invoice.cart is not None:
|
|
|
|
if self.invoice.cart.revision != self.invoice.cart_revision:
|
|
|
|
return False
|
|
|
|
return True
|
|
|
|
|
|
|
|
def void(self):
|
|
|
|
''' Voids the invoice. '''
|
|
|
|
self.invoice.void = True
|
|
|
|
|
|
|
|
def pay(self, reference, amount):
|
|
|
|
''' Pays the invoice by the given amount. If the payment
|
|
|
|
equals the total on the invoice, finalise the invoice.
|
|
|
|
(NB should be transactional.)
|
|
|
|
'''
|
|
|
|
if self.invoice.cart is not None:
|
|
|
|
cart = CartController(self.invoice.cart)
|
2016-01-22 06:02:07 +00:00
|
|
|
cart.validate_cart() # Raises ValidationError if invalid
|
2016-01-22 05:01:30 +00:00
|
|
|
|
|
|
|
''' Adds a payment '''
|
|
|
|
payment = rego.Payment.objects.create(
|
|
|
|
invoice=self.invoice,
|
|
|
|
reference=reference,
|
|
|
|
amount=amount,
|
|
|
|
)
|
|
|
|
payment.save()
|
|
|
|
|
2016-03-24 03:20:29 +00:00
|
|
|
payments = rego.Payment.objects.filter(invoice=self.invoice)
|
2016-01-22 05:01:30 +00:00
|
|
|
agg = payments.aggregate(Sum("amount"))
|
|
|
|
total = agg["amount__sum"]
|
|
|
|
|
2016-01-22 06:02:07 +00:00
|
|
|
if total == self.invoice.value:
|
2016-01-22 05:01:30 +00:00
|
|
|
self.invoice.paid = True
|
|
|
|
|
|
|
|
cart = self.invoice.cart
|
|
|
|
cart.active = False
|
|
|
|
cart.save()
|
|
|
|
|
|
|
|
self.invoice.save()
|