Files @ eff5686dcf29
Branch filter:

Location: symposion_app/registrasion/controllers/invoice.py

Christopher Neugebauer
Adds logic for required categories
from decimal import Decimal
from django.core.exceptions import ObjectDoesNotExist
from django.db.models import Sum

from registrasion import models as rego

from cart import CartController


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)
            cart_controller.validate_cart()  # Raises ValidationError on fail.
            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)
        product_items = product_items.order_by(
            "product__category__order", "product__order"
        )
        discount_items = rego.DiscountItem.objects.filter(cart=cart)
        invoice_value = Decimal()
        for item in product_items:
            product = item.product
            line_item = rego.LineItem.objects.create(
                invoice=invoice,
                description="%s - %s" % (product.category.name, product.name),
                quantity=item.quantity,
                price=product.price,
            )
            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)
            cart.validate_cart()  # Raises ValidationError if invalid

        ''' Adds a payment '''
        payment = rego.Payment.objects.create(
            invoice=self.invoice,
            reference=reference,
            amount=amount,
        )
        payment.save()

        payments = rego.Payment.objects .filter(invoice=self.invoice)
        agg = payments.aggregate(Sum("amount"))
        total = agg["amount__sum"]

        if total == self.invoice.value:
            self.invoice.paid = True

            cart = self.invoice.cart
            cart.active = False
            cart.save()

            self.invoice.save()