from cashonly.core.models import Account, Transaction, SalesLogEntry, Product from django.core.files import File from django.contrib.auth.models import User from django.utils.translation import ugettext_noop from django.db.models.signals import pre_save, post_save, pre_delete from django.dispatch import receiver from django.db import transaction from django.conf import settings import PIL.Image import io class AccountManager: def __init__(self, account): self.account = account @transaction.atomic def add_transaction(self, amount, subject, description): self.account.refresh_from_db() self.account.credit = self.account.credit + amount self.account.save() Transaction.objects.create(account=self.account, subject=subject, amount=amount, description=description) def change_credit(self, amount, operator, comment=None): if amount > 0: subject = ugettext_noop('Deposit') elif amount < 0: subject = ugettext_noop('Payout') else: raise ValueError('Amount must not be zero.') desc = ugettext_noop('Authorized by %(first)s %(last)s') % \ {'first': operator.first_name, 'last': operator.last_name} if comment is not None and len(comment) > 0: desc += ' (%s)' % (comment) self.add_transaction(amount, subject, desc) @transaction.atomic def buy_products(self, products): if min(products.values()) <= 0: raise ValueError('Non-positive amount in products dict.') total_value = sum(map(lambda p: p.price * products[p], products.keys())) if self.account.debit_limit is not None: debit_limit = self.account.debit_limit else: debit_limit = settings.CASHONLY_DEFAULT_DEBIT_LIMIT if self.account.credit - total_value >= debit_limit * -1: desc = '' for product in products.keys(): if not product.active: raise ValueError('Trying to buy a disabled product.') amount = products[product] desc += '%d x %s\n' % (amount, product.name) SalesLogEntry.objects.create( account=self.account, product=product, count=amount, unit_price=product.price ) self.add_transaction(-total_value, ugettext_noop('Purchase'), desc) return True else: return False def buy_product(self, product, amount): return self.buy_products({product: amount}) def set_pin(self, pin): self.account.pin = pin self.account.save() def clear_pin(self): self.set_pin('') def check_pin(self, pin): return self.account.pin == pin @receiver(post_save, sender=User) def user_post_save_handler(sender, instance, created, **kwargs): # TODO: add possibility to disable this via settings variable if created: Account.objects.create(user=instance) @receiver(pre_delete, sender=SalesLogEntry) def logentry_pre_delete_handler(sender, instance, **kwargs): accmgr = AccountManager(instance.account) accmgr.add_transaction( instance.unit_price * instance.count, ugettext_noop('Cancellation'), '%d x %s' % (instance.count, instance.product.name) ) @receiver(pre_save, sender=Product) def product_post_save_handler(sender, instance, **kwargs): # FIXME img = instance.image if img: scaledFile = io.StringIO() img.open(mode='r') with img: scaled = PIL.Image.open(img) thumbnail_size = getattr(settings, 'THUMBNAIL_SIZE', (150, 150)) scaled.thumbnail(thumbnail_size, PIL.Image.ANTIALIAS) scaled.save(scaledFile, 'PNG') scaledFile.seek(0) instance.image_thumbnail.save(img.url, File(scaledFile), save=False)