wbcommission 1.49.8__tar.gz → 1.49.10__tar.gz

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-1.49.8 → wbcommission-1.49.10}/PKG-INFO +1 -1
  2. {wbcommission-1.49.8 → wbcommission-1.49.10}/wbcommission/analytics/marginality.py +23 -9
  3. {wbcommission-1.49.8 → wbcommission-1.49.10}/wbcommission/models/account_service.py +1 -1
  4. {wbcommission-1.49.8 → wbcommission-1.49.10}/wbcommission/models/commission.py +26 -14
  5. {wbcommission-1.49.8 → wbcommission-1.49.10}/wbcommission/serializers/signals.py +3 -3
  6. {wbcommission-1.49.8 → wbcommission-1.49.10}/wbcommission/tests/analytics/test_marginality.py +2 -1
  7. {wbcommission-1.49.8 → wbcommission-1.49.10}/wbcommission/tests/conftest.py +1 -2
  8. {wbcommission-1.49.8 → wbcommission-1.49.10}/wbcommission/viewsets/rebate.py +12 -3
  9. {wbcommission-1.49.8 → wbcommission-1.49.10}/.gitignore +0 -0
  10. {wbcommission-1.49.8 → wbcommission-1.49.10}/pyproject.toml +0 -0
  11. {wbcommission-1.49.8 → wbcommission-1.49.10}/wbcommission/__init__.py +0 -0
  12. {wbcommission-1.49.8 → wbcommission-1.49.10}/wbcommission/admin/__init__.py +0 -0
  13. {wbcommission-1.49.8 → wbcommission-1.49.10}/wbcommission/admin/accounts.py +0 -0
  14. {wbcommission-1.49.8 → wbcommission-1.49.10}/wbcommission/admin/commission.py +0 -0
  15. {wbcommission-1.49.8 → wbcommission-1.49.10}/wbcommission/admin/rebate.py +0 -0
  16. {wbcommission-1.49.8 → wbcommission-1.49.10}/wbcommission/analytics/__init__.py +0 -0
  17. {wbcommission-1.49.8 → wbcommission-1.49.10}/wbcommission/apps.py +0 -0
  18. {wbcommission-1.49.8 → wbcommission-1.49.10}/wbcommission/dynamic_preferences_registry.py +0 -0
  19. {wbcommission-1.49.8 → wbcommission-1.49.10}/wbcommission/factories/__init__.py +0 -0
  20. {wbcommission-1.49.8 → wbcommission-1.49.10}/wbcommission/factories/commission.py +0 -0
  21. {wbcommission-1.49.8 → wbcommission-1.49.10}/wbcommission/factories/rebate.py +0 -0
  22. {wbcommission-1.49.8 → wbcommission-1.49.10}/wbcommission/filters/__init__.py +0 -0
  23. {wbcommission-1.49.8 → wbcommission-1.49.10}/wbcommission/filters/rebate.py +0 -0
  24. {wbcommission-1.49.8 → wbcommission-1.49.10}/wbcommission/filters/signals.py +0 -0
  25. {wbcommission-1.49.8 → wbcommission-1.49.10}/wbcommission/generators/__init__.py +0 -0
  26. {wbcommission-1.49.8 → wbcommission-1.49.10}/wbcommission/generators/rebate_generator.py +0 -0
  27. {wbcommission-1.49.8 → wbcommission-1.49.10}/wbcommission/migrations/0001_initial.py +0 -0
  28. {wbcommission-1.49.8 → wbcommission-1.49.10}/wbcommission/migrations/0002_commissionrule_remove_accountcustomer_account_and_more.py +0 -0
  29. {wbcommission-1.49.8 → wbcommission-1.49.10}/wbcommission/migrations/0003_alter_commission_account.py +0 -0
  30. {wbcommission-1.49.8 → wbcommission-1.49.10}/wbcommission/migrations/0004_rebate_audit_log.py +0 -0
  31. {wbcommission-1.49.8 → wbcommission-1.49.10}/wbcommission/migrations/0005_alter_rebate_audit_log.py +0 -0
  32. {wbcommission-1.49.8 → wbcommission-1.49.10}/wbcommission/migrations/0006_commissionrule_consider_zero_percent_for_exclusion.py +0 -0
  33. {wbcommission-1.49.8 → wbcommission-1.49.10}/wbcommission/migrations/0007_remove_commission_unique_crm_recipient_account_and_more.py +0 -0
  34. {wbcommission-1.49.8 → wbcommission-1.49.10}/wbcommission/migrations/0008_alter_commission_options_alter_commission_order.py +0 -0
  35. {wbcommission-1.49.8 → wbcommission-1.49.10}/wbcommission/migrations/__init__.py +0 -0
  36. {wbcommission-1.49.8 → wbcommission-1.49.10}/wbcommission/models/__init__.py +0 -0
  37. {wbcommission-1.49.8 → wbcommission-1.49.10}/wbcommission/models/rebate.py +0 -0
  38. {wbcommission-1.49.8 → wbcommission-1.49.10}/wbcommission/models/signals.py +0 -0
  39. {wbcommission-1.49.8 → wbcommission-1.49.10}/wbcommission/permissions.py +0 -0
  40. {wbcommission-1.49.8 → wbcommission-1.49.10}/wbcommission/reports/__init__.py +0 -0
  41. {wbcommission-1.49.8 → wbcommission-1.49.10}/wbcommission/reports/audit_report.py +0 -0
  42. {wbcommission-1.49.8 → wbcommission-1.49.10}/wbcommission/reports/customer_report.py +0 -0
  43. {wbcommission-1.49.8 → wbcommission-1.49.10}/wbcommission/reports/utils.py +0 -0
  44. {wbcommission-1.49.8 → wbcommission-1.49.10}/wbcommission/serializers/__init__.py +0 -0
  45. {wbcommission-1.49.8 → wbcommission-1.49.10}/wbcommission/serializers/commissions.py +0 -0
  46. {wbcommission-1.49.8 → wbcommission-1.49.10}/wbcommission/serializers/rebate.py +0 -0
  47. {wbcommission-1.49.8 → wbcommission-1.49.10}/wbcommission/tests/__init__.py +0 -0
  48. {wbcommission-1.49.8 → wbcommission-1.49.10}/wbcommission/tests/analytics/__init__.py +0 -0
  49. {wbcommission-1.49.8 → wbcommission-1.49.10}/wbcommission/tests/models/__init__.py +0 -0
  50. {wbcommission-1.49.8 → wbcommission-1.49.10}/wbcommission/tests/models/mixins.py +0 -0
  51. {wbcommission-1.49.8 → wbcommission-1.49.10}/wbcommission/tests/models/test_account_service.py +0 -0
  52. {wbcommission-1.49.8 → wbcommission-1.49.10}/wbcommission/tests/models/test_commission.py +0 -0
  53. {wbcommission-1.49.8 → wbcommission-1.49.10}/wbcommission/tests/models/test_rebate.py +0 -0
  54. {wbcommission-1.49.8 → wbcommission-1.49.10}/wbcommission/tests/signals.py +0 -0
  55. {wbcommission-1.49.8 → wbcommission-1.49.10}/wbcommission/tests/test_permissions.py +0 -0
  56. {wbcommission-1.49.8 → wbcommission-1.49.10}/wbcommission/tests/viewsets/__init__.py +0 -0
  57. {wbcommission-1.49.8 → wbcommission-1.49.10}/wbcommission/tests/viewsets/test_rebate.py +0 -0
  58. {wbcommission-1.49.8 → wbcommission-1.49.10}/wbcommission/urls.py +0 -0
  59. {wbcommission-1.49.8 → wbcommission-1.49.10}/wbcommission/viewsets/__init__.py +0 -0
  60. {wbcommission-1.49.8 → wbcommission-1.49.10}/wbcommission/viewsets/buttons/__init__.py +0 -0
  61. {wbcommission-1.49.8 → wbcommission-1.49.10}/wbcommission/viewsets/buttons/rebate.py +0 -0
  62. {wbcommission-1.49.8 → wbcommission-1.49.10}/wbcommission/viewsets/buttons/signals.py +0 -0
  63. {wbcommission-1.49.8 → wbcommission-1.49.10}/wbcommission/viewsets/commissions.py +0 -0
  64. {wbcommission-1.49.8 → wbcommission-1.49.10}/wbcommission/viewsets/display/__init__.py +0 -0
  65. {wbcommission-1.49.8 → wbcommission-1.49.10}/wbcommission/viewsets/display/commissions.py +0 -0
  66. {wbcommission-1.49.8 → wbcommission-1.49.10}/wbcommission/viewsets/display/rebate.py +0 -0
  67. {wbcommission-1.49.8 → wbcommission-1.49.10}/wbcommission/viewsets/endpoints/__init__.py +0 -0
  68. {wbcommission-1.49.8 → wbcommission-1.49.10}/wbcommission/viewsets/endpoints/commissions.py +0 -0
  69. {wbcommission-1.49.8 → wbcommission-1.49.10}/wbcommission/viewsets/endpoints/rebate.py +0 -0
  70. {wbcommission-1.49.8 → wbcommission-1.49.10}/wbcommission/viewsets/menu/__init__.py +0 -0
  71. {wbcommission-1.49.8 → wbcommission-1.49.10}/wbcommission/viewsets/menu/commissions.py +0 -0
  72. {wbcommission-1.49.8 → wbcommission-1.49.10}/wbcommission/viewsets/menu/rebate.py +0 -0
  73. {wbcommission-1.49.8 → wbcommission-1.49.10}/wbcommission/viewsets/mixins.py +0 -0
  74. {wbcommission-1.49.8 → wbcommission-1.49.10}/wbcommission/viewsets/titles/__init__.py +0 -0
  75. {wbcommission-1.49.8 → wbcommission-1.49.10}/wbcommission/viewsets/titles/commissions.py +0 -0
  76. {wbcommission-1.49.8 → wbcommission-1.49.10}/wbcommission/viewsets/titles/rebate.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: wbcommission
3
- Version: 1.49.8
3
+ Version: 1.49.10
4
4
  Summary: A workbench module for managing human resources.
5
5
  Author-email: Christopher Wittlinger <c.wittlinger@stainly.com>
6
6
  Requires-Dist: reportlab==3.*
@@ -1,10 +1,11 @@
1
- from datetime import date
1
+ from datetime import date, timedelta
2
2
  from decimal import Decimal
3
3
 
4
4
  import numpy as np
5
5
  import pandas as pd
6
6
  from django.db.models import Case, OuterRef, Subquery, Value, When
7
7
  from django.db.models.functions import Coalesce
8
+ from pandas._libs.tslibs.offsets import BDay
8
9
  from wbcore.contrib.currency.models import CurrencyFXRates
9
10
  from wbfdm.models import InstrumentPrice
10
11
  from wbportfolio.models import Fees
@@ -20,6 +21,9 @@ class MarginalityCalculator:
20
21
  }
21
22
 
22
23
  def __init__(self, products, from_date: date, to_date: date):
24
+ bday_from_date = (from_date + timedelta(days=1) - BDay(1)).date()
25
+ bday_to_date = (to_date - timedelta(days=1) + BDay(1)).date()
26
+
23
27
  products = products.annotate(
24
28
  fx_rate=Coalesce(
25
29
  Subquery(
@@ -40,7 +44,7 @@ class MarginalityCalculator:
40
44
  # compute net marginality
41
45
  self.df_aum = pd.DataFrame(
42
46
  InstrumentPrice.objects.annotate_base_data()
43
- .filter(instrument__in=products, date__gte=from_date, date__lte=to_date)
47
+ .filter(instrument__in=products, date__gte=bday_from_date, date__lte=bday_to_date)
44
48
  .values_list("calculated", "net_value_usd", "date", "outstanding_shares", "instrument"),
45
49
  columns=["calculated", "net_value_usd", "date", "outstanding_shares", "instrument"],
46
50
  ).rename(columns={"instrument": "id"})
@@ -66,10 +70,9 @@ class MarginalityCalculator:
66
70
  ).dropna()
67
71
 
68
72
  # Build the fees dataframe where product id is the index and colum are the every fees type available and value are the amount.
69
-
70
73
  fees = Fees.valid_objects.filter(
71
- transaction_date__lte=to_date,
72
- transaction_date__gte=from_date,
74
+ transaction_date__lte=bday_to_date,
75
+ transaction_date__gte=bday_from_date,
73
76
  transaction_subtype__in=self.FEE_MAP.keys(),
74
77
  linked_product__in=products,
75
78
  ).annotate(
@@ -93,10 +96,14 @@ class MarginalityCalculator:
93
96
  self.df_fees["total"] = self.df_fees.sum(axis=1)
94
97
  self.df_fees = self.df_fees.reindex(self.df_aum.index, fill_value=0)
95
98
  self.df_fees = self._rolling_average_monday(self.df_fees)
99
+ self.df_fees = self.df_fees[
100
+ (self.df_fees.index.get_level_values(1) >= pd.Timestamp(from_date))
101
+ & (self.df_fees.index.get_level_values(1) <= pd.Timestamp(to_date))
102
+ ]
96
103
 
97
104
  # Build the fees dataframe where product id is the index and colum are the every fees type available and value are the amount.
98
105
  self.df_rebates = pd.DataFrame(
99
- Rebate.objects.filter(date__gte=from_date, date__lte=to_date, product__in=products).values_list(
106
+ Rebate.objects.filter(date__gte=bday_from_date, date__lte=bday_to_date, product__in=products).values_list(
100
107
  "product", "value", "date", "commission_type__key"
101
108
  ),
102
109
  columns=["product", "value", "date", "commission_type__key"],
@@ -118,9 +125,16 @@ class MarginalityCalculator:
118
125
  self.df_rebates = self.df_rebates.reindex(self.df_aum.index, fill_value=0)
119
126
  self.df_rebates["total"] = self.df_rebates.sum(axis=1)
120
127
  self.df_rebates = self._rolling_average_monday(self.df_rebates)
121
- # Iniliaze basic column
122
-
123
- self.empty_column = pd.Series(0.0, dtype="float64", index=self.df_fees.index)
128
+ self.df_rebates = self.df_rebates[
129
+ (self.df_rebates.index.get_level_values(1) >= pd.Timestamp(from_date))
130
+ & (self.df_rebates.index.get_level_values(1) <= pd.Timestamp(to_date))
131
+ ]
132
+ # Initialize basic column
133
+ self.df_aum = self.df_aum[
134
+ (self.df_aum.index.get_level_values(1) >= pd.Timestamp(from_date))
135
+ & (self.df_aum.index.get_level_values(1) <= pd.Timestamp(to_date))
136
+ ]
137
+ self.empty_column = pd.Series(0.0, dtype="float64", index=self.df_aum.index)
124
138
  self._set_basics_statistics()
125
139
 
126
140
  def _set_basics_statistics(self):
@@ -91,7 +91,7 @@ class AccountRebateManager:
91
91
  only_content_object_ids: list[int] | None = None,
92
92
  start_date: date | None = None,
93
93
  terminal_account_filter_dict: dict[str, Any] | None = None,
94
- **kwargs
94
+ **kwargs,
95
95
  ) -> Generator[tuple[Account, Product, date], None, None]:
96
96
  """
97
97
  Given the parameters and the instance root account and commission type, yield all valid terminal account, product and date
@@ -201,13 +201,21 @@ class CommissionType(WBModel):
201
201
  ) # If fees are negative, then we need to return the negative of the recipient fees
202
202
 
203
203
  # Yield rebate information
204
- yield terminal_account, compute_date, commission, content_object, recipient, rebate_gain, {
205
- "terminal_account_holding_ratio": terminal_account_holding_ratio,
206
- "root_account_total_holding": root_account_total_holding,
207
- "commission_percent": actual_percent,
208
- "commission_pool": commission_pool,
209
- "recipient_percent": recipient_percent,
210
- }
204
+ yield (
205
+ terminal_account,
206
+ compute_date,
207
+ commission,
208
+ content_object,
209
+ recipient,
210
+ rebate_gain,
211
+ {
212
+ "terminal_account_holding_ratio": terminal_account_holding_ratio,
213
+ "root_account_total_holding": root_account_total_holding,
214
+ "commission_percent": actual_percent,
215
+ "commission_pool": commission_pool,
216
+ "recipient_percent": recipient_percent,
217
+ },
218
+ )
211
219
 
212
220
  def __str__(self) -> str:
213
221
  return self.name
@@ -433,11 +441,14 @@ class Commission(OrderedModel, WBModel):
433
441
  )
434
442
  total_weighting = Decimal(account_roles.aggregate(s=Sum("weighting"))["s"] or 0)
435
443
  for account_role in account_roles:
436
- yield account_role.entry, Decimal(
437
- account_role.weighting
438
- ) / total_weighting if total_weighting else Decimal(0)
444
+ yield (
445
+ account_role.entry,
446
+ Decimal(account_role.weighting) / total_weighting if total_weighting else Decimal(0),
447
+ )
439
448
  else:
440
- role_recipients = PortfolioRole.objects.exclude(weighting=0).filter(
449
+ role_recipients = PortfolioRole.objects.exclude(
450
+ weighting=0
451
+ ).filter(
441
452
  (Q(role_type=self.portfolio_role_recipient) & (Q(instrument=product) | Q(instrument__isnull=True)))
442
453
  & (Q(start__lte=val_date) | Q(start__isnull=True))
443
454
  & (
@@ -446,9 +457,10 @@ class Commission(OrderedModel, WBModel):
446
457
  )
447
458
  total_weighting = Decimal(role_recipients.aggregate(s=Sum("weighting"))["s"] or 0)
448
459
  for portfolio_role in role_recipients:
449
- yield portfolio_role.person.entry_ptr, Decimal(
450
- Decimal(portfolio_role.weighting) / total_weighting
451
- ) if total_weighting else Decimal(0)
460
+ yield (
461
+ portfolio_role.person.entry_ptr,
462
+ Decimal(Decimal(portfolio_role.weighting) / total_weighting) if total_weighting else Decimal(0),
463
+ )
452
464
 
453
465
  @classmethod
454
466
  def get_endpoint_basename(cls) -> str:
@@ -21,7 +21,7 @@ def commission_adding_additional_resource(sender, serializer, instance, request,
21
21
  "generate_customer_commission_report": f'{reverse("wbcommission:rebate-customerreport", request=request)}?recipient_id={instance.id}',
22
22
  }
23
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}'
24
+ res["generate_audit_commission_report"] = (
25
+ f'{reverse("wbcommission:rebate-auditreport", request=request)}?recipient_id={instance.id}'
26
+ )
27
27
  return res
@@ -3,7 +3,8 @@ from faker import Faker
3
3
  from pandas.tseries.offsets import BDay
4
4
  from rest_framework.reverse import reverse
5
5
  from rest_framework.test import APIClient, APIRequestFactory
6
- from wbportfolio.factories import FeesFactory, InstrumentPriceFactory, ProductFactory
6
+ from wbfdm.factories import InstrumentPriceFactory
7
+ from wbportfolio.factories import FeesFactory, ProductFactory
7
8
  from wbportfolio.models import Product
8
9
 
9
10
  from wbcommission.analytics.marginality import MarginalityCalculator
@@ -23,12 +23,11 @@ from wbcore.contrib.geography.factories import (
23
23
  )
24
24
  from wbcore.contrib.geography.tests.signals import app_pre_migration
25
25
  from wbcrm.factories import AccountFactory, AccountRoleFactory, AccountRoleTypeFactory
26
- from wbfdm.factories import ExchangeFactory, InstrumentFactory, InstrumentTypeFactory
26
+ from wbfdm.factories import ExchangeFactory, InstrumentFactory, InstrumentTypeFactory, InstrumentPriceFactory
27
27
  from wbportfolio.factories import (
28
28
  ClaimFactory,
29
29
  CustomerTradeFactory,
30
30
  FeesFactory,
31
- InstrumentPriceFactory,
32
31
  PortfolioFactory,
33
32
  ProductFactory,
34
33
  ProductPortfolioRoleFactory,
@@ -4,11 +4,13 @@ from decimal import Decimal
4
4
 
5
5
  import numpy as np
6
6
  import pandas as pd
7
+ from django.contrib.messages import warning
7
8
  from django.db.models import Exists, ExpressionWrapper, F, FloatField, OuterRef, Sum
8
9
  from django.db.models.functions import Coalesce
9
10
  from django.shortcuts import get_object_or_404
10
11
  from django.utils.dateparse import parse_date
11
12
  from django.utils.functional import cached_property
13
+ from django.utils.translation import gettext_lazy as _
12
14
  from rest_framework import status
13
15
  from rest_framework.decorators import action
14
16
  from rest_framework.response import Response
@@ -406,11 +408,11 @@ class RebateProductMarginalityViewSet(ExportPandasAPIViewSet):
406
408
 
407
409
  @cached_property
408
410
  def start(self) -> date | None:
409
- return get_date_interval_from_request(self.request, exclude_weekend=True)[0]
411
+ return get_date_interval_from_request(self.request, exclude_weekend=False)[0]
410
412
 
411
413
  @cached_property
412
414
  def end(self) -> date | None:
413
- return get_date_interval_from_request(self.request, exclude_weekend=True)[1]
415
+ return get_date_interval_from_request(self.request, exclude_weekend=False)[1]
414
416
 
415
417
  def get_queryset(self):
416
418
  if self.request.user.has_perm("wbcommission.administrate_commission"):
@@ -420,12 +422,19 @@ class RebateProductMarginalityViewSet(ExportPandasAPIViewSet):
420
422
  date__gte=self.start,
421
423
  date__lte=self.end,
422
424
  )
423
- return super().get_queryset().annotate(has_aum=Exists(has_aum_subquery)).filter(has_aum=True)
425
+ return super().get_queryset().annotate(has_aum=Exists(has_aum_subquery))
424
426
  return Product.objects.none()
425
427
 
426
428
  def get_dataframe(self, request, queryset, **kwargs):
427
429
  df = pd.DataFrame()
428
430
  if queryset.exists() and self.start and self.end:
431
+ if self.start.weekday() in (5, 6) or self.end.weekday() in (5, 6):
432
+ warning(
433
+ request,
434
+ _(
435
+ "The selected date range includes a Saturday or Sunday. Please note that fees and rebates are normalized over the weekend, as fees continue to accumulate during this period."
436
+ ),
437
+ )
429
438
  marginality_calculator = MarginalityCalculator(queryset, self.start, self.end)
430
439
  df_products = (
431
440
  pd.DataFrame(
File without changes