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.
- wbcommission/__init__.py +1 -0
- wbcommission/admin/__init__.py +4 -0
- wbcommission/admin/accounts.py +22 -0
- wbcommission/admin/commission.py +85 -0
- wbcommission/admin/rebate.py +7 -0
- wbcommission/analytics/__init__.py +0 -0
- wbcommission/analytics/marginality.py +181 -0
- wbcommission/apps.py +5 -0
- wbcommission/dynamic_preferences_registry.py +0 -0
- wbcommission/factories/__init__.py +9 -0
- wbcommission/factories/commission.py +100 -0
- wbcommission/factories/rebate.py +16 -0
- wbcommission/filters/__init__.py +7 -0
- wbcommission/filters/rebate.py +187 -0
- wbcommission/filters/signals.py +44 -0
- wbcommission/generators/__init__.py +2 -0
- wbcommission/generators/rebate_generator.py +93 -0
- wbcommission/migrations/0001_initial.py +299 -0
- wbcommission/migrations/0002_commissionrule_remove_accountcustomer_account_and_more.py +395 -0
- wbcommission/migrations/0003_alter_commission_account.py +24 -0
- wbcommission/migrations/0004_rebate_audit_log.py +19 -0
- wbcommission/migrations/0005_alter_rebate_audit_log.py +20 -0
- wbcommission/migrations/0006_commissionrule_consider_zero_percent_for_exclusion.py +21 -0
- wbcommission/migrations/0007_remove_commission_unique_crm_recipient_account_and_more.py +50 -0
- wbcommission/migrations/0008_alter_commission_options_alter_commission_order.py +26 -0
- wbcommission/migrations/__init__.py +0 -0
- wbcommission/models/__init__.py +9 -0
- wbcommission/models/account_service.py +217 -0
- wbcommission/models/commission.py +679 -0
- wbcommission/models/rebate.py +319 -0
- wbcommission/models/signals.py +45 -0
- wbcommission/permissions.py +6 -0
- wbcommission/reports/__init__.py +0 -0
- wbcommission/reports/audit_report.py +51 -0
- wbcommission/reports/customer_report.py +299 -0
- wbcommission/reports/utils.py +30 -0
- wbcommission/serializers/__init__.py +3 -0
- wbcommission/serializers/commissions.py +26 -0
- wbcommission/serializers/rebate.py +87 -0
- wbcommission/serializers/signals.py +27 -0
- wbcommission/tests/__init__.py +0 -0
- wbcommission/tests/analytics/__init__.py +0 -0
- wbcommission/tests/analytics/test_marginality.py +253 -0
- wbcommission/tests/conftest.py +89 -0
- wbcommission/tests/models/__init__.py +0 -0
- wbcommission/tests/models/mixins.py +22 -0
- wbcommission/tests/models/test_account_service.py +293 -0
- wbcommission/tests/models/test_commission.py +587 -0
- wbcommission/tests/models/test_rebate.py +136 -0
- wbcommission/tests/signals.py +0 -0
- wbcommission/tests/test_permissions.py +66 -0
- wbcommission/tests/viewsets/__init__.py +0 -0
- wbcommission/tests/viewsets/test_rebate.py +76 -0
- wbcommission/urls.py +42 -0
- wbcommission/viewsets/__init__.py +7 -0
- wbcommission/viewsets/buttons/__init__.py +2 -0
- wbcommission/viewsets/buttons/rebate.py +46 -0
- wbcommission/viewsets/buttons/signals.py +53 -0
- wbcommission/viewsets/commissions.py +21 -0
- wbcommission/viewsets/display/__init__.py +5 -0
- wbcommission/viewsets/display/commissions.py +21 -0
- wbcommission/viewsets/display/rebate.py +117 -0
- wbcommission/viewsets/endpoints/__init__.py +4 -0
- wbcommission/viewsets/endpoints/commissions.py +0 -0
- wbcommission/viewsets/endpoints/rebate.py +21 -0
- wbcommission/viewsets/menu/__init__.py +1 -0
- wbcommission/viewsets/menu/commissions.py +0 -0
- wbcommission/viewsets/menu/rebate.py +13 -0
- wbcommission/viewsets/mixins.py +39 -0
- wbcommission/viewsets/rebate.py +481 -0
- wbcommission/viewsets/titles/__init__.py +1 -0
- wbcommission/viewsets/titles/commissions.py +0 -0
- wbcommission/viewsets/titles/rebate.py +11 -0
- wbcommission-2.2.1.dist-info/METADATA +11 -0
- wbcommission-2.2.1.dist-info/RECORD +76 -0
- 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,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,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
|
+
)
|
|
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
|
+
)
|