Files @ 2f4ebc22afea
Branch filter:

Location: symposion_app/registrasion/controllers/invoice.py

Christopher Neugebauer
Merge branch 'category_form'
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()