2019-08-11 11:18:19 +00:00
|
|
|
import logging
|
|
|
|
import os
|
2018-06-03 13:34:04 +00:00
|
|
|
from itertools import chain
|
|
|
|
|
2019-08-11 11:18:19 +00:00
|
|
|
from django.conf import settings
|
|
|
|
from django.contrib import messages
|
|
|
|
from django.contrib.auth.mixins import LoginRequiredMixin
|
2018-08-29 22:52:32 +00:00
|
|
|
from django.contrib.auth.models import User
|
2019-08-11 11:18:19 +00:00
|
|
|
from django.core.files import File
|
|
|
|
from django.db.models import Sum
|
2018-08-29 22:52:32 +00:00
|
|
|
from django.shortcuts import redirect, get_object_or_404
|
2018-06-03 13:34:04 +00:00
|
|
|
from django.urls import reverse
|
2018-08-01 10:25:43 +00:00
|
|
|
from django.utils import timezone
|
2019-08-11 11:18:19 +00:00
|
|
|
from django.views.generic import TemplateView, ListView, DetailView
|
|
|
|
from django.views.generic.edit import CreateView, UpdateView, DeleteView
|
2018-06-03 13:34:04 +00:00
|
|
|
|
2018-08-15 18:53:54 +00:00
|
|
|
from camps.mixins import CampViewMixin
|
2019-08-11 11:18:19 +00:00
|
|
|
from economy.models import Chain, Credebtor, Expense, Reimbursement, Revenue
|
2018-04-22 06:28:46 +00:00
|
|
|
from profiles.models import Profile
|
2018-06-03 13:34:04 +00:00
|
|
|
from program.models import SpeakerProposal, EventProposal
|
2019-08-11 11:18:19 +00:00
|
|
|
from shop.models import OrderProductRelation, Order
|
2018-08-29 22:52:32 +00:00
|
|
|
from teams.models import Team
|
2019-08-11 11:18:19 +00:00
|
|
|
from tickets.models import ShopTicket, SponsorTicket, DiscountTicket, TicketType
|
2018-08-29 22:52:32 +00:00
|
|
|
from .mixins import *
|
2018-06-03 13:34:04 +00:00
|
|
|
|
2017-10-03 19:14:07 +00:00
|
|
|
logger = logging.getLogger("bornhack.%s" % __name__)
|
|
|
|
|
|
|
|
|
2018-08-29 22:52:32 +00:00
|
|
|
class BackofficeIndexView(CampViewMixin, RaisePermissionRequiredMixin, TemplateView):
|
|
|
|
"""
|
|
|
|
The Backoffice index view only requires camps.backoffice_permission so we use RaisePermissionRequiredMixin directly
|
|
|
|
"""
|
2019-06-16 12:32:24 +00:00
|
|
|
|
|
|
|
permission_required = "camps.backoffice_permission"
|
2018-06-03 16:33:51 +00:00
|
|
|
template_name = "index.html"
|
2017-10-03 19:14:07 +00:00
|
|
|
|
2018-06-03 13:34:04 +00:00
|
|
|
|
2018-08-29 22:52:32 +00:00
|
|
|
class ProductHandoutView(CampViewMixin, InfoTeamPermissionMixin, ListView):
|
2017-12-12 20:57:17 +00:00
|
|
|
template_name = "product_handout.html"
|
2018-08-01 09:33:36 +00:00
|
|
|
|
|
|
|
def get_queryset(self, **kwargs):
|
|
|
|
return OrderProductRelation.objects.filter(
|
2019-07-18 19:20:29 +00:00
|
|
|
ticket_generated=False,
|
2018-08-01 09:33:36 +00:00
|
|
|
order__paid=True,
|
|
|
|
order__refunded=False,
|
2019-06-16 12:32:24 +00:00
|
|
|
order__cancelled=False,
|
|
|
|
).order_by("order")
|
2017-10-03 19:14:07 +00:00
|
|
|
|
2017-12-12 20:57:17 +00:00
|
|
|
|
2018-08-29 22:52:32 +00:00
|
|
|
class BadgeHandoutView(CampViewMixin, InfoTeamPermissionMixin, ListView):
|
2017-12-12 20:57:17 +00:00
|
|
|
template_name = "badge_handout.html"
|
2019-06-16 12:32:24 +00:00
|
|
|
context_object_name = "tickets"
|
2017-12-12 20:57:17 +00:00
|
|
|
|
|
|
|
def get_queryset(self, **kwargs):
|
2019-07-18 19:20:29 +00:00
|
|
|
shoptickets = ShopTicket.objects.filter(badge_ticket_generated=False)
|
|
|
|
sponsortickets = SponsorTicket.objects.filter(badge_ticket_generated=False)
|
|
|
|
discounttickets = DiscountTicket.objects.filter(badge_ticket_generated=False)
|
2017-12-12 20:57:17 +00:00
|
|
|
return list(chain(shoptickets, sponsortickets, discounttickets))
|
|
|
|
|
|
|
|
|
2018-08-29 22:52:32 +00:00
|
|
|
class TicketCheckinView(CampViewMixin, InfoTeamPermissionMixin, ListView):
|
2017-12-12 20:57:17 +00:00
|
|
|
template_name = "ticket_checkin.html"
|
2019-06-16 12:32:24 +00:00
|
|
|
context_object_name = "tickets"
|
2017-12-12 20:57:17 +00:00
|
|
|
|
|
|
|
def get_queryset(self, **kwargs):
|
2019-07-18 19:04:49 +00:00
|
|
|
shoptickets = ShopTicket.objects.filter(used=False)
|
|
|
|
sponsortickets = SponsorTicket.objects.filter(used=False)
|
|
|
|
discounttickets = DiscountTicket.objects.filter(used=False)
|
2017-12-12 20:57:17 +00:00
|
|
|
return list(chain(shoptickets, sponsortickets, discounttickets))
|
|
|
|
|
2018-04-22 06:28:46 +00:00
|
|
|
|
2018-08-29 22:52:32 +00:00
|
|
|
class ApproveNamesView(CampViewMixin, OrgaTeamPermissionMixin, ListView):
|
2018-04-22 06:28:46 +00:00
|
|
|
template_name = "approve_public_credit_names.html"
|
2019-06-16 12:32:24 +00:00
|
|
|
context_object_name = "profiles"
|
2018-04-22 06:28:46 +00:00
|
|
|
|
|
|
|
def get_queryset(self, **kwargs):
|
2019-06-16 12:32:24 +00:00
|
|
|
return Profile.objects.filter(public_credit_name_approved=False).exclude(
|
|
|
|
public_credit_name=""
|
|
|
|
)
|
2018-04-22 06:28:46 +00:00
|
|
|
|
2018-06-03 13:34:04 +00:00
|
|
|
|
2018-08-29 22:52:32 +00:00
|
|
|
class ManageProposalsView(CampViewMixin, ContentTeamPermissionMixin, ListView):
|
2018-06-03 13:34:04 +00:00
|
|
|
"""
|
|
|
|
This view shows a list of pending SpeakerProposal and EventProposals.
|
|
|
|
"""
|
2019-06-16 12:32:24 +00:00
|
|
|
|
2018-06-03 13:34:04 +00:00
|
|
|
template_name = "manage_proposals.html"
|
2019-06-16 12:32:24 +00:00
|
|
|
context_object_name = "speakerproposals"
|
2018-06-03 13:34:04 +00:00
|
|
|
|
|
|
|
def get_queryset(self, **kwargs):
|
|
|
|
return SpeakerProposal.objects.filter(
|
2019-06-16 12:32:24 +00:00
|
|
|
camp=self.camp, proposal_status=SpeakerProposal.PROPOSAL_PENDING
|
2018-06-03 13:34:04 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
def get_context_data(self, **kwargs):
|
|
|
|
context = super().get_context_data(**kwargs)
|
2019-06-16 12:32:24 +00:00
|
|
|
context["eventproposals"] = EventProposal.objects.filter(
|
|
|
|
track__camp=self.camp, proposal_status=EventProposal.PROPOSAL_PENDING
|
2018-06-03 13:34:04 +00:00
|
|
|
)
|
|
|
|
return context
|
|
|
|
|
|
|
|
|
2018-11-20 16:12:32 +00:00
|
|
|
class ProposalManageBaseView(CampViewMixin, ContentTeamPermissionMixin, UpdateView):
|
2018-06-03 13:34:04 +00:00
|
|
|
"""
|
|
|
|
This class contains the shared logic between SpeakerProposalManageView and EventProposalManageView
|
|
|
|
"""
|
2019-06-16 12:32:24 +00:00
|
|
|
|
2018-06-03 13:34:04 +00:00
|
|
|
fields = []
|
|
|
|
|
|
|
|
def form_valid(self, form):
|
|
|
|
"""
|
|
|
|
We have two submit buttons in this form, Approve and Reject
|
|
|
|
"""
|
|
|
|
logger.debug(form.data)
|
2019-06-16 12:32:24 +00:00
|
|
|
if "approve" in form.data:
|
2018-06-03 13:34:04 +00:00
|
|
|
# approve button was pressed
|
|
|
|
form.instance.mark_as_approved(self.request)
|
2019-06-16 12:32:24 +00:00
|
|
|
elif "reject" in form.data:
|
2018-06-03 13:34:04 +00:00
|
|
|
# reject button was pressed
|
|
|
|
form.instance.mark_as_rejected(self.request)
|
|
|
|
else:
|
|
|
|
messages.error(self.request, "Unknown submit action")
|
2019-06-16 12:32:24 +00:00
|
|
|
return redirect(
|
|
|
|
reverse("backoffice:manage_proposals", kwargs={"camp_slug": self.camp.slug})
|
|
|
|
)
|
2018-06-03 13:34:04 +00:00
|
|
|
|
|
|
|
|
2018-11-20 16:12:32 +00:00
|
|
|
class SpeakerProposalManageView(ProposalManageBaseView):
|
2018-06-03 13:34:04 +00:00
|
|
|
"""
|
|
|
|
This view allows an admin to approve/reject SpeakerProposals
|
|
|
|
"""
|
2019-06-16 12:32:24 +00:00
|
|
|
|
2018-06-03 13:34:04 +00:00
|
|
|
model = SpeakerProposal
|
|
|
|
template_name = "manage_speakerproposal.html"
|
|
|
|
|
|
|
|
|
2018-11-20 16:12:32 +00:00
|
|
|
class EventProposalManageView(ProposalManageBaseView):
|
2018-06-03 13:34:04 +00:00
|
|
|
"""
|
|
|
|
This view allows an admin to approve/reject EventProposals
|
|
|
|
"""
|
2019-06-16 12:32:24 +00:00
|
|
|
|
2018-06-03 13:34:04 +00:00
|
|
|
model = EventProposal
|
|
|
|
template_name = "manage_eventproposal.html"
|
|
|
|
|
2018-08-01 09:50:16 +00:00
|
|
|
|
2018-08-29 22:52:32 +00:00
|
|
|
class MerchandiseOrdersView(CampViewMixin, OrgaTeamPermissionMixin, ListView):
|
2018-08-01 09:50:16 +00:00
|
|
|
template_name = "orders_merchandise.html"
|
|
|
|
|
|
|
|
def get_queryset(self, **kwargs):
|
2019-06-16 12:32:24 +00:00
|
|
|
camp_prefix = "BornHack {}".format(timezone.now().year)
|
|
|
|
|
|
|
|
return (
|
|
|
|
OrderProductRelation.objects.filter(
|
2019-07-18 19:20:29 +00:00
|
|
|
ticket_generated=False,
|
2019-06-16 12:32:24 +00:00
|
|
|
order__paid=True,
|
|
|
|
order__refunded=False,
|
|
|
|
order__cancelled=False,
|
|
|
|
product__category__name="Merchandise",
|
|
|
|
)
|
|
|
|
.filter(product__name__startswith=camp_prefix)
|
|
|
|
.order_by("order")
|
|
|
|
)
|
2018-08-01 09:50:16 +00:00
|
|
|
|
|
|
|
|
2018-08-29 22:52:32 +00:00
|
|
|
class MerchandiseToOrderView(CampViewMixin, OrgaTeamPermissionMixin, TemplateView):
|
2018-08-01 09:50:16 +00:00
|
|
|
template_name = "merchandise_to_order.html"
|
|
|
|
|
|
|
|
def get_context_data(self, **kwargs):
|
2019-06-16 12:32:24 +00:00
|
|
|
camp_prefix = "BornHack {}".format(timezone.now().year)
|
2018-08-01 09:50:16 +00:00
|
|
|
|
|
|
|
order_relations = OrderProductRelation.objects.filter(
|
2019-07-18 19:20:29 +00:00
|
|
|
ticket_generated=False,
|
2018-08-01 09:50:16 +00:00
|
|
|
order__paid=True,
|
|
|
|
order__refunded=False,
|
|
|
|
order__cancelled=False,
|
2019-06-16 12:32:24 +00:00
|
|
|
product__category__name="Merchandise",
|
|
|
|
).filter(product__name__startswith=camp_prefix)
|
2018-08-01 09:50:16 +00:00
|
|
|
|
|
|
|
merchandise_orders = {}
|
|
|
|
for relation in order_relations:
|
|
|
|
try:
|
|
|
|
quantity = merchandise_orders[relation.product.name] + relation.quantity
|
|
|
|
merchandise_orders[relation.product.name] = quantity
|
|
|
|
except KeyError:
|
|
|
|
merchandise_orders[relation.product.name] = relation.quantity
|
|
|
|
|
|
|
|
context = super().get_context_data(**kwargs)
|
2019-06-16 12:32:24 +00:00
|
|
|
context["merchandise"] = merchandise_orders
|
2018-08-01 09:50:16 +00:00
|
|
|
return context
|
2018-08-05 07:29:58 +00:00
|
|
|
|
|
|
|
|
2018-08-29 22:52:32 +00:00
|
|
|
class VillageOrdersView(CampViewMixin, OrgaTeamPermissionMixin, ListView):
|
2018-08-05 07:29:58 +00:00
|
|
|
template_name = "orders_village.html"
|
|
|
|
|
|
|
|
def get_queryset(self, **kwargs):
|
2019-06-16 12:32:24 +00:00
|
|
|
camp_prefix = "BornHack {}".format(timezone.now().year)
|
|
|
|
|
|
|
|
return (
|
|
|
|
OrderProductRelation.objects.filter(
|
2019-07-18 19:20:29 +00:00
|
|
|
ticket_generated=False,
|
2019-06-16 12:32:24 +00:00
|
|
|
order__paid=True,
|
|
|
|
order__refunded=False,
|
|
|
|
order__cancelled=False,
|
|
|
|
product__category__name="Villages",
|
|
|
|
)
|
|
|
|
.filter(product__name__startswith=camp_prefix)
|
|
|
|
.order_by("order")
|
|
|
|
)
|
2018-08-05 07:29:58 +00:00
|
|
|
|
|
|
|
|
2018-08-29 22:52:32 +00:00
|
|
|
class VillageToOrderView(CampViewMixin, OrgaTeamPermissionMixin, TemplateView):
|
2018-08-05 07:29:58 +00:00
|
|
|
template_name = "village_to_order.html"
|
|
|
|
|
|
|
|
def get_context_data(self, **kwargs):
|
2019-06-16 12:32:24 +00:00
|
|
|
camp_prefix = "BornHack {}".format(timezone.now().year)
|
2018-08-05 07:29:58 +00:00
|
|
|
|
|
|
|
order_relations = OrderProductRelation.objects.filter(
|
2019-07-18 19:20:29 +00:00
|
|
|
ticket_generated=False,
|
2018-08-05 07:29:58 +00:00
|
|
|
order__paid=True,
|
|
|
|
order__refunded=False,
|
|
|
|
order__cancelled=False,
|
2019-06-16 12:32:24 +00:00
|
|
|
product__category__name="Villages",
|
|
|
|
).filter(product__name__startswith=camp_prefix)
|
2018-08-05 07:29:58 +00:00
|
|
|
|
|
|
|
village_orders = {}
|
|
|
|
for relation in order_relations:
|
|
|
|
try:
|
|
|
|
quantity = village_orders[relation.product.name] + relation.quantity
|
|
|
|
village_orders[relation.product.name] = quantity
|
|
|
|
except KeyError:
|
|
|
|
village_orders[relation.product.name] = relation.quantity
|
|
|
|
|
|
|
|
context = super().get_context_data(**kwargs)
|
2019-06-16 12:32:24 +00:00
|
|
|
context["village"] = village_orders
|
2018-08-05 07:29:58 +00:00
|
|
|
return context
|
|
|
|
|
2018-08-29 22:52:32 +00:00
|
|
|
|
2019-03-30 05:54:45 +00:00
|
|
|
################################
|
|
|
|
###### CHAINS & CREDEBTORS #####
|
|
|
|
|
|
|
|
|
|
|
|
class ChainListView(CampViewMixin, EconomyTeamPermissionMixin, ListView):
|
|
|
|
model = Chain
|
2019-06-16 12:32:24 +00:00
|
|
|
template_name = "chain_list_backoffice.html"
|
2019-03-30 05:54:45 +00:00
|
|
|
|
|
|
|
|
|
|
|
class ChainDetailView(CampViewMixin, EconomyTeamPermissionMixin, DetailView):
|
|
|
|
model = Chain
|
2019-06-16 12:32:24 +00:00
|
|
|
template_name = "chain_detail_backoffice.html"
|
|
|
|
slug_url_kwarg = "chain_slug"
|
2019-03-30 05:54:45 +00:00
|
|
|
|
|
|
|
|
|
|
|
class CredebtorDetailView(CampViewMixin, EconomyTeamPermissionMixin, DetailView):
|
|
|
|
model = Credebtor
|
2019-06-16 12:32:24 +00:00
|
|
|
template_name = "credebtor_detail_backoffice.html"
|
|
|
|
slug_url_kwarg = "credebtor_slug"
|
2019-03-30 05:54:45 +00:00
|
|
|
|
|
|
|
|
2018-11-20 16:12:32 +00:00
|
|
|
################################
|
|
|
|
########### EXPENSES ###########
|
|
|
|
|
2019-06-16 12:32:24 +00:00
|
|
|
|
2018-11-20 16:12:32 +00:00
|
|
|
class ExpenseListView(CampViewMixin, EconomyTeamPermissionMixin, ListView):
|
2018-08-29 22:52:32 +00:00
|
|
|
model = Expense
|
2019-06-16 12:32:24 +00:00
|
|
|
template_name = "expense_list_backoffice.html"
|
2018-08-29 22:52:32 +00:00
|
|
|
|
2018-08-30 17:32:23 +00:00
|
|
|
def get_queryset(self, **kwargs):
|
|
|
|
"""
|
2018-11-20 16:12:32 +00:00
|
|
|
Exclude unapproved expenses, they are shown seperately
|
2018-08-30 17:32:23 +00:00
|
|
|
"""
|
|
|
|
queryset = super().get_queryset(**kwargs)
|
|
|
|
return queryset.exclude(approved__isnull=True)
|
|
|
|
|
|
|
|
def get_context_data(self, **kwargs):
|
|
|
|
"""
|
|
|
|
Include unapproved expenses seperately
|
|
|
|
"""
|
|
|
|
context = super().get_context_data(**kwargs)
|
2019-06-16 12:32:24 +00:00
|
|
|
context["unapproved_expenses"] = Expense.objects.filter(
|
|
|
|
camp=self.camp, approved__isnull=True
|
|
|
|
)
|
2018-08-30 17:32:23 +00:00
|
|
|
return context
|
2018-08-29 22:52:32 +00:00
|
|
|
|
2018-11-20 16:12:32 +00:00
|
|
|
|
|
|
|
class ExpenseDetailView(CampViewMixin, EconomyTeamPermissionMixin, UpdateView):
|
2018-08-29 22:52:32 +00:00
|
|
|
model = Expense
|
2019-06-16 12:32:24 +00:00
|
|
|
template_name = "expense_detail_backoffice.html"
|
|
|
|
fields = ["notes"]
|
2018-08-29 22:52:32 +00:00
|
|
|
|
|
|
|
def form_valid(self, form):
|
|
|
|
"""
|
|
|
|
We have two submit buttons in this form, Approve and Reject
|
|
|
|
"""
|
|
|
|
expense = form.save()
|
2019-06-16 12:32:24 +00:00
|
|
|
if "approve" in form.data:
|
2018-08-29 22:52:32 +00:00
|
|
|
# approve button was pressed
|
2018-08-29 23:35:37 +00:00
|
|
|
expense.approve(self.request)
|
2019-06-16 12:32:24 +00:00
|
|
|
elif "reject" in form.data:
|
2018-08-29 22:52:32 +00:00
|
|
|
# reject button was pressed
|
2018-08-29 23:35:37 +00:00
|
|
|
expense.reject(self.request)
|
2018-08-29 22:52:32 +00:00
|
|
|
else:
|
|
|
|
messages.error(self.request, "Unknown submit action")
|
2019-06-16 12:32:24 +00:00
|
|
|
return redirect(
|
|
|
|
reverse("backoffice:expense_list", kwargs={"camp_slug": self.camp.slug})
|
|
|
|
)
|
2018-11-20 16:12:32 +00:00
|
|
|
|
2018-08-29 22:52:32 +00:00
|
|
|
|
2018-11-20 16:12:32 +00:00
|
|
|
######################################
|
|
|
|
########### REIMBURSEMENTS ###########
|
2018-08-29 22:52:32 +00:00
|
|
|
|
2019-06-16 12:32:24 +00:00
|
|
|
|
2018-08-29 22:52:32 +00:00
|
|
|
class ReimbursementListView(CampViewMixin, EconomyTeamPermissionMixin, ListView):
|
|
|
|
model = Reimbursement
|
2019-06-16 12:32:24 +00:00
|
|
|
template_name = "reimbursement_list_backoffice.html"
|
2018-08-29 22:52:32 +00:00
|
|
|
|
|
|
|
|
|
|
|
class ReimbursementDetailView(CampViewMixin, EconomyTeamPermissionMixin, DetailView):
|
|
|
|
model = Reimbursement
|
2019-06-16 12:32:24 +00:00
|
|
|
template_name = "reimbursement_detail_backoffice.html"
|
2018-08-29 22:52:32 +00:00
|
|
|
|
|
|
|
|
2019-06-16 12:32:24 +00:00
|
|
|
class ReimbursementCreateUserSelectView(
|
|
|
|
CampViewMixin, EconomyTeamPermissionMixin, ListView
|
|
|
|
):
|
|
|
|
template_name = "reimbursement_create_userselect.html"
|
2018-08-29 22:52:32 +00:00
|
|
|
|
|
|
|
def get_queryset(self):
|
|
|
|
queryset = User.objects.filter(
|
|
|
|
id__in=Expense.objects.filter(
|
|
|
|
camp=self.camp,
|
|
|
|
reimbursement__isnull=True,
|
|
|
|
paid_by_bornhack=False,
|
|
|
|
approved=True,
|
2019-06-16 12:32:24 +00:00
|
|
|
)
|
|
|
|
.values_list("user", flat=True)
|
|
|
|
.distinct()
|
2018-08-29 22:52:32 +00:00
|
|
|
)
|
|
|
|
return queryset
|
|
|
|
|
|
|
|
|
|
|
|
class ReimbursementCreateView(CampViewMixin, EconomyTeamPermissionMixin, CreateView):
|
|
|
|
model = Reimbursement
|
2019-06-16 12:32:24 +00:00
|
|
|
template_name = "reimbursement_create.html"
|
|
|
|
fields = ["notes", "paid"]
|
2018-08-29 22:52:32 +00:00
|
|
|
|
|
|
|
def dispatch(self, request, *args, **kwargs):
|
|
|
|
""" Get the user from kwargs """
|
2019-06-16 12:32:24 +00:00
|
|
|
self.reimbursement_user = get_object_or_404(User, pk=kwargs["user_id"])
|
2018-08-29 22:52:32 +00:00
|
|
|
|
|
|
|
# get response now so we have self.camp available below
|
|
|
|
response = super().dispatch(request, *args, **kwargs)
|
|
|
|
|
|
|
|
# return the response
|
|
|
|
return response
|
|
|
|
|
|
|
|
def get(self, request, *args, **kwargs):
|
|
|
|
# does this user have any approved and un-reimbursed expenses?
|
2019-06-16 12:32:24 +00:00
|
|
|
if not self.reimbursement_user.expenses.filter(
|
|
|
|
reimbursement__isnull=True, approved=True, paid_by_bornhack=False
|
|
|
|
):
|
|
|
|
messages.error(
|
|
|
|
request, "This user has no approved and unreimbursed expenses!"
|
|
|
|
)
|
|
|
|
return redirect(
|
|
|
|
reverse("backoffice:index", kwargs={"camp_slug": self.camp.slug})
|
|
|
|
)
|
2018-08-29 22:52:32 +00:00
|
|
|
return super().get(request, *args, **kwargs)
|
|
|
|
|
|
|
|
def get_context_data(self, **kwargs):
|
|
|
|
context = super().get_context_data(**kwargs)
|
2019-06-16 12:32:24 +00:00
|
|
|
context["expenses"] = Expense.objects.filter(
|
2018-08-29 22:52:32 +00:00
|
|
|
user=self.reimbursement_user,
|
|
|
|
approved=True,
|
|
|
|
reimbursement__isnull=True,
|
|
|
|
paid_by_bornhack=False,
|
|
|
|
)
|
2019-06-16 12:32:24 +00:00
|
|
|
context["total_amount"] = context["expenses"].aggregate(Sum("amount"))
|
|
|
|
context["reimbursement_user"] = self.reimbursement_user
|
2018-08-29 22:52:32 +00:00
|
|
|
return context
|
|
|
|
|
|
|
|
def form_valid(self, form):
|
|
|
|
"""
|
|
|
|
Set user and camp for the Reimbursement before saving
|
|
|
|
"""
|
|
|
|
# get the expenses for this user
|
2019-06-16 12:32:24 +00:00
|
|
|
expenses = Expense.objects.filter(
|
|
|
|
user=self.reimbursement_user,
|
|
|
|
approved=True,
|
|
|
|
reimbursement__isnull=True,
|
|
|
|
paid_by_bornhack=False,
|
|
|
|
)
|
2018-08-29 22:52:32 +00:00
|
|
|
if not expenses:
|
|
|
|
messages.error(self.request, "No expenses found")
|
2019-06-16 12:32:24 +00:00
|
|
|
return redirect(
|
|
|
|
reverse(
|
|
|
|
"backoffice:reimbursement_list",
|
|
|
|
kwargs={"camp_slug": self.camp.slug},
|
|
|
|
)
|
|
|
|
)
|
2018-08-29 22:52:32 +00:00
|
|
|
|
|
|
|
# get the Economy team for this camp
|
|
|
|
try:
|
2019-06-16 12:32:24 +00:00
|
|
|
economyteam = Team.objects.get(
|
|
|
|
camp=self.camp, name=settings.ECONOMYTEAM_NAME
|
|
|
|
)
|
2018-08-29 22:52:32 +00:00
|
|
|
except Team.DoesNotExist:
|
|
|
|
messages.error(self.request, "No economy team found")
|
2019-06-16 12:32:24 +00:00
|
|
|
return redirect(
|
|
|
|
reverse(
|
|
|
|
"backoffice:reimbursement_list",
|
|
|
|
kwargs={"camp_slug": self.camp.slug},
|
|
|
|
)
|
|
|
|
)
|
2018-08-29 22:52:32 +00:00
|
|
|
|
|
|
|
# create reimbursement in database
|
|
|
|
reimbursement = form.save(commit=False)
|
|
|
|
reimbursement.reimbursement_user = self.reimbursement_user
|
|
|
|
reimbursement.user = self.request.user
|
|
|
|
reimbursement.camp = self.camp
|
|
|
|
reimbursement.save()
|
|
|
|
|
|
|
|
# add all expenses to reimbursement
|
|
|
|
for expense in expenses:
|
|
|
|
expense.reimbursement = reimbursement
|
|
|
|
expense.save()
|
|
|
|
|
|
|
|
# create expense for this reimbursement
|
|
|
|
expense = Expense()
|
2019-06-16 12:32:24 +00:00
|
|
|
expense.camp = self.camp
|
|
|
|
expense.user = self.request.user
|
|
|
|
expense.amount = reimbursement.amount
|
|
|
|
expense.description = "Payment of reimbursement %s to %s" % (
|
|
|
|
reimbursement.pk,
|
|
|
|
reimbursement.reimbursement_user,
|
|
|
|
)
|
|
|
|
expense.paid_by_bornhack = True
|
|
|
|
expense.responsible_team = economyteam
|
|
|
|
expense.approved = True
|
|
|
|
expense.reimbursement = reimbursement
|
|
|
|
expense.invoice_date = timezone.now()
|
|
|
|
expense.creditor = Credebtor.objects.get(name="Reimbursement")
|
|
|
|
expense.invoice.save(
|
|
|
|
"na.jpg",
|
|
|
|
File(
|
|
|
|
open(
|
|
|
|
os.path.join(settings.DJANGO_BASE_PATH, "static_src/img/na.jpg"),
|
|
|
|
"rb",
|
|
|
|
)
|
|
|
|
),
|
|
|
|
)
|
2018-08-29 22:52:32 +00:00
|
|
|
expense.save()
|
|
|
|
|
2019-06-16 12:32:24 +00:00
|
|
|
messages.success(
|
|
|
|
self.request,
|
|
|
|
"Reimbursement %s has been created with invoice_date %s"
|
|
|
|
% (reimbursement.pk, timezone.now()),
|
|
|
|
)
|
|
|
|
return redirect(
|
|
|
|
reverse(
|
|
|
|
"backoffice:reimbursement_detail",
|
|
|
|
kwargs={"camp_slug": self.camp.slug, "pk": reimbursement.pk},
|
|
|
|
)
|
|
|
|
)
|
2018-08-29 22:52:32 +00:00
|
|
|
|
2018-11-20 16:12:32 +00:00
|
|
|
|
|
|
|
class ReimbursementUpdateView(CampViewMixin, EconomyTeamPermissionMixin, UpdateView):
|
|
|
|
model = Reimbursement
|
2019-06-16 12:32:24 +00:00
|
|
|
template_name = "reimbursement_form.html"
|
|
|
|
fields = ["notes", "paid"]
|
2018-11-20 16:12:32 +00:00
|
|
|
|
|
|
|
def get_success_url(self):
|
2019-06-16 12:32:24 +00:00
|
|
|
return reverse(
|
|
|
|
"backoffice:reimbursement_detail",
|
|
|
|
kwargs={"camp_slug": self.camp.slug, "pk": self.get_object().pk},
|
|
|
|
)
|
|
|
|
|
2018-11-20 16:12:32 +00:00
|
|
|
|
|
|
|
class ReimbursementDeleteView(CampViewMixin, EconomyTeamPermissionMixin, DeleteView):
|
|
|
|
model = Reimbursement
|
2019-06-16 12:32:24 +00:00
|
|
|
template_name = "reimbursement_delete.html"
|
|
|
|
fields = ["notes", "paid"]
|
2018-11-20 16:12:32 +00:00
|
|
|
|
|
|
|
def dispatch(self, request, *args, **kwargs):
|
|
|
|
response = super().dispatch(request, *args, **kwargs)
|
|
|
|
if self.get_object().paid:
|
2019-06-16 12:32:24 +00:00
|
|
|
messages.error(
|
|
|
|
request,
|
|
|
|
"This reimbursement has already been paid so it cannot be deleted",
|
|
|
|
)
|
|
|
|
return redirect(
|
|
|
|
reverse(
|
|
|
|
"backoffice:reimbursement_list",
|
|
|
|
kwargs={"camp_slug": self.camp.slug},
|
|
|
|
)
|
|
|
|
)
|
2018-11-20 16:12:32 +00:00
|
|
|
return response
|
|
|
|
|
|
|
|
|
|
|
|
################################
|
|
|
|
########### REVENUES ###########
|
|
|
|
|
2019-06-16 12:32:24 +00:00
|
|
|
|
2018-11-20 16:12:32 +00:00
|
|
|
class RevenueListView(CampViewMixin, EconomyTeamPermissionMixin, ListView):
|
|
|
|
model = Revenue
|
2019-06-16 12:32:24 +00:00
|
|
|
template_name = "revenue_list_backoffice.html"
|
2018-11-20 16:12:32 +00:00
|
|
|
|
|
|
|
def get_queryset(self, **kwargs):
|
|
|
|
"""
|
|
|
|
Exclude unapproved revenues, they are shown seperately
|
|
|
|
"""
|
|
|
|
queryset = super().get_queryset(**kwargs)
|
|
|
|
return queryset.exclude(approved__isnull=True)
|
|
|
|
|
|
|
|
def get_context_data(self, **kwargs):
|
|
|
|
"""
|
|
|
|
Include unapproved revenues seperately
|
|
|
|
"""
|
|
|
|
context = super().get_context_data(**kwargs)
|
2019-06-16 12:32:24 +00:00
|
|
|
context["unapproved_revenues"] = Revenue.objects.filter(
|
|
|
|
camp=self.camp, approved__isnull=True
|
|
|
|
)
|
2018-11-20 16:12:32 +00:00
|
|
|
return context
|
|
|
|
|
|
|
|
|
|
|
|
class RevenueDetailView(CampViewMixin, EconomyTeamPermissionMixin, UpdateView):
|
|
|
|
model = Revenue
|
2019-06-16 12:32:24 +00:00
|
|
|
template_name = "revenue_detail_backoffice.html"
|
|
|
|
fields = ["notes"]
|
2018-11-20 16:12:32 +00:00
|
|
|
|
|
|
|
def form_valid(self, form):
|
|
|
|
"""
|
|
|
|
We have two submit buttons in this form, Approve and Reject
|
|
|
|
"""
|
|
|
|
revenue = form.save()
|
2019-06-16 12:32:24 +00:00
|
|
|
if "approve" in form.data:
|
2018-11-20 16:12:32 +00:00
|
|
|
# approve button was pressed
|
|
|
|
revenue.approve(self.request)
|
2019-06-16 12:32:24 +00:00
|
|
|
elif "reject" in form.data:
|
2018-11-20 16:12:32 +00:00
|
|
|
# reject button was pressed
|
|
|
|
revenue.reject(self.request)
|
|
|
|
else:
|
|
|
|
messages.error(self.request, "Unknown submit action")
|
2019-06-16 12:32:24 +00:00
|
|
|
return redirect(
|
|
|
|
reverse("backoffice:revenue_list", kwargs={"camp_slug": self.camp.slug})
|
|
|
|
)
|
2019-07-17 20:02:47 +00:00
|
|
|
|
|
|
|
|
2019-08-03 22:18:52 +00:00
|
|
|
def _ticket_getter_by_token(token):
|
|
|
|
for ticket_class in [ShopTicket, SponsorTicket, DiscountTicket]:
|
|
|
|
try:
|
|
|
|
return ticket_class.objects.get(token=token), False
|
|
|
|
except ticket_class.DoesNotExist:
|
|
|
|
try:
|
|
|
|
return ticket_class.objects.get(badge_token=token), True
|
|
|
|
except ticket_class.DoesNotExist:
|
|
|
|
pass
|
2019-07-17 20:02:47 +00:00
|
|
|
|
|
|
|
|
2019-08-03 22:18:52 +00:00
|
|
|
def _ticket_getter_by_pk(pk):
|
|
|
|
for ticket_class in [ShopTicket, SponsorTicket, DiscountTicket]:
|
|
|
|
try:
|
|
|
|
return ticket_class.objects.get(pk=pk)
|
|
|
|
except ticket_class.DoesNotExist:
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
2019-08-08 10:43:59 +00:00
|
|
|
class ScanTicketsView(LoginRequiredMixin, InfoTeamPermissionMixin, CampViewMixin,TemplateView):
|
|
|
|
template_name = "info_desk/scan.html"
|
2019-08-03 22:18:52 +00:00
|
|
|
|
|
|
|
ticket = None
|
2019-08-08 10:43:59 +00:00
|
|
|
order = None
|
|
|
|
order_search = False
|
2019-07-17 20:02:47 +00:00
|
|
|
|
|
|
|
def get_context_data(self, **kwargs):
|
|
|
|
context = super().get_context_data(**kwargs)
|
|
|
|
|
2019-08-03 22:18:52 +00:00
|
|
|
if self.ticket:
|
|
|
|
context["ticket"] = self.ticket
|
|
|
|
|
|
|
|
elif "ticket_token" in self.request.POST:
|
|
|
|
|
|
|
|
# Slice to get rid of the first character which is a '#'
|
|
|
|
ticket_token = self.request.POST.get("ticket_token")[1:]
|
|
|
|
|
|
|
|
ticket, is_badge = _ticket_getter_by_token(ticket_token)
|
|
|
|
|
|
|
|
if ticket:
|
2019-07-17 20:02:47 +00:00
|
|
|
context["ticket"] = ticket
|
2019-08-03 22:18:52 +00:00
|
|
|
context["is_badge"] = is_badge
|
|
|
|
else:
|
2019-07-17 20:02:47 +00:00
|
|
|
messages.warning(self.request, "Ticket not found!")
|
|
|
|
|
2019-08-08 10:43:59 +00:00
|
|
|
elif self.order_search:
|
|
|
|
context['order'] = self.order
|
|
|
|
|
2019-07-17 20:02:47 +00:00
|
|
|
return context
|
2019-08-03 22:18:52 +00:00
|
|
|
|
|
|
|
def post(self, request, **kwargs):
|
|
|
|
if 'check_in_ticket_id' in request.POST:
|
|
|
|
self.ticket = self.check_in_ticket(request)
|
|
|
|
elif 'badge_ticket_id' in request.POST:
|
|
|
|
self.ticket = self.hand_out_badge(request)
|
2019-08-08 10:43:59 +00:00
|
|
|
elif 'find_order_id' in request.POST:
|
|
|
|
self.order_search = True
|
|
|
|
try:
|
|
|
|
order_id = self.request.POST.get('find_order_id')
|
|
|
|
self.order = Order.objects.get(id=order_id)
|
|
|
|
except Order.DoesNotExist:
|
|
|
|
pass
|
|
|
|
elif 'mark_as_paid' in request.POST:
|
|
|
|
self.mark_order_as_paid(request)
|
2019-08-03 22:18:52 +00:00
|
|
|
|
|
|
|
return super().get(request, **kwargs)
|
|
|
|
|
|
|
|
def check_in_ticket(self, request):
|
|
|
|
check_in_ticket_id = request.POST.get("check_in_ticket_id")
|
|
|
|
ticket_to_check_in = _ticket_getter_by_pk(check_in_ticket_id)
|
|
|
|
ticket_to_check_in.used = True
|
|
|
|
ticket_to_check_in.save()
|
|
|
|
messages.info(request, "Ticket checked-in!")
|
|
|
|
return ticket_to_check_in
|
|
|
|
|
|
|
|
def hand_out_badge(self, request):
|
|
|
|
badge_ticket_id = request.POST.get('badge_ticket_id')
|
|
|
|
ticket_to_handout_badge_for = _ticket_getter_by_pk(badge_ticket_id)
|
|
|
|
ticket_to_handout_badge_for.badge_handed_out = True
|
|
|
|
ticket_to_handout_badge_for.save()
|
|
|
|
messages.info(request, "Badge marked as handed out!")
|
|
|
|
return ticket_to_handout_badge_for
|
2019-08-08 10:43:59 +00:00
|
|
|
|
|
|
|
def mark_order_as_paid(self, request):
|
|
|
|
order = Order.objects.get(id=request.POST.get('mark_as_paid'))
|
|
|
|
order.mark_as_paid()
|
|
|
|
messages.success(request, "Order #{} has been marked as paid!".format(order.id))
|
2019-08-11 11:18:19 +00:00
|
|
|
|
|
|
|
|
|
|
|
class ShopTicketOverview(LoginRequiredMixin, CampViewMixin, ListView):
|
|
|
|
|
|
|
|
model = ShopTicket
|
|
|
|
|
|
|
|
template_name = "shop_ticket_overview.html"
|
|
|
|
|
|
|
|
context_object_name = "shop_tickets"
|
|
|
|
|
|
|
|
def get_context_data(self, *, object_list=None, **kwargs):
|
|
|
|
kwargs['ticket_types'] = TicketType.objects.filter(camp=self.camp)
|
|
|
|
return super().get_context_data(object_list=object_list, **kwargs)
|
|
|
|
|