diff --git a/src/bornhack/environment_settings.py.dist.dev b/src/bornhack/environment_settings.py.dist.dev index 55097a7ce..269dc0fc6 100644 --- a/src/bornhack/environment_settings.py.dist.dev +++ b/src/bornhack/environment_settings.py.dist.dev @@ -63,6 +63,10 @@ IRCBOT_CHANNELS = { IRCBOT_PUBLIC_CHANNEL = "#my-bornhack-channel" IRCBOT_VOLUNTEER_CHANNEL = "#my-bornhack-channel" +ACCOUNTINGSYSTEM_EMAIL = "accounting_system@example.com" +ECONOMYTEAM_EMAIL = "economy@example.com" +ECONOMYTEAM_NAME = "Economy" + BANKACCOUNT_IBAN = "LOL" BANKACCOUNT_SWIFTBIC = "lol" BANKACCOUNT_REG = "lol" diff --git a/src/economy/views.py b/src/economy/views.py index 06ad877f0..35bb0267e 100644 --- a/src/economy/views.py +++ b/src/economy/views.py @@ -6,8 +6,8 @@ from django.conf import settings from django.contrib import messages from django.contrib.auth.mixins import LoginRequiredMixin -from django.db.models import Prefetch from django.core.exceptions import ValidationError +from django.db.models import Prefetch from django.db.models import Q from django.db.models import Sum from django.http import HttpResponse @@ -415,16 +415,8 @@ class ReimbursementCreateView(CampViewMixin, ExpensePermissionMixin, CreateView) def dispatch(self, request, *args, **kwargs): """Get any approved and un-reimbursed expenses and revenues, or return error.""" - self.expenses = request.user.expenses.filter( - reimbursement__isnull=True, - approved=True, - payment_status="PAID_NEEDS_REIMBURSEMENT", - ) - self.revenues = request.user.revenues.filter( - reimbursement__isnull=True, - approved=True, - payment_status="PAID_NEEDS_REDISBURSEMENT", - ) + self.expenses = request.user.profile.paid_expenses_needs_reimbursement + self.revenues = request.user.profile.paid_revenues_needs_redisbursement if not self.expenses and not self.revenues: messages.error( request, @@ -458,28 +450,8 @@ def form_valid(self, form): reverse("economy:dashboard", kwargs={"camp_slug": self.camp.slug}), ) - # get the expenses for this user - expenses = Expense.objects.filter( - user=self.request.user, - approved=True, - reimbursement__isnull=True, - payment_status="PAID_NEEDS_REIMBURSEMENT", - ) - expenses_total = expenses.aggregate(Sum("amount"))["amount__sum"] or 0 - - # get the revenues for this user - revenues = Revenue.objects.filter( - user=self.request.user, - approved=True, - reimbursement__isnull=True, - payment_status="PAID_NEEDS_REDISBURSEMENT", - ) - revenues_total = revenues.aggregate(Sum("amount"))["amount__sum"] or 0 - if not expenses and not revenues: - messages.error(self.request, "No approved unhandled expenses or revenues found") - return redirect( - reverse("economy:dashboard", kwargs={"camp_slug": self.camp.slug}), - ) + expenses_total = self.expenses.aggregate(Sum("amount"))["amount__sum"] or 0 + revenues_total = self.revenues.aggregate(Sum("amount"))["amount__sum"] or 0 # calculate the reimbursement total reimbursement_total = expenses_total - revenues_total @@ -502,12 +474,12 @@ def form_valid(self, form): reimbursement.save() # add all expenses to reimbursement - for expense in expenses: + for expense in self.expenses: expense.reimbursement = reimbursement expense.save() # add all revenues to reimbursement - for revenue in revenues: + for revenue in self.revenues: revenue.reimbursement = reimbursement revenue.save() diff --git a/src/profiles/apps.py b/src/profiles/apps.py index f155ea304..435c6fe9a 100644 --- a/src/profiles/apps.py +++ b/src/profiles/apps.py @@ -9,8 +9,9 @@ from .signal_handlers import create_profile from .signal_handlers import profile_pre_save -from .signal_handlers import set_session_on_login +from .signal_handlers import redisbursement_msg_on_login from .signal_handlers import reimbursement_msg_on_login +from .signal_handlers import set_session_on_login logger = logging.getLogger(f"bornhack.{__name__}") @@ -42,3 +43,8 @@ def ready(self) -> None: sender=User, dispatch_uid="reimbursement_msg_on_login_signal", ) + user_logged_in.connect( + redisbursement_msg_on_login, + sender=User, + dispatch_uid="redisbursement_msg_on_login_signal", + ) diff --git a/src/profiles/models.py b/src/profiles/models.py index 9ab0c5bb3..e5213145b 100644 --- a/src/profiles/models.py +++ b/src/profiles/models.py @@ -7,6 +7,8 @@ from django.utils.translation import gettext_lazy as _ from django_prometheus.models import ExportModelOperationsMixin +from economy.models import Expense +from economy.models import Revenue from utils.models import CreatedUpdatedModel from utils.models import UUIDModel @@ -76,22 +78,22 @@ class Meta: ) @property - def email(self): + def email(self) -> str: return self.user.email def __str__(self) -> str: return self.user.username def approve_public_credit_name(self) -> None: - """This method just sets profile.public_credit_name_approved=True and calls save() - It is used in an admin action. + """This method just sets profile.public_credit_name_approved=True + and calls save(). It is used in an admin action. """ self.public_credit_name_approved = True self.save() @property - def get_public_credit_name(self): - """Convenience method to return profile.public_credit_name if it is approved, + def get_public_credit_name(self) -> str: + """Convenience method to return profile.public_credit_name if approved, and the string "Unnamed" otherwise. """ if self.public_credit_name_approved: @@ -99,8 +101,28 @@ def get_public_credit_name(self): return "Unnamed" @property - def get_name(self): + def get_name(self) -> str: """Convenience method to return profile.name if set, otherwise username.""" if self.name: return self.name return self.user.username + + @property + def paid_expenses_needs_reimbursement(self) -> models.QuerySet: + """The paid_expense_needs_reimbursement property.""" + return Expense.objects.filter( + user=self.user, + approved=True, + reimbursement__isnull=True, + payment_status="PAID_NEEDS_REIMBURSEMENT", + ) + + @property + def paid_revenues_needs_redisbursement(self) -> models.QuerySet: + """The paid_revenues_needs_redisbursement property.""" + return Revenue.objects.filter( + user=self.user, + approved=True, + reimbursement__isnull=True, + payment_status="PAID_NEEDS_REDISBURSEMENT", + ) diff --git a/src/profiles/signal_handlers.py b/src/profiles/signal_handlers.py index cc6ccddbc..a833b81cf 100644 --- a/src/profiles/signal_handlers.py +++ b/src/profiles/signal_handlers.py @@ -79,15 +79,26 @@ def set_session_on_login(sender, request, user, **kwargs) -> None: """Signal handler called on_login to set session["theme"] from the user profile.""" request.session["theme"] = request.user.profile.theme -def reimbursement_msg_on_login(sender, request, user, **kwargs) -> None: - """ - Add message when user has approved expenses without matching reimbursement. - """ - approved_expenses = user.expenses.all().filter(approved=True, reimbursement=None) - if approved_expenses.exists(): - messages.info( - request, - f"NOTE: You have {approved_expenses.count()} expenses with a missing reimbursement. Please create a reimbursement once all your expenses have been approved." +def reimbursement_msg_on_login(sender, request, user, **kwargs) -> None: + """Add message for reminding user about missing reimbursement.""" + expenses = user.profile.paid_expenses_needs_reimbursement + if expenses.exists(): + msg = ( + f"NOTE: You have {expenses.count()} expense(s) with missing " + "reimbursement. Please create a reimbursement once all your " + "expenses have been approved." ) + messages.info(request, msg) + +def redisbursement_msg_on_login(sender, request, user, **kwargs) -> None: + """Add message for reminding user about missing redisbursement.""" + revenues = user.profile.paid_revenues_needs_redisbursement + if revenues.exists(): + msg = ( + f"NOTE: You have {revenues.count()} revenue(s) with missing " + "redisbursement. Please create a redisbursement once all your " + "revenue(s) have been approved." + ) + messages.info(request, msg) diff --git a/src/utils/bootstrap/base.py b/src/utils/bootstrap/base.py index e52db4551..4a79eba37 100644 --- a/src/utils/bootstrap/base.py +++ b/src/utils/bootstrap/base.py @@ -2037,18 +2037,8 @@ def create_camp_reimbursements(self, camp: Camp) -> None: .distinct(), ) for user in users: - expenses = Expense.objects.filter( - user=user, - approved=True, - reimbursement__isnull=True, - payment_status="PAID_NEEDS_REIMBURSEMENT", - ) - revenues = Revenue.objects.filter( - user=user, - approved=True, - reimbursement__isnull=True, - payment_status="PAID_NEEDS_REDISBURSEMENT", - ) + expenses = user.profile.paid_expenses_needs_reimbursement + revenues = user.profile.paid_revenues_needs_redisbursement if not expenses and not revenues: continue reimbursement = Reimbursement.objects.create(