nk20/apps/note/views.py

239 lines
10 KiB
Python

# Copyright (C) 2018-2021 by BDE ENS Paris-Saclay
# SPDX-License-Identifier: GPL-3.0-or-later
import json
from django.conf import settings
from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import PermissionDenied
from django.db.models import Q, F
from django.utils.translation import gettext_lazy as _
from django.views.generic import CreateView, UpdateView, DetailView
from django_tables2 import SingleTableView
from django.urls import reverse_lazy
from activity.models import Entry
from note_kfet.inputs import AmountInput
from permission.backends import PermissionBackend
from permission.views import ProtectQuerysetMixin
from .forms import TransactionTemplateForm, SearchTransactionForm
from .models import TemplateCategory, Transaction, TransactionTemplate, RecurrentTransaction, NoteSpecial, Note
from .models.transactions import SpecialTransaction
from .tables import HistoryTable, ButtonTable
class TransactionCreateView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView):
"""
View for the creation of Transaction between two note which are not :models:`transactions.RecurrentTransaction`.
e.g. for donation/transfer between people and clubs or for credit/debit with :models:`note.NoteSpecial`
"""
template_name = "note/transaction_form.html"
# SingleTableView creates `context["table"]` we will load it with transaction history
model = Transaction
# Transaction history table
table_class = HistoryTable
extra_context = {"title": _("Transfer money")}
def get_queryset(self, **kwargs):
# retrieves only Transaction that user has the right to see.
return Transaction.objects.filter(
PermissionBackend.filter_queryset(self.request.user, Transaction, "view")
).order_by("-created_at").all()[:20]
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['amount_widget'] = AmountInput(attrs={"id": "amount"})
context['polymorphic_ctype'] = ContentType.objects.get_for_model(Transaction).pk
context['special_polymorphic_ctype'] = ContentType.objects.get_for_model(SpecialTransaction).pk
context['special_types'] = NoteSpecial.objects\
.filter(PermissionBackend.filter_queryset(self.request.user, NoteSpecial, "view"))\
.order_by("special_type").all()
# Add a shortcut for entry page for open activities
if "activity" in settings.INSTALLED_APPS:
from activity.models import Activity
activities_open = Activity.objects.filter(open=True).filter(
PermissionBackend.filter_queryset(self.request.user, Activity, "view")).distinct().all()
context["activities_open"] = [a for a in activities_open
if PermissionBackend.check_perm(self.request.user,
"activity.add_entry",
Entry(activity=a,
note=self.request.user.note, ))]
return context
class TransactionTemplateCreateView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView):
"""
Create Transaction template
"""
model = TransactionTemplate
form_class = TransactionTemplateForm
success_url = reverse_lazy('note:template_list')
extra_context = {"title": _("Create new button")}
class TransactionTemplateListView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView):
"""
List Transaction templates
"""
model = TransactionTemplate
table_class = ButtonTable
extra_context = {"title": _("Search button")}
def get_queryset(self, **kwargs):
"""
Filter the user list with the given pattern.
"""
qs = super().get_queryset().distinct()
if "search" in self.request.GET:
pattern = self.request.GET["search"]
qs = qs.filter(
Q(name__iregex="^" + pattern)
| Q(destination__club__name__iregex="^" + pattern)
| Q(category__name__iregex="^" + pattern)
| Q(description__iregex=pattern)
)
qs = qs.order_by('-display', 'category__name', 'destination__club__name', 'name')
return qs
class TransactionTemplateUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView):
"""
Update Transaction template
"""
model = TransactionTemplate
form_class = TransactionTemplateForm
success_url = reverse_lazy('note:template_list')
extra_context = {"title": _("Update button")}
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
if "logs" in settings.INSTALLED_APPS:
from logs.models import Changelog
update_logs = Changelog.objects.filter(
model=ContentType.objects.get_for_model(TransactionTemplate),
instance_pk=self.object.pk,
action="edit",
)
price_history = []
for log in update_logs.all():
old_dict = json.loads(log.previous)
new_dict = json.loads(log.data)
if "amount" not in old_dict:
# The amount price of the button was not modified in this changelog
continue
old_price = old_dict["amount"]
new_price = new_dict["amount"]
if old_price != new_price:
price_history.append(dict(price=old_price, time=log.timestamp))
price_history.append(dict(price=self.object.amount, time=None))
price_history.reverse()
context["price_history"] = price_history
return context
class ConsoView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView):
"""
The Magic View that make people pay their beer and burgers.
(Most of the magic happens in the dark world of Javascript see `static/note/js/consos.js`)
"""
model = Transaction
template_name = "note/conso_form.html"
extra_context = {"title": _("Consumptions")}
# Transaction history table
table_class = HistoryTable
def dispatch(self, request, *args, **kwargs):
# Check that the user is authenticated
if not request.user.is_authenticated:
return self.handle_no_permission()
templates = TransactionTemplate.objects.filter(
PermissionBackend().filter_queryset(self.request.user, TransactionTemplate, "view")
)
if not templates.exists():
raise PermissionDenied(_("You can't see any button."))
return super().dispatch(request, *args, **kwargs)
def get_queryset(self, **kwargs):
"""
restrict to the transaction history the user can see.
"""
return Transaction.objects.filter(
PermissionBackend.filter_queryset(self.request.user, Transaction, "view")
).order_by("-created_at").all()[:20]
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
categories = TemplateCategory.objects.order_by('name').all()
# for each category, find which transaction templates the user can see.
for category in categories:
category.templates_filtered = category.templates.filter(
PermissionBackend().filter_queryset(self.request.user, TransactionTemplate, "view")
).filter(display=True).order_by('name').all()
context['categories'] = [cat for cat in categories if cat.templates_filtered]
# some transactiontemplate are put forward to find them easily
context['highlighted'] = TransactionTemplate.objects.filter(highlighted=True).filter(
PermissionBackend().filter_queryset(self.request.user, TransactionTemplate, "view")
).order_by('name').all()
context['polymorphic_ctype'] = ContentType.objects.get_for_model(RecurrentTransaction).pk
return context
class TransactionSearchView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView):
model = Note
context_object_name = "note"
template_name = "note/search_transactions.html"
extra_context = {"title": _("Search transactions")}
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
form = SearchTransactionForm(data=self.request.GET if self.request.GET else None)
context["form"] = form
form.full_clean()
data = form.cleaned_data if form.is_valid() else {}
transactions = Transaction.objects.annotate(total_amount=F("quantity") * F("amount")).filter(
PermissionBackend.filter_queryset(self.request.user, Transaction, "view"))\
.filter(Q(source=self.object) | Q(destination=self.object)).order_by('-created_at')
if "source" in data and data["source"]:
transactions = transactions.filter(source_id=data["source"].note_id)
if "destination" in data and data["destination"]:
transactions = transactions.filter(destination_id=data["destination"].note_id)
if "type" in data and data["type"]:
transactions = transactions.filter(polymorphic_ctype__in=data["type"])
if "reason" in data and data["reason"]:
transactions = transactions.filter(reason__iregex=data["reason"])
if "valid" in data and data["valid"]:
transactions = transactions.filter(valid=data["valid"])
if "amount_gte" in data and data["amount_gte"]:
transactions = transactions.filter(total_amount__gte=data["amount_gte"])
if "amount_lte" in data and data["amount_lte"]:
transactions = transactions.filter(total_amount__lte=data["amount_lte"])
if "created_after" in data and data["created_after"]:
transactions = transactions.filter(created_at__gte=data["created_after"])
if "created_before" in data and data["created_before"]:
transactions = transactions.filter(created_at__lte=data["created_before"])
table = HistoryTable(transactions)
table.paginate(per_page=100, page=self.request.GET.get("page", 1))
context["table"] = table
return context