From e1598090f7187deace52cfb77170b6002374f6b6 Mon Sep 17 00:00:00 2001 From: Frederic Date: Thu, 29 Jun 2017 22:04:42 +0200 Subject: [PATCH] pep8 --- cashonly/admin.py | 232 ++++++------- cashonly/auth.py | 27 +- cashonly/formfields.py | 11 +- cashonly/management/commands/dailydigest.py | 83 ++--- cashonly/management/commands/debtreminder.py | 33 +- cashonly/models.py | 336 ++++++++++--------- cashonly/urls.py | 33 +- cashonly/version.py | 4 +- cashonly/views.py | 49 +-- 9 files changed, 419 insertions(+), 389 deletions(-) diff --git a/cashonly/admin.py b/cashonly/admin.py index 1520f09..58641e7 100644 --- a/cashonly/admin.py +++ b/cashonly/admin.py @@ -8,141 +8,147 @@ from django.utils.translation import ugettext as _ from django.utils.translation import ugettext_lazy from django.utils.translation import ugettext_noop + class AccountForm(forms.ModelForm): - credit_change = forms.DecimalField(max_digits=5, decimal_places=2, - required=False, - label=ugettext_lazy('amount')) + credit_change = forms.DecimalField(max_digits=5, decimal_places=2, + required=False, + label=ugettext_lazy('amount')) + + credit_change_comment = forms.CharField(max_length=64, required=False, + label=ugettext_lazy('comment')) - credit_change_comment = forms.CharField(max_length=64, required=False, - label=ugettext_lazy('comment')) + pin_change = DigitField(min_length=4, required=False, + label=ugettext_lazy('PIN')) - #pin_change = forms.RegexField(regex='^\d{4,}$', required=False, - # label=ugettext_lazy('PIN')) - pin_change = DigitField(min_length=4, required=False, - label=ugettext_lazy('PIN')) + pin_empty = forms.BooleanField(required=False, + label=ugettext_lazy('clear PIN')) - pin_empty = forms.BooleanField(required=False, - label=ugettext_lazy('clear PIN')) + class Meta: + model = Account - class Meta: - model = Account + # Include all fields (omitting this causes a RemovedInDjango18Warning) + exclude = [] - # Include all fields (omitting this causes a RemovedInDjango18Warning) - exclude = [] class AccountAdmin(admin.ModelAdmin): - list_display = ('user', 'card_number', 'credit', 'transaction_link') - form = AccountForm - readonly_fields = ('user', 'credit',) - fieldsets = ( - (None, { - 'fields': ('user', 'card_number', 'credit'), - }), - (ugettext_lazy('credit change'), { - 'fields': ('credit_change', 'credit_change_comment'), - }), - (ugettext_lazy('change PIN'), { - 'fields': ('pin_change', 'pin_empty'), - }), - ) - - # Disable manual creation of accounts. - # Accounts are create after user creation automatically. - def has_add_permission(self, request): - return False - - def transaction_link(self, account): - return '%s' % \ - (reverse("admin:cashonly_transaction_changelist"), account.id, - _('Transactions')) - - transaction_link.short_description = ugettext_lazy('Transaction link') - - transaction_link.allow_tags = True - - def save_model(self, request, obj, form, change): - pin = form.cleaned_data['pin_change'] - pin_empty = form.cleaned_data['pin_empty'] - - if pin_empty: - obj.clear_pin() - else: - if pin is not None and len(pin) != 0: - obj.set_pin(pin) - - PAYOUT_SUBJECT = ugettext_noop('Payout') - DEPOSIT_SUBJECT = ugettext_noop('Deposit') - DESCRIPTION = ugettext_noop('Authorized by %(first)s %(last)s') - - amount = form.cleaned_data['credit_change'] - comment = form.cleaned_data['credit_change_comment'] - - if amount is not None and amount != 0: - if amount > 0: - subject = DEPOSIT_SUBJECT - else: - subject = PAYOUT_SUBJECT - - desc = DESCRIPTION % {'first': request.user.first_name, - 'last': request.user.last_name} - if comment is not None and len(comment) > 0: - desc += ' (%s)' % (comment) - obj.change_credit(amount, subject, desc) - - # Make sure the object is saved in any case - obj.save() + list_display = ('user', 'card_number', 'credit', 'transaction_link') + form = AccountForm + readonly_fields = ('user', 'credit',) + fieldsets = ( + (None, { + 'fields': ('user', 'card_number', 'credit'), + }), + (ugettext_lazy('credit change'), { + 'fields': ('credit_change', 'credit_change_comment'), + }), + (ugettext_lazy('change PIN'), { + 'fields': ('pin_change', 'pin_empty'), + }), + ) + + # Disable manual creation of accounts. + # Accounts are create after user creation automatically. + def has_add_permission(self, request): + return False + + def transaction_link(self, account): + return '%s' % \ + (reverse("admin:cashonly_transaction_changelist"), account.id, + _('Transactions')) + + transaction_link.short_description = ugettext_lazy('Transaction link') + + transaction_link.allow_tags = True + + def save_model(self, request, obj, form, change): + pin = form.cleaned_data['pin_change'] + pin_empty = form.cleaned_data['pin_empty'] + + if pin_empty: + obj.clear_pin() + else: + if pin is not None and len(pin) != 0: + obj.set_pin(pin) + + PAYOUT_SUBJECT = ugettext_noop('Payout') + DEPOSIT_SUBJECT = ugettext_noop('Deposit') + DESCRIPTION = ugettext_noop('Authorized by %(first)s %(last)s') + + amount = form.cleaned_data['credit_change'] + comment = form.cleaned_data['credit_change_comment'] + + if amount is not None and amount != 0: + if amount > 0: + subject = DEPOSIT_SUBJECT + else: + subject = PAYOUT_SUBJECT + + desc = DESCRIPTION % {'first': request.user.first_name, + 'last': request.user.last_name} + if comment is not None and len(comment) > 0: + desc += ' (%s)' % (comment) + obj.change_credit(amount, subject, desc) + + # Make sure the object is saved in any case + obj.save() class ProductBarcodeInline(admin.TabularInline): - model = ProductBarcode - extra = 1 + model = ProductBarcode + extra = 1 + class ProductAdmin(admin.ModelAdmin): - list_display = ('name', 'category', 'price', 'active') - list_filter = ['category', 'active'] - ordering = ('-active', 'name') - inlines = [ProductBarcodeInline] - fields = ('name', 'price', 'active', 'category', 'image') + list_display = ('name', 'category', 'price', 'active') + list_filter = ['category', 'active'] + ordering = ('-active', 'name') + inlines = [ProductBarcodeInline] + fields = ('name', 'price', 'active', 'category', 'image') + class ProductCategoryAdmin(admin.ModelAdmin): - list_display = ('name', 'comment') + list_display = ('name', 'comment') + class SalesLogEntryAdmin(admin.ModelAdmin): - list_display = ('account', 'timestamp', 'product', 'count', 'unit_price') - list_filter = ['account', 'timestamp', 'product'] - # Make sales log entries completely read-only - readonly_fields = map(lambda f: f.name, SalesLogEntry._meta.fields) + list_display = ('account', 'timestamp', 'product', 'count', 'unit_price') + list_filter = ['account', 'timestamp', 'product'] + # Make sales log entries completely read-only + readonly_fields = map(lambda f: f.name, SalesLogEntry._meta.fields) + class TransactionAdmin(admin.ModelAdmin): - list_display = ('account', 'timestamp', 'subject', 'description', 'amount') - list_filter = ['account', 'timestamp', 'subject'] - - # Disable mass deletion in the overview page - actions = None - - date_hierarchy = 'timestamp' - - # Disable tampering with the transactions completely. - def has_add_permission(self, request): - return False - def has_change_permission(self, request, obj=None): - if obj is None: - return True - return False - def has_delete_permission(self, request, obj=None): - return False - - # Needed to not trigger an ImproperlyConfigured exception. - # FIXME: a bit too hacky - def changelist_view(self, request, extra_context=None): - self.list_display_links = (None, ) - return super(TransactionAdmin, self).changelist_view(request, - extra_context=None) + list_display = ('account', 'timestamp', 'subject', 'description', 'amount') + list_filter = ['account', 'timestamp', 'subject'] + + # Disable mass deletion in the overview page + actions = None + + date_hierarchy = 'timestamp' + + # Disable tampering with the transactions completely. + def has_add_permission(self, request): + return False + + def has_change_permission(self, request, obj=None): + if obj is None: + return True + return False + + def has_delete_permission(self, request, obj=None): + return False + + # Needed to not trigger an ImproperlyConfigured exception. + # FIXME: a bit too hacky + def changelist_view(self, request, extra_context=None): + self.list_display_links = (None, ) + return super(TransactionAdmin, self).changelist_view( + request, + extra_context=None, + ) admin.site.register(Account, AccountAdmin) admin.site.register(Product, ProductAdmin) -#admin.site.register(ProductBarcode) admin.site.register(ProductCategory, ProductCategoryAdmin) admin.site.register(Transaction, TransactionAdmin) admin.site.register(SalesLogEntry, SalesLogEntryAdmin) - diff --git a/cashonly/auth.py b/cashonly/auth.py index 268eecf..a0263ad 100644 --- a/cashonly/auth.py +++ b/cashonly/auth.py @@ -1,21 +1,22 @@ from django.contrib.auth.models import User from cashonly.models import Account + class CashBackend(object): - def authenticate(self, card_number=None, pin=None): - if not card_number.isdigit(): - raise ValueError('card_number has to consist of digits only!') + def authenticate(self, card_number=None, pin=None): + if not card_number.isdigit(): + raise ValueError('card_number has to consist of digits only!') - if len(pin) > 0 and not pin.isdigit(): - raise ValueError('pin has to consist of digits only or \ - has to be empty!') + if len(pin) > 0 and not pin.isdigit(): + raise ValueError('pin has to consist of digits only or \ + has to be empty!') - try: - account = Account.objects.get(card_number=card_number) - except Account.DoesNotExist: - return None + try: + account = Account.objects.get(card_number=card_number) + except Account.DoesNotExist: + return None - if account.check_pin(pin): - return account.user + if account.check_pin(pin): + return account.user - return None + return None diff --git a/cashonly/formfields.py b/cashonly/formfields.py index 98c4216..c9049a5 100644 --- a/cashonly/formfields.py +++ b/cashonly/formfields.py @@ -3,11 +3,12 @@ from django.core.exceptions import ValidationError from django.utils.translation import ugettext_lazy as _ from django.core.validators import EMPTY_VALUES + class DigitField(CharField): - def clean(self, value): - super(DigitField, self).clean(value) + def clean(self, value): + super(DigitField, self).clean(value) - if value not in EMPTY_VALUES and not value.isdigit(): - raise ValidationError(_('Please enter only digits.')) + if value not in EMPTY_VALUES and not value.isdigit(): + raise ValidationError(_('Please enter only digits.')) - return value + return value diff --git a/cashonly/management/commands/dailydigest.py b/cashonly/management/commands/dailydigest.py index 694a9d1..4dcea6f 100644 --- a/cashonly/management/commands/dailydigest.py +++ b/cashonly/management/commands/dailydigest.py @@ -13,56 +13,57 @@ import datetime RANGE = 24 USERSETTINGS_URL = 'https://cypher/kasse/usersettings/' + class Command(NoArgsCommand): - help = 'Sends out the daily digest to all users with transactions' + \ - 'in the last %dh' % RANGE + help = 'Sends out the daily digest to all users with transactions' + \ + 'in the last %dh' % RANGE - def handle_noargs(self, **options): - translation.activate('de') + def handle_noargs(self, **options): + translation.activate('de') - tpl = get_template('cashonly/daily_digest.txt') + tpl = get_template('cashonly/daily_digest.txt') - messages = [] - for a in Account.objects.all(): - name = '%s %s' % (a.user.first_name, a.user.last_name) - context = {'name': name, - 'credit': a.credit, - 'range': RANGE, - 'url': USERSETTINGS_URL} + messages = [] + for a in Account.objects.all(): + name = '%s %s' % (a.user.first_name, a.user.last_name) + context = {'name': name, + 'credit': a.credit, + 'range': RANGE, + 'url': USERSETTINGS_URL} - transactions = Transaction.objects.filter(account = a) \ - .filter(timestamp__gte=(datetime.datetime.now() - - datetime.timedelta(hours = RANGE))) + transactions = Transaction.objects.filter(account=a) \ + .filter(timestamp__gte=(datetime.datetime.now() - + datetime.timedelta(hours=RANGE))) - if transactions.count() > 0: - lengths = {'timestamp': len(_('date')), - 'description': len(_('subject')), - 'amount': len(_('amount'))} + if transactions.count() > 0: + lengths = {'timestamp': len(_('date')), + 'description': len(_('subject')), + 'amount': len(_('amount'))} - sum = 0 - for t in transactions: - lengths['timestamp'] = \ - max(lengths['timestamp'], len(DateFormat(t.timestamp) \ - .format(get_format('SHORT_DATETIME_FORMAT')))) - lengths['description'] = \ - max(lengths['description'], len(t.description)) - lengths['amount'] = \ - max(lengths['amount'], len(str(t.amount))) - t.description = t.description.split('\n') + sum = 0 + for t in transactions: + lengths['timestamp'] = \ + max(lengths['timestamp'], len(DateFormat(t.timestamp) + .format(get_format('SHORT_DATETIME_FORMAT')))) + lengths['description'] = \ + max(lengths['description'], len(t.description)) + lengths['amount'] = \ + max(lengths['amount'], len(str(t.amount))) + t.description = t.description.split('\n') - sum += t.amount + sum += t.amount - lengths['sum'] = lengths['timestamp'] + \ - lengths['description'] + lengths['amount'] - context['lengths'] = lengths - context['tl'] = transactions - context['sum'] = sum + lengths['sum'] = lengths['timestamp'] + \ + lengths['description'] + lengths['amount'] + context['lengths'] = lengths + context['tl'] = transactions + context['sum'] = sum - rcpts = ['%s <%s>' % (name, a.user.email)] + rcpts = ['%s <%s>' % (name, a.user.email)] - messages.append(('%s%s' % (settings.EMAIL_SUBJECT_PREFIX, - _('Account Statement')), - tpl.render(Context(context)), - settings.DEFAULT_FROM_EMAIL, rcpts)) + messages.append(('%s%s' % (settings.EMAIL_SUBJECT_PREFIX, + _('Account Statement')), + tpl.render(Context(context)), + settings.DEFAULT_FROM_EMAIL, rcpts)) - send_mass_mail(tuple(messages)) + send_mass_mail(tuple(messages)) diff --git a/cashonly/management/commands/debtreminder.py b/cashonly/management/commands/debtreminder.py index 09a10f9..950a25b 100644 --- a/cashonly/management/commands/debtreminder.py +++ b/cashonly/management/commands/debtreminder.py @@ -8,25 +8,26 @@ from django.template.loader import get_template from django.utils import translation from django.utils.translation import ugettext as _ + class Command(NoArgsCommand): - help = 'Sends a reminder mail to every with a negative credit' + help = 'Sends a reminder mail to every with a negative credit' + + def handle_noargs(self, **options): + translation.activate('de') - def handle_noargs(self, **options): - translation.activate('de') + tpl = get_template('cashonly/debt_reminder.txt') - tpl = get_template('cashonly/debt_reminder.txt') + messages = [] + for a in Account.objects.all(): + if a.credit < 0: + name = '%s %s' % (a.user.first_name, a.user.last_name) + context = {'name': name, 'credit': a.credit} - messages = [] - for a in Account.objects.all(): - if a.credit < 0: - name = '%s %s' % (a.user.first_name, a.user.last_name) - context = {'name': name, 'credit': a.credit} - - rcpts = ['%s <%s>' % (name, a.user.email)] + rcpts = ['%s <%s>' % (name, a.user.email)] - messages.append(('%s%s' % (settings.EMAIL_SUBJECT_PREFIX, - _('Debt Reminder')), - tpl.render(Context(context)), - settings.DEFAULT_FROM_EMAIL, rcpts)) + messages.append(('%s%s' % (settings.EMAIL_SUBJECT_PREFIX, + _('Debt Reminder')), + tpl.render(Context(context)), + settings.DEFAULT_FROM_EMAIL, rcpts)) - send_mass_mail(tuple(messages)) + send_mass_mail(tuple(messages)) diff --git a/cashonly/models.py b/cashonly/models.py index 30ea3e9..9075539 100644 --- a/cashonly/models.py +++ b/cashonly/models.py @@ -11,189 +11,199 @@ from django.db import transaction import PIL.Image import StringIO + class Account(models.Model): - user = models.OneToOneField(User) - card_number = models.CharField(max_length=32, unique=True, blank=True, - null=True, verbose_name = _('card number')) - pin = models.CharField(max_length=32, blank=True, verbose_name = _('PIN')) - daily_digest = models.BooleanField(verbose_name = _('daily digest'), default=True) - credit = models.DecimalField(max_digits=5, decimal_places=2, default=0, - verbose_name = _('credit')) - - def __unicode__(self): - return self.user.username - - class Meta: - verbose_name = _('account') - verbose_name_plural = _('accounts') - - @receiver(post_save, sender=User) - def user_post_save_handler(sender, instance, created, **kwargs): - if created: - # We don't have ldap_user on creation, so just add the account - account = Account(user=instance) - account.save() - else: - # When we already have an account, - # we can add the number form LDAP (mongo shit) - if hasattr(instance, 'ldap_user') \ - and instance.ldap_user.attrs.has_key('employeenumber'): - instance.account.card_number = \ - instance.ldap_user.attrs['employeenumber'][0] - instance.account.save() - - @transaction.atomic - def change_credit(self, amount, subject, desc): - # For atomicity fetch current value first - cur = Account.objects.filter(pk=self.pk)[0] - self.credit = cur.credit + amount - self.save() - - trans = Transaction(account=self, subject=subject, - amount=amount, description=desc) - trans.save() - - def buy_products(self, products): - # TODO place it somewhere else - MAX_DEBIT = -35 - BUY_SUBJECT = ugettext_noop('Purchase') - - 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.credit - total_value >= MAX_DEBIT: - desc = '' - for product in products.keys(): - if not product.active: - raise ValueError('Trying to buy a disabled product.') - amount = products[product] - - logentry = SalesLogEntry(account=self, product=product, - count=amount, unit_price=product.price) - logentry.save() - - desc += '%d x %s\n' % (amount, product.name) - - self.change_credit(-total_value, BUY_SUBJECT, desc) - return True - else: - return False - - def buy_product(self, product, amount=1): - return self.buy_products({product: amount}) - - def set_pin(self, pin): - # TODO: hash pin - self.pin = pin - self.save() - - def clear_pin(self): - self.pin = '' - self.save() - - def check_pin(self, pin): - return pin == self.pin + user = models.OneToOneField(User) + card_number = models.CharField(max_length=32, unique=True, blank=True, + null=True, verbose_name=_('card number')) + pin = models.CharField(max_length=32, blank=True, verbose_name=_('PIN')) + daily_digest = models.BooleanField(verbose_name=_('daily digest'), + default=True) + credit = models.DecimalField(max_digits=5, decimal_places=2, default=0, + verbose_name=_('credit')) + + def __unicode__(self): + return self.user.username + + class Meta: + verbose_name = _('account') + verbose_name_plural = _('accounts') + + @receiver(post_save, sender=User) + def user_post_save_handler(sender, instance, created, **kwargs): + if created: + # We don't have ldap_user on creation, so just add the account + account = Account(user=instance) + account.save() + else: + # When we already have an account, + # we can add the number form LDAP (mongo shit) + if (hasattr(instance, 'ldap_user') and + 'employeenumber' in instance.ldap_user.attrs): + instance.account.card_number = \ + instance.ldap_user.attrs['employeenumber'][0] + instance.account.save() + + @transaction.atomic + def change_credit(self, amount, subject, desc): + # For atomicity fetch current value first + cur = Account.objects.filter(pk=self.pk)[0] + self.credit = cur.credit + amount + self.save() + + trans = Transaction(account=self, subject=subject, + amount=amount, description=desc) + trans.save() + + def buy_products(self, products): + # TODO place it somewhere else + MAX_DEBIT = -35 + BUY_SUBJECT = ugettext_noop('Purchase') + + 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.credit - total_value >= MAX_DEBIT: + desc = '' + for product in products.keys(): + if not product.active: + raise ValueError('Trying to buy a disabled product.') + amount = products[product] + + logentry = SalesLogEntry(account=self, product=product, + count=amount, + unit_price=product.price) + logentry.save() + + desc += '%d x %s\n' % (amount, product.name) + + self.change_credit(-total_value, BUY_SUBJECT, desc) + return True + else: + return False + + def buy_product(self, product, amount=1): + return self.buy_products({product: amount}) + + def set_pin(self, pin): + # TODO: hash pin + self.pin = pin + self.save() + + def clear_pin(self): + self.pin = '' + self.save() + + def check_pin(self, pin): + return pin == self.pin + class ProductCategory(models.Model): - name = models.CharField(max_length=32, unique=True, - verbose_name = _('name')) - comment = models.CharField(max_length=128, blank=True, - verbose_name = _('comment')) + name = models.CharField(max_length=32, unique=True, + verbose_name=_('name')) + comment = models.CharField(max_length=128, blank=True, + verbose_name=_('comment')) + + def __unicode__(self): + return "%s (%s)" % (self.name, self.comment) - def __unicode__(self): - return "%s (%s)" % (self.name, self.comment) + class Meta: + verbose_name = _('product category') + verbose_name_plural = _('product categories') - class Meta: - verbose_name = _('product category') - verbose_name_plural = _('product categories') class Product(models.Model): - name = models.CharField(max_length=32, unique=True, - verbose_name = _('name')) - price = models.DecimalField(max_digits=5, decimal_places=2, - verbose_name = _('price')) - active = models.BooleanField(default = True, verbose_name = _('active')) - category = models.ForeignKey(ProductCategory, blank=True, null=True, - verbose_name = _('category')) - image = models.ImageField(upload_to="products", verbose_name = _('image'), - blank=True, null=True) - image_thumbnail = models.ImageField(upload_to="products_thumb", - verbose_name = _('image'), - blank=True, null=True) - - def __unicode__(self): - return self.name - - class Meta: - verbose_name = _('product') - verbose_name_plural = _('products') + name = models.CharField(max_length=32, unique=True, + verbose_name=_('name')) + price = models.DecimalField(max_digits=5, decimal_places=2, + verbose_name=_('price')) + active = models.BooleanField(default=True, verbose_name=_('active')) + category = models.ForeignKey(ProductCategory, blank=True, null=True, + verbose_name=_('category')) + image = models.ImageField(upload_to="products", verbose_name=_('image'), + blank=True, null=True) + image_thumbnail = models.ImageField(upload_to="products_thumb", + verbose_name=_('image'), + blank=True, null=True) + + def __unicode__(self): + return self.name + + class Meta: + verbose_name = _('product') + verbose_name_plural = _('products') + @receiver(pre_save, sender=Product) def product_post_save_handler(sender, instance, **kwargs): - img = instance.image - if img: - scaledFile = StringIO.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) + img = instance.image + if img: + scaledFile = StringIO.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) + instance.image_thumbnail.save(img.url, File(scaledFile), save=False) class ProductBarcode(models.Model): - barcode = models.CharField(max_length=32, unique=True, - verbose_name = _('barcode')) - comment = models.CharField(max_length=128, blank=True, - verbose_name = _('comment')) - product = models.ForeignKey(Product, verbose_name = _('product')) + barcode = models.CharField(max_length=32, unique=True, + verbose_name=_('barcode')) + comment = models.CharField(max_length=128, blank=True, + verbose_name=_('comment')) + product = models.ForeignKey(Product, verbose_name=_('product')) + + def __unicode__(self): + return self.barcode - def __unicode__(self): - return self.barcode + class Meta: + verbose_name = _('barcode') + verbose_name_plural = _('barcodes') - class Meta: - verbose_name = _('barcode') - verbose_name_plural = _('barcodes') class Transaction(models.Model): - account = models.ForeignKey(Account, verbose_name = _('account')) - timestamp = models.DateTimeField(auto_now_add=True, - verbose_name = _('timestamp')) - subject = models.CharField(max_length=32, verbose_name = _('subject')) - description = models.TextField(verbose_name = _('description')) - amount = models.DecimalField(max_digits=5, decimal_places=2, - verbose_name = _('amount')) - - class Meta: - verbose_name = _('transaction') - verbose_name_plural = _('transactions') + account = models.ForeignKey(Account, verbose_name=_('account')) + timestamp = models.DateTimeField(auto_now_add=True, + verbose_name=_('timestamp')) + subject = models.CharField(max_length=32, verbose_name=_('subject')) + description = models.TextField(verbose_name=_('description')) + amount = models.DecimalField(max_digits=5, decimal_places=2, + verbose_name=_('amount')) + + class Meta: + verbose_name = _('transaction') + verbose_name_plural = _('transactions') + class SalesLogEntry(models.Model): - account = models.ForeignKey(Account, verbose_name = _('account')) - product = models.ForeignKey(Product, verbose_name = _('product')) - count = models.IntegerField(verbose_name = _('count')) - unit_price = models.DecimalField(max_digits=5, decimal_places=2, - verbose_name = _('unit price')) - timestamp = models.DateTimeField(auto_now_add=True, - verbose_name = _('timestamp')) - - def __unicode__(self): - return '%dx %s - %s' % (self.count, self.product, self.account) - class Meta: - verbose_name = _('sales log entry') - verbose_name_plural = _('sales log entries') + account = models.ForeignKey(Account, verbose_name=_('account')) + product = models.ForeignKey(Product, verbose_name=_('product')) + count = models.IntegerField(verbose_name=_('count')) + unit_price = models.DecimalField(max_digits=5, decimal_places=2, + verbose_name=_('unit price')) + timestamp = models.DateTimeField(auto_now_add=True, + verbose_name=_('timestamp')) + + def __unicode__(self): + return '%dx %s - %s' % (self.count, self.product, self.account) + + class Meta: + verbose_name = _('sales log entry') + verbose_name_plural = _('sales log entries') + @receiver(pre_delete, sender=SalesLogEntry) def logentry_pre_delete_handler(sender, instance, **kwargs): - SUBJECT = ugettext_noop('Cancellation') - DESC = '%d x %s' - - instance.account.change_credit( - instance.unit_price * instance.count, - SUBJECT, DESC % (instance.count, instance.product.name) - ) - + SUBJECT = ugettext_noop('Cancellation') + DESC = '%d x %s' + + instance.account.change_credit( + instance.unit_price * instance.count, + SUBJECT, DESC % (instance.count, instance.product.name) + ) diff --git a/cashonly/urls.py b/cashonly/urls.py index 5e6f427..e8b7094 100644 --- a/cashonly/urls.py +++ b/cashonly/urls.py @@ -2,29 +2,32 @@ from django.conf.urls import url from cashonly import views urlpatterns = [ - url(r'^$', views.overview, name='overview'), + url(r'^$', views.overview, name='overview'), - url(r'^product/(?P\d+)/$', views.ProductView.as_view(), name='product'), + url(r'^product/(?P\d+)/$', views.ProductView.as_view(), + name='product'), - url(r'transactions/$', views.transactions, {'detailed': False, 'page':1}, name='transactions'), + url(r'transactions/$', views.transactions, {'detailed': False, 'page': 1}, + name='transactions'), - url(r'transactions/(?P\d+)/$', views.transactions, {'detailed': False}, name='transactions'), + url(r'transactions/(?P\d+)/$', views.transactions, + {'detailed': False}, name='transactions'), - url(r'transactions/(?P\d+)/detailed/$', views.transactions, {'detailed': True}, - name='transactions_detailed'), + url(r'transactions/(?P\d+)/detailed/$', views.transactions, + {'detailed': True}, name='transactions_detailed'), - url(r'products/((?P\d+)/)?$', views.products, name='products'), + url(r'products/((?P\d+)/)?$', views.products, + name='products'), - url(r'buy/(?P\d+)/$', views.buy, name='buy'), + url(r'buy/(?P\d+)/$', views.buy, name='buy'), - url(r'buy/(?P\d+)/really/$', views.buy, - {'confirm': True}, name='buy_really'), + url(r'buy/(?P\d+)/really/$', views.buy, + {'confirm': True}, name='buy_really'), - url(r'buy/thanks/$', views.buy_thanks, name='buy_thanks'), + url(r'buy/thanks/$', views.buy_thanks, name='buy_thanks'), - url(r'buy/error/$', views.buy_error, name='buy_error'), + url(r'buy/error/$', views.buy_error, name='buy_error'), - url(r'usersettings(/(?P\w+))?/$', views.usersettings, - name='usersettings'), + url(r'usersettings(/(?P\w+))?/$', views.usersettings, + name='usersettings'), ] - diff --git a/cashonly/version.py b/cashonly/version.py index 412bb46..97b7d39 100644 --- a/cashonly/version.py +++ b/cashonly/version.py @@ -1,3 +1 @@ - -CASHONLY_VERSION='2.1.2' - +CASHONLY_VERSION = '2.1.2' diff --git a/cashonly/views.py b/cashonly/views.py index 4ac3b86..62710d0 100644 --- a/cashonly/views.py +++ b/cashonly/views.py @@ -9,22 +9,27 @@ from django.utils.translation import ugettext_lazy import cashonly.version import datetime + def version_number_context_processor(request): return {'version_number': cashonly.version.CASHONLY_VERSION} + @login_required def overview(request): a = request.user.account time = datetime.datetime.now() - datetime.timedelta(hours=12) - transactions = Transaction.objects.filter(account=a).filter(timestamp__gte=time).order_by('-timestamp') + transactions = Transaction.objects.filter(account=a) \ + .filter(timestamp__gte=time).order_by('-timestamp') - # FIXME: distinct doesn't work as expected, so fetch 20 rows and hope that there are 3 distinct products - purchases = Product.objects.filter(saleslogentry__account=a).order_by('-saleslogentry__timestamp').distinct()[:20] + # FIXME: distinct doesn't work as expected, so fetch 20 rows and hope that + # there are 3 distinct products + purchases = Product.objects.filter(saleslogentry__account=a) \ + .order_by('-saleslogentry__timestamp').distinct()[:20] products = [] # Find 3 products for p in purchases: - if not p in products: + if p not in products: products.append(p) if len(products) == 3: @@ -53,8 +58,10 @@ def transactions(request, detailed, page): except paginator.EmptyPage: transaction_list = paginator.page(paginator.num_pages) - return render(request, 'cashonly/transaction_list.html', { 'transaction_list': transaction_list, - 'detailed': detailed }) + return render(request, 'cashonly/transaction_list.html', + {'transaction_list': transaction_list, + 'detailed': detailed}) + def products(request, category_id=None): if category_id is None: @@ -62,13 +69,15 @@ def products(request, category_id=None): products = Product.objects.filter(active=True) else: category = get_object_or_404(ProductCategory, id=category_id) - products = Product.objects.filter(active=True).filter(category=category) + products = Product.objects.filter(active=True) \ + .filter(category=category) categories = ProductCategory.objects.all() - return render(request, 'cashonly/product_list.html', { 'product_list': products, - 'category': category, - 'categories': categories }) + return render(request, 'cashonly/product_list.html', + {'product_list': products, 'category': category, + 'categories': categories}) + @login_required def buy(request, product_id, confirm=False): @@ -80,20 +89,25 @@ def buy(request, product_id, confirm=False): else: return redirect('buy_error') else: - return render(request, 'cashonly/buy_confirm.html', {'product': product}) + return render(request, 'cashonly/buy_confirm.html', + {'product': product}) + @login_required def buy_thanks(request): return render(request, 'cashonly/buy_thanks.html') + @login_required def buy_error(request): return render(request, 'cashonly/buy_error.html') + class UserSettingsForm(forms.Form): daily_digest = forms.BooleanField(required=False, label=ugettext_lazy('daily digest')) + class UserPinForm(forms.Form): pin = forms.CharField(max_length=32, widget=forms.PasswordInput, label=ugettext_lazy('PIN'), required=False) @@ -104,14 +118,14 @@ class UserPinForm(forms.Form): def clean(self): cleaned_data = super(UserPinForm, self).clean() - if not (cleaned_data.has_key('pin') or - cleaned_data.has_key('pin_confirm')): + if 'pin' not in cleaned_data and 'pin_confirm' not in cleaned_data: return cleaned_data if cleaned_data['pin'] != cleaned_data['pin_confirm']: raise forms.ValidationError(_('PINs do not match.')) return cleaned_data + @login_required def usersettings(request, submit=None): daily_digest = request.user.account.daily_digest @@ -137,10 +151,5 @@ def usersettings(request, submit=None): request.user.account.save() return render(request, 'cashonly/usersettings_saved.html') - return render(request, 'cashonly/usersettings.html', { 'settings_form': settings_form, - 'pin_form': pin_form}) - - - - - + return render(request, 'cashonly/usersettings.html', + {'settings_form': settings_form, 'pin_form': pin_form})