Files
@ c7b6c81071fe
Branch filter:
Location: symposion_app/registrasion/controllers/invoice.py
c7b6c81071fe
5.8 KiB
text/x-python
adds setuptools’ build directory to the flake8 ignore path.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 | from decimal import Decimal
from django.core.exceptions import ObjectDoesNotExist
from django.core.exceptions import ValidationError
from django.db import transaction
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
self.update_validity() # Make sure this invoice is up-to-date
@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,
void=False,
)
except ObjectDoesNotExist:
cart_controller = CartController(cart)
cart_controller.validate_cart() # Raises ValidationError on fail.
# Void past invoices for this cart
rego.Invoice.objects.filter(cart=cart).update(void=True)
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 update_validity(self):
''' Updates the validity of this invoice if the cart it is attached to
has updated. '''
if self.invoice.cart is not None:
if self.invoice.cart.revision != self.invoice.cart_revision:
self.void()
def void(self):
''' Voids the invoice if it is valid to do so. '''
if self.invoice.paid:
raise ValidationError("Paid invoices cannot be voided, "
"only refunded.")
self.invoice.void = True
self.invoice.save()
@transaction.atomic
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:
cart = CartController(self.invoice.cart)
cart.validate_cart() # Raises ValidationError if invalid
if self.invoice.void:
raise ValidationError("Void invoices cannot be paid")
if self.invoice.paid:
raise ValidationError("Paid invoices cannot be paid again")
''' 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
if self.invoice.cart:
cart = self.invoice.cart
cart.active = False
cart.save()
self.invoice.save()
@transaction.atomic
def refund(self, reference, amount):
''' Refunds the invoice by the given amount. The invoice is
marked as unpaid, and the underlying cart is marked as released.
'''
if self.invoice.void:
raise ValidationError("Void invoices cannot be refunded")
''' Adds a payment '''
payment = rego.Payment.objects.create(
invoice=self.invoice,
reference=reference,
amount=0 - amount,
)
payment.save()
self.invoice.paid = False
self.invoice.void = True
if self.invoice.cart:
cart = self.invoice.cart
cart.released = True
cart.save()
self.invoice.save()
|