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,44 @@
|
|
|
1
|
+
from django.db.models import Q
|
|
2
|
+
from django.dispatch import receiver
|
|
3
|
+
from wbcore import filters as wb_filters
|
|
4
|
+
from wbcore.contrib.directory.filters import EntryFilter
|
|
5
|
+
from wbcore.signals.filters import add_filters
|
|
6
|
+
from wbcrm.models.accounts import Account
|
|
7
|
+
from wbportfolio.contrib.company_portfolio.filters import PersonFilter
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@receiver(add_filters, sender=EntryFilter)
|
|
11
|
+
def add_account_filter(sender, request=None, *args, **kwargs):
|
|
12
|
+
def filter_account(queryset, name, value):
|
|
13
|
+
if value:
|
|
14
|
+
return queryset.filter(account_customers__account=value)
|
|
15
|
+
return queryset
|
|
16
|
+
|
|
17
|
+
return {
|
|
18
|
+
"account": wb_filters.ModelChoiceFilter(
|
|
19
|
+
label="Account",
|
|
20
|
+
field_name="account",
|
|
21
|
+
queryset=Account.objects.all(),
|
|
22
|
+
endpoint=Account.get_representation_endpoint(),
|
|
23
|
+
value_key=Account.get_representation_value_key(),
|
|
24
|
+
label_key=Account.get_representation_label_key(),
|
|
25
|
+
method=filter_account,
|
|
26
|
+
)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@receiver(add_filters, sender=PersonFilter)
|
|
31
|
+
def add_has_user_account_filter(sender, request=None, *args, **kwargs):
|
|
32
|
+
def filter_has_user_account(queryset, name, value):
|
|
33
|
+
if value is True:
|
|
34
|
+
return queryset.filter(Q(user_account__isnull=False))
|
|
35
|
+
elif value is False:
|
|
36
|
+
return queryset.filter(Q(user_account__isnull=True))
|
|
37
|
+
else:
|
|
38
|
+
return queryset
|
|
39
|
+
|
|
40
|
+
return {
|
|
41
|
+
"has_user_account": wb_filters.BooleanFilter(
|
|
42
|
+
field_name="has_user_account", label="Has User Account", method=filter_has_user_account
|
|
43
|
+
)
|
|
44
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
from collections import defaultdict
|
|
2
|
+
from datetime import date, datetime
|
|
3
|
+
from decimal import Decimal
|
|
4
|
+
from functools import reduce
|
|
5
|
+
from typing import Iterable
|
|
6
|
+
|
|
7
|
+
from django.db.models import QuerySet, Sum
|
|
8
|
+
from wbaccounting.generators.base import AbstractBookingEntryGenerator
|
|
9
|
+
from wbaccounting.models import BookingEntry
|
|
10
|
+
from wbcommission.models import Rebate
|
|
11
|
+
from wbcore.contrib.directory.models import Entry
|
|
12
|
+
from wbportfolio.models import Product
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class RebateGenerator(AbstractBookingEntryGenerator):
|
|
16
|
+
TITLE = "Rebate Generation"
|
|
17
|
+
|
|
18
|
+
@staticmethod
|
|
19
|
+
def generate_booking_entries(from_date: date, to_date: date, counterparty: Entry) -> Iterable[BookingEntry]:
|
|
20
|
+
rebates = Rebate.objects.filter(recipient=counterparty, date__gte=from_date, date__lte=to_date)
|
|
21
|
+
|
|
22
|
+
for product_id in rebates.distinct("product_id").values_list("product_id", flat=True):
|
|
23
|
+
product = Product.objects.get(id=product_id)
|
|
24
|
+
|
|
25
|
+
for rebate_key, rebate_title in (("management", "Management"), ("performance", "Performance")):
|
|
26
|
+
rebate_sum = (
|
|
27
|
+
rebates.filter(product_id=product_id, commission_type__key=rebate_key)
|
|
28
|
+
.aggregate(sum=Sum("value"))
|
|
29
|
+
.get("sum")
|
|
30
|
+
or 0.0
|
|
31
|
+
)
|
|
32
|
+
if rebate_sum > 0:
|
|
33
|
+
yield BookingEntry(
|
|
34
|
+
title=f"{product.name} {product.isin} {rebate_title} Fees",
|
|
35
|
+
booking_date=date.today(),
|
|
36
|
+
reference_date=to_date,
|
|
37
|
+
gross_value=Decimal(-1 * rebate_sum),
|
|
38
|
+
vat=Decimal(counterparty.entry_accounting_information.vat) or Decimal(0.0),
|
|
39
|
+
currency=product.currency,
|
|
40
|
+
counterparty=counterparty,
|
|
41
|
+
parameters={
|
|
42
|
+
"from_date": from_date.strftime("%d.%m.%Y"),
|
|
43
|
+
"to_date": to_date.strftime("%d.%m.%Y"),
|
|
44
|
+
},
|
|
45
|
+
backlinks={
|
|
46
|
+
"comm-rebates": {
|
|
47
|
+
"title": "Rebates",
|
|
48
|
+
"reverse": "wbcommission:rebatetable-list",
|
|
49
|
+
"parameters": {
|
|
50
|
+
"date": f'{from_date.strftime("%Y-%m-%d")},{to_date.strftime("%Y-%m-%d")}',
|
|
51
|
+
"product": product_id,
|
|
52
|
+
"recipient": counterparty.id,
|
|
53
|
+
"group_by": "ACCOUNT",
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
@staticmethod
|
|
60
|
+
def _compare(d1: str, d2: str) -> str:
|
|
61
|
+
d1_lb, d1_ub = d1.split(",")
|
|
62
|
+
d2_lb, d2_ub = d2.split(",")
|
|
63
|
+
|
|
64
|
+
lb = min(datetime.strptime(d1_lb, "%Y-%m-%d"), datetime.strptime(d2_lb, "%Y-%m-%d"))
|
|
65
|
+
ub = max(datetime.strptime(d1_ub, "%Y-%m-%d"), datetime.strptime(d2_ub, "%Y-%m-%d"))
|
|
66
|
+
|
|
67
|
+
return f"{lb.strftime('%Y-%m-%d')},{ub.strftime('%Y-%m-%d')}"
|
|
68
|
+
|
|
69
|
+
@staticmethod
|
|
70
|
+
def _merge_key_into_dict(d: dict, key: str, value: dict) -> dict:
|
|
71
|
+
d[key]["title"] = value["title"]
|
|
72
|
+
d[key]["reverse"] = value["reverse"]
|
|
73
|
+
if "recipient" in value["parameters"]:
|
|
74
|
+
d[key]["parameters"]["recipient"] = value["parameters"]["recipient"]
|
|
75
|
+
d[key]["parameters"]["date"] = RebateGenerator._compare(
|
|
76
|
+
d[key]["parameters"].get("date", "9999-12-31,1111-01-01"), value["parameters"]["date"]
|
|
77
|
+
)
|
|
78
|
+
return d
|
|
79
|
+
|
|
80
|
+
@staticmethod
|
|
81
|
+
def _iter_backlinks(booking_entries: QuerySet) -> Iterable:
|
|
82
|
+
for backlink in booking_entries:
|
|
83
|
+
yield from backlink.items()
|
|
84
|
+
|
|
85
|
+
@staticmethod
|
|
86
|
+
def merge_backlinks(booking_entries: QuerySet[BookingEntry]) -> dict:
|
|
87
|
+
return reduce(
|
|
88
|
+
lambda d, v: RebateGenerator._merge_key_into_dict(d, *v),
|
|
89
|
+
RebateGenerator._iter_backlinks(
|
|
90
|
+
booking_entries.filter(backlinks__isnull=False).values_list("backlinks", flat=True)
|
|
91
|
+
),
|
|
92
|
+
defaultdict(lambda: dict(parameters=dict())),
|
|
93
|
+
)
|
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
# Generated by Django 4.1.9 on 2023-06-28 12:21
|
|
2
|
+
|
|
3
|
+
from decimal import Decimal
|
|
4
|
+
|
|
5
|
+
import django.db.models.deletion
|
|
6
|
+
import wbcommission.models.rebate
|
|
7
|
+
from django.contrib.postgres.operations import BtreeGistExtension
|
|
8
|
+
from django.db import migrations, models
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class Migration(migrations.Migration):
|
|
12
|
+
initial = True
|
|
13
|
+
|
|
14
|
+
dependencies = [
|
|
15
|
+
("wbcrm", "0005_account_accountrole_accountroletype_and_more"),
|
|
16
|
+
("wbportfolio", "0046_add_product_default_account"),
|
|
17
|
+
("directory", "0004_entry_is_draft_entry"),
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
operations = [
|
|
21
|
+
BtreeGistExtension(),
|
|
22
|
+
migrations.CreateModel(
|
|
23
|
+
name="AccountCustomer",
|
|
24
|
+
fields=[
|
|
25
|
+
(
|
|
26
|
+
"id",
|
|
27
|
+
models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID"),
|
|
28
|
+
),
|
|
29
|
+
(
|
|
30
|
+
"customer_type",
|
|
31
|
+
models.CharField(
|
|
32
|
+
choices=[("DIRECT", "Direct"), ("INDIRECT", "Indirect")],
|
|
33
|
+
default="DIRECT",
|
|
34
|
+
max_length=16,
|
|
35
|
+
verbose_name="Customer Type",
|
|
36
|
+
),
|
|
37
|
+
),
|
|
38
|
+
(
|
|
39
|
+
"account",
|
|
40
|
+
models.ForeignKey(
|
|
41
|
+
on_delete=django.db.models.deletion.CASCADE,
|
|
42
|
+
related_name="customers",
|
|
43
|
+
to="wbcrm.account",
|
|
44
|
+
verbose_name="Account",
|
|
45
|
+
),
|
|
46
|
+
),
|
|
47
|
+
(
|
|
48
|
+
"entry",
|
|
49
|
+
models.ForeignKey(
|
|
50
|
+
on_delete=django.db.models.deletion.CASCADE,
|
|
51
|
+
related_name="account_customers",
|
|
52
|
+
to="directory.entry",
|
|
53
|
+
verbose_name="Customer",
|
|
54
|
+
),
|
|
55
|
+
),
|
|
56
|
+
],
|
|
57
|
+
options={
|
|
58
|
+
"verbose_name": "Customer",
|
|
59
|
+
"verbose_name_plural": "Customers",
|
|
60
|
+
"notification_types": [
|
|
61
|
+
(
|
|
62
|
+
"wbcommission.accountcustomer.notify",
|
|
63
|
+
"New Account Customer Notification",
|
|
64
|
+
"Notifies you when a new account customer has been created",
|
|
65
|
+
True,
|
|
66
|
+
True,
|
|
67
|
+
False,
|
|
68
|
+
)
|
|
69
|
+
],
|
|
70
|
+
},
|
|
71
|
+
),
|
|
72
|
+
migrations.CreateModel(
|
|
73
|
+
name="CommissionAccountRole",
|
|
74
|
+
fields=[
|
|
75
|
+
(
|
|
76
|
+
"id",
|
|
77
|
+
models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID"),
|
|
78
|
+
),
|
|
79
|
+
(
|
|
80
|
+
"role_type",
|
|
81
|
+
models.CharField(
|
|
82
|
+
choices=[("SALES", "Sales"), ("CUSTOMER_MANAGER", "Customer Manager")],
|
|
83
|
+
default="SALES",
|
|
84
|
+
max_length=16,
|
|
85
|
+
verbose_name="Role Type",
|
|
86
|
+
),
|
|
87
|
+
),
|
|
88
|
+
("start", models.DateField(blank=True, null=True, verbose_name="Start")),
|
|
89
|
+
("end", models.DateField(blank=True, null=True, verbose_name="End")),
|
|
90
|
+
("weighting", models.FloatField(default=1, verbose_name="Weight")),
|
|
91
|
+
(
|
|
92
|
+
"account",
|
|
93
|
+
models.ForeignKey(
|
|
94
|
+
on_delete=django.db.models.deletion.CASCADE,
|
|
95
|
+
related_name="rebates_roles",
|
|
96
|
+
to="wbcrm.account",
|
|
97
|
+
verbose_name="Account Rebate roles",
|
|
98
|
+
),
|
|
99
|
+
),
|
|
100
|
+
(
|
|
101
|
+
"customer",
|
|
102
|
+
models.ForeignKey(
|
|
103
|
+
on_delete=django.db.models.deletion.CASCADE,
|
|
104
|
+
related_name="rebate_account_customer",
|
|
105
|
+
to="directory.entry",
|
|
106
|
+
verbose_name="Customer",
|
|
107
|
+
),
|
|
108
|
+
),
|
|
109
|
+
(
|
|
110
|
+
"person",
|
|
111
|
+
models.ForeignKey(
|
|
112
|
+
on_delete=django.db.models.deletion.PROTECT,
|
|
113
|
+
related_name="rebates_account_roles",
|
|
114
|
+
to="directory.person",
|
|
115
|
+
verbose_name="Person",
|
|
116
|
+
),
|
|
117
|
+
),
|
|
118
|
+
],
|
|
119
|
+
options={
|
|
120
|
+
"verbose_name": "Commission Account Role",
|
|
121
|
+
"verbose_name_plural": "Commission Account Roles",
|
|
122
|
+
},
|
|
123
|
+
),
|
|
124
|
+
migrations.CreateModel(
|
|
125
|
+
name="AccountCustomerCommissionConstraint",
|
|
126
|
+
fields=[
|
|
127
|
+
(
|
|
128
|
+
"id",
|
|
129
|
+
models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID"),
|
|
130
|
+
),
|
|
131
|
+
("start_year", models.PositiveIntegerField(blank=True, null=True)),
|
|
132
|
+
("end_year", models.PositiveIntegerField(blank=True, null=True)),
|
|
133
|
+
("payable_year_end", models.BooleanField(default=True)),
|
|
134
|
+
("payable_continous", models.BooleanField(default=True)),
|
|
135
|
+
(
|
|
136
|
+
"customer",
|
|
137
|
+
models.ForeignKey(
|
|
138
|
+
on_delete=django.db.models.deletion.CASCADE,
|
|
139
|
+
related_name="constraints",
|
|
140
|
+
to="wbcommission.accountcustomer",
|
|
141
|
+
),
|
|
142
|
+
),
|
|
143
|
+
(
|
|
144
|
+
"products",
|
|
145
|
+
models.ManyToManyField(blank=True, related_name="customer_constraints", to="wbportfolio.product"),
|
|
146
|
+
),
|
|
147
|
+
],
|
|
148
|
+
options={
|
|
149
|
+
"verbose_name": "Account Customer Commission Constraint",
|
|
150
|
+
"verbose_name_plural": "Account Customer Commission Constrains",
|
|
151
|
+
},
|
|
152
|
+
),
|
|
153
|
+
migrations.CreateModel(
|
|
154
|
+
name="Rebate",
|
|
155
|
+
fields=[
|
|
156
|
+
(
|
|
157
|
+
"id",
|
|
158
|
+
models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID"),
|
|
159
|
+
),
|
|
160
|
+
("date", models.DateField(verbose_name="Date")),
|
|
161
|
+
(
|
|
162
|
+
"management_fees",
|
|
163
|
+
models.DecimalField(
|
|
164
|
+
decimal_places=4, default=Decimal(0.0), max_digits=16, verbose_name="Management Fees"
|
|
165
|
+
),
|
|
166
|
+
),
|
|
167
|
+
(
|
|
168
|
+
"pre_performance_fees",
|
|
169
|
+
models.DecimalField(
|
|
170
|
+
decimal_places=4,
|
|
171
|
+
default=Decimal(0.0),
|
|
172
|
+
max_digits=16,
|
|
173
|
+
verbose_name="Pre Performance Fees",
|
|
174
|
+
),
|
|
175
|
+
),
|
|
176
|
+
(
|
|
177
|
+
"performance_fees",
|
|
178
|
+
models.DecimalField(
|
|
179
|
+
decimal_places=4, default=Decimal(0.0), max_digits=16, verbose_name="Performance Fees"
|
|
180
|
+
),
|
|
181
|
+
),
|
|
182
|
+
(
|
|
183
|
+
"account",
|
|
184
|
+
models.ForeignKey(
|
|
185
|
+
on_delete=django.db.models.deletion.CASCADE,
|
|
186
|
+
related_name="rebates",
|
|
187
|
+
to="wbcrm.account",
|
|
188
|
+
verbose_name="Account",
|
|
189
|
+
),
|
|
190
|
+
),
|
|
191
|
+
(
|
|
192
|
+
"product",
|
|
193
|
+
models.ForeignKey(
|
|
194
|
+
on_delete=django.db.models.deletion.CASCADE,
|
|
195
|
+
related_name="rebates",
|
|
196
|
+
to="wbportfolio.product",
|
|
197
|
+
verbose_name="Product",
|
|
198
|
+
),
|
|
199
|
+
),
|
|
200
|
+
(
|
|
201
|
+
"recipient",
|
|
202
|
+
models.ForeignKey(
|
|
203
|
+
on_delete=django.db.models.deletion.PROTECT,
|
|
204
|
+
related_name="recipient_rebates",
|
|
205
|
+
to="directory.entry",
|
|
206
|
+
verbose_name="Recipient",
|
|
207
|
+
),
|
|
208
|
+
),
|
|
209
|
+
],
|
|
210
|
+
options={
|
|
211
|
+
"verbose_name": "Rebate",
|
|
212
|
+
"verbose_name_plural": "Rebates",
|
|
213
|
+
"unique_together": {("date", "recipient", "account", "product")},
|
|
214
|
+
"index_together": {("date", "recipient", "product")},
|
|
215
|
+
},
|
|
216
|
+
bases=(wbcommission.models.rebate.BookingEntryCalculatedValueMixin, models.Model),
|
|
217
|
+
),
|
|
218
|
+
migrations.CreateModel(
|
|
219
|
+
name="Commission",
|
|
220
|
+
fields=[
|
|
221
|
+
(
|
|
222
|
+
"id",
|
|
223
|
+
models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID"),
|
|
224
|
+
),
|
|
225
|
+
(
|
|
226
|
+
"role_recipient",
|
|
227
|
+
models.CharField(
|
|
228
|
+
blank=True,
|
|
229
|
+
choices=[
|
|
230
|
+
("portfolio_managers", "Portfolio Managers"),
|
|
231
|
+
("analysts", "Analysts"),
|
|
232
|
+
("sales", "Sales"),
|
|
233
|
+
("customer_managers", "Customer Managers"),
|
|
234
|
+
],
|
|
235
|
+
max_length=32,
|
|
236
|
+
null=True,
|
|
237
|
+
verbose_name="Recipient Role",
|
|
238
|
+
),
|
|
239
|
+
),
|
|
240
|
+
("order", models.IntegerField()),
|
|
241
|
+
(
|
|
242
|
+
"commission_type",
|
|
243
|
+
models.CharField(
|
|
244
|
+
choices=[
|
|
245
|
+
("net_management_fees", "Net Management Fees"),
|
|
246
|
+
("gross_management_fees", "Gross Management Fees"),
|
|
247
|
+
("net_performance_fees", "Net Performance Fees"),
|
|
248
|
+
("gross_performance_fees", "Gross Performance Fees"),
|
|
249
|
+
],
|
|
250
|
+
default="net_management_fees",
|
|
251
|
+
max_length=32,
|
|
252
|
+
verbose_name="Type",
|
|
253
|
+
),
|
|
254
|
+
),
|
|
255
|
+
("start_date", models.DateField(verbose_name="Start Date")),
|
|
256
|
+
(
|
|
257
|
+
"minimum_assets_under_management",
|
|
258
|
+
models.IntegerField(default=0, verbose_name="Minimum AUM"),
|
|
259
|
+
),
|
|
260
|
+
("percent", models.FloatField(verbose_name="Percent")),
|
|
261
|
+
(
|
|
262
|
+
"account",
|
|
263
|
+
models.ForeignKey(
|
|
264
|
+
limit_choices_to=models.Q(("is_terminal_account", True)),
|
|
265
|
+
on_delete=django.db.models.deletion.CASCADE,
|
|
266
|
+
related_name="account_commissions",
|
|
267
|
+
to="wbcrm.account",
|
|
268
|
+
verbose_name="Account",
|
|
269
|
+
),
|
|
270
|
+
),
|
|
271
|
+
(
|
|
272
|
+
"crm_recipient",
|
|
273
|
+
models.ForeignKey(
|
|
274
|
+
blank=True,
|
|
275
|
+
null=True,
|
|
276
|
+
on_delete=django.db.models.deletion.PROTECT,
|
|
277
|
+
related_name="recipient_commissions",
|
|
278
|
+
to="directory.entry",
|
|
279
|
+
verbose_name="Recipient",
|
|
280
|
+
),
|
|
281
|
+
),
|
|
282
|
+
],
|
|
283
|
+
options={
|
|
284
|
+
"verbose_name": "Commission",
|
|
285
|
+
"verbose_name_plural": "Commissions",
|
|
286
|
+
"unique_together": {
|
|
287
|
+
(
|
|
288
|
+
"account",
|
|
289
|
+
"order",
|
|
290
|
+
"crm_recipient",
|
|
291
|
+
"role_recipient",
|
|
292
|
+
"start_date",
|
|
293
|
+
"commission_type",
|
|
294
|
+
"minimum_assets_under_management",
|
|
295
|
+
)
|
|
296
|
+
},
|
|
297
|
+
},
|
|
298
|
+
),
|
|
299
|
+
]
|