|
|
|
@ -3,17 +3,17 @@ from django import forms
@@ -3,17 +3,17 @@ from django import forms
|
|
|
|
|
from django.shortcuts import render, get_object_or_404, redirect |
|
|
|
|
from django.contrib.auth.decorators import login_required |
|
|
|
|
from django.core import paginator |
|
|
|
|
from django.contrib.auth.models import User |
|
|
|
|
from cashonly.core.models import * |
|
|
|
|
from cashonly.core.services import AccountManager |
|
|
|
|
from django.utils.translation import ugettext as _ |
|
|
|
|
from django.utils.translation import ugettext_lazy |
|
|
|
|
from django.utils.translation import gettext as _ |
|
|
|
|
from django.utils.translation import gettext_lazy |
|
|
|
|
from django.db import IntegrityError |
|
|
|
|
#import cashonly.core.version |
|
|
|
|
|
|
|
|
|
# import cashonly.core.version |
|
|
|
|
import datetime |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#def version_number_context_processor(request): |
|
|
|
|
# def version_number_context_processor(request): |
|
|
|
|
# return {'version_number': cashonly.version.CASHONLY_VERSION} |
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -22,17 +22,26 @@ def overview(request):
@@ -22,17 +22,26 @@ def overview(request):
|
|
|
|
|
try: |
|
|
|
|
a = request.user.account |
|
|
|
|
except User.account.RelatedObjectDoesNotExist: |
|
|
|
|
return render(request, 'cashonly/web/index.html', |
|
|
|
|
{'latest_transactions': [], 'latest_purchases': []}) |
|
|
|
|
return render( |
|
|
|
|
request, |
|
|
|
|
"cashonly/web/index.html", |
|
|
|
|
{"latest_transactions": [], "latest_purchases": []}, |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
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] |
|
|
|
|
purchases = ( |
|
|
|
|
Product.objects.filter(saleslogentry__account=a) |
|
|
|
|
.order_by("-saleslogentry__timestamp") |
|
|
|
|
.distinct()[:20] |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
products = [] |
|
|
|
|
# Find 3 products |
|
|
|
@ -43,9 +52,11 @@ def overview(request):
@@ -43,9 +52,11 @@ def overview(request):
|
|
|
|
|
if len(products) == 3: |
|
|
|
|
break |
|
|
|
|
|
|
|
|
|
return render(request, 'cashonly/web/index.html', |
|
|
|
|
{'latest_transactions': transactions, |
|
|
|
|
'latest_purchases': products}) |
|
|
|
|
return render( |
|
|
|
|
request, |
|
|
|
|
"cashonly/web/index.html", |
|
|
|
|
{"latest_transactions": transactions, "latest_purchases": products}, |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ProductView(generic.DetailView): |
|
|
|
@ -55,7 +66,7 @@ class ProductView(generic.DetailView):
@@ -55,7 +66,7 @@ class ProductView(generic.DetailView):
|
|
|
|
|
@login_required |
|
|
|
|
def transactions(request, detailed, page): |
|
|
|
|
a = request.user.account |
|
|
|
|
transactions = Transaction.objects.filter(account=a).order_by('-timestamp') |
|
|
|
|
transactions = Transaction.objects.filter(account=a).order_by("-timestamp") |
|
|
|
|
|
|
|
|
|
if page is None: |
|
|
|
|
page = 1 |
|
|
|
@ -66,9 +77,11 @@ def transactions(request, detailed, page):
@@ -66,9 +77,11 @@ def transactions(request, detailed, page):
|
|
|
|
|
except paginator.EmptyPage: |
|
|
|
|
transaction_list = paginator.page(paginator.num_pages) |
|
|
|
|
|
|
|
|
|
return render(request, 'cashonly/web/transaction_list.html', |
|
|
|
|
{'transaction_list': transaction_list, |
|
|
|
|
'detailed': detailed}) |
|
|
|
|
return render( |
|
|
|
|
request, |
|
|
|
|
"cashonly/web/transaction_list.html", |
|
|
|
|
{"transaction_list": transaction_list, "detailed": detailed}, |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def products(request, category_id=None): |
|
|
|
@ -77,14 +90,15 @@ def products(request, category_id=None):
@@ -77,14 +90,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/web/product_list.html', |
|
|
|
|
{'product_list': products, 'category': category, |
|
|
|
|
'categories': categories}) |
|
|
|
|
return render( |
|
|
|
|
request, |
|
|
|
|
"cashonly/web/product_list.html", |
|
|
|
|
{"product_list": products, "category": category, "categories": categories}, |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@login_required |
|
|
|
@ -94,121 +108,134 @@ def buy(request, product_id, confirm=False):
@@ -94,121 +108,134 @@ def buy(request, product_id, confirm=False):
|
|
|
|
|
if confirm: |
|
|
|
|
accmgr = AccountManager(request.user.account) |
|
|
|
|
if accmgr.buy_product(product, 1): |
|
|
|
|
return redirect('buy_thanks') |
|
|
|
|
return redirect("buy_thanks") |
|
|
|
|
else: |
|
|
|
|
return redirect('buy_error') |
|
|
|
|
return redirect("buy_error") |
|
|
|
|
else: |
|
|
|
|
return render(request, 'cashonly/web/buy_confirm.html', |
|
|
|
|
{'product': product}) |
|
|
|
|
return render(request, "cashonly/web/buy_confirm.html", {"product": product}) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@login_required |
|
|
|
|
def buy_thanks(request): |
|
|
|
|
return render(request, 'cashonly/web/buy_thanks.html') |
|
|
|
|
return render(request, "cashonly/web/buy_thanks.html") |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@login_required |
|
|
|
|
def buy_error(request): |
|
|
|
|
return render(request, 'cashonly/web/buy_error.html') |
|
|
|
|
return render(request, "cashonly/web/buy_error.html") |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class UserSettingsForm(forms.Form): |
|
|
|
|
daily_digest = forms.BooleanField(required=False, |
|
|
|
|
label=ugettext_lazy('daily digest')) |
|
|
|
|
daily_digest = forms.BooleanField( |
|
|
|
|
required=False, label=gettext_lazy("daily digest") |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class UserPinForm(forms.Form): |
|
|
|
|
pin = forms.CharField(max_length=32, widget=forms.PasswordInput, |
|
|
|
|
label=ugettext_lazy('PIN'), required=False) |
|
|
|
|
pin_confirm = forms.CharField(max_length=32, widget=forms.PasswordInput, |
|
|
|
|
label=ugettext_lazy('PIN (confirmation)'), |
|
|
|
|
required=False) |
|
|
|
|
pin = forms.CharField( |
|
|
|
|
max_length=32, |
|
|
|
|
widget=forms.PasswordInput, |
|
|
|
|
label=gettext_lazy("PIN"), |
|
|
|
|
required=False, |
|
|
|
|
) |
|
|
|
|
pin_confirm = forms.CharField( |
|
|
|
|
max_length=32, |
|
|
|
|
widget=forms.PasswordInput, |
|
|
|
|
label=gettext_lazy("PIN (confirmation)"), |
|
|
|
|
required=False, |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
def clean(self): |
|
|
|
|
cleaned_data = super(UserPinForm, self).clean() |
|
|
|
|
|
|
|
|
|
if 'pin' not in cleaned_data and 'pin_confirm' not in cleaned_data: |
|
|
|
|
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.')) |
|
|
|
|
if cleaned_data["pin"] != cleaned_data["pin_confirm"]: |
|
|
|
|
raise forms.ValidationError(_("PINs do not match.")) |
|
|
|
|
|
|
|
|
|
return cleaned_data |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class UserAvatarForm(forms.Form): |
|
|
|
|
avatar = forms.ImageField(label=ugettext_lazy('avatar'), required=False) |
|
|
|
|
avatar = forms.ImageField(label=gettext_lazy("avatar"), required=False) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class UserCardNumberForm(forms.Form): |
|
|
|
|
card_number = forms.CharField(max_length=32, |
|
|
|
|
label=ugettext_lazy('card number'), |
|
|
|
|
required=False) |
|
|
|
|
card_number = forms.CharField( |
|
|
|
|
max_length=32, label=gettext_lazy("card number"), required=False |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@login_required |
|
|
|
|
def usersettings(request, submit=None): |
|
|
|
|
daily_digest = request.user.account.daily_digest |
|
|
|
|
settings_form = UserSettingsForm({'daily_digest': daily_digest}) |
|
|
|
|
settings_form = UserSettingsForm({"daily_digest": daily_digest}) |
|
|
|
|
pin_form = UserPinForm() |
|
|
|
|
avatar_form = UserAvatarForm() |
|
|
|
|
card_number_form = UserCardNumberForm({ |
|
|
|
|
'card_number': request.user.account.card_number |
|
|
|
|
}) |
|
|
|
|
card_number_form = UserCardNumberForm( |
|
|
|
|
{"card_number": request.user.account.card_number} |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
if request.method == 'POST': |
|
|
|
|
if submit == 'pin': |
|
|
|
|
if request.method == "POST": |
|
|
|
|
if submit == "pin": |
|
|
|
|
pin_form = UserPinForm(request.POST) |
|
|
|
|
|
|
|
|
|
if pin_form.is_valid(): |
|
|
|
|
pin = pin_form.cleaned_data['pin'] |
|
|
|
|
pin = pin_form.cleaned_data["pin"] |
|
|
|
|
accmgr = AccountManager(request.user.account) |
|
|
|
|
if pin is not None: |
|
|
|
|
accmgr.set_pin(pin) |
|
|
|
|
else: |
|
|
|
|
accmgr.clear_pin() |
|
|
|
|
return render(request, 'cashonly/web/usersettings_saved.html') |
|
|
|
|
return render(request, "cashonly/web/usersettings_saved.html") |
|
|
|
|
|
|
|
|
|
elif submit == 'settings': |
|
|
|
|
elif submit == "settings": |
|
|
|
|
settings_form = UserSettingsForm(request.POST) |
|
|
|
|
|
|
|
|
|
if settings_form.is_valid(): |
|
|
|
|
daily_digest = settings_form.cleaned_data['daily_digest'] |
|
|
|
|
daily_digest = settings_form.cleaned_data["daily_digest"] |
|
|
|
|
request.user.account.daily_digest = daily_digest |
|
|
|
|
request.user.account.save() |
|
|
|
|
return render(request, 'cashonly/web/usersettings_saved.html') |
|
|
|
|
elif submit == 'avatar': |
|
|
|
|
return render(request, "cashonly/web/usersettings_saved.html") |
|
|
|
|
elif submit == "avatar": |
|
|
|
|
avatar_form = UserAvatarForm(request.POST, request.FILES) |
|
|
|
|
if avatar_form.is_valid(): |
|
|
|
|
if 'delete' in request.POST: |
|
|
|
|
if "delete" in request.POST: |
|
|
|
|
request.user.account.avatar = None |
|
|
|
|
else: |
|
|
|
|
request.user.account.avatar = \ |
|
|
|
|
avatar_form.cleaned_data['avatar'] |
|
|
|
|
request.user.account.avatar = avatar_form.cleaned_data["avatar"] |
|
|
|
|
request.user.account.save() |
|
|
|
|
return render(request, 'cashonly/web/usersettings_saved.html') |
|
|
|
|
return render(request, "cashonly/web/usersettings_saved.html") |
|
|
|
|
else: |
|
|
|
|
# TODO handle upload error (e.g. wrong mime type?) |
|
|
|
|
pass |
|
|
|
|
elif submit == 'card_number': |
|
|
|
|
elif submit == "card_number": |
|
|
|
|
card_number_form = UserCardNumberForm(request.POST) |
|
|
|
|
# TODO validate card number |
|
|
|
|
if card_number_form.is_valid(): |
|
|
|
|
request.user.account.card_number = \ |
|
|
|
|
card_number_form.cleaned_data['card_number'] |
|
|
|
|
request.user.account.card_number = card_number_form.cleaned_data[ |
|
|
|
|
"card_number" |
|
|
|
|
] |
|
|
|
|
try: |
|
|
|
|
request.user.account.save() |
|
|
|
|
except IntegrityError: |
|
|
|
|
return render( |
|
|
|
|
request, |
|
|
|
|
'cashonly/web/usersettings_error.html', |
|
|
|
|
{'msg': _('Card number is already in use.')} |
|
|
|
|
"cashonly/web/usersettings_error.html", |
|
|
|
|
{"msg": _("Card number is already in use.")}, |
|
|
|
|
) |
|
|
|
|
return render(request, 'cashonly/web/usersettings_saved.html') |
|
|
|
|
return render(request, "cashonly/web/usersettings_saved.html") |
|
|
|
|
else: |
|
|
|
|
pass |
|
|
|
|
# TODO handle error (invalid card number) |
|
|
|
|
|
|
|
|
|
return render(request, 'cashonly/web/usersettings.html', |
|
|
|
|
{'settings_form': settings_form, 'pin_form': pin_form, |
|
|
|
|
'avatar_form': avatar_form, |
|
|
|
|
'card_number_form': card_number_form}) |
|
|
|
|
return render( |
|
|
|
|
request, |
|
|
|
|
"cashonly/web/usersettings.html", |
|
|
|
|
{ |
|
|
|
|
"settings_form": settings_form, |
|
|
|
|
"pin_form": pin_form, |
|
|
|
|
"avatar_form": avatar_form, |
|
|
|
|
"card_number_form": card_number_form, |
|
|
|
|
}, |
|
|
|
|
) |
|
|
|
|