wbcommission 2.2.1__py2.py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of wbcommission might be problematic. Click here for more details.

Files changed (76) hide show
  1. wbcommission/__init__.py +1 -0
  2. wbcommission/admin/__init__.py +4 -0
  3. wbcommission/admin/accounts.py +22 -0
  4. wbcommission/admin/commission.py +85 -0
  5. wbcommission/admin/rebate.py +7 -0
  6. wbcommission/analytics/__init__.py +0 -0
  7. wbcommission/analytics/marginality.py +181 -0
  8. wbcommission/apps.py +5 -0
  9. wbcommission/dynamic_preferences_registry.py +0 -0
  10. wbcommission/factories/__init__.py +9 -0
  11. wbcommission/factories/commission.py +100 -0
  12. wbcommission/factories/rebate.py +16 -0
  13. wbcommission/filters/__init__.py +7 -0
  14. wbcommission/filters/rebate.py +187 -0
  15. wbcommission/filters/signals.py +44 -0
  16. wbcommission/generators/__init__.py +2 -0
  17. wbcommission/generators/rebate_generator.py +93 -0
  18. wbcommission/migrations/0001_initial.py +299 -0
  19. wbcommission/migrations/0002_commissionrule_remove_accountcustomer_account_and_more.py +395 -0
  20. wbcommission/migrations/0003_alter_commission_account.py +24 -0
  21. wbcommission/migrations/0004_rebate_audit_log.py +19 -0
  22. wbcommission/migrations/0005_alter_rebate_audit_log.py +20 -0
  23. wbcommission/migrations/0006_commissionrule_consider_zero_percent_for_exclusion.py +21 -0
  24. wbcommission/migrations/0007_remove_commission_unique_crm_recipient_account_and_more.py +50 -0
  25. wbcommission/migrations/0008_alter_commission_options_alter_commission_order.py +26 -0
  26. wbcommission/migrations/__init__.py +0 -0
  27. wbcommission/models/__init__.py +9 -0
  28. wbcommission/models/account_service.py +217 -0
  29. wbcommission/models/commission.py +679 -0
  30. wbcommission/models/rebate.py +319 -0
  31. wbcommission/models/signals.py +45 -0
  32. wbcommission/permissions.py +6 -0
  33. wbcommission/reports/__init__.py +0 -0
  34. wbcommission/reports/audit_report.py +51 -0
  35. wbcommission/reports/customer_report.py +299 -0
  36. wbcommission/reports/utils.py +30 -0
  37. wbcommission/serializers/__init__.py +3 -0
  38. wbcommission/serializers/commissions.py +26 -0
  39. wbcommission/serializers/rebate.py +87 -0
  40. wbcommission/serializers/signals.py +27 -0
  41. wbcommission/tests/__init__.py +0 -0
  42. wbcommission/tests/analytics/__init__.py +0 -0
  43. wbcommission/tests/analytics/test_marginality.py +253 -0
  44. wbcommission/tests/conftest.py +89 -0
  45. wbcommission/tests/models/__init__.py +0 -0
  46. wbcommission/tests/models/mixins.py +22 -0
  47. wbcommission/tests/models/test_account_service.py +293 -0
  48. wbcommission/tests/models/test_commission.py +587 -0
  49. wbcommission/tests/models/test_rebate.py +136 -0
  50. wbcommission/tests/signals.py +0 -0
  51. wbcommission/tests/test_permissions.py +66 -0
  52. wbcommission/tests/viewsets/__init__.py +0 -0
  53. wbcommission/tests/viewsets/test_rebate.py +76 -0
  54. wbcommission/urls.py +42 -0
  55. wbcommission/viewsets/__init__.py +7 -0
  56. wbcommission/viewsets/buttons/__init__.py +2 -0
  57. wbcommission/viewsets/buttons/rebate.py +46 -0
  58. wbcommission/viewsets/buttons/signals.py +53 -0
  59. wbcommission/viewsets/commissions.py +21 -0
  60. wbcommission/viewsets/display/__init__.py +5 -0
  61. wbcommission/viewsets/display/commissions.py +21 -0
  62. wbcommission/viewsets/display/rebate.py +117 -0
  63. wbcommission/viewsets/endpoints/__init__.py +4 -0
  64. wbcommission/viewsets/endpoints/commissions.py +0 -0
  65. wbcommission/viewsets/endpoints/rebate.py +21 -0
  66. wbcommission/viewsets/menu/__init__.py +1 -0
  67. wbcommission/viewsets/menu/commissions.py +0 -0
  68. wbcommission/viewsets/menu/rebate.py +13 -0
  69. wbcommission/viewsets/mixins.py +39 -0
  70. wbcommission/viewsets/rebate.py +481 -0
  71. wbcommission/viewsets/titles/__init__.py +1 -0
  72. wbcommission/viewsets/titles/commissions.py +0 -0
  73. wbcommission/viewsets/titles/rebate.py +11 -0
  74. wbcommission-2.2.1.dist-info/METADATA +11 -0
  75. wbcommission-2.2.1.dist-info/RECORD +76 -0
  76. wbcommission-2.2.1.dist-info/WHEEL +5 -0
@@ -0,0 +1,136 @@
1
+ from unittest.mock import patch
2
+
3
+ import pytest
4
+ from django.contrib.auth import get_user_model
5
+ from django.contrib.auth.models import Permission
6
+ from faker import Faker
7
+ from wbcommission.models import CommissionType, Rebate
8
+
9
+ from .mixins import AccountManagerFixture
10
+
11
+ fake = Faker()
12
+
13
+
14
+ @pytest.mark.django_db
15
+ class TestRebateModel(AccountManagerFixture):
16
+ def test_init(self, rebate):
17
+ assert rebate.id is not None
18
+
19
+ def test_rebate_view_permission(self, rebate_factory, user, commission_factory, commission_role_factory):
20
+ # Here we simply test that if the user can see the underlying commission line, they can see the rebate (i.e. we check that the permission is applied for the queryset)
21
+ # We do extensive unit testing on what commisison line a user can see on the corresponding test "test_filter_for_user"
22
+ hidden_commission = commission_factory.create()
23
+ hidden_rebate = rebate_factory.create(commission=hidden_commission) # noqa
24
+
25
+ commission_with_role = commission_factory.create()
26
+ commission_role_factory.create(commission=commission_with_role, person=user.profile)
27
+ visible_rebate = rebate_factory.create(commission=commission_with_role)
28
+ assert set(Rebate.objects.filter_for_user(user)) == {visible_rebate}
29
+
30
+ user.user_permissions.add(
31
+ Permission.objects.get(content_type__app_label="wbcommission", codename="administrate_commission")
32
+ )
33
+ user = get_user_model().objects.get(id=user.id)
34
+ assert set(Rebate.objects.filter_for_user(user)) == {visible_rebate, hidden_rebate}
35
+
36
+ @patch.object(CommissionType, "compute_rebates")
37
+ @pytest.mark.parametrize("rebate_value", [fake.pydecimal(right_digits=4, min_value=0, max_value=1000000)])
38
+ def test_manage_rebate(self, mock_fct, commission_factory, fees_factory, rebate_value):
39
+ fees = fees_factory.create()
40
+ commission = commission_factory.create()
41
+ # We check that the method create a rebate if none exists
42
+ mock_fct.return_value = [
43
+ (
44
+ commission.account,
45
+ fees.transaction_date,
46
+ commission,
47
+ fees.linked_product,
48
+ commission.crm_recipient,
49
+ rebate_value,
50
+ dict(),
51
+ )
52
+ ]
53
+ Rebate.manage_rebate(commission.account)
54
+ new_rebate = Rebate.objects.get(
55
+ commission=commission,
56
+ product=fees.linked_product,
57
+ date=fees.transaction_date,
58
+ recipient=commission.crm_recipient,
59
+ )
60
+ assert new_rebate.value == rebate_value
61
+
62
+ # we check that the method update the existing rebate
63
+ mock_fct.return_value = [
64
+ (
65
+ commission.account,
66
+ fees.transaction_date,
67
+ commission,
68
+ fees.linked_product,
69
+ commission.crm_recipient,
70
+ rebate_value * 2,
71
+ dict(),
72
+ )
73
+ ]
74
+ Rebate.manage_rebate(commission.account)
75
+ new_rebate.refresh_from_db()
76
+ assert new_rebate.value == rebate_value * 2
77
+
78
+ # test that if rebate not valid anymore, a recomputation will automatically remove it
79
+ new_commission = commission_factory.create(account=commission.account)
80
+ mock_fct.return_value = [
81
+ (
82
+ commission.account,
83
+ fees.transaction_date,
84
+ new_commission,
85
+ fees.linked_product,
86
+ new_commission.crm_recipient,
87
+ rebate_value,
88
+ dict(),
89
+ )
90
+ ]
91
+ Rebate.manage_rebate(commission.account, prune_existing=True)
92
+ with pytest.raises((Rebate.DoesNotExist,)):
93
+ new_rebate.refresh_from_db()
94
+ assert (
95
+ Rebate.objects.get(
96
+ commission=new_commission,
97
+ product=fees.linked_product,
98
+ date=fees.transaction_date,
99
+ recipient=new_commission.crm_recipient,
100
+ ).value
101
+ == rebate_value
102
+ )
103
+
104
+ def test_account_merging(self, account_factory, rebate_factory):
105
+ """
106
+ We want to test that:
107
+ - Existing rebate for the same unique lookup arg are summed
108
+ - If not existing rebate, then account are shifted
109
+ """
110
+ base_account = account_factory.create()
111
+ merged_account = account_factory.create()
112
+ base_rebate = rebate_factory.create(account=base_account)
113
+ base_rebate.refresh_from_db()
114
+ base_value = base_rebate.value # for safekeeping
115
+
116
+ # a rebate from the account that is going to be merged but with same date, recipient, product, and type. We expect this rebate to be deleted and its value summed to the base rebate
117
+ merged_rebate_but_similar = rebate_factory.create(
118
+ account=merged_account,
119
+ date=base_rebate.date,
120
+ recipient=base_rebate.recipient,
121
+ product=base_rebate.product,
122
+ commission_type=base_rebate.commission_type,
123
+ )
124
+ merged_rebate_but_similar.refresh_from_db()
125
+ # rebate from the merged account but completly different: there won't be any existing rebate, hence the account will just be shifted.
126
+ merged_rebate = rebate_factory.create(account=merged_account)
127
+
128
+ base_account.merge(merged_account)
129
+ base_rebate.refresh_from_db()
130
+ assert base_rebate.value == base_value + merged_rebate_but_similar.value
131
+
132
+ # check that the redundant rebate was deleted
133
+ with pytest.raises(Rebate.DoesNotExist):
134
+ merged_rebate_but_similar.refresh_from_db()
135
+ merged_rebate.refresh_from_db()
136
+ assert merged_rebate.account == base_account
File without changes
@@ -0,0 +1,66 @@
1
+ import pytest
2
+ from django.contrib.auth.models import Permission
3
+ from faker import Faker
4
+ from rest_framework.reverse import reverse
5
+ from rest_framework.test import APIClient
6
+ from wbcommission.permissions import IsCommissionAdmin
7
+ from wbcore.contrib.authentication.factories import UserFactory
8
+ from wbcore.contrib.authentication.models import User
9
+ from wbcore.tests.test_permissions.test_backend import create_internal_user
10
+
11
+ fake = Faker()
12
+
13
+
14
+ @pytest.mark.django_db
15
+ class TestPermissionClass:
16
+ @pytest.fixture()
17
+ def admin_user(self):
18
+ user = UserFactory.create()
19
+ user.user_permissions.add(
20
+ Permission.objects.get(content_type__app_label="wbcommission", codename="administrate_commission")
21
+ )
22
+ return User.objects.get(id=user.id)
23
+
24
+ def test_normal_user_permission(self, rf, user):
25
+ rf.user = user
26
+ assert not IsCommissionAdmin().has_permission(rf, None)
27
+
28
+ def test_admin_permission(self, rf, admin_user):
29
+ rf.user = admin_user
30
+ assert IsCommissionAdmin().has_permission(rf, None)
31
+
32
+ def test_recompute_only_for_admin(self, user, admin_user):
33
+ client = APIClient()
34
+ url = reverse("wbcommission:rebate-recompute")
35
+
36
+ client.force_authenticate(user=user)
37
+ response = client.patch(url, data={"start_date": fake.date_object()})
38
+ assert response.status_code == 403
39
+
40
+ client.force_authenticate(user=admin_user)
41
+ response = client.patch(url, data={"start_date": fake.date_object()})
42
+ assert response.status_code == 200
43
+
44
+ def test_auditreport_only_for_admin(self, user, admin_user, company):
45
+ client = APIClient()
46
+ url = reverse("wbcommission:rebate-auditreport")
47
+
48
+ client.force_authenticate(user=user)
49
+ response = client.patch(url + f"?recipient_id={company.id}")
50
+ assert response.status_code == 403
51
+
52
+ client.force_authenticate(user=admin_user)
53
+ response = client.patch(url + f"?recipient_id={company.id}")
54
+ assert response.status_code == 200
55
+
56
+ def test_customerreport_only_for_internaluser(self, user, admin_user, company):
57
+ client = APIClient()
58
+ url = reverse("wbcommission:rebate-customerreport")
59
+
60
+ client.force_authenticate(user=user)
61
+ response = client.patch(url + f"?recipient_id={company.id}")
62
+ assert response.status_code == 403
63
+
64
+ client.force_authenticate(user=create_internal_user())
65
+ response = client.patch(url + f"?recipient_id={company.id}")
66
+ assert response.status_code == 200
File without changes
@@ -0,0 +1,76 @@
1
+ import pytest
2
+ from django.db.models import Sum
3
+ from faker import Faker
4
+ from rest_framework.test import APIRequestFactory
5
+ from wbcommission.factories import (
6
+ AccountTypeRoleCommissionFactory,
7
+ CommissionFactory,
8
+ RebateFactory,
9
+ )
10
+ from wbcommission.models.rebate import Rebate
11
+ from wbcommission.viewsets.rebate import RebateModelViewSet, RebatePandasView
12
+ from wbcore.contrib.authentication.factories.users import UserFactory
13
+ from wbcore.contrib.directory.models import Entry
14
+ from wbcore.utils.strings import format_number
15
+ from wbcrm.factories.accounts import AccountFactory, AccountRoleFactory
16
+
17
+ fake = Faker()
18
+
19
+
20
+ @pytest.mark.django_db
21
+ class TestRebateModelViewSet:
22
+ @pytest.fixture
23
+ def account_user(self):
24
+ # True, we create a superuser
25
+
26
+ # if fake.pybool():
27
+ # user = UserFactory.create(is_superuser=True)
28
+ # else:
29
+ user = UserFactory.create(is_superuser=False)
30
+ entry = Entry.objects.get(id=user.profile.id)
31
+
32
+ # Create a bunch of account and roles
33
+ public_account = AccountFactory.create(is_public=True)
34
+ role = AccountRoleFactory.create(account=public_account, entry=entry)
35
+ RebateFactory.create(
36
+ account=public_account,
37
+ commission=AccountTypeRoleCommissionFactory.create(account_role_type_recipient=role.role_type),
38
+ ) # shown rebate
39
+
40
+ RebateFactory.create(commission=CommissionFactory.create(crm_recipient=entry)) # shown rebate
41
+ RebateFactory.create() # hidden rebate
42
+ return user
43
+
44
+ def test_ensure_permission_on_rebatetable(self, account_user):
45
+ """
46
+ We ensure that all claims viewset doesn't show more that what the user is allowed to see.
47
+ For claim, the allowed claim are all the claims where the account is among the account they is allowed to see
48
+ """
49
+ allowed_rebates = Rebate.objects.filter_for_user(account_user)
50
+
51
+ request = APIRequestFactory().get("")
52
+ request.query_params = {}
53
+ request.user = account_user
54
+ viewset = RebatePandasView(request=request)
55
+ assert allowed_rebates.exists()
56
+ assert allowed_rebates.count() < Rebate.objects.count() # Ensure that the filtering works
57
+ assert set(allowed_rebates) == set(viewset.get_queryset())
58
+ assert not viewset._get_dataframe().empty
59
+
60
+ def test_ensure_permission_on_rebatemodelview(self, account_user):
61
+ """
62
+ We ensure that all claims viewset doesn't show more that what the user is allowed to see.
63
+ For claim, the allowed claim are all the claims where the account is among the account they is allowed to see
64
+ """
65
+ allowed_rebates = Rebate.objects.filter_for_user(account_user)
66
+
67
+ request = APIRequestFactory().get("")
68
+ request.user = account_user
69
+ viewset = RebateModelViewSet(request=request)
70
+ queryset = viewset.get_queryset()
71
+ assert allowed_rebates.exists()
72
+ assert allowed_rebates.count() < Rebate.objects.count() # Ensure that the filtering works
73
+ assert set(allowed_rebates) == set(queryset)
74
+ assert viewset.get_aggregates(queryset, queryset)["value_usd"]["Σ"] == float(
75
+ format_number(allowed_rebates.aggregate(s=Sum("value"))["s"])
76
+ )
wbcommission/urls.py ADDED
@@ -0,0 +1,42 @@
1
+ from django.apps import apps
2
+ from django.urls import include, path
3
+ from wbcommission import viewsets
4
+ from wbcore.routers import WBCoreRouter
5
+
6
+ router = WBCoreRouter()
7
+
8
+ router.register(
9
+ r"rebatemarginalitytable",
10
+ viewsets.RebateProductMarginalityViewSet,
11
+ basename="rebatemarginalitytable",
12
+ )
13
+ router.register(
14
+ r"rebatetable",
15
+ viewsets.RebatePandasView,
16
+ basename="rebatetable",
17
+ )
18
+ router.register(
19
+ r"rebate",
20
+ viewsets.RebateModelViewSet,
21
+ basename="rebate",
22
+ )
23
+ router.register(
24
+ r"commissiontype",
25
+ viewsets.CommissionTypeModelViewSet,
26
+ basename="commissiontype",
27
+ )
28
+ router.register(
29
+ r"commissiontyperepresentation",
30
+ viewsets.CommissionTypeRepresentationModelViewSet,
31
+ basename="commissiontyperepresentation",
32
+ )
33
+
34
+ if apps.is_installed("wbportfolio"):
35
+ from wbportfolio.viewsets.transactions.claim import ClaimAPIModelViewSet
36
+
37
+ router.register(r"claim-api", ClaimAPIModelViewSet, basename="claim-api")
38
+
39
+ account_router = WBCoreRouter()
40
+
41
+
42
+ urlpatterns = [path("", include(router.urls)), path("account/<int:account_id>/", include(account_router.urls))]
@@ -0,0 +1,7 @@
1
+ from .buttons import * # noqa
2
+ from .rebate import (
3
+ RebateModelViewSet,
4
+ RebatePandasView,
5
+ RebateProductMarginalityViewSet,
6
+ )
7
+ from .commissions import CommissionTypeRepresentationModelViewSet, CommissionTypeModelViewSet
@@ -0,0 +1,2 @@
1
+ from .signals import *
2
+ from .rebate import RebateTableButtonConfig
@@ -0,0 +1,46 @@
1
+ from django.utils.translation import gettext as _
2
+ from rest_framework.reverse import reverse
3
+ from wbcore import serializers as wb_serializers
4
+ from wbcore.contrib.icons import WBIcon
5
+ from wbcore.enums import RequestType
6
+ from wbcore.metadata.configs import buttons as bt
7
+ from wbcore.metadata.configs.buttons.view_config import ButtonViewConfig
8
+ from wbcore.metadata.configs.display.instance_display.shortcuts import (
9
+ create_simple_display,
10
+ )
11
+ from wbcore.utils.date import current_quarter_date_start
12
+ from wbcrm.models.accounts import Account
13
+ from wbcrm.serializers.accounts import AccountRepresentationSerializer
14
+
15
+
16
+ class RebateTableButtonConfig(ButtonViewConfig):
17
+ def get_custom_buttons(self):
18
+ class RecomputeRebateSerializer(wb_serializers.Serializer):
19
+ start_date = wb_serializers.DateField(label=_("Start"), default=current_quarter_date_start)
20
+ only_accounts = wb_serializers.PrimaryKeyRelatedField(queryset=Account.objects.filter(level=0))
21
+ _only_accounts = AccountRepresentationSerializer(
22
+ source="only_accounts", filter_params={"level": 0}, many=True
23
+ )
24
+ prune_existing = wb_serializers.BooleanField(
25
+ default=False,
26
+ help_text="If true, existing rebate for the specified accounts and start date will be deleted before regenerated. Usually necessary if commission rule were lowered or claim removed from account",
27
+ )
28
+
29
+ if self.request.user.has_perm("wbcommission.administrate_commission"):
30
+ return {
31
+ bt.ActionButton(
32
+ method=RequestType.PATCH,
33
+ identifiers=("wbcommission:rebates",),
34
+ action_label="Trigger Rebate computation",
35
+ endpoint=reverse("wbcommission:rebate-recompute", args=[], request=self.request),
36
+ description_fields="<p>Trigger Rebate computation</p>",
37
+ serializer=RecomputeRebateSerializer,
38
+ icon=WBIcon.REGENERATE.icon,
39
+ title="Trigger Rebate computation",
40
+ label="Trigger Rebate computation",
41
+ instance_display=create_simple_display(
42
+ [["start_date", "prune_existing"], ["only_accounts", "only_accounts"]]
43
+ ),
44
+ ),
45
+ }
46
+ return {}
@@ -0,0 +1,53 @@
1
+ from django.dispatch import receiver
2
+ from wbcore.contrib.directory.viewsets import (
3
+ CompanyModelViewSet,
4
+ EntryModelViewSet,
5
+ PersonModelViewSet,
6
+ )
7
+ from wbcore.contrib.icons import WBIcon
8
+ from wbcore.enums import RequestType
9
+ from wbcore.metadata.configs import buttons as bt
10
+ from wbcore.metadata.configs.display.instance_display.shortcuts import (
11
+ create_simple_display,
12
+ )
13
+ from wbcore.signals.instance_buttons import add_instance_button
14
+ from wbcore.utils.serializers import StartEndDateSerializer
15
+
16
+
17
+ @receiver(add_instance_button, sender=PersonModelViewSet)
18
+ @receiver(add_instance_button, sender=EntryModelViewSet)
19
+ @receiver(add_instance_button, sender=CompanyModelViewSet)
20
+ def crm_adding_instance_buttons(sender, many, *args, **kwargs):
21
+ if many:
22
+ return
23
+ return bt.DropDownButton(
24
+ label="Commission",
25
+ icon=WBIcon.UNFOLD.icon,
26
+ buttons=(
27
+ bt.WidgetButton(key="rebates", label="Rebates", icon=WBIcon.DEAL_MONEY.icon),
28
+ bt.ActionButton(
29
+ method=RequestType.PATCH,
30
+ identifiers=("wbcommission:rebates",),
31
+ action_label="Send Customer Report",
32
+ key="generate_customer_commission_report",
33
+ description_fields="<p>Generate Commission Report</p>",
34
+ serializer=StartEndDateSerializer,
35
+ icon=WBIcon.CHART_SWITCHES.icon,
36
+ title="Generate Commission reports",
37
+ label="Generate Commission reports",
38
+ instance_display=create_simple_display([["start"], ["end"]]),
39
+ ),
40
+ bt.ActionButton(
41
+ method=RequestType.PATCH,
42
+ identifiers=("wbcommission:rebates",),
43
+ action_label="Send Audit Report",
44
+ key="generate_audit_commission_report",
45
+ description_fields="<p>Generate Audit Commission Report</p>",
46
+ serializer=StartEndDateSerializer,
47
+ icon=WBIcon.NOTEBOOK.icon,
48
+ title="Generate Audit Commission reports",
49
+ label="Generate Audit Commission reports",
50
+ instance_display=create_simple_display([["start"], ["end"]]),
51
+ ),
52
+ ),
53
+ )
@@ -0,0 +1,21 @@
1
+ from wbcommission.models import CommissionType
2
+ from wbcore import viewsets
3
+
4
+ from ..serializers import (
5
+ CommissionTypeModelSerializer,
6
+ CommissionTypeRepresentationSerializer,
7
+ )
8
+ from .display import CommissionTypeDisplayConfigClass
9
+
10
+
11
+ class CommissionTypeRepresentationModelViewSet(viewsets.RepresentationViewSet):
12
+ serializer_class = CommissionTypeRepresentationSerializer
13
+ queryset = CommissionType.objects.all()
14
+ ordering_fields = search_fields = ordering = ["name"]
15
+
16
+
17
+ class CommissionTypeModelViewSet(viewsets.ModelViewSet):
18
+ serializer_class = CommissionTypeModelSerializer
19
+ queryset = CommissionType.objects.all()
20
+ ordering_fields = search_fields = ordering = ["name"]
21
+ display_config_class = CommissionTypeDisplayConfigClass
@@ -0,0 +1,5 @@
1
+ from .rebate import (
2
+ RebatePandasViewDisplayConfig,
3
+ RebateProductMarginalityDisplayConfig,
4
+ )
5
+ from .commissions import CommissionTypeDisplayConfigClass
@@ -0,0 +1,21 @@
1
+ from typing import Optional
2
+
3
+ from wbcore.metadata.configs import display as dp
4
+ from wbcore.metadata.configs.display.instance_display.shortcuts import (
5
+ Display,
6
+ create_simple_display,
7
+ )
8
+ from wbcore.metadata.configs.display.view_config import DisplayViewConfig
9
+
10
+
11
+ class CommissionTypeDisplayConfigClass(DisplayViewConfig):
12
+ def get_list_display(self) -> Optional[dp.ListDisplay]:
13
+ return dp.ListDisplay(
14
+ fields=[
15
+ dp.Field(key="name", label="Name"),
16
+ dp.Field(key="key", label="Key"),
17
+ ]
18
+ )
19
+
20
+ def get_instance_display(self) -> Display:
21
+ return create_simple_display([["name", "key"]])
@@ -0,0 +1,117 @@
1
+ from typing import Optional
2
+
3
+ from wbcore.enums import Unit
4
+ from wbcore.metadata.configs import display as dp
5
+ from wbcore.metadata.configs.display.view_config import DisplayViewConfig
6
+
7
+
8
+ class RebatePandasViewDisplayConfig(DisplayViewConfig):
9
+ def get_list_display(self) -> Optional[dp.ListDisplay]:
10
+ return dp.ListDisplay(
11
+ fields=[
12
+ dp.Field(key="title", label="Title"),
13
+ *[dp.Field(key="rebate_" + k, label=v) for k, v in self.view.rebate_types.items()],
14
+ dp.Field(key="rebate_total", label="Total Rebate"),
15
+ ]
16
+ )
17
+
18
+
19
+ class RebateProductMarginalityDisplayConfig(DisplayViewConfig):
20
+ def get_list_display(self) -> Optional[dp.ListDisplay]:
21
+ return dp.ListDisplay(
22
+ fields=[
23
+ dp.Field(key="title", label="Product", width=Unit.PIXEL(400)),
24
+ dp.Field(
25
+ label="Management",
26
+ open_by_default=False,
27
+ key=None,
28
+ children=[
29
+ dp.Field(key="base_management_fees_percent", label="Base Fees", width=Unit.PIXEL(400)),
30
+ dp.Field(key="management_fees", label="Fees", width=Unit.PIXEL(120)),
31
+ dp.Field(key="management_rebates", label="Rebates", width=Unit.PIXEL(120)),
32
+ dp.Field(key="management_marginality", label="Marginality", width=Unit.PIXEL(120)),
33
+ dp.Field(
34
+ key="net_management_marginality",
35
+ label="Net Marginality",
36
+ width=Unit.PIXEL(150),
37
+ formatting_rules=[
38
+ dp.FormattingRule(
39
+ style={
40
+ "fontWeight": "bold",
41
+ },
42
+ ),
43
+ ],
44
+ ),
45
+ ],
46
+ ),
47
+ dp.Field(
48
+ label="Performance",
49
+ open_by_default=False,
50
+ key=None,
51
+ children=[
52
+ dp.Field(key="base_performance_fees_percent", label="Base Fees", width=Unit.PIXEL(120)),
53
+ dp.Field(key="performance_fees", label="Fees", width=Unit.PIXEL(120)),
54
+ dp.Field(key="performance_rebates", label="Rebates", width=Unit.PIXEL(120)),
55
+ dp.Field(key="performance_marginality", label="Marginality", width=Unit.PIXEL(120)),
56
+ dp.Field(
57
+ key="net_performance_marginality",
58
+ label="Net Marginality",
59
+ width=Unit.PIXEL(150),
60
+ formatting_rules=[
61
+ dp.FormattingRule(
62
+ style={
63
+ "fontWeight": "bold",
64
+ },
65
+ ),
66
+ ],
67
+ ),
68
+ ],
69
+ ),
70
+ dp.Field(
71
+ label="Total",
72
+ open_by_default=False,
73
+ key=None,
74
+ children=[
75
+ dp.Field(key="total_fees", label="Fees", width=Unit.PIXEL(120)),
76
+ dp.Field(key="total_rebates", label="Rebates", width=Unit.PIXEL(120)),
77
+ dp.Field(key="total_marginality_percent", label="Marginality", width=Unit.PIXEL(120)),
78
+ dp.Field(
79
+ key="total_fees_usd",
80
+ label="Fees ($)",
81
+ width=Unit.PIXEL(120),
82
+ formatting_rules=[
83
+ dp.FormattingRule(
84
+ style={
85
+ "fontWeight": "bold",
86
+ },
87
+ ),
88
+ ],
89
+ ),
90
+ dp.Field(
91
+ key="total_rebates_usd",
92
+ label="Rebates ($)",
93
+ width=Unit.PIXEL(120),
94
+ formatting_rules=[
95
+ dp.FormattingRule(
96
+ style={
97
+ "fontWeight": "bold",
98
+ },
99
+ ),
100
+ ],
101
+ ),
102
+ dp.Field(
103
+ key="total_marginality_usd",
104
+ label="Net Marginality ($)",
105
+ width=Unit.PIXEL(120),
106
+ formatting_rules=[
107
+ dp.FormattingRule(
108
+ style={
109
+ "fontWeight": "bold",
110
+ },
111
+ ),
112
+ ],
113
+ ),
114
+ ],
115
+ ),
116
+ ]
117
+ )
@@ -0,0 +1,4 @@
1
+ from wbcommission.viewsets.endpoints.rebate import (
2
+ RebatePandasViewEndpointConfig,
3
+ RebateProductMarginalityEndpointConfig,
4
+ )
File without changes
@@ -0,0 +1,21 @@
1
+ from rest_framework.reverse import reverse
2
+ from wbcore.metadata.configs.endpoints import EndpointViewConfig
3
+
4
+
5
+ class RebatePandasViewEndpointConfig(EndpointViewConfig):
6
+ def get_endpoint(self, **kwargs):
7
+ return None
8
+
9
+ def get_list_endpoint(self, **kwargs):
10
+ return reverse("wbcommission:rebatetable-list", request=self.request)
11
+
12
+
13
+ class RebateProductMarginalityEndpointConfig(EndpointViewConfig):
14
+ def get_endpoint(self, **kwargs):
15
+ return None
16
+
17
+ def get_list_endpoint(self, **kwargs):
18
+ return reverse("wbcommission:rebatemarginalitytable-list", request=self.request)
19
+
20
+ def get_instance_endpoint(self, **kwargs):
21
+ return reverse("wbportfolio:product-list", request=self.request)
@@ -0,0 +1 @@
1
+ from .rebate import REBATE_MARGINALITY_MENUITEM, REBATE_MENUITEM
File without changes
@@ -0,0 +1,13 @@
1
+ from wbcore.menus import ItemPermission, MenuItem
2
+ from wbportfolio.permissions import is_manager
3
+
4
+ REBATE_MENUITEM = MenuItem(
5
+ label="Rebates",
6
+ endpoint="wbcommission:rebatetable-list",
7
+ permission=ItemPermission(permissions=["wbcommission.view_rebate"]),
8
+ )
9
+ REBATE_MARGINALITY_MENUITEM = MenuItem(
10
+ label="Marginality",
11
+ endpoint="wbcommission:rebatemarginalitytable-list",
12
+ permission=ItemPermission(permissions=["wbcommission.view_rebate"], method=is_manager),
13
+ )