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,299 @@
|
|
|
1
|
+
from datetime import date
|
|
2
|
+
from decimal import Decimal
|
|
3
|
+
from io import BytesIO
|
|
4
|
+
|
|
5
|
+
import xlsxwriter
|
|
6
|
+
from celery import shared_task
|
|
7
|
+
from django.db.models import Case, F, Sum, When
|
|
8
|
+
from wbcommission.models import CommissionType, Rebate
|
|
9
|
+
from wbcore.contrib.authentication.models import User
|
|
10
|
+
from wbcore.contrib.currency.models import CurrencyFXRates
|
|
11
|
+
from wbcore.contrib.directory.models import Entry
|
|
12
|
+
from wbcrm.models.accounts import (
|
|
13
|
+
Account,
|
|
14
|
+
AccountRole,
|
|
15
|
+
AccountRoleType,
|
|
16
|
+
AccountRoleValidity,
|
|
17
|
+
)
|
|
18
|
+
from wbfdm.models import InstrumentPrice
|
|
19
|
+
from wbportfolio.models.transactions.claim import Claim
|
|
20
|
+
from xlsxwriter.utility import xl_rowcol_to_cell
|
|
21
|
+
|
|
22
|
+
from .utils import create_report_and_send
|
|
23
|
+
|
|
24
|
+
AccountCustomer = None
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@shared_task(queue="commission")
|
|
28
|
+
def create_customer_report_and_send_as_task(user_id: int, recipient_id: int, start_date: date, end_date: date):
|
|
29
|
+
user = User.objects.get(id=user_id)
|
|
30
|
+
recipient = Entry.objects.get(id=recipient_id)
|
|
31
|
+
create_report_and_send(user, recipient, start_date, end_date, create_report)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def create_report(user, customer, start_date, end_date):
|
|
35
|
+
output = BytesIO()
|
|
36
|
+
workbook = xlsxwriter.Workbook(output, {"in_memory": True})
|
|
37
|
+
|
|
38
|
+
base_format = workbook.add_format({"font_name": "Liberation Sans", "font_size": 10})
|
|
39
|
+
bold_format = workbook.add_format({"font_name": "Liberation Sans", "font_size": 10, "bold": True})
|
|
40
|
+
decimal_format = workbook.add_format({"font_name": "Liberation Sans", "font_size": 10, "num_format": "#,##.00"})
|
|
41
|
+
bold_decimal_format = workbook.add_format(
|
|
42
|
+
{"font_name": "Liberation Sans", "font_size": 10, "num_format": "#,##.00", "bold": True}
|
|
43
|
+
)
|
|
44
|
+
percent_format = workbook.add_format({"font_name": "Liberation Sans", "font_size": 10, "num_format": "#,##.00 %"})
|
|
45
|
+
bold_percent_format = workbook.add_format(
|
|
46
|
+
{"font_name": "Liberation Sans", "font_size": 10, "num_format": "#,##.00 %", "bold": True}
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
# HERE STARTS THE FIRST WORKSHEET
|
|
50
|
+
worksheet_products = workbook.add_worksheet(f"Products ({end_date:%d.%m.%Y})")
|
|
51
|
+
|
|
52
|
+
worksheet_products.write_string(0, 0, "Product", bold_format)
|
|
53
|
+
worksheet_products.write_string(0, 1, "Currency", bold_format)
|
|
54
|
+
worksheet_products.write_string(0, 2, "Price", bold_format)
|
|
55
|
+
worksheet_products.write_string(0, 3, "Price USD", bold_format)
|
|
56
|
+
worksheet_products.write_string(0, 4, "Direct Assets", bold_format)
|
|
57
|
+
worksheet_products.write_string(0, 5, "Direct Assets USD", bold_format)
|
|
58
|
+
worksheet_products.write_string(0, 6, "Indirect Assets", bold_format)
|
|
59
|
+
worksheet_products.write_string(0, 7, "Indirect Assets USD", bold_format)
|
|
60
|
+
worksheet_products.write_string(0, 8, "Total Assets USD", bold_format)
|
|
61
|
+
|
|
62
|
+
worksheet_products.write_formula(1, 5, "=SUM(F4:F99999)", bold_decimal_format)
|
|
63
|
+
worksheet_products.write_formula(1, 7, "=SUM(H4:H99999)", bold_decimal_format)
|
|
64
|
+
worksheet_products.write_formula(1, 8, "=SUM(I4:I99999)", bold_decimal_format)
|
|
65
|
+
|
|
66
|
+
fx_rates = CurrencyFXRates.get_fx_rates_subquery(end_date)
|
|
67
|
+
customer_accounts = Account.get_accounts_for_customer(customer)
|
|
68
|
+
valid_customer_roles = (
|
|
69
|
+
AccountRole.objects.filter(entry=customer)
|
|
70
|
+
.annotate(is_currently_valid=AccountRoleValidity.get_role_validity_subquery(date.today()))
|
|
71
|
+
.filter(is_currently_valid=True)
|
|
72
|
+
)
|
|
73
|
+
account_types = AccountRoleType.objects.filter(id__in=valid_customer_roles.values("role_type"))
|
|
74
|
+
|
|
75
|
+
user_claims = Claim.objects.filter_for_customer(customer, include_related_roles=True).filter_for_user(
|
|
76
|
+
user, validity_date=date.today()
|
|
77
|
+
)
|
|
78
|
+
products = (
|
|
79
|
+
user_claims.filter(
|
|
80
|
+
date__lte=end_date,
|
|
81
|
+
status=Claim.Status.APPROVED,
|
|
82
|
+
)
|
|
83
|
+
.annotate(
|
|
84
|
+
owner_asset_shares=Case(When(account__in=customer_accounts, then=F("shares")), default=Decimal(0)),
|
|
85
|
+
**{
|
|
86
|
+
f"{account_type.key}_assets_shares": Case(
|
|
87
|
+
When(
|
|
88
|
+
account__in=valid_customer_roles.filter(role_type=account_type).values("account"),
|
|
89
|
+
then=F("shares"),
|
|
90
|
+
),
|
|
91
|
+
default=Decimal(0),
|
|
92
|
+
)
|
|
93
|
+
for account_type in account_types
|
|
94
|
+
},
|
|
95
|
+
)
|
|
96
|
+
.values("product")
|
|
97
|
+
.annotate(
|
|
98
|
+
price=InstrumentPrice.subquery_closest_value(
|
|
99
|
+
"net_value", val_date=end_date, instrument_pk_name="product__pk"
|
|
100
|
+
),
|
|
101
|
+
product_id=F("product_id"),
|
|
102
|
+
title=F("product__name"),
|
|
103
|
+
currency=F("product__currency__symbol"),
|
|
104
|
+
sum_shares=Sum("shares"),
|
|
105
|
+
fx_rate=fx_rates,
|
|
106
|
+
sum_owner_assets_shares=Sum("owner_asset_shares"),
|
|
107
|
+
**{
|
|
108
|
+
f"sum_{account_type.key}_assets_shares": Sum(f"{account_type.key}_assets_shares")
|
|
109
|
+
for account_type in account_types
|
|
110
|
+
},
|
|
111
|
+
)
|
|
112
|
+
.order_by("title")
|
|
113
|
+
)
|
|
114
|
+
for row, product in enumerate(products, start=3):
|
|
115
|
+
product_cell = xl_rowcol_to_cell(row, 0)
|
|
116
|
+
currency_cell = xl_rowcol_to_cell(row, 1)
|
|
117
|
+
price_cell = xl_rowcol_to_cell(row, 2)
|
|
118
|
+
price_usd_cell = xl_rowcol_to_cell(row, 3)
|
|
119
|
+
owner_assets_cell = xl_rowcol_to_cell(row, 4)
|
|
120
|
+
owner_assets_usd_cell = xl_rowcol_to_cell(row, 5)
|
|
121
|
+
account_role_cells = dict()
|
|
122
|
+
account_role_cells_usd = dict()
|
|
123
|
+
index = 6
|
|
124
|
+
for account_type in account_types:
|
|
125
|
+
account_role_cells[account_type.key] = xl_rowcol_to_cell(row, index)
|
|
126
|
+
account_role_cells_usd[account_type.key] = xl_rowcol_to_cell(row, index + 1)
|
|
127
|
+
index += 2
|
|
128
|
+
total_assets_usd_cell = xl_rowcol_to_cell(row, index)
|
|
129
|
+
|
|
130
|
+
worksheet_products.write_string(product_cell, product["title"], base_format)
|
|
131
|
+
worksheet_products.write_string(currency_cell, product["currency"], base_format)
|
|
132
|
+
worksheet_products.write_number(price_cell, product["price"], decimal_format)
|
|
133
|
+
worksheet_products.write_formula(price_usd_cell, f"{price_cell} * {product['fx_rate']}", decimal_format)
|
|
134
|
+
|
|
135
|
+
worksheet_products.write_formula(
|
|
136
|
+
owner_assets_cell, f"{product['sum_owner_assets_shares']} * {price_cell}", decimal_format
|
|
137
|
+
)
|
|
138
|
+
worksheet_products.write_formula(
|
|
139
|
+
owner_assets_usd_cell, f"{owner_assets_cell} * {product['fx_rate']}", decimal_format
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
for account_type in account_types:
|
|
143
|
+
worksheet_products.write_formula(
|
|
144
|
+
account_role_cells[account_type.key],
|
|
145
|
+
f"{product[f'sum_{account_type.key}_assets_shares']} * {price_cell}",
|
|
146
|
+
decimal_format,
|
|
147
|
+
)
|
|
148
|
+
worksheet_products.write_formula(
|
|
149
|
+
account_role_cells_usd[account_type.key],
|
|
150
|
+
f"{account_role_cells[account_type.key]} * {product['fx_rate']}",
|
|
151
|
+
decimal_format,
|
|
152
|
+
)
|
|
153
|
+
worksheet_products.write_formula(
|
|
154
|
+
total_assets_usd_cell,
|
|
155
|
+
" + ".join([owner_assets_usd_cell] + list(account_role_cells_usd.values())),
|
|
156
|
+
decimal_format,
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
# HERE STARTS THE SECOND WORKSHEET
|
|
160
|
+
worksheet_trade_performance = workbook.add_worksheet(f"Trades ({end_date:%d.%m.%Y})")
|
|
161
|
+
|
|
162
|
+
worksheet_trade_performance.write_string(0, 0, "Trade Date", bold_format)
|
|
163
|
+
worksheet_trade_performance.write_string(0, 1, "Shares", bold_format)
|
|
164
|
+
worksheet_trade_performance.write_string(0, 2, "Bank", bold_format)
|
|
165
|
+
worksheet_trade_performance.write_string(0, 3, "Root Account", bold_format)
|
|
166
|
+
worksheet_trade_performance.write_string(0, 4, "Account", bold_format)
|
|
167
|
+
worksheet_trade_performance.write_string(0, 5, "Product", bold_format)
|
|
168
|
+
worksheet_trade_performance.write_string(0, 6, "Price (Trade Date)", bold_format)
|
|
169
|
+
worksheet_trade_performance.write_string(0, 7, "Net Value (Trade Date)", bold_format)
|
|
170
|
+
worksheet_trade_performance.write_string(0, 8, "Net Value (Trade Date) USD", bold_format)
|
|
171
|
+
worksheet_trade_performance.write_string(0, 9, f"Price ({end_date:%d.%m.%Y})", bold_format)
|
|
172
|
+
worksheet_trade_performance.write_string(0, 10, f"Net Value ({end_date:%d.%m.%Y})", bold_format)
|
|
173
|
+
worksheet_trade_performance.write_string(0, 11, f"Net Value ({end_date:%d.%m.%Y}) USD", bold_format)
|
|
174
|
+
worksheet_trade_performance.write_string(0, 12, "Performance", bold_format)
|
|
175
|
+
|
|
176
|
+
worksheet_trade_performance.write_formula(1, 8, "=SUM(I4:I99999)", bold_decimal_format)
|
|
177
|
+
worksheet_trade_performance.write_formula(1, 11, "=SUM(L4:L99999)", bold_decimal_format)
|
|
178
|
+
worksheet_trade_performance.write_formula(1, 12, "=(L2/I2)-1", bold_percent_format)
|
|
179
|
+
|
|
180
|
+
fx_rates_date = CurrencyFXRates.get_fx_rates_subquery("date")
|
|
181
|
+
|
|
182
|
+
fx_rates_end = CurrencyFXRates.get_fx_rates_subquery(end_date)
|
|
183
|
+
|
|
184
|
+
claims = (
|
|
185
|
+
user_claims.filter(
|
|
186
|
+
date__lte=end_date,
|
|
187
|
+
status=Claim.Status.APPROVED,
|
|
188
|
+
)
|
|
189
|
+
.annotate(
|
|
190
|
+
price_date=InstrumentPrice.subquery_closest_value("net_value", instrument_pk_name="product__pk"),
|
|
191
|
+
price_end=InstrumentPrice.subquery_closest_value(
|
|
192
|
+
"net_value", val_date=end_date, instrument_pk_name="product__pk"
|
|
193
|
+
),
|
|
194
|
+
fx_rates_date=fx_rates_date,
|
|
195
|
+
fx_rates_end=fx_rates_end,
|
|
196
|
+
)
|
|
197
|
+
.order_by("date")
|
|
198
|
+
).select_related("product", "account")
|
|
199
|
+
|
|
200
|
+
for row, claim in enumerate(claims, start=3):
|
|
201
|
+
trade_date_cell = xl_rowcol_to_cell(row, 0)
|
|
202
|
+
shares_cell = xl_rowcol_to_cell(row, 1)
|
|
203
|
+
bank_cell = xl_rowcol_to_cell(row, 2)
|
|
204
|
+
root_account_cell = xl_rowcol_to_cell(row, 3)
|
|
205
|
+
account_cell = xl_rowcol_to_cell(row, 4)
|
|
206
|
+
product_cell = xl_rowcol_to_cell(row, 5)
|
|
207
|
+
price_trade_cell = xl_rowcol_to_cell(row, 6)
|
|
208
|
+
net_value_trade_cell = xl_rowcol_to_cell(row, 7)
|
|
209
|
+
net_value_usd_trade_cell = xl_rowcol_to_cell(row, 8)
|
|
210
|
+
price_end_cell = xl_rowcol_to_cell(row, 9)
|
|
211
|
+
net_value_end_cell = xl_rowcol_to_cell(row, 10)
|
|
212
|
+
net_value_usd_end_cell = xl_rowcol_to_cell(row, 11)
|
|
213
|
+
performance_cell = xl_rowcol_to_cell(row, 12)
|
|
214
|
+
|
|
215
|
+
worksheet_trade_performance.write_string(trade_date_cell, f"{claim.date:%d.%m.%Y}", base_format)
|
|
216
|
+
worksheet_trade_performance.write_number(shares_cell, claim.shares, decimal_format)
|
|
217
|
+
worksheet_trade_performance.write_string(bank_cell, claim.bank, base_format)
|
|
218
|
+
worksheet_trade_performance.write_string(root_account_cell, claim.account.get_root().title, base_format)
|
|
219
|
+
worksheet_trade_performance.write_string(account_cell, str(claim.account), base_format)
|
|
220
|
+
worksheet_trade_performance.write_string(product_cell, claim.product.title, base_format)
|
|
221
|
+
|
|
222
|
+
worksheet_trade_performance.write_number(
|
|
223
|
+
price_trade_cell, claim.price_date or claim.product.share_price, decimal_format
|
|
224
|
+
)
|
|
225
|
+
worksheet_trade_performance.write_formula(
|
|
226
|
+
net_value_trade_cell, f"={shares_cell}*{price_trade_cell}", decimal_format
|
|
227
|
+
)
|
|
228
|
+
worksheet_trade_performance.write_formula(
|
|
229
|
+
net_value_usd_trade_cell, f"={net_value_trade_cell}*{claim.fx_rates_date}", decimal_format
|
|
230
|
+
)
|
|
231
|
+
|
|
232
|
+
worksheet_trade_performance.write_number(price_end_cell, claim.price_end, decimal_format)
|
|
233
|
+
worksheet_trade_performance.write_formula(
|
|
234
|
+
net_value_end_cell, f"={shares_cell}*{price_end_cell}", decimal_format
|
|
235
|
+
)
|
|
236
|
+
worksheet_trade_performance.write_formula(
|
|
237
|
+
net_value_usd_end_cell, f"={net_value_end_cell}*{claim.fx_rates_end}", decimal_format
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
worksheet_trade_performance.write_formula(
|
|
241
|
+
performance_cell, f"=({net_value_usd_end_cell}/{net_value_usd_trade_cell})-1", percent_format
|
|
242
|
+
)
|
|
243
|
+
|
|
244
|
+
# HERE STARTS THE THIRD WORKSHEET
|
|
245
|
+
worksheet_rebates = workbook.add_worksheet(f"Rebates ({start_date:%d.%m.%Y}-{end_date:%d.%m.%Y})")
|
|
246
|
+
|
|
247
|
+
worksheet_rebates.write_string(0, 0, "Date", bold_format)
|
|
248
|
+
worksheet_rebates.write_string(0, 1, "Product", bold_format)
|
|
249
|
+
worksheet_rebates.write_string(0, 2, "Account", bold_format)
|
|
250
|
+
for index, commission_type in enumerate(CommissionType.objects.all()):
|
|
251
|
+
worksheet_rebates.write_string(0, index + 3, f"{commission_type.name.title()} Fees", bold_format)
|
|
252
|
+
worksheet_rebates.write_formula(
|
|
253
|
+
1, index + 3, f"=SUM({chr(ord('@') + (index + 3))}3]:E99999)", bold_decimal_format
|
|
254
|
+
)
|
|
255
|
+
|
|
256
|
+
rebates_for_user = Rebate.objects.filter_for_user(user, validity_date=date.today())
|
|
257
|
+
rebates = (
|
|
258
|
+
rebates_for_user.filter(recipient=customer, date__gte=start_date, date__lte=end_date)
|
|
259
|
+
.select_related("account", "product")
|
|
260
|
+
.annotate(
|
|
261
|
+
**{
|
|
262
|
+
f"{commission_type.key}__value": Case(
|
|
263
|
+
When(commission_type=commission_type, then=F("value")), default=Decimal(0)
|
|
264
|
+
)
|
|
265
|
+
for commission_type in CommissionType.objects.all()
|
|
266
|
+
}
|
|
267
|
+
)
|
|
268
|
+
.values("date", "account", "product")
|
|
269
|
+
.annotate(
|
|
270
|
+
product_title=F("product__name"),
|
|
271
|
+
account_id=F("account__id"),
|
|
272
|
+
account_title=F("account__computed_str"),
|
|
273
|
+
**{
|
|
274
|
+
f"sum_{commission_type.key}_value": Sum(f"{commission_type.key}__value")
|
|
275
|
+
for commission_type in CommissionType.objects.all()
|
|
276
|
+
},
|
|
277
|
+
)
|
|
278
|
+
.order_by("date", "product", "account_id")
|
|
279
|
+
)
|
|
280
|
+
|
|
281
|
+
for row, rebate in enumerate(rebates, start=4):
|
|
282
|
+
date_cell = xl_rowcol_to_cell(row, 0)
|
|
283
|
+
product_cell = xl_rowcol_to_cell(row, 1)
|
|
284
|
+
account_cell = xl_rowcol_to_cell(row, 2)
|
|
285
|
+
|
|
286
|
+
worksheet_rebates.write_string(date_cell, f"{rebate['date']:%d.%m.%Y}", base_format)
|
|
287
|
+
worksheet_rebates.write_string(product_cell, rebate["product_title"], base_format)
|
|
288
|
+
worksheet_rebates.write_string(account_cell, rebate["account_title"], base_format)
|
|
289
|
+
for index, commission_type in enumerate(CommissionType.objects.all()):
|
|
290
|
+
cell = xl_rowcol_to_cell(row, 3 + index)
|
|
291
|
+
worksheet_rebates.write_number(cell, rebate[f"sum_{commission_type.key}_value"], decimal_format)
|
|
292
|
+
|
|
293
|
+
workbook.close()
|
|
294
|
+
output.seek(0)
|
|
295
|
+
return (
|
|
296
|
+
output,
|
|
297
|
+
"customer_report_{}_{}_{}.xlsx".format(customer.computed_str, start_date, end_date),
|
|
298
|
+
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
299
|
+
)
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
from contextlib import suppress
|
|
2
|
+
from datetime import date
|
|
3
|
+
|
|
4
|
+
from django.core.mail import EmailMultiAlternatives
|
|
5
|
+
from django.template.loader import get_template
|
|
6
|
+
from wbcore.contrib.authentication.models import User
|
|
7
|
+
from wbcore.contrib.directory.models import Entry
|
|
8
|
+
from wbcore.utils.html import convert_html2text
|
|
9
|
+
|
|
10
|
+
AccountCustomer = None
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def create_report_and_send(user: User, customer: Entry, start_date: date, end_date: date, report_callback):
|
|
14
|
+
with suppress(ValueError):
|
|
15
|
+
report_stream, file_name, file_extension = report_callback(user, customer, start_date, end_date)
|
|
16
|
+
|
|
17
|
+
html = get_template("portfolio/email/customer_report.html")
|
|
18
|
+
|
|
19
|
+
context = {"profile": user, "customer": customer}
|
|
20
|
+
html_content = html.render(context)
|
|
21
|
+
|
|
22
|
+
msg = EmailMultiAlternatives("Report", body=convert_html2text(html_content), to=[user.email])
|
|
23
|
+
msg.attach_alternative(html_content, "text/html")
|
|
24
|
+
report_stream.seek(0)
|
|
25
|
+
msg.attach(
|
|
26
|
+
file_name,
|
|
27
|
+
report_stream.read(),
|
|
28
|
+
file_extension,
|
|
29
|
+
)
|
|
30
|
+
msg.send()
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from wbcommission.models import CommissionType
|
|
2
|
+
from wbcore import serializers as wb_serializers
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class CommissionTypeRepresentationSerializer(wb_serializers.RepresentationSerializer):
|
|
6
|
+
_detail = wb_serializers.HyperlinkField(reverse_name="wbcommission:commissiontype-detail")
|
|
7
|
+
|
|
8
|
+
class Meta:
|
|
9
|
+
model = CommissionType
|
|
10
|
+
fields = (
|
|
11
|
+
"id",
|
|
12
|
+
"name",
|
|
13
|
+
"key",
|
|
14
|
+
"_detail",
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class CommissionTypeModelSerializer(wb_serializers.ModelSerializer):
|
|
19
|
+
class Meta:
|
|
20
|
+
model = CommissionType
|
|
21
|
+
read_only_fields = ("key",)
|
|
22
|
+
fields = (
|
|
23
|
+
"id",
|
|
24
|
+
"name",
|
|
25
|
+
"key",
|
|
26
|
+
)
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
from wbcommission.models import Rebate
|
|
2
|
+
from wbcore import serializers as wb_serializers
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class RebateModelSerializer(wb_serializers.ModelSerializer):
|
|
6
|
+
id = wb_serializers.PrimaryKeyField()
|
|
7
|
+
value_usd = wb_serializers.FloatField(read_only=True)
|
|
8
|
+
|
|
9
|
+
class Meta:
|
|
10
|
+
model = Rebate
|
|
11
|
+
fields = ("id", "value_usd")
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class RebateProductMarginalitySerializer(wb_serializers.Serializer):
|
|
15
|
+
id = wb_serializers.PrimaryKeyField()
|
|
16
|
+
title = wb_serializers.CharField(read_only=True)
|
|
17
|
+
currency_symbol = wb_serializers.CharField(read_only=True)
|
|
18
|
+
sum_management_fees = wb_serializers.DecimalField(max_digits=16, decimal_places=2, label="Management Fees")
|
|
19
|
+
sum_performance_fees = wb_serializers.DecimalField(max_digits=16, decimal_places=2, label="Management Rebates")
|
|
20
|
+
sum_crystalized_performance_fees = wb_serializers.DecimalField(
|
|
21
|
+
max_digits=16, decimal_places=2, label="Crystalized Performance Fees"
|
|
22
|
+
)
|
|
23
|
+
sum_management_rebates = wb_serializers.DecimalField(max_digits=16, decimal_places=2, label="Performance Fees")
|
|
24
|
+
sum_performance_rebates = wb_serializers.DecimalField(max_digits=16, decimal_places=2, label="Performance Rebates")
|
|
25
|
+
marginality_management = wb_serializers.DecimalField(
|
|
26
|
+
max_digits=16, decimal_places=2, label="Marginality Management"
|
|
27
|
+
)
|
|
28
|
+
marginality_performance = wb_serializers.DecimalField(
|
|
29
|
+
max_digits=16, decimal_places=2, label="Marginality Performance"
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
total_fees = wb_serializers.DecimalField(max_digits=16, decimal_places=2, label="Total Fees")
|
|
33
|
+
total_rebates = wb_serializers.DecimalField(max_digits=16, decimal_places=2, label="Total Rebates")
|
|
34
|
+
total_marginality = wb_serializers.DecimalField(max_digits=16, decimal_places=2, label="Total Marginality")
|
|
35
|
+
|
|
36
|
+
total_fees_usd = wb_serializers.DecimalField(max_digits=16, decimal_places=2, label="Total Fees (USD)")
|
|
37
|
+
total_rebates_usd = wb_serializers.DecimalField(max_digits=16, decimal_places=2, label="Total Rebates (USD)")
|
|
38
|
+
total_usd = wb_serializers.DecimalField(max_digits=16, decimal_places=2, label="Total Net (USD)")
|
|
39
|
+
|
|
40
|
+
class Meta:
|
|
41
|
+
percent_fields = [
|
|
42
|
+
"marginality_management",
|
|
43
|
+
"marginality_performance",
|
|
44
|
+
"total_marginality",
|
|
45
|
+
]
|
|
46
|
+
decorators = {
|
|
47
|
+
"sum_management_fees": wb_serializers.decorator(
|
|
48
|
+
decorator_type="text", position="left", value="{{currency_symbol}}"
|
|
49
|
+
),
|
|
50
|
+
"sum_performance_fees": wb_serializers.decorator(
|
|
51
|
+
decorator_type="text", position="left", value="{{currency_symbol}}"
|
|
52
|
+
),
|
|
53
|
+
"sum_management_rebates": wb_serializers.decorator(
|
|
54
|
+
decorator_type="text", position="left", value="{{currency_symbol}}"
|
|
55
|
+
),
|
|
56
|
+
"sum_performance_rebates": wb_serializers.decorator(
|
|
57
|
+
decorator_type="text", position="left", value="{{currency_symbol}}"
|
|
58
|
+
),
|
|
59
|
+
"total_fees": wb_serializers.decorator(
|
|
60
|
+
decorator_type="text", position="left", value="{{currency_symbol}}"
|
|
61
|
+
),
|
|
62
|
+
"total_rebates": wb_serializers.decorator(
|
|
63
|
+
decorator_type="text", position="left", value="{{currency_symbol}}"
|
|
64
|
+
),
|
|
65
|
+
"total_fees_usd": wb_serializers.decorator(decorator_type="text", position="left", value="$"),
|
|
66
|
+
"total_rebates_usd": wb_serializers.decorator(decorator_type="text", position="left", value="$"),
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
fields = (
|
|
70
|
+
"id",
|
|
71
|
+
"title",
|
|
72
|
+
"currency_symbol",
|
|
73
|
+
"sum_management_fees",
|
|
74
|
+
"sum_performance_fees",
|
|
75
|
+
"sum_crystalized_performance_fees",
|
|
76
|
+
"sum_management_rebates",
|
|
77
|
+
"sum_performance_rebates",
|
|
78
|
+
"marginality_management",
|
|
79
|
+
"marginality_performance",
|
|
80
|
+
"total_fees",
|
|
81
|
+
"total_rebates",
|
|
82
|
+
"total_marginality",
|
|
83
|
+
"total_fees_usd",
|
|
84
|
+
"total_rebates_usd",
|
|
85
|
+
"total_usd",
|
|
86
|
+
)
|
|
87
|
+
read_only_fields = fields
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
from django.dispatch import receiver
|
|
2
|
+
from rest_framework.reverse import reverse
|
|
3
|
+
from wbcore.contrib.directory.serializers import (
|
|
4
|
+
CompanyModelSerializer,
|
|
5
|
+
EntryModelSerializer,
|
|
6
|
+
PersonModelSerializer,
|
|
7
|
+
)
|
|
8
|
+
from wbcore.filters.defaults import current_quarter_date_end, current_quarter_date_start
|
|
9
|
+
from wbcore.signals import add_instance_additional_resource
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
# register the rebate and generate report addition resources to the entry model serializers
|
|
13
|
+
@receiver(add_instance_additional_resource, sender=CompanyModelSerializer)
|
|
14
|
+
@receiver(add_instance_additional_resource, sender=PersonModelSerializer)
|
|
15
|
+
@receiver(add_instance_additional_resource, sender=EntryModelSerializer)
|
|
16
|
+
def commission_adding_additional_resource(sender, serializer, instance, request, user, **kwargs):
|
|
17
|
+
start = current_quarter_date_start()
|
|
18
|
+
end = current_quarter_date_end()
|
|
19
|
+
res = {
|
|
20
|
+
"rebates": f'{reverse("wbcommission:rebatetable-list", args=[], request=request)}?group_by=PRODUCT&recipient={instance.id}&date={start:%Y-%m-%d},{end:%Y-%m-%d}',
|
|
21
|
+
"generate_customer_commission_report": f'{reverse("wbcommission:rebate-customerreport", request=request)}?recipient_id={instance.id}',
|
|
22
|
+
}
|
|
23
|
+
if user.has_perm("wbcommission.administrate_commission"):
|
|
24
|
+
res[
|
|
25
|
+
"generate_audit_commission_report"
|
|
26
|
+
] = f'{reverse("wbcommission:rebate-auditreport", request=request)}?recipient_id={instance.id}'
|
|
27
|
+
return res
|
|
File without changes
|
|
File without changes
|