wbportfolio 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 wbportfolio might be problematic. Click here for more details.

Files changed (486) hide show
  1. wbportfolio/__init__.py +1 -0
  2. wbportfolio/admin/__init__.py +12 -0
  3. wbportfolio/admin/asset.py +47 -0
  4. wbportfolio/admin/custodians.py +9 -0
  5. wbportfolio/admin/portfolio.py +127 -0
  6. wbportfolio/admin/portfolio_relationships.py +22 -0
  7. wbportfolio/admin/product_groups.py +42 -0
  8. wbportfolio/admin/products.py +80 -0
  9. wbportfolio/admin/reconciliations.py +14 -0
  10. wbportfolio/admin/registers.py +17 -0
  11. wbportfolio/admin/roles.py +19 -0
  12. wbportfolio/admin/synchronization/__init__.py +2 -0
  13. wbportfolio/admin/synchronization/admin.py +114 -0
  14. wbportfolio/admin/synchronization/portfolio_synchronization.py +18 -0
  15. wbportfolio/admin/synchronization/price_computation.py +21 -0
  16. wbportfolio/admin/transactions/__init__.py +5 -0
  17. wbportfolio/admin/transactions/claim.py +16 -0
  18. wbportfolio/admin/transactions/dividends.py +14 -0
  19. wbportfolio/admin/transactions/fees.py +35 -0
  20. wbportfolio/admin/transactions/trades.py +49 -0
  21. wbportfolio/admin/transactions/transactions.py +37 -0
  22. wbportfolio/analysis/__init__.py +0 -0
  23. wbportfolio/analysis/claims.py +235 -0
  24. wbportfolio/apps.py +5 -0
  25. wbportfolio/contrib/__init__.py +0 -0
  26. wbportfolio/contrib/company_portfolio/__init__.py +0 -0
  27. wbportfolio/contrib/company_portfolio/admin.py +28 -0
  28. wbportfolio/contrib/company_portfolio/apps.py +29 -0
  29. wbportfolio/contrib/company_portfolio/configs/__init__.py +3 -0
  30. wbportfolio/contrib/company_portfolio/configs/display.py +182 -0
  31. wbportfolio/contrib/company_portfolio/configs/endpoints.py +34 -0
  32. wbportfolio/contrib/company_portfolio/configs/previews.py +37 -0
  33. wbportfolio/contrib/company_portfolio/constants.py +1 -0
  34. wbportfolio/contrib/company_portfolio/dynamic_preferences_registry.py +87 -0
  35. wbportfolio/contrib/company_portfolio/factories.py +32 -0
  36. wbportfolio/contrib/company_portfolio/filters.py +127 -0
  37. wbportfolio/contrib/company_portfolio/management.py +19 -0
  38. wbportfolio/contrib/company_portfolio/migrations/0001_initial.py +214 -0
  39. wbportfolio/contrib/company_portfolio/migrations/__init__.py +0 -0
  40. wbportfolio/contrib/company_portfolio/models.py +334 -0
  41. wbportfolio/contrib/company_portfolio/scripts.py +76 -0
  42. wbportfolio/contrib/company_portfolio/serializers.py +303 -0
  43. wbportfolio/contrib/company_portfolio/tasks.py +19 -0
  44. wbportfolio/contrib/company_portfolio/tests/__init__.py +0 -0
  45. wbportfolio/contrib/company_portfolio/tests/conftest.py +161 -0
  46. wbportfolio/contrib/company_portfolio/tests/test_models.py +161 -0
  47. wbportfolio/contrib/company_portfolio/urls.py +29 -0
  48. wbportfolio/contrib/company_portfolio/viewsets.py +195 -0
  49. wbportfolio/defaults/__init__.py +0 -0
  50. wbportfolio/defaults/fees/__init__.py +0 -0
  51. wbportfolio/defaults/fees/default.py +92 -0
  52. wbportfolio/defaults/portfolio/__init__.py +0 -0
  53. wbportfolio/defaults/portfolio/default_rebalancing.py +45 -0
  54. wbportfolio/dynamic_preferences_registry.py +58 -0
  55. wbportfolio/factories/__init__.py +35 -0
  56. wbportfolio/factories/adjustments.py +17 -0
  57. wbportfolio/factories/assets.py +75 -0
  58. wbportfolio/factories/claim.py +39 -0
  59. wbportfolio/factories/custodians.py +11 -0
  60. wbportfolio/factories/dividends.py +14 -0
  61. wbportfolio/factories/fees.py +15 -0
  62. wbportfolio/factories/indexes.py +17 -0
  63. wbportfolio/factories/portfolio_cash_flow.py +20 -0
  64. wbportfolio/factories/portfolio_cash_targets.py +15 -0
  65. wbportfolio/factories/portfolio_swing_pricings.py +15 -0
  66. wbportfolio/factories/portfolios.py +59 -0
  67. wbportfolio/factories/product_groups.py +28 -0
  68. wbportfolio/factories/products.py +56 -0
  69. wbportfolio/factories/pytest_utils.py +121 -0
  70. wbportfolio/factories/reconciliations.py +23 -0
  71. wbportfolio/factories/roles.py +20 -0
  72. wbportfolio/factories/synchronization.py +40 -0
  73. wbportfolio/factories/trades.py +35 -0
  74. wbportfolio/factories/transactions.py +21 -0
  75. wbportfolio/fdm/__init__.py +0 -0
  76. wbportfolio/fdm/tasks.py +12 -0
  77. wbportfolio/filters/__init__.py +32 -0
  78. wbportfolio/filters/assets.py +485 -0
  79. wbportfolio/filters/assets_and_net_new_money_progression.py +42 -0
  80. wbportfolio/filters/custodians.py +10 -0
  81. wbportfolio/filters/esg.py +22 -0
  82. wbportfolio/filters/performances.py +171 -0
  83. wbportfolio/filters/portfolios.py +24 -0
  84. wbportfolio/filters/positions.py +178 -0
  85. wbportfolio/filters/products.py +157 -0
  86. wbportfolio/filters/roles.py +26 -0
  87. wbportfolio/filters/signals.py +92 -0
  88. wbportfolio/filters/transactions/__init__.py +20 -0
  89. wbportfolio/filters/transactions/claim.py +394 -0
  90. wbportfolio/filters/transactions/fees.py +66 -0
  91. wbportfolio/filters/transactions/trades.py +224 -0
  92. wbportfolio/filters/transactions/transactions.py +98 -0
  93. wbportfolio/import_export/__init__.py +0 -0
  94. wbportfolio/import_export/backends/__init__.py +2 -0
  95. wbportfolio/import_export/backends/ubs/__init__.py +3 -0
  96. wbportfolio/import_export/backends/ubs/asset_position.py +45 -0
  97. wbportfolio/import_export/backends/ubs/fees.py +63 -0
  98. wbportfolio/import_export/backends/ubs/instrument_price.py +44 -0
  99. wbportfolio/import_export/backends/ubs/mixin.py +15 -0
  100. wbportfolio/import_export/backends/utils.py +58 -0
  101. wbportfolio/import_export/backends/wbfdm/__init__.py +2 -0
  102. wbportfolio/import_export/backends/wbfdm/adjustment.py +50 -0
  103. wbportfolio/import_export/backends/wbfdm/dividend.py +16 -0
  104. wbportfolio/import_export/backends/wbfdm/mixin.py +15 -0
  105. wbportfolio/import_export/handlers/__init__.py +0 -0
  106. wbportfolio/import_export/handlers/adjustment.py +39 -0
  107. wbportfolio/import_export/handlers/asset_position.py +167 -0
  108. wbportfolio/import_export/handlers/dividend.py +80 -0
  109. wbportfolio/import_export/handlers/fees.py +58 -0
  110. wbportfolio/import_export/handlers/portfolio_cash_flow.py +57 -0
  111. wbportfolio/import_export/handlers/register.py +43 -0
  112. wbportfolio/import_export/handlers/trade.py +191 -0
  113. wbportfolio/import_export/parsers/__init__.py +0 -0
  114. wbportfolio/import_export/parsers/default_mapping.py +30 -0
  115. wbportfolio/import_export/parsers/jpmorgan/__init__.py +0 -0
  116. wbportfolio/import_export/parsers/jpmorgan/customer_trade.py +63 -0
  117. wbportfolio/import_export/parsers/jpmorgan/fees.py +64 -0
  118. wbportfolio/import_export/parsers/jpmorgan/strategy.py +116 -0
  119. wbportfolio/import_export/parsers/jpmorgan/valuation.py +41 -0
  120. wbportfolio/import_export/parsers/leonteq/__init__.py +0 -0
  121. wbportfolio/import_export/parsers/leonteq/customer_trade.py +47 -0
  122. wbportfolio/import_export/parsers/leonteq/equity.py +81 -0
  123. wbportfolio/import_export/parsers/leonteq/fees.py +70 -0
  124. wbportfolio/import_export/parsers/leonteq/trade.py +94 -0
  125. wbportfolio/import_export/parsers/leonteq/valuation.py +39 -0
  126. wbportfolio/import_export/parsers/natixis/__init__.py +0 -0
  127. wbportfolio/import_export/parsers/natixis/customer_trade.py +62 -0
  128. wbportfolio/import_export/parsers/natixis/d1_customer_trade.py +66 -0
  129. wbportfolio/import_export/parsers/natixis/d1_equity.py +80 -0
  130. wbportfolio/import_export/parsers/natixis/d1_fees.py +58 -0
  131. wbportfolio/import_export/parsers/natixis/d1_trade.py +70 -0
  132. wbportfolio/import_export/parsers/natixis/d1_valuation.py +41 -0
  133. wbportfolio/import_export/parsers/natixis/dividend.py +53 -0
  134. wbportfolio/import_export/parsers/natixis/equity.py +60 -0
  135. wbportfolio/import_export/parsers/natixis/fees.py +53 -0
  136. wbportfolio/import_export/parsers/natixis/trade.py +63 -0
  137. wbportfolio/import_export/parsers/natixis/utils.py +76 -0
  138. wbportfolio/import_export/parsers/natixis/valuation.py +46 -0
  139. wbportfolio/import_export/parsers/refinitiv/__init__.py +0 -0
  140. wbportfolio/import_export/parsers/refinitiv/adjustment.py +24 -0
  141. wbportfolio/import_export/parsers/sg_lux/__init__.py +0 -0
  142. wbportfolio/import_export/parsers/sg_lux/custodian_positions.py +70 -0
  143. wbportfolio/import_export/parsers/sg_lux/customer_trade.py +75 -0
  144. wbportfolio/import_export/parsers/sg_lux/customer_trade_pending_slk.py +140 -0
  145. wbportfolio/import_export/parsers/sg_lux/customer_trade_slk.py +80 -0
  146. wbportfolio/import_export/parsers/sg_lux/customer_trade_without_pw.py +57 -0
  147. wbportfolio/import_export/parsers/sg_lux/equity.py +137 -0
  148. wbportfolio/import_export/parsers/sg_lux/fees.py +56 -0
  149. wbportfolio/import_export/parsers/sg_lux/perf_fees.py +51 -0
  150. wbportfolio/import_export/parsers/sg_lux/portfolio_cash_flow.py +29 -0
  151. wbportfolio/import_export/parsers/sg_lux/portfolio_future_cash_flow.py +36 -0
  152. wbportfolio/import_export/parsers/sg_lux/registers.py +210 -0
  153. wbportfolio/import_export/parsers/sg_lux/sylk.py +248 -0
  154. wbportfolio/import_export/parsers/sg_lux/utils.py +36 -0
  155. wbportfolio/import_export/parsers/sg_lux/valuation.py +53 -0
  156. wbportfolio/import_export/parsers/societe_generale/__init__.py +0 -0
  157. wbportfolio/import_export/parsers/societe_generale/customer_trade.py +54 -0
  158. wbportfolio/import_export/parsers/societe_generale/strategy.py +94 -0
  159. wbportfolio/import_export/parsers/societe_generale/valuation.py +37 -0
  160. wbportfolio/import_export/parsers/tellco/__init__.py +0 -0
  161. wbportfolio/import_export/parsers/tellco/customer_trade.py +64 -0
  162. wbportfolio/import_export/parsers/tellco/equity.py +86 -0
  163. wbportfolio/import_export/parsers/tellco/valuation.py +52 -0
  164. wbportfolio/import_export/parsers/ubs/__init__.py +0 -0
  165. wbportfolio/import_export/parsers/ubs/api/__init__.py +0 -0
  166. wbportfolio/import_export/parsers/ubs/api/asset_position.py +106 -0
  167. wbportfolio/import_export/parsers/ubs/api/fees.py +31 -0
  168. wbportfolio/import_export/parsers/ubs/api/instrument_price.py +20 -0
  169. wbportfolio/import_export/parsers/ubs/api/utils.py +0 -0
  170. wbportfolio/import_export/parsers/ubs/customer_trade.py +60 -0
  171. wbportfolio/import_export/parsers/ubs/equity.py +97 -0
  172. wbportfolio/import_export/parsers/ubs/historical_customer_trade.py +67 -0
  173. wbportfolio/import_export/parsers/ubs/valuation.py +52 -0
  174. wbportfolio/import_export/parsers/vontobel/__init__.py +0 -0
  175. wbportfolio/import_export/parsers/vontobel/asset_position.py +97 -0
  176. wbportfolio/import_export/parsers/vontobel/customer_trade.py +54 -0
  177. wbportfolio/import_export/parsers/vontobel/historical_customer_trade.py +40 -0
  178. wbportfolio/import_export/parsers/vontobel/instrument.py +34 -0
  179. wbportfolio/import_export/parsers/vontobel/management_fees.py +86 -0
  180. wbportfolio/import_export/parsers/vontobel/performance_fees.py +35 -0
  181. wbportfolio/import_export/parsers/vontobel/trade.py +38 -0
  182. wbportfolio/import_export/parsers/vontobel/utils.py +17 -0
  183. wbportfolio/import_export/parsers/vontobel/valuation.py +29 -0
  184. wbportfolio/import_export/resources/__init__.py +0 -0
  185. wbportfolio/import_export/resources/assets.py +68 -0
  186. wbportfolio/import_export/resources/trades.py +41 -0
  187. wbportfolio/import_export/utils.py +42 -0
  188. wbportfolio/metric/__init__.py +0 -0
  189. wbportfolio/metric/backends/__init__.py +2 -0
  190. wbportfolio/metric/backends/base.py +86 -0
  191. wbportfolio/metric/backends/constants.py +222 -0
  192. wbportfolio/metric/backends/portfolio_base.py +255 -0
  193. wbportfolio/metric/backends/portfolio_esg.py +66 -0
  194. wbportfolio/metric/tests/__init__.py +0 -0
  195. wbportfolio/metric/tests/conftest.py +4 -0
  196. wbportfolio/metric/tests/test_portfolio_base.py +135 -0
  197. wbportfolio/metric/tests/test_portfolio_esg.py +69 -0
  198. wbportfolio/migrations/0001_initial_squashed.py +13848 -0
  199. wbportfolio/migrations/0002_product_default_sub_account_squashed_0039_alter_assetallocation_company_and_more.py +3836 -0
  200. wbportfolio/migrations/0040_instrument_financial_instrument.py +26 -0
  201. wbportfolio/migrations/0041_remove_listresearch_research_ptr_and_more.py +129 -0
  202. wbportfolio/migrations/0042_instrumentlist_instrumentlistthroughmodel_and_more.py +71 -0
  203. wbportfolio/migrations/0043_alter_instrumentlistthroughmodel_options_and_more.py +238 -0
  204. wbportfolio/migrations/0044_alter_instrumentlist_identifier.py +35 -0
  205. wbportfolio/migrations/0045_alter_instrument_financial_instrument.py +26 -0
  206. wbportfolio/migrations/0046_add_product_default_account.py +166 -0
  207. wbportfolio/migrations/0047_remove_product_default_sub_account.py +14 -0
  208. wbportfolio/migrations/0048_alter_trade_status.py +29 -0
  209. wbportfolio/migrations/0049_trade_claimed_shares.py +25 -0
  210. wbportfolio/migrations/0050_fees_fee_date_fees_wbportfolio_transac_1f7a29_idx.py +44 -0
  211. wbportfolio/migrations/0051_delete_macroreview.py +11 -0
  212. wbportfolio/migrations/0052_remove_cash_instrument_ptr_and_more.py +888 -0
  213. wbportfolio/migrations/0053_remove_product_group.py +132 -0
  214. wbportfolio/migrations/0054_portfolioinstrumentpreferredclassificationthroughmodel_and_more.py +270 -0
  215. wbportfolio/migrations/0055_remove_product__custom_management_rebates_and_more.py +139 -0
  216. wbportfolio/migrations/0056_remove_companyportfoliodata_assets_under_management_currency_and_more.py +56 -0
  217. wbportfolio/migrations/0057_alter_portfolio_preferred_instrument_classifications_and_more.py +36 -0
  218. wbportfolio/migrations/0058_pmsinstrument.py +23 -0
  219. wbportfolio/migrations/0059_fees_unique_fees.py +51 -0
  220. wbportfolio/migrations/0060_alter_portfolioportfoliothroughmodel_type.py +21 -0
  221. wbportfolio/migrations/0061_portfolio_bank_accounts_product_bank_account_and_more.py +175 -0
  222. wbportfolio/migrations/0062_alter_dailyportfoliocashflow_options.py +20 -0
  223. wbportfolio/migrations/0063_accountreconciliation_accountreconciliationline_and_more.py +133 -0
  224. wbportfolio/migrations/0064_alter_portfolio_managers_portfolio_is_tracked_and_more.py +40 -0
  225. wbportfolio/migrations/0065_alter_portfolio_managers_claim_as_shares_and_more.py +73 -0
  226. wbportfolio/migrations/0066_assetposition_initial_shares_at_custodian_and_more.py +108 -0
  227. wbportfolio/migrations/0067_assetposition_unique_asset_position.py +77 -0
  228. wbportfolio/migrations/0068_trade_internal_trade_trade_marked_as_internal_and_more.py +59 -0
  229. wbportfolio/migrations/0069_remove_portfolio_is_invested_and_more.py +56 -0
  230. wbportfolio/migrations/0070_remove_assetposition_unique_asset_position_and_more.py +82 -0
  231. wbportfolio/migrations/0071_alter_trade_options_alter_trade_order.py +22 -0
  232. wbportfolio/migrations/__init__.py +0 -0
  233. wbportfolio/models/__init__.py +26 -0
  234. wbportfolio/models/adjustments.py +246 -0
  235. wbportfolio/models/asset.py +869 -0
  236. wbportfolio/models/custodians.py +101 -0
  237. wbportfolio/models/indexes.py +33 -0
  238. wbportfolio/models/mixins/__init__.py +0 -0
  239. wbportfolio/models/mixins/instruments.py +127 -0
  240. wbportfolio/models/mixins/liquidity_stress_test.py +1307 -0
  241. wbportfolio/models/portfolio.py +1039 -0
  242. wbportfolio/models/portfolio_cash_flow.py +167 -0
  243. wbportfolio/models/portfolio_cash_targets.py +46 -0
  244. wbportfolio/models/portfolio_relationship.py +135 -0
  245. wbportfolio/models/portfolio_swing_pricings.py +51 -0
  246. wbportfolio/models/product_groups.py +230 -0
  247. wbportfolio/models/products.py +569 -0
  248. wbportfolio/models/reconciliations/__init__.py +2 -0
  249. wbportfolio/models/reconciliations/account_reconciliation_lines.py +192 -0
  250. wbportfolio/models/reconciliations/account_reconciliations.py +102 -0
  251. wbportfolio/models/reconciliations/reconciliations.py +25 -0
  252. wbportfolio/models/registers.py +132 -0
  253. wbportfolio/models/roles.py +208 -0
  254. wbportfolio/models/synchronization/__init__.py +3 -0
  255. wbportfolio/models/synchronization/portfolio_synchronization.py +292 -0
  256. wbportfolio/models/synchronization/price_computation.py +200 -0
  257. wbportfolio/models/synchronization/synchronization.py +188 -0
  258. wbportfolio/models/transactions/__init__.py +7 -0
  259. wbportfolio/models/transactions/claim.py +634 -0
  260. wbportfolio/models/transactions/dividends.py +31 -0
  261. wbportfolio/models/transactions/expiry.py +7 -0
  262. wbportfolio/models/transactions/fees.py +153 -0
  263. wbportfolio/models/transactions/trade_proposals.py +502 -0
  264. wbportfolio/models/transactions/trades.py +704 -0
  265. wbportfolio/models/transactions/transactions.py +211 -0
  266. wbportfolio/models/utils.py +12 -0
  267. wbportfolio/permissions.py +13 -0
  268. wbportfolio/pms/__init__.py +0 -0
  269. wbportfolio/pms/statistics/__init__.py +0 -0
  270. wbportfolio/pms/trading/__init__.py +1 -0
  271. wbportfolio/pms/trading/handler.py +164 -0
  272. wbportfolio/pms/typing.py +194 -0
  273. wbportfolio/preferences.py +6 -0
  274. wbportfolio/reports/__init__.py +0 -0
  275. wbportfolio/reports/monthly_position_report.py +74 -0
  276. wbportfolio/risk_management/__init__.py +0 -0
  277. wbportfolio/risk_management/backends/__init__.py +11 -0
  278. wbportfolio/risk_management/backends/accounts.py +166 -0
  279. wbportfolio/risk_management/backends/controversy_portfolio.py +63 -0
  280. wbportfolio/risk_management/backends/exposure_portfolio.py +203 -0
  281. wbportfolio/risk_management/backends/instrument_list_portfolio.py +89 -0
  282. wbportfolio/risk_management/backends/liquidity_risk.py +86 -0
  283. wbportfolio/risk_management/backends/liquidity_stress_instrument.py +86 -0
  284. wbportfolio/risk_management/backends/mixins.py +220 -0
  285. wbportfolio/risk_management/backends/product_integrity.py +111 -0
  286. wbportfolio/risk_management/backends/stop_loss_instrument.py +24 -0
  287. wbportfolio/risk_management/backends/stop_loss_portfolio.py +36 -0
  288. wbportfolio/risk_management/backends/ucits_portfolio.py +63 -0
  289. wbportfolio/risk_management/tests/__init__.py +0 -0
  290. wbportfolio/risk_management/tests/conftest.py +15 -0
  291. wbportfolio/risk_management/tests/test_accounts.py +98 -0
  292. wbportfolio/risk_management/tests/test_controversy_portfolio.py +33 -0
  293. wbportfolio/risk_management/tests/test_exposure_portfolio.py +94 -0
  294. wbportfolio/risk_management/tests/test_instrument_list_portfolio.py +60 -0
  295. wbportfolio/risk_management/tests/test_liquidity_risk.py +47 -0
  296. wbportfolio/risk_management/tests/test_product_integrity.py +55 -0
  297. wbportfolio/risk_management/tests/test_stop_loss_instrument.py +110 -0
  298. wbportfolio/risk_management/tests/test_stop_loss_portfolio.py +119 -0
  299. wbportfolio/risk_management/tests/test_ucits_portfolio.py +39 -0
  300. wbportfolio/serializers/__init__.py +42 -0
  301. wbportfolio/serializers/adjustments.py +24 -0
  302. wbportfolio/serializers/assets.py +166 -0
  303. wbportfolio/serializers/custodians.py +26 -0
  304. wbportfolio/serializers/portfolio_cash_flow.py +48 -0
  305. wbportfolio/serializers/portfolio_cash_targets.py +20 -0
  306. wbportfolio/serializers/portfolio_relationship.py +53 -0
  307. wbportfolio/serializers/portfolio_swing_pricing.py +20 -0
  308. wbportfolio/serializers/portfolios.py +143 -0
  309. wbportfolio/serializers/positions.py +76 -0
  310. wbportfolio/serializers/product_group.py +88 -0
  311. wbportfolio/serializers/products.py +331 -0
  312. wbportfolio/serializers/reconciliations.py +171 -0
  313. wbportfolio/serializers/registers.py +72 -0
  314. wbportfolio/serializers/roles.py +60 -0
  315. wbportfolio/serializers/signals.py +157 -0
  316. wbportfolio/serializers/synchronization.py +18 -0
  317. wbportfolio/serializers/transactions/__init__.py +24 -0
  318. wbportfolio/serializers/transactions/claim.py +310 -0
  319. wbportfolio/serializers/transactions/dividends.py +18 -0
  320. wbportfolio/serializers/transactions/expiry.py +18 -0
  321. wbportfolio/serializers/transactions/fees.py +32 -0
  322. wbportfolio/serializers/transactions/trades.py +315 -0
  323. wbportfolio/serializers/transactions/transactions.py +84 -0
  324. wbportfolio/tasks.py +125 -0
  325. wbportfolio/tests/__init__.py +0 -0
  326. wbportfolio/tests/conftest.py +164 -0
  327. wbportfolio/tests/models/__init__.py +0 -0
  328. wbportfolio/tests/models/test_account_reconciliation.py +191 -0
  329. wbportfolio/tests/models/test_assets.py +193 -0
  330. wbportfolio/tests/models/test_custodians.py +12 -0
  331. wbportfolio/tests/models/test_customer_trades.py +113 -0
  332. wbportfolio/tests/models/test_dividends.py +7 -0
  333. wbportfolio/tests/models/test_imports.py +192 -0
  334. wbportfolio/tests/models/test_instrument_mixins.py +48 -0
  335. wbportfolio/tests/models/test_merge.py +133 -0
  336. wbportfolio/tests/models/test_portfolio_cash_flow.py +112 -0
  337. wbportfolio/tests/models/test_portfolio_cash_targets.py +27 -0
  338. wbportfolio/tests/models/test_portfolio_swing_pricings.py +42 -0
  339. wbportfolio/tests/models/test_portfolios.py +676 -0
  340. wbportfolio/tests/models/test_product_groups.py +80 -0
  341. wbportfolio/tests/models/test_products.py +187 -0
  342. wbportfolio/tests/models/test_roles.py +82 -0
  343. wbportfolio/tests/models/test_splits.py +233 -0
  344. wbportfolio/tests/models/test_synchronization.py +617 -0
  345. wbportfolio/tests/models/transactions/__init__.py +0 -0
  346. wbportfolio/tests/models/transactions/test_claim.py +129 -0
  347. wbportfolio/tests/models/transactions/test_fees.py +65 -0
  348. wbportfolio/tests/models/transactions/test_trades.py +204 -0
  349. wbportfolio/tests/models/utils.py +13 -0
  350. wbportfolio/tests/serializers/__init__.py +0 -0
  351. wbportfolio/tests/serializers/test_claims.py +21 -0
  352. wbportfolio/tests/signals.py +151 -0
  353. wbportfolio/tests/tests.py +31 -0
  354. wbportfolio/tests/viewsets/__init__.py +0 -0
  355. wbportfolio/tests/viewsets/test_assets.py +67 -0
  356. wbportfolio/tests/viewsets/test_performances.py +72 -0
  357. wbportfolio/tests/viewsets/test_products.py +92 -0
  358. wbportfolio/tests/viewsets/transactions/__init__.py +0 -0
  359. wbportfolio/tests/viewsets/transactions/test_claims.py +146 -0
  360. wbportfolio/urls.py +247 -0
  361. wbportfolio/utils.py +30 -0
  362. wbportfolio/viewsets/__init__.py +57 -0
  363. wbportfolio/viewsets/adjustments.py +46 -0
  364. wbportfolio/viewsets/assets.py +562 -0
  365. wbportfolio/viewsets/assets_and_net_new_money_progression.py +117 -0
  366. wbportfolio/viewsets/charts/__init__.py +1 -0
  367. wbportfolio/viewsets/charts/assets.py +247 -0
  368. wbportfolio/viewsets/configs/__init__.py +6 -0
  369. wbportfolio/viewsets/configs/buttons/__init__.py +23 -0
  370. wbportfolio/viewsets/configs/buttons/adjustments.py +13 -0
  371. wbportfolio/viewsets/configs/buttons/assets.py +145 -0
  372. wbportfolio/viewsets/configs/buttons/claims.py +83 -0
  373. wbportfolio/viewsets/configs/buttons/custodians.py +76 -0
  374. wbportfolio/viewsets/configs/buttons/fees.py +14 -0
  375. wbportfolio/viewsets/configs/buttons/mixins.py +88 -0
  376. wbportfolio/viewsets/configs/buttons/portfolios.py +115 -0
  377. wbportfolio/viewsets/configs/buttons/products.py +41 -0
  378. wbportfolio/viewsets/configs/buttons/reconciliations.py +65 -0
  379. wbportfolio/viewsets/configs/buttons/registers.py +11 -0
  380. wbportfolio/viewsets/configs/buttons/signals.py +68 -0
  381. wbportfolio/viewsets/configs/buttons/trade_proposals.py +25 -0
  382. wbportfolio/viewsets/configs/buttons/trades.py +144 -0
  383. wbportfolio/viewsets/configs/display/__init__.py +61 -0
  384. wbportfolio/viewsets/configs/display/adjustments.py +81 -0
  385. wbportfolio/viewsets/configs/display/assets.py +265 -0
  386. wbportfolio/viewsets/configs/display/claim.py +299 -0
  387. wbportfolio/viewsets/configs/display/custodians.py +24 -0
  388. wbportfolio/viewsets/configs/display/esg.py +88 -0
  389. wbportfolio/viewsets/configs/display/fees.py +133 -0
  390. wbportfolio/viewsets/configs/display/portfolio_cash_flow.py +103 -0
  391. wbportfolio/viewsets/configs/display/portfolio_relationship.py +38 -0
  392. wbportfolio/viewsets/configs/display/portfolios.py +125 -0
  393. wbportfolio/viewsets/configs/display/positions.py +75 -0
  394. wbportfolio/viewsets/configs/display/product_groups.py +54 -0
  395. wbportfolio/viewsets/configs/display/product_performance.py +241 -0
  396. wbportfolio/viewsets/configs/display/products.py +249 -0
  397. wbportfolio/viewsets/configs/display/reconciliations.py +151 -0
  398. wbportfolio/viewsets/configs/display/registers.py +71 -0
  399. wbportfolio/viewsets/configs/display/roles.py +49 -0
  400. wbportfolio/viewsets/configs/display/trade_proposals.py +97 -0
  401. wbportfolio/viewsets/configs/display/trades.py +359 -0
  402. wbportfolio/viewsets/configs/display/transactions.py +55 -0
  403. wbportfolio/viewsets/configs/endpoints/__init__.py +75 -0
  404. wbportfolio/viewsets/configs/endpoints/adjustments.py +17 -0
  405. wbportfolio/viewsets/configs/endpoints/assets.py +115 -0
  406. wbportfolio/viewsets/configs/endpoints/claim.py +106 -0
  407. wbportfolio/viewsets/configs/endpoints/custodians.py +6 -0
  408. wbportfolio/viewsets/configs/endpoints/esg.py +14 -0
  409. wbportfolio/viewsets/configs/endpoints/fees.py +26 -0
  410. wbportfolio/viewsets/configs/endpoints/portfolio_relationship.py +23 -0
  411. wbportfolio/viewsets/configs/endpoints/portfolios.py +43 -0
  412. wbportfolio/viewsets/configs/endpoints/positions.py +18 -0
  413. wbportfolio/viewsets/configs/endpoints/product_groups.py +11 -0
  414. wbportfolio/viewsets/configs/endpoints/product_performance.py +29 -0
  415. wbportfolio/viewsets/configs/endpoints/products.py +37 -0
  416. wbportfolio/viewsets/configs/endpoints/reconciliations.py +31 -0
  417. wbportfolio/viewsets/configs/endpoints/roles.py +9 -0
  418. wbportfolio/viewsets/configs/endpoints/trade_proposals.py +17 -0
  419. wbportfolio/viewsets/configs/endpoints/trades.py +82 -0
  420. wbportfolio/viewsets/configs/endpoints/transactions.py +17 -0
  421. wbportfolio/viewsets/configs/menu/__init__.py +30 -0
  422. wbportfolio/viewsets/configs/menu/adjustments.py +8 -0
  423. wbportfolio/viewsets/configs/menu/assets.py +8 -0
  424. wbportfolio/viewsets/configs/menu/claim.py +41 -0
  425. wbportfolio/viewsets/configs/menu/custodians.py +11 -0
  426. wbportfolio/viewsets/configs/menu/fees.py +13 -0
  427. wbportfolio/viewsets/configs/menu/instrument_prices.py +10 -0
  428. wbportfolio/viewsets/configs/menu/portfolio_cash_flow.py +8 -0
  429. wbportfolio/viewsets/configs/menu/portfolios.py +15 -0
  430. wbportfolio/viewsets/configs/menu/positions.py +14 -0
  431. wbportfolio/viewsets/configs/menu/product_groups.py +10 -0
  432. wbportfolio/viewsets/configs/menu/product_performance.py +25 -0
  433. wbportfolio/viewsets/configs/menu/products.py +15 -0
  434. wbportfolio/viewsets/configs/menu/reconciliations.py +7 -0
  435. wbportfolio/viewsets/configs/menu/registers.py +10 -0
  436. wbportfolio/viewsets/configs/menu/roles.py +16 -0
  437. wbportfolio/viewsets/configs/menu/trades.py +18 -0
  438. wbportfolio/viewsets/configs/menu/transactions.py +8 -0
  439. wbportfolio/viewsets/configs/previews/__init__.py +1 -0
  440. wbportfolio/viewsets/configs/previews/portfolios.py +21 -0
  441. wbportfolio/viewsets/configs/titles/__init__.py +65 -0
  442. wbportfolio/viewsets/configs/titles/adjustments.py +19 -0
  443. wbportfolio/viewsets/configs/titles/assets.py +57 -0
  444. wbportfolio/viewsets/configs/titles/assets_and_net_new_money_progression.py +6 -0
  445. wbportfolio/viewsets/configs/titles/claim.py +81 -0
  446. wbportfolio/viewsets/configs/titles/custodians.py +12 -0
  447. wbportfolio/viewsets/configs/titles/esg.py +10 -0
  448. wbportfolio/viewsets/configs/titles/fees.py +25 -0
  449. wbportfolio/viewsets/configs/titles/instrument_prices.py +20 -0
  450. wbportfolio/viewsets/configs/titles/portfolios.py +32 -0
  451. wbportfolio/viewsets/configs/titles/positions.py +11 -0
  452. wbportfolio/viewsets/configs/titles/product_groups.py +12 -0
  453. wbportfolio/viewsets/configs/titles/product_performance.py +16 -0
  454. wbportfolio/viewsets/configs/titles/products.py +6 -0
  455. wbportfolio/viewsets/configs/titles/registers.py +12 -0
  456. wbportfolio/viewsets/configs/titles/roles.py +23 -0
  457. wbportfolio/viewsets/configs/titles/trades.py +51 -0
  458. wbportfolio/viewsets/configs/titles/transactions.py +8 -0
  459. wbportfolio/viewsets/custodians.py +66 -0
  460. wbportfolio/viewsets/esg.py +165 -0
  461. wbportfolio/viewsets/mixins.py +48 -0
  462. wbportfolio/viewsets/portfolio_cash_flow.py +31 -0
  463. wbportfolio/viewsets/portfolio_cash_targets.py +8 -0
  464. wbportfolio/viewsets/portfolio_relationship.py +46 -0
  465. wbportfolio/viewsets/portfolio_swing_pricing.py +8 -0
  466. wbportfolio/viewsets/portfolios.py +154 -0
  467. wbportfolio/viewsets/positions.py +292 -0
  468. wbportfolio/viewsets/product_groups.py +84 -0
  469. wbportfolio/viewsets/product_performance.py +646 -0
  470. wbportfolio/viewsets/products.py +529 -0
  471. wbportfolio/viewsets/reconciliations.py +160 -0
  472. wbportfolio/viewsets/registers.py +75 -0
  473. wbportfolio/viewsets/roles.py +44 -0
  474. wbportfolio/viewsets/signals.py +42 -0
  475. wbportfolio/viewsets/synchronization.py +25 -0
  476. wbportfolio/viewsets/transactions/__init__.py +40 -0
  477. wbportfolio/viewsets/transactions/claim.py +933 -0
  478. wbportfolio/viewsets/transactions/fees.py +190 -0
  479. wbportfolio/viewsets/transactions/mixins.py +19 -0
  480. wbportfolio/viewsets/transactions/trade_proposals.py +93 -0
  481. wbportfolio/viewsets/transactions/trades.py +395 -0
  482. wbportfolio/viewsets/transactions/transactions.py +123 -0
  483. wbportfolio-2.2.1.dist-info/METADATA +21 -0
  484. wbportfolio-2.2.1.dist-info/RECORD +486 -0
  485. wbportfolio-2.2.1.dist-info/WHEEL +5 -0
  486. wbportfolio-2.2.1.dist-info/licenses/LICENSE +4 -0
@@ -0,0 +1,157 @@
1
+ from datetime import date
2
+
3
+ from django.dispatch import receiver
4
+ from rest_framework.reverse import reverse
5
+ from wbcore.contrib.directory.serializers import (
6
+ CompanyModelSerializer,
7
+ EntryModelSerializer,
8
+ PersonModelSerializer,
9
+ )
10
+ from wbcore.filters.defaults import current_quarter_date_start
11
+ from wbcore.signals import add_instance_additional_resource
12
+ from wbcrm.serializers.accounts import AccountModelSerializer
13
+ from wbfdm.serializers import InstrumentModelSerializer
14
+ from wbportfolio.models import PortfolioRole, Trade
15
+ from wbportfolio.serializers.products import ProductModelSerializer
16
+
17
+
18
+ @receiver(add_instance_additional_resource, sender=InstrumentModelSerializer)
19
+ @receiver(add_instance_additional_resource, sender=ProductModelSerializer)
20
+ def add_instrument_serializer_resources(sender, serializer, instance, request, user, **kwargs):
21
+ additional_resources = dict()
22
+
23
+ has_assets = any([i.assets.exists() for i in instance.get_descendants(include_self=True)])
24
+ if PortfolioRole.is_portfolio_manager(user.profile) and has_assets:
25
+ additional_resources["assets"] = reverse(
26
+ "wbportfolio:instrument-asset-list", args=[instance.id], request=request
27
+ )
28
+ additional_resources["assetschart"] = reverse(
29
+ "wbportfolio:instrument-assetpositionchart-list", args=[instance.id], request=request
30
+ )
31
+
32
+ if instance.portfolios.exists():
33
+ additional_resources["distribution_chart"] = reverse(
34
+ "wbportfolio:portfolio-distributionchart-list", args=[instance.portfolio.id], request=request
35
+ )
36
+ additional_resources["distribution_table"] = reverse(
37
+ "wbportfolio:portfolio-distributiontable-list", args=[instance.portfolio.id], request=request
38
+ )
39
+
40
+ if user.profile.is_internal or user.is_superuser:
41
+ if Trade.valid_customer_trade_objects.filter(underlying_instrument=instance).exists():
42
+ additional_resources["instrument_subscriptionsredemptions"] = reverse(
43
+ "wbportfolio:instrument-subscriptionredemption-list", args=[instance.id], request=request
44
+ )
45
+ if (
46
+ Trade.objects.filter(underlying_instrument__in=instance.get_descendants(include_self=True))
47
+ .exclude(transaction_subtype__in=[Trade.Type.REDEMPTION, Trade.Type.SUBSCRIPTION])
48
+ .exists()
49
+ ):
50
+ additional_resources[
51
+ "instrument_trades"
52
+ ] = f'{reverse("wbportfolio:instrument-trade-list", args=[instance.id], request=request)}?is_customer_trade=False'
53
+
54
+ if PortfolioRole.is_manager(user.profile):
55
+ additional_resources["portfoliorole"] = reverse(
56
+ "wbportfolio:instrument-portfoliorole-list", args=[instance.id], request=request
57
+ )
58
+ return additional_resources
59
+
60
+
61
+ @receiver(add_instance_additional_resource, sender=InstrumentModelSerializer)
62
+ @receiver(add_instance_additional_resource, sender=ProductModelSerializer)
63
+ def asset_portfolio_resources(sender, serializer, instance, request, user, **kwargs):
64
+ additional_resources = dict()
65
+ portfolio = instance.portfolio
66
+
67
+ if portfolio and portfolio.assets.exists():
68
+ if PortfolioRole.is_analyst(user.profile, portfolio=portfolio):
69
+ additional_resources["portfolio_positions"] = reverse(
70
+ "wbportfolio:portfolio-asset-list", args=[portfolio.id], request=request
71
+ )
72
+ additional_resources["portfolio_positions_contributors"] = reverse(
73
+ "wbportfolio:portfolio-contributor-list",
74
+ args=[portfolio.id],
75
+ request=request,
76
+ )
77
+ if request.user.has_perm("wbportfolio.administrate_instrument") and (
78
+ portfolio.portfolio_synchronization or hasattr(instance, "price_computation")
79
+ ):
80
+ additional_resources[
81
+ "resynchronize"
82
+ ] = f'{reverse("wbportfolio:portfolio-resynchronize", args=[portfolio.id], request=request)}?instrument={instance.id}'
83
+ return additional_resources
84
+
85
+
86
+ @receiver(add_instance_additional_resource, sender=InstrumentModelSerializer)
87
+ @receiver(add_instance_additional_resource, sender=ProductModelSerializer)
88
+ def transactions_resources(sender, serializer, instance, request, user, **kwargs):
89
+ additional_resources = dict()
90
+ portfolio = instance.primary_portfolio
91
+ if portfolio:
92
+ if portfolio.transactions.exists() and user.profile.is_internal:
93
+ additional_resources[
94
+ "portfolio_subscriptionsredemptions"
95
+ ] = f'{reverse("wbportfolio:portfolio-trade-list", args=[portfolio.id], request=request)}?is_customer_trade=True'
96
+ additional_resources[
97
+ "portfolio_trades"
98
+ ] = f'{reverse("wbportfolio:portfolio-trade-list", args=[portfolio.id], request=request)}?is_customer_trade=False'
99
+
100
+ additional_resources["portfolio_transactions"] = reverse(
101
+ "wbportfolio:portfolio-transaction-list", args=[portfolio.id], request=request
102
+ )
103
+ if PortfolioRole.is_portfolio_manager(user.profile, portfolio=portfolio):
104
+ additional_resources["portfolio_fees"] = reverse(
105
+ "wbportfolio:portfolio-fees-list", args=[portfolio.id], request=request
106
+ )
107
+ additional_resources["portfolio_aggregatedfees"] = reverse(
108
+ "wbportfolio:portfolio-feesaggregated-list", args=[portfolio.id], request=request
109
+ )
110
+
111
+ return additional_resources
112
+
113
+
114
+ @receiver(add_instance_additional_resource, sender=ProductModelSerializer)
115
+ def product_resources(sender, serializer, instance, request, user, **kwargs):
116
+ additional_resources = dict()
117
+ if user.profile.is_internal or user.is_superuser:
118
+ additional_resources["custodiandistribution"] = reverse(
119
+ "wbportfolio:instrument-custodiandistribution-list",
120
+ args=[instance.id],
121
+ request=request,
122
+ )
123
+ additional_resources["customerdistribution"] = reverse(
124
+ "wbportfolio:instrument-customerdistribution-list",
125
+ args=[instance.id],
126
+ request=request,
127
+ )
128
+ additional_resources["tradechart"] = reverse(
129
+ "wbportfolio:product-nominalchart-list",
130
+ args=[instance.id],
131
+ request=request,
132
+ )
133
+ additional_resources["aumchart"] = reverse(
134
+ "wbportfolio:product-aumchart-list",
135
+ args=[instance.id],
136
+ request=request,
137
+ )
138
+ return additional_resources
139
+
140
+
141
+ @receiver(add_instance_additional_resource, sender=CompanyModelSerializer)
142
+ @receiver(add_instance_additional_resource, sender=PersonModelSerializer)
143
+ @receiver(add_instance_additional_resource, sender=EntryModelSerializer)
144
+ def claim_adding_additional_resource(sender, serializer, instance, request, user, **kwargs):
145
+ start = current_quarter_date_start()
146
+ return {
147
+ "claims": reverse("wbportfolio:entry-claim-list", args=[instance.id], request=request),
148
+ "aum": f'{reverse("wbportfolio:aumtable-list", args=[], request=request)}?group_by=PRODUCT&account_owner={instance.id}&date_gte={start:%Y-%m-%d}&date_lte={date.today():%Y-%m-%d}',
149
+ }
150
+
151
+
152
+ @receiver(add_instance_additional_resource, sender=AccountModelSerializer)
153
+ def account_reconciliation_resource(sender, serializer, instance, request, user, **kwargs):
154
+ return {
155
+ "wbportfolio-accountreconciliation": f'{reverse("wbportfolio:accountreconciliation-list", request=request)}?account={instance.id}',
156
+ "wbportfolio-reconcile-account": f'{reverse("wbportfolio:accountreconciliation-list", request=request)}?account={instance.id}',
157
+ }
@@ -0,0 +1,18 @@
1
+ from wbcore import serializers as wb_serializers
2
+ from wbportfolio.models import PortfolioSynchronization, PriceComputation
3
+
4
+
5
+ class PortfolioSynchronizationRepresentationSerializer(wb_serializers.RepresentationSerializer):
6
+ _detail = wb_serializers.HyperlinkField(reverse_name="wbportfolio:portfolio-detail")
7
+
8
+ class Meta:
9
+ model = PortfolioSynchronization
10
+ fields = ("id", "name", "import_path", "_detail")
11
+
12
+
13
+ class PriceComputationRepresentationSerializer(wb_serializers.RepresentationSerializer):
14
+ _detail = wb_serializers.HyperlinkField(reverse_name="wbportfolio:portfolio-detail")
15
+
16
+ class Meta:
17
+ model = PriceComputation
18
+ fields = ("id", "name", "import_path", "_detail")
@@ -0,0 +1,24 @@
1
+ from .claim import (
2
+ ClaimAccountSerializer,
3
+ ClaimAPIModelSerializer,
4
+ ClaimCustomerModelSerializer,
5
+ ClaimModelSerializer,
6
+ ClaimRepresentationSerializer,
7
+ ClaimTradeModelSerializer,
8
+ NegativeTermimalAccountPerProductModelSerializer,
9
+ )
10
+ from .dividends import DividendModelSerializer, DividendRepresentationSerializer
11
+ from .expiry import ExpiryModelSerializer, ExpiryRepresentationSerializer
12
+ from .fees import FeesModelSerializer, FeesRepresentationSerializer
13
+ from .trades import (
14
+ TradeModelSerializer,
15
+ TradeProposalModelSerializer,
16
+ TradeProposalRepresentationSerializer,
17
+ TradeRepresentationSerializer,
18
+ TradeTradeProposalModelSerializer,
19
+ ReadOnlyTradeTradeProposalModelSerializer,
20
+ )
21
+ from .transactions import (
22
+ TransactionModelSerializer,
23
+ TransactionRepresentationSerializer,
24
+ )
@@ -0,0 +1,310 @@
1
+ from django.utils import timezone
2
+ from django.utils.functional import cached_property
3
+ from rest_framework.serializers import ValidationError
4
+ from wbcore import serializers
5
+ from wbcore.contrib.directory.models import Person
6
+ from wbcore.contrib.directory.serializers import EntryRepresentationSerializer
7
+ from wbcrm.models import Account
8
+ from wbcrm.serializers.accounts import TerminalAccountRepresentationSerializer
9
+ from wbportfolio.models import Product, Trade
10
+ from wbportfolio.models.transactions.claim import Claim
11
+ from wbportfolio.serializers.products import (
12
+ ProductCustomerRepresentationSerializer,
13
+ ProductRepresentationSerializer,
14
+ )
15
+ from wbportfolio.serializers.transactions.trades import (
16
+ TradeClaimRepresentationSerializer,
17
+ )
18
+
19
+
20
+ class ClaimAPIModelSerializer(serializers.ModelSerializer):
21
+ product = serializers.PrimaryKeyRelatedField(
22
+ required=False, queryset=Product.objects.all(), default=serializers.DefaultFromGET("product")
23
+ )
24
+ product_repr = serializers.StringRelatedField(source="product")
25
+ claimant_repr = serializers.StringRelatedField(source="claimant")
26
+ isin = serializers.SerializerMethodField()
27
+
28
+ def get_isin(self, obj):
29
+ return obj.product.isin
30
+
31
+ @cached_property
32
+ def user_profile(self) -> Person | None:
33
+ if request := self.context.get("request"):
34
+ return request.user.profile
35
+ return None
36
+
37
+ def validate(self, data):
38
+ if "creator" not in data:
39
+ data["creator"] = self.user_profile
40
+
41
+ if "claimant" not in data:
42
+ data["claimant"] = self.user_profile
43
+
44
+ if not self.instance and (request := self.context.get("request")):
45
+ if "product" not in data and "isin" not in request.data:
46
+ raise ValidationError({"product": "Either product or ISIN has to be supplied"})
47
+
48
+ if "isin" in request.data:
49
+ try:
50
+ data["product"] = Product.objects.get(isin=request.data["isin"])
51
+ except Product.DoesNotExist:
52
+ raise ValidationError({"isin": "A product with this ISIN does not exist."})
53
+ if trade := data.get("trade", None):
54
+ if not trade.is_claimable:
55
+ raise ValidationError({"trade": "Only a claimable trade can be selected"})
56
+ return data
57
+
58
+ class Meta:
59
+ model = Claim
60
+ fields = (
61
+ "id",
62
+ "status",
63
+ "product",
64
+ "product_repr",
65
+ "claimant",
66
+ "claimant_repr",
67
+ "date",
68
+ "bank",
69
+ "reference",
70
+ "shares",
71
+ "isin",
72
+ "external_id",
73
+ )
74
+
75
+
76
+ class ClaimRepresentationSerializer(serializers.RepresentationSerializer):
77
+ _detail = serializers.HyperlinkField(reverse_name="wbportfolio:claim-detail")
78
+
79
+ class Meta:
80
+ model = Claim
81
+ fields = (
82
+ "id",
83
+ "shares",
84
+ "bank",
85
+ "account",
86
+ "_detail",
87
+ )
88
+
89
+
90
+ class ClaimModelSerializer(serializers.ModelSerializer):
91
+ account = serializers.PrimaryKeyRelatedField(
92
+ required=False,
93
+ queryset=Account.objects.all(),
94
+ default=serializers.DefaultFromGetOrKwargs("account", "account_id"),
95
+ many=False,
96
+ )
97
+ _account = TerminalAccountRepresentationSerializer(source="account")
98
+ _trade = TradeClaimRepresentationSerializer(
99
+ source="trade",
100
+ optional_get_parameters={"product": "underlying_instrument", "date": "pivot_date"},
101
+ depends_on=[{"field": "product", "options": {}}],
102
+ )
103
+ _product = ProductRepresentationSerializer(source="product")
104
+ _creator = EntryRepresentationSerializer(source="creator")
105
+ claimant = serializers.PrimaryKeyRelatedField(
106
+ required=False,
107
+ queryset=Person.objects.all(),
108
+ default=serializers.CurrentUserDefault("profile"),
109
+ many=False,
110
+ read_only=lambda view: not view.request.user.profile.is_internal,
111
+ )
112
+ _claimant = EntryRepresentationSerializer(source="claimant")
113
+
114
+ product = serializers.PrimaryKeyRelatedField(
115
+ required=False, queryset=Product.objects.all(), default=serializers.DefaultFromGET("product"), many=False
116
+ )
117
+
118
+ creator = serializers.PrimaryKeyRelatedField(
119
+ required=False, queryset=Person.objects.all(), default=serializers.CurrentUserDefault("profile"), many=False
120
+ )
121
+
122
+ currency = serializers.CharField(read_only=True, required=False)
123
+ last_nav = serializers.FloatField(
124
+ default=0, read_only=True, label="NAV", required=False, help_text="The latest product's NAV"
125
+ )
126
+ total_value = serializers.FloatField(
127
+ default=0,
128
+ read_only=True,
129
+ label="Value",
130
+ required=False,
131
+ help_text="The total value (shares * Nav) in the trade currency",
132
+ )
133
+ total_value_usd = serializers.FloatField(
134
+ default=0, read_only=True, label="Value ($)", required=False, help_text="The total value (shares * Nav) in USD"
135
+ )
136
+ date = serializers.DateField(default=lambda: timezone.now().date())
137
+ trade_comment = serializers.TextField(read_only=True, required=False)
138
+
139
+ def validate(self, data):
140
+ # If shares or nominal_amount is supplied we can just continue
141
+ if "shares" in data or "nominal_amount" in data:
142
+ # Pop the trade_type and quantity fields, as they are not needed anymore
143
+ data.pop("trade_type", None)
144
+ data.pop("quantity", None)
145
+ return data
146
+
147
+ # If we do not have a trade type and instance is None, then we have to raise an error
148
+ if "trade_type" not in data and self.instance is None:
149
+ raise ValidationError({"trade_type": "Trade type is required"})
150
+
151
+ trade_type = data.pop("trade_type", self.instance.shares > 0 if self.instance else True)
152
+ as_shares = data.get("as_shares", self.instance.as_shares if self.instance else True)
153
+
154
+ # If a new quantity is provided, we take that one, if not we take the value from the instance, depending on the as_shares flag from before this instance was saved
155
+ quantity = data.pop(
156
+ "quantity",
157
+ (
158
+ abs(self.instance.shares)
159
+ if self.instance and self.instance.as_shares
160
+ else abs(self.instance.nominal_amount)
161
+ if self.instance
162
+ else None
163
+ ),
164
+ )
165
+ if quantity is None:
166
+ raise ValidationError({"quantity": "You must provide a quantity"})
167
+
168
+ # We set shares/nominal_amount properly based on as_shares and the given quantity
169
+ if as_shares:
170
+ data["nominal_amount"] = None
171
+ data["shares"] = quantity if trade_type else quantity * -1
172
+ else:
173
+ data["shares"] = None
174
+ data["nominal_amount"] = quantity if trade_type else quantity * -1
175
+ return data
176
+
177
+ def create(self, validated_data):
178
+ instance = super().create(validated_data)
179
+
180
+ # We need to attach the two virtual fields back to the instance, as it won't be reloaded from the db
181
+ instance.trade_type = instance.shares > 0
182
+ instance.quantity = instance.shares if instance.as_shares else instance.nominal_amount
183
+ return instance
184
+
185
+ def update(self, instance, validated_data):
186
+ instance = super().update(instance, validated_data)
187
+
188
+ # We need to attach the two virtual fields back to the instance, as it won't be reloaded from the db
189
+ instance.trade_type = instance.shares > 0
190
+ instance.quantity = instance.shares if instance.as_shares else instance.nominal_amount
191
+ return instance
192
+
193
+ trade_type = serializers.BooleanField(
194
+ labels=["Subscription", "Redemption"],
195
+ values=[True, False],
196
+ active_background_color=["#2e7d32", "white", "#d32f2f"],
197
+ active_color=["white", "black", "white"],
198
+ required=False,
199
+ )
200
+ as_shares = serializers.BooleanField(labels=["Shares", "Nominal"], values=[True, False], label="Quantity Type")
201
+ quantity = serializers.DecimalField(
202
+ max_digits=15,
203
+ decimal_places=4, # the number of decimal for quantity needs to match the number for the shares field
204
+ signed=False,
205
+ depends_on=[{"field": "trade_type", "options": {}}, {"field": "as_shares", "options": {}}],
206
+ required=False,
207
+ )
208
+
209
+ class Meta:
210
+ model = Claim
211
+ decorators = {
212
+ "last_nav": serializers.decorator(decorator_type="text", position="left", value="{{currency}}"),
213
+ "total_value": serializers.decorator(decorator_type="text", position="left", value="{{currency}}"),
214
+ "total_value_usd": serializers.decorator(decorator_type="text", position="left", value="$"),
215
+ }
216
+ fields = (
217
+ "id",
218
+ "status",
219
+ "account",
220
+ "_account",
221
+ "trade",
222
+ "_trade",
223
+ "product",
224
+ "_product",
225
+ "claimant",
226
+ "_claimant",
227
+ "creator",
228
+ "_creator",
229
+ "date",
230
+ "bank",
231
+ "reference",
232
+ "shares",
233
+ "last_nav",
234
+ "total_value",
235
+ "total_value_usd",
236
+ "currency",
237
+ "trade_type",
238
+ "quantity",
239
+ "as_shares",
240
+ "nominal_amount",
241
+ "trade_comment",
242
+ "_additional_resources",
243
+ "_buttons",
244
+ )
245
+
246
+
247
+ class ClaimCustomerModelSerializer(ClaimModelSerializer):
248
+ _product = ProductCustomerRepresentationSerializer(source="product")
249
+
250
+ class Meta:
251
+ model = Claim
252
+ fields = (
253
+ "id",
254
+ "status",
255
+ "account",
256
+ "_account",
257
+ "account",
258
+ "product",
259
+ "_product",
260
+ "claimant",
261
+ "_claimant",
262
+ "creator",
263
+ "_creator",
264
+ "date",
265
+ "bank",
266
+ "last_nav",
267
+ "total_value",
268
+ "total_value_usd",
269
+ "reference",
270
+ "shares",
271
+ "nominal_amount",
272
+ "trade_type",
273
+ "quantity",
274
+ "as_shares",
275
+ "_additional_resources",
276
+ )
277
+
278
+
279
+ class ClaimTradeModelSerializer(ClaimModelSerializer):
280
+ bank = serializers.CharField(default=serializers.DefaultFromView("trade.bank"))
281
+ product = serializers.PrimaryKeyRelatedField(
282
+ default=serializers.DefaultFromView("trade.product"),
283
+ many=False,
284
+ queryset=Product.objects.all(),
285
+ )
286
+ date = serializers.DateField(default=serializers.DefaultFromView("trade.transaction_date"))
287
+
288
+ def validate(self, data):
289
+ trade = Trade.objects.get(id=self.context["view"].kwargs["trade_id"])
290
+ data["date"] = trade.transaction_date
291
+ data["product"] = trade.product
292
+ data["bank"] = trade.bank
293
+ return super().validate(data)
294
+
295
+
296
+ class ClaimAccountSerializer(ClaimModelSerializer):
297
+ _account = TerminalAccountRepresentationSerializer(source="account")
298
+
299
+
300
+ class NegativeTermimalAccountPerProductModelSerializer(serializers.ModelSerializer):
301
+ id = serializers.PrimaryKeyCharField(read_only=True)
302
+ product_repr = serializers.CharField(read_only=True)
303
+ account_repr = serializers.CharField(read_only=True)
304
+ product_id = serializers.PrimaryKeyField(read_only=True)
305
+ account_id = serializers.PrimaryKeyField(read_only=True)
306
+ sum_shares = serializers.DecimalField(read_only=True, max_digits=12, decimal_places=2)
307
+
308
+ class Meta:
309
+ model = Claim
310
+ fields = ("id", "product_repr", "account_repr", "product_id", "account_id", "sum_shares")
@@ -0,0 +1,18 @@
1
+ from wbportfolio.models import DividendTransaction
2
+
3
+ from .transactions import (
4
+ TransactionModelSerializer,
5
+ TransactionRepresentationSerializer,
6
+ )
7
+
8
+
9
+ class DividendRepresentationSerializer(TransactionRepresentationSerializer):
10
+ class Meta:
11
+ model = DividendTransaction
12
+ fields = TransactionRepresentationSerializer.Meta.fields
13
+
14
+
15
+ class DividendModelSerializer(TransactionModelSerializer):
16
+ class Meta:
17
+ model = DividendTransaction
18
+ fields = TransactionModelSerializer.Meta.fields
@@ -0,0 +1,18 @@
1
+ from wbportfolio.models import Expiry
2
+
3
+ from .transactions import (
4
+ TransactionModelSerializer,
5
+ TransactionRepresentationSerializer,
6
+ )
7
+
8
+
9
+ class ExpiryRepresentationSerializer(TransactionRepresentationSerializer):
10
+ class Meta:
11
+ model = Expiry
12
+ fields = TransactionRepresentationSerializer.Meta.fields
13
+
14
+
15
+ class ExpiryModelSerializer(TransactionModelSerializer):
16
+ class Meta:
17
+ model = Expiry
18
+ fields = TransactionModelSerializer.Meta.fields
@@ -0,0 +1,32 @@
1
+ from wbcore import serializers as wb_serializers
2
+ from wbportfolio.models import Fees
3
+
4
+ from .transactions import (
5
+ TransactionModelSerializer,
6
+ TransactionRepresentationSerializer,
7
+ )
8
+
9
+
10
+ class FeesRepresentationSerializer(TransactionRepresentationSerializer):
11
+ class Meta:
12
+ model = Fees
13
+ fields = ("transaction_subtype",) + TransactionRepresentationSerializer.Meta.fields
14
+
15
+
16
+ class FeesModelSerializer(TransactionModelSerializer):
17
+ @wb_serializers.register_resource()
18
+ def additional_resources(self, instance, request, user):
19
+ if (view := request.parser_context.get("view")) and view.is_manager and instance.import_source:
20
+ return {"import_source": instance.import_source.file.url}
21
+ return {}
22
+
23
+ class Meta:
24
+ model = Fees
25
+ decorators = TransactionModelSerializer.Meta.decorators
26
+ fields = (
27
+ "_additional_resources",
28
+ "transaction_subtype",
29
+ "calculated",
30
+ "fee_date",
31
+ "linked_product",
32
+ ) + TransactionModelSerializer.Meta.fields