From 8c490d6ee351d4d10c89447786b8941492ba0590 Mon Sep 17 00:00:00 2001 From: Mark Veidemanis Date: Sat, 18 Mar 2023 14:06:50 +0000 Subject: [PATCH] Implement link group detail screen with profit simulation --- app/urls.py | 5 + core/forms.py | 17 --- core/models.py | 18 +++ core/templates/base.html | 3 +- core/templates/partials/aggregator-info.html | 6 + core/templates/partials/aggregator-list.html | 2 + core/templates/partials/linkgroup-info.html | 130 +++++++++++++++++++ core/templates/partials/linkgroup-list.html | 9 ++ core/templates/partials/platform-list.html | 9 ++ core/views/aggregators.py | 12 +- core/views/linkgroups.py | 102 ++++++++++++++- 11 files changed, 292 insertions(+), 21 deletions(-) create mode 100644 core/templates/partials/linkgroup-info.html diff --git a/app/urls.py b/app/urls.py index 9f19af7..80e90bc 100644 --- a/app/urls.py +++ b/app/urls.py @@ -252,4 +252,9 @@ urlpatterns = [ linkgroups.LinkGroupDelete.as_view(), name="linkgroup_delete", ), + path( + "links//info//", + linkgroups.LinkGroupInfo.as_view(), + name="linkgroup_info", + ), ] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) diff --git a/core/forms.py b/core/forms.py index 7b7d0cf..0d23fef 100644 --- a/core/forms.py +++ b/core/forms.py @@ -268,8 +268,6 @@ class LinkGroupForm(RestrictedFormMixin, ModelForm): model = LinkGroup fields = ( "name", - "aggregators", - "platforms", "platform_owner_cut_percentage", "requisition_owner_cut_percentage", "operator_cut_percentage", @@ -278,27 +276,12 @@ class LinkGroupForm(RestrictedFormMixin, ModelForm): help_texts = { "name": "The name of the link group.", - "aggregators": "The aggregators to use.", - "platforms": "The platforms to use.", "platform_owner_cut_percentage": "The percentage of the total profit of this group to give to the platform owners.", "requisition_owner_cut_percentage": "The percentage of the total profit of this group to give to the requisition owners.", "operator_cut_percentage": "The percentage of the total profit of this group to give to the operator.", "enabled": "Whether or not this link group is enabled.", } - platforms = forms.ModelMultipleChoiceField( - queryset=Platform.objects.all(), - widget=forms.CheckboxSelectMultiple, - help_text=Meta.help_texts["platforms"], - required=True, - ) - aggregators = forms.ModelMultipleChoiceField( - queryset=Aggregator.objects.all(), - widget=forms.CheckboxSelectMultiple, - help_text=Meta.help_texts["aggregators"], - required=True, - ) - def clean(self): cleaned_data = super(LinkGroupForm, self).clean() platform_owner_cut_percentage = cleaned_data.get( diff --git a/core/models.py b/core/models.py index 31bd1b2..0af0532 100644 --- a/core/models.py +++ b/core/models.py @@ -69,6 +69,24 @@ class LinkGroup(models.Model): def __str__(self): return self.name + def payees(self): + payees = {} + for platform in self.platform_set.all(): + for payee in platform.payees.all(): + if "platform" not in payees: + payees["platform"] = [] + payees["platform"].append(payee) + + for aggregator in self.aggregator_set.all(): + agg_reqs = aggregator.requisition_set.all() + for req in agg_reqs: + for payee in req.payees.all(): + if "requisition" not in payees: + payees["requisition"] = [] + payees["requisition"].append(payee) + + return payees + class Aggregator(models.Model): """ diff --git a/core/templates/base.html b/core/templates/base.html index 3b24e5c..bb67e42 100644 --- a/core/templates/base.html +++ b/core/templates/base.html @@ -173,6 +173,7 @@ padding-right: 5px; padding-left: 5px; } + .grid-stack-item:hover .ui-resizable-handle { display: block !important; } @@ -360,7 +361,7 @@ {% block outer_content %} {% endblock %}
-
+
{% block content_wrapper %} {% block content %} {% endblock %} diff --git a/core/templates/partials/aggregator-info.html b/core/templates/partials/aggregator-info.html index cf7cac4..1452c04 100644 --- a/core/templates/partials/aggregator-info.html +++ b/core/templates/partials/aggregator-info.html @@ -10,6 +10,7 @@ created institution accounts + payees actions {% for item in object_list %} @@ -26,6 +27,11 @@ {{ item.created }} {{ item.institution_id }} {{ item.accounts|length }} + + {% for payee in item.requisition.payees.all %} + {{ payee.name }}{% if not forloop.last %}, {% endif %} + {% endfor %} +
+ +
diff --git a/core/templates/partials/platform-list.html b/core/templates/partials/platform-list.html index 2f5f7a4..6e7bc4f 100644 --- a/core/templates/partials/platform-list.html +++ b/core/templates/partials/platform-list.html @@ -1,4 +1,5 @@ {% load cache %} +{% load joinsep %} {% load cachalot cache %} {% get_last_invalidation 'core.Platform' as last %} {% include 'mixins/partials/notify.html' %} @@ -15,6 +16,8 @@ user name service + payees + link group enabled actions @@ -32,6 +35,12 @@ {{ item.user }} {{ item.name }} {{ item.get_service_display }} + + {% for payee in item.payees.all %} + {{ payee.name }}{% if not forloop.last %}, {% endif %} + {% endfor %} + + {{ item.link_group|default_if_none:"—" }} {% if item.enabled %} diff --git a/core/views/aggregators.py b/core/views/aggregators.py index ccd0881..86cd617 100644 --- a/core/views/aggregators.py +++ b/core/views/aggregators.py @@ -14,7 +14,7 @@ from two_factor.views.mixins import OTPRequiredMixin from core.clients.aggregators.nordigen import NordigenClient from core.forms import AggregatorForm -from core.models import Aggregator +from core.models import Aggregator, Requisition from core.util import logs from core.views.helpers import synchronize_async_helper @@ -95,6 +95,16 @@ class ReqsList(LoginRequiredMixin, OTPRequiredMixin, ObjectList): run = synchronize_async_helper(NordigenClient(aggregator)) reqs = synchronize_async_helper(run.get_requisitions()) + for req in reqs: + # Add in Requisition object + requisition_id = req["id"] + requisition = Requisition.objects.filter( + user=self.request.user, + aggregator=aggregator, + requisition_id=requisition_id, + ).first() + if requisition: + req["requisition"] = requisition return reqs diff --git a/core/views/linkgroups.py b/core/views/linkgroups.py index a4d6f47..79dd792 100644 --- a/core/views/linkgroups.py +++ b/core/views/linkgroups.py @@ -1,9 +1,107 @@ from django.contrib.auth.mixins import LoginRequiredMixin -from mixins.views import ObjectCreate, ObjectDelete, ObjectList, ObjectUpdate +from django.http import HttpResponse +from mixins.views import ( + ObjectCreate, + ObjectDelete, + ObjectList, + ObjectRead, + ObjectUpdate, +) +from rest_framework import status from two_factor.views.mixins import OTPRequiredMixin from core.forms import LinkGroupForm -from core.models import LinkGroup +from core.models import Aggregator, LinkGroup, Platform, Requisition + + +class LinkGroupInfo(LoginRequiredMixin, OTPRequiredMixin, ObjectRead): + context_object_name_singular = "linkgroup" + context_object_name = "linkgroups" + detail_template = "partials/linkgroup-info.html" + + def get_object(self, **kwargs): + pk = self.kwargs.get("pk") + linkgroup = LinkGroup.objects.filter( + user=self.request.user, + id=pk, + ).first() + if not linkgroup: + return HttpResponse(status=status.HTTP_404_NOT_FOUND) + return linkgroup + + def get_context_data(self): + context = super().get_context_data() + + aggregators = Aggregator.objects.filter( + user=self.request.user, + link_group=self.object, + ) + platforms = Platform.objects.filter( + user=self.request.user, + link_group=self.object, + ) + requisitions = Requisition.objects.filter( + user=self.request.user, + aggregator__in=aggregators, + ) + context["aggregators"] = aggregators + context["platforms"] = platforms + context["requisitions"] = requisitions + context["linkgroup"] = self.object + + payees = self.object.payees() + + simulation = {} + profit = 1000 + profit_platform = profit * (self.object.platform_owner_cut_percentage / 100) + profit_requisition = profit * ( + self.object.requisition_owner_cut_percentage / 100 + ) + profit_operator = profit * (self.object.operator_cut_percentage / 100) + + pay_list = {} + + platform_pay_list = [] + for payee in payees["platform"]: + cast = { + "name": payee.name, + "address": payee.address, + "amount": profit_platform / len(payees["platform"]), + "max": profit_platform, + } + if payee not in pay_list: + pay_list[payee] = {} + if "amount" in pay_list[payee]: + pay_list[payee]["amount"] += cast["amount"] + else: + pay_list[payee] = dict(cast) + platform_pay_list.append(cast) + + requisition_pay_list = [] + for payee in payees["requisition"]: + cast = { + "name": payee.name, + "address": payee.address, + "amount": profit_requisition / len(payees["requisition"]), + "max": profit_requisition, + } + if payee not in pay_list: + pay_list[payee] = {} + if "amount" in pay_list[payee]: + pay_list[payee]["amount"] += cast["amount"] + else: + pay_list[payee] = dict(cast) + requisition_pay_list.append(cast) + + simulation[("Platform", profit_platform)] = platform_pay_list + simulation[("Requisition", profit_requisition)] = requisition_pay_list + simulation[("Operator", profit_operator)] = [] + + context["pay_list"] = pay_list + + context["simulation"] = simulation + + return context class LinkGroupList(LoginRequiredMixin, OTPRequiredMixin, ObjectList):