aa-ledger 1.0.3__py3-none-any.whl → 2.0.0__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.
Files changed (280) hide show
  1. {aa_ledger-1.0.3.dist-info → aa_ledger-2.0.0.dist-info}/METADATA +6 -6
  2. aa_ledger-2.0.0.dist-info/RECORD +267 -0
  3. {aa_ledger-1.0.3.dist-info → aa_ledger-2.0.0.dist-info}/WHEEL +1 -1
  4. ledger/__init__.py +2 -2
  5. ledger/admin.py +23 -18
  6. ledger/api/__init__.py +23 -7
  7. ledger/api/{ledger/admin.py → admin.py} +25 -31
  8. ledger/api/alliance.py +755 -0
  9. ledger/api/character.py +786 -0
  10. ledger/api/corporation.py +1141 -0
  11. ledger/api/{helpers.py → helpers/core.py} +33 -33
  12. ledger/api/helpers/icons.py +372 -0
  13. ledger/api/helpers/planetary_helper.py +354 -0
  14. ledger/api/planetary.py +354 -0
  15. ledger/api/schema.py +240 -15
  16. ledger/app_settings.py +18 -26
  17. ledger/auth_hooks.py +2 -2
  18. ledger/constants.py +50 -177
  19. ledger/decorators.py +2 -46
  20. ledger/forms.py +133 -39
  21. ledger/helpers/billboard.py +194 -144
  22. ledger/helpers/cache.py +105 -0
  23. ledger/helpers/discord.py +2 -4
  24. ledger/helpers/eveonline.py +160 -0
  25. ledger/helpers/ledger_data.py +23 -0
  26. ledger/helpers/ref_type.py +53 -78
  27. ledger/locale/cs_CZ/LC_MESSAGES/django.mo +0 -0
  28. ledger/locale/cs_CZ/LC_MESSAGES/django.po +349 -193
  29. ledger/locale/de/LC_MESSAGES/django.mo +0 -0
  30. ledger/locale/de/LC_MESSAGES/django.po +528 -379
  31. ledger/locale/django.pot +717 -553
  32. ledger/locale/es/LC_MESSAGES/django.mo +0 -0
  33. ledger/locale/es/LC_MESSAGES/django.po +349 -194
  34. ledger/locale/fr_FR/LC_MESSAGES/django.mo +0 -0
  35. ledger/locale/fr_FR/LC_MESSAGES/django.po +349 -193
  36. ledger/locale/it_IT/LC_MESSAGES/django.mo +0 -0
  37. ledger/locale/it_IT/LC_MESSAGES/django.po +349 -193
  38. ledger/locale/ja/LC_MESSAGES/django.mo +0 -0
  39. ledger/locale/ja/LC_MESSAGES/django.po +348 -193
  40. ledger/locale/ko_KR/LC_MESSAGES/django.mo +0 -0
  41. ledger/locale/ko_KR/LC_MESSAGES/django.po +349 -193
  42. ledger/locale/nl_NL/LC_MESSAGES/django.mo +0 -0
  43. ledger/locale/nl_NL/LC_MESSAGES/django.po +349 -193
  44. ledger/locale/pl_PL/LC_MESSAGES/django.mo +0 -0
  45. ledger/locale/pl_PL/LC_MESSAGES/django.po +350 -193
  46. ledger/locale/ru/LC_MESSAGES/django.mo +0 -0
  47. ledger/locale/ru/LC_MESSAGES/django.po +348 -193
  48. ledger/locale/sk/LC_MESSAGES/django.mo +0 -0
  49. ledger/locale/sk/LC_MESSAGES/django.po +348 -193
  50. ledger/locale/uk/LC_MESSAGES/django.mo +0 -0
  51. ledger/locale/uk/LC_MESSAGES/django.po +348 -193
  52. ledger/locale/zh_Hans/LC_MESSAGES/django.mo +0 -0
  53. ledger/locale/zh_Hans/LC_MESSAGES/django.po +348 -193
  54. ledger/managers/character_audit_manager.py +28 -20
  55. ledger/managers/character_journal_manager.py +187 -358
  56. ledger/managers/character_mining_manager.py +64 -30
  57. ledger/managers/character_planetary_manager.py +185 -138
  58. ledger/managers/corporation_audit_manager.py +36 -27
  59. ledger/managers/corporation_journal_manager.py +94 -57
  60. ledger/managers/general_manager.py +12 -8
  61. ledger/migrations/0018_remove_characterplanet_ledger_char_planet__58a5b6_idx_and_more.py +44 -0
  62. ledger/migrations/0019_rename_characteraudit_characterowner_and_more.py +48 -0
  63. ledger/models/__init__.py +5 -11
  64. ledger/models/characteraudit.py +101 -109
  65. ledger/models/corporationaudit.py +94 -49
  66. ledger/models/general.py +105 -211
  67. ledger/models/helpers/update_manager.py +302 -0
  68. ledger/models/planetary.py +60 -205
  69. ledger/providers.py +101 -0
  70. ledger/static/ledger/css/{ledger.css → aa-ledger.css} +54 -28
  71. ledger/static/ledger/js/aa-ledger.js +124 -0
  72. ledger/static/ledger/js/charts.js +25 -1
  73. ledger/static/ledger/js/view-alliance-ledger.js +383 -0
  74. ledger/static/ledger/js/view-character-ledger.js +388 -0
  75. ledger/static/ledger/js/view-corporation-ledger.js +402 -0
  76. ledger/static/ledger/js/view-planetary.js +492 -0
  77. ledger/static/ledger/libs/amCharts/5.14.4/js/flow.js +2 -0
  78. ledger/static/ledger/libs/amCharts/5.14.4/js/index.js +2 -0
  79. ledger/static/ledger/libs/amCharts/5.14.4/js/percent.js +2 -0
  80. ledger/static/ledger/libs/amCharts/5.14.4/js/themes/Animated.js +2 -0
  81. ledger/static/ledger/libs/amCharts/5.14.4/js/themes/Dark.js +2 -0
  82. ledger/static/ledger/libs/amCharts/5.14.4/js/xy.js +2 -0
  83. ledger/static/ledger/libs/datatables/2.3.5/css/dataTables.bootstrap5.css +610 -0
  84. ledger/static/ledger/libs/datatables/2.3.5/js/dataTables.bootstrap5.js +122 -0
  85. ledger/static/ledger/libs/datatables/2.3.5/js/dataTables.js +14127 -0
  86. ledger/static/ledger/libs/datatables/Extensions/ColumnControl/1.1.1/css/columnControl.bootstrap5.css +516 -0
  87. ledger/static/ledger/libs/datatables/Extensions/ColumnControl/1.1.1/css/columnControl.dataTables.css +529 -0
  88. ledger/static/ledger/libs/datatables/Extensions/ColumnControl/1.1.1/js/columnControl.bootstrap5.js +73 -0
  89. ledger/static/ledger/libs/datatables/Extensions/ColumnControl/1.1.1/js/dataTables.columnControl.js +3090 -0
  90. ledger/static/ledger/libs/datatables/Extensions/FixedHeader/4.0.4/css/fixedHeader.bootstrap5.css +20 -0
  91. ledger/static/ledger/libs/datatables/Extensions/FixedHeader/4.0.4/js/dataTables.fixedHeader.js +1203 -0
  92. ledger/static/ledger/libs/datatables/Extensions/FixedHeader/4.0.4/js/fixedHeader.bootstrap5.js +59 -0
  93. ledger/tasks.py +157 -146
  94. ledger/templates/ledger/base.html +59 -21
  95. ledger/templates/ledger/bundles/aa-ledger-css.html +3 -0
  96. ledger/templates/ledger/bundles/aa-ledger-js.html +3 -0
  97. ledger/templates/ledger/bundles/view-alliance-ledger-js.html +14 -0
  98. ledger/templates/ledger/bundles/view-character-ledger-js.html +15 -0
  99. ledger/templates/ledger/bundles/view-character-planetary-css.html +3 -0
  100. ledger/templates/ledger/bundles/view-character-planetary-js.html +4 -0
  101. ledger/templates/ledger/bundles/view-corporation-ledger-js.html +15 -0
  102. ledger/templates/ledger/partials/modal/confirm.html +0 -1
  103. ledger/templates/ledger/partials/modal/request-accept-delete-alliance.html +38 -0
  104. ledger/templates/ledger/partials/modal/request-accept-delete-character.html +38 -0
  105. ledger/templates/ledger/partials/modal/request-accept-delete-corporation.html +38 -0
  106. ledger/templates/ledger/partials/modal/request-accept-switch-notification.html +38 -0
  107. ledger/templates/ledger/partials/modal/request-view-alliance-details.html +26 -0
  108. ledger/templates/ledger/partials/modal/request-view-character-details.html +26 -0
  109. ledger/templates/ledger/partials/modal/request-view-corporation-details.html +26 -0
  110. ledger/templates/ledger/partials/modal/request-view-extractor.html +32 -0
  111. ledger/templates/ledger/partials/modal/request-view-factory.html +31 -0
  112. ledger/templates/ledger/partials/{menu → navigation}/administration.html +8 -0
  113. ledger/templates/ledger/partials/{menu → navigation}/navigation.html +2 -2
  114. ledger/templates/ledger/partials/{administration → view-alliance-administration}/alliance_corporations.html +3 -3
  115. ledger/templates/ledger/partials/view-alliance-administration/dashboard.html +81 -0
  116. ledger/templates/ledger/partials/view-alliance-ledger/alliance-billboard.html +25 -0
  117. ledger/templates/ledger/partials/view-alliance-ledger/alliance-ledger-details.html +21 -0
  118. ledger/templates/ledger/partials/view-alliance-ledger/alliance-table.html +24 -0
  119. ledger/templates/ledger/partials/view-alliance-ledger/information/daily.html +18 -0
  120. ledger/templates/ledger/partials/view-alliance-ledger/information/hourly.html +18 -0
  121. ledger/templates/ledger/partials/view-alliance-ledger/information/summary.html +19 -0
  122. ledger/templates/ledger/partials/{administration → view-character-administration}/character.html +1 -9
  123. ledger/templates/ledger/partials/{administration → view-character-administration}/dashboard.html +0 -34
  124. ledger/templates/ledger/partials/view-character-ledger/character-billboard.html +25 -0
  125. ledger/templates/ledger/partials/view-character-ledger/character-ledger-details.html +21 -0
  126. ledger/templates/ledger/partials/view-character-ledger/character-table.html +25 -0
  127. ledger/templates/ledger/partials/view-character-ledger/information/daily.html +18 -0
  128. ledger/templates/ledger/partials/view-character-ledger/information/hourly.html +18 -0
  129. ledger/templates/ledger/partials/view-character-ledger/information/summary.html +19 -0
  130. ledger/templates/ledger/partials/view-character-planetary/extractor-table.html +24 -0
  131. ledger/templates/ledger/partials/view-character-planetary/factory-table.html +24 -0
  132. ledger/templates/ledger/partials/view-character-planetary/planetary-table.html +22 -0
  133. ledger/templates/ledger/partials/view-character-planetary/storage-table.html +23 -0
  134. ledger/templates/ledger/partials/{administration → view-corporation-administration}/corporation.html +5 -13
  135. ledger/templates/ledger/partials/{administration → view-corporation-administration}/corporation_characters.html +1 -1
  136. ledger/templates/ledger/partials/view-corporation-administration/dashboard.html +81 -0
  137. ledger/templates/ledger/partials/view-corporation-ledger/corporation-billboard.html +25 -0
  138. ledger/templates/ledger/partials/view-corporation-ledger/corporation-ledger-details.html +21 -0
  139. ledger/templates/ledger/partials/view-corporation-ledger/corporation-table.html +26 -0
  140. ledger/templates/ledger/partials/view-corporation-ledger/information/daily.html +18 -0
  141. ledger/templates/ledger/partials/view-corporation-ledger/information/hourly.html +18 -0
  142. ledger/templates/ledger/partials/view-corporation-ledger/information/summary.html +19 -0
  143. ledger/templates/ledger/view-administration.html +62 -0
  144. ledger/templates/ledger/view-alliance-administration.html +49 -0
  145. ledger/templates/ledger/view-alliance-ledger.html +72 -0
  146. ledger/templates/ledger/view-alliance-overview.html +131 -0
  147. ledger/templates/ledger/view-character-administration.html +42 -0
  148. ledger/templates/ledger/view-character-ledger.html +73 -0
  149. ledger/templates/ledger/view-character-overview.html +135 -0
  150. ledger/templates/ledger/view-character-planetary-overview.html +135 -0
  151. ledger/templates/ledger/view-character-planetary.html +73 -0
  152. ledger/templates/ledger/view-corporation-administration.html +42 -0
  153. ledger/templates/ledger/view-corporation-ledger.html +73 -0
  154. ledger/templates/ledger/view-corporation-overview.html +131 -0
  155. ledger/templatetags/ledger.py +3 -5
  156. ledger/tests/__init__.py +187 -0
  157. ledger/tests/test_admin.py +164 -68
  158. ledger/tests/test_auth_hook.py +31 -13
  159. ledger/tests/test_decarators.py +14 -79
  160. ledger/tests/test_discord_installed.py +0 -1
  161. ledger/tests/test_helpers/test_ledger_data.py +19 -0
  162. ledger/tests/test_managers/test_character_audit_manager.py +111 -69
  163. ledger/tests/test_managers/test_character_journal_manager.py +48 -208
  164. ledger/tests/test_managers/test_character_mining_manager.py +37 -16
  165. ledger/tests/test_managers/test_corporation_division_manager.py +66 -28
  166. ledger/tests/test_managers/test_corporation_journal_manager.py +39 -42
  167. ledger/tests/test_managers/test_general_manager.py +78 -18
  168. ledger/tests/test_managers/test_planetary_manager.py +73 -32
  169. ledger/tests/test_models/test_characteraudit.py +58 -74
  170. ledger/tests/test_models/test_characterminingledger.py +20 -26
  171. ledger/tests/test_models/test_characterwalletjournal.py +10 -33
  172. ledger/tests/test_models/test_corporationaudit.py +41 -35
  173. ledger/tests/test_models/test_corporationwalletjournal.py +35 -32
  174. ledger/tests/test_models/test_general.py +44 -11
  175. ledger/tests/test_models/test_planetary.py +14 -80
  176. ledger/tests/test_templatetags.py +2 -7
  177. ledger/tests/test_views/corporation/test_add_corp.py +16 -35
  178. ledger/tests/test_views/corporation/test_delete_corporation.py +66 -42
  179. ledger/tests/test_views/test_access.py +512 -545
  180. ledger/tests/test_views/test_add_ally.py +57 -46
  181. ledger/tests/test_views/test_add_char.py +21 -33
  182. ledger/tests/test_views/test_delete_character.py +24 -21
  183. ledger/tests/testdata/README_ESI_STUB.md +430 -0
  184. ledger/tests/testdata/esi_stub_openapi.py +511 -0
  185. ledger/tests/testdata/integrations/__init__.py +0 -0
  186. ledger/tests/testdata/{load_eveuniverse.py → integrations/eveuniverse.py} +0 -1
  187. ledger/tests/testdata/integrations/planetary.py +13 -0
  188. ledger/tests/testdata/json/factory.json +281 -0
  189. ledger/tests/testdata/json/inactive.json +281 -0
  190. ledger/tests/testdata/json/pins.json +175 -272
  191. ledger/tests/testdata/json/route.json +95 -528
  192. ledger/tests/testdata/test_esi_stub.py +468 -0
  193. ledger/tests/testdata/utils.py +601 -0
  194. ledger/thirdparty/charlink_hook.py +60 -30
  195. ledger/urls.py +0 -135
  196. ledger/views/alliance/add_ally.py +2 -4
  197. ledger/views/alliance/alliance_ledger.py +64 -147
  198. ledger/views/character/add_char.py +8 -10
  199. ledger/views/character/character_ledger.py +60 -126
  200. ledger/views/character/planetary.py +5 -98
  201. ledger/views/corporation/add_corp.py +10 -12
  202. ledger/views/corporation/corporation_ledger.py +65 -327
  203. ledger/views/index.py +92 -30
  204. aa_ledger-1.0.3.dist-info/RECORD +0 -236
  205. ledger/api/api_helper/planetary_helper.py +0 -107
  206. ledger/api/ledger/__init__.py +0 -7
  207. ledger/api/ledger/planetary.py +0 -231
  208. ledger/helpers/alliance.py +0 -317
  209. ledger/helpers/character.py +0 -251
  210. ledger/helpers/core.py +0 -665
  211. ledger/helpers/corporation.py +0 -427
  212. ledger/helpers/data_exporter.py +0 -452
  213. ledger/static/ledger/js/planetary-confirm.js +0 -66
  214. ledger/static/ledger/js/planetary.js +0 -143
  215. ledger/templates/ledger/admin.html +0 -43
  216. ledger/templates/ledger/allyledger/admin/alliance_administration.html +0 -46
  217. ledger/templates/ledger/allyledger/admin/alliance_overview.html +0 -108
  218. ledger/templates/ledger/allyledger/alliance_ledger.html +0 -86
  219. ledger/templates/ledger/bundles/character-ledger-bundles.html +0 -66
  220. ledger/templates/ledger/bundles/corporation-ledger-bundles.html +0 -75
  221. ledger/templates/ledger/bundles/ledger-bundles.html +0 -23
  222. ledger/templates/ledger/bundles/ledger-css.html +0 -3
  223. ledger/templates/ledger/bundles/planetary-bundles.html +0 -50
  224. ledger/templates/ledger/bundles/table-css.html +0 -3
  225. ledger/templates/ledger/charledger/admin/character_administration.html +0 -39
  226. ledger/templates/ledger/charledger/admin/character_overview.html +0 -106
  227. ledger/templates/ledger/charledger/character_ledger.html +0 -94
  228. ledger/templates/ledger/charledger/planetary/admin/planetary_overview.html +0 -123
  229. ledger/templates/ledger/charledger/planetary/planetary_ledger.html +0 -54
  230. ledger/templates/ledger/corpledger/admin/corporation_administration.html +0 -39
  231. ledger/templates/ledger/corpledger/admin/corporation_overview.html +0 -108
  232. ledger/templates/ledger/corpledger/corporation_ledger.html +0 -129
  233. ledger/templates/ledger/data-export.html +0 -78
  234. ledger/templates/ledger/error.html +0 -31
  235. ledger/templates/ledger/partials/form/error-message.html +0 -1
  236. ledger/templates/ledger/partials/information/daily.html +0 -56
  237. ledger/templates/ledger/partials/information/day.html +0 -48
  238. ledger/templates/ledger/partials/information/error.html +0 -8
  239. ledger/templates/ledger/partials/information/hourly.html +0 -53
  240. ledger/templates/ledger/partials/information/summary.html +0 -88
  241. ledger/templates/ledger/partials/information/view_character_content.html +0 -35
  242. ledger/templates/ledger/partials/modal/switchalarm_confirm.html +0 -39
  243. ledger/templates/ledger/partials/modal/view_extractor.html +0 -48
  244. ledger/templates/ledger/partials/modal/view_factory.html +0 -123
  245. ledger/templates/ledger/partials/table/char-ledger.html +0 -85
  246. ledger/templates/ledger/partials/table/corp-ledger.html +0 -66
  247. ledger/templates/ledger/partials/table/planetary.html +0 -18
  248. ledger/templates/ledger/partials/thirdparty/billboard.html +0 -22
  249. ledger/templates/ledger/partials/view/card.html +0 -160
  250. ledger/templates/ledger/permission.html +0 -2
  251. ledger/tests/test_helpers/test_billboard.py +0 -11
  252. ledger/tests/test_helpers/test_data_exporter.py +0 -207
  253. ledger/tests/test_tasks.py +0 -282
  254. ledger/tests/test_view_helpers/test_core.py +0 -47
  255. ledger/tests/test_views/corporation/test_corporation.py +0 -267
  256. ledger/tests/test_views/test_planetary.py +0 -137
  257. ledger/tests/testdata/esi_stub.py +0 -109
  258. ledger/tests/testdata/esi_stub_migration.py +0 -80
  259. ledger/tests/testdata/generate_characteraudit.py +0 -106
  260. ledger/tests/testdata/generate_corporationaudit.py +0 -74
  261. ledger/tests/testdata/generate_events.py +0 -31
  262. ledger/tests/testdata/generate_miningledger.py +0 -13
  263. ledger/tests/testdata/generate_planets.py +0 -48
  264. ledger/tests/testdata/generate_walletjournal.py +0 -42
  265. ledger/tests/testdata/json/czarno-pins.json +0 -240
  266. ledger/tests/testdata/json/czarno-routes.json +0 -165
  267. ledger/tests/testdata/json/pins2.json +0 -538
  268. {aa_ledger-1.0.3.dist-info → aa_ledger-2.0.0.dist-info}/licenses/LICENSE +0 -0
  269. /ledger/{tests/test_view_helpers → api/helpers}/__init__.py +0 -0
  270. /ledger/templates/ledger/bundles/{ally-administration-bundles.html → view-alliance-administration-js.html} +0 -0
  271. /ledger/templates/ledger/bundles/{char-administration-bundles.html → view-character-administration-js.html} +0 -0
  272. /ledger/templates/ledger/bundles/{corp-administration-bundles.html → view-corporation-administration-js.html} +0 -0
  273. /ledger/templates/ledger/partials/{administration → view-alliance-administration}/alliance.html +0 -0
  274. /ledger/tests/testdata/{esi.json → esi_test_data.json} +0 -0
  275. /ledger/tests/testdata/{allianceauth.json → integrations/allianceauth.json} +0 -0
  276. /ledger/tests/testdata/{load_allianceauth.py → integrations/allianceauth.py} +0 -0
  277. /ledger/tests/testdata/{eveentity.json → integrations/eveentity.json} +0 -0
  278. /ledger/tests/testdata/{load_eveentity.py → integrations/eveentity.py} +0 -0
  279. /ledger/tests/testdata/{eveuniverse.json → integrations/eveuniverse.json} +0 -0
  280. /ledger/tests/testdata/{planetary.json → integrations/planetary.json} +0 -0
@@ -0,0 +1,1141 @@
1
+ # Standard Library
2
+ from collections import defaultdict
3
+ from decimal import Decimal
4
+
5
+ # Third Party
6
+ from ninja import NinjaAPI, Schema
7
+
8
+ # Django
9
+ from django.contrib.humanize.templatetags.humanize import intcomma
10
+ from django.core.handlers.wsgi import WSGIRequest
11
+ from django.db.models import Q, QuerySet
12
+ from django.utils import timezone
13
+ from django.utils.translation import gettext as _
14
+
15
+ # Alliance Auth
16
+ from allianceauth.authentication.models import UserProfile
17
+ from allianceauth.services.hooks import get_extension_logger
18
+
19
+ # AA Ledger
20
+ from ledger import __title__
21
+ from ledger.api.helpers.core import (
22
+ get_corporationowner_or_none,
23
+ )
24
+ from ledger.api.helpers.icons import (
25
+ get_corporation_details_info_button,
26
+ get_corporation_ledger_popover_button,
27
+ get_ref_type_details_popover_button,
28
+ )
29
+ from ledger.api.schema import (
30
+ BillboardSchema,
31
+ CategorySchema,
32
+ CorporationLedgerRequestInfo,
33
+ EntitySchema,
34
+ LedgerDetailsResponse,
35
+ LedgerDetailsSummary,
36
+ LedgerResponse,
37
+ LedgerSchema,
38
+ OwnerSchema,
39
+ )
40
+ from ledger.constants import NPC_ENTITIES
41
+ from ledger.helpers.billboard import BillboardSystem
42
+ from ledger.helpers.cache import CacheManager
43
+ from ledger.helpers.eveonline import get_character_portrait_url
44
+ from ledger.helpers.ledger_data import get_footer_text_class
45
+ from ledger.helpers.ref_type import RefTypeManager
46
+ from ledger.models.corporationaudit import (
47
+ CorporationOwner,
48
+ CorporationWalletJournalEntry,
49
+ )
50
+ from ledger.models.general import EveEntity
51
+ from ledger.providers import AppLogger
52
+
53
+ logger = AppLogger(get_extension_logger(__name__), __title__)
54
+
55
+
56
+ class LedgerEntitySchema(Schema):
57
+ entity: EntitySchema
58
+ ledger: LedgerSchema
59
+ actions: str = ""
60
+
61
+
62
+ class CorporationLedgerResponse(LedgerResponse):
63
+ """
64
+ Schema for Corporation Ledger Response.
65
+
66
+ This schema represents the response structure for corporation ledger data,
67
+ extending the base :class:`LedgerResponse` to include corporation-specific ledger data.
68
+
69
+ Attributes:
70
+ information (CorporationLedgerRequestInfo): The request information for the corporation ledger.
71
+ entities (list[LedgerEntitySchema]): The list of ledger entities.
72
+
73
+ """
74
+
75
+ information: CorporationLedgerRequestInfo
76
+ entities: list[LedgerEntitySchema]
77
+
78
+
79
+ class CorporationApiEndpoints:
80
+ tags = ["Corporation"]
81
+
82
+ # pylint: disable=too-many-statements, function-redefined, duplicate-code
83
+ # flake8: noqa: F811
84
+ def __init__(self, api: NinjaAPI):
85
+ self.cache_manager = CacheManager()
86
+ self.billboard = BillboardSystem()
87
+
88
+ @api.get(
89
+ "corporation/{corporation_id}/division/{division_id}/date/{year}/",
90
+ response={200: CorporationLedgerResponse, 403: dict, 404: dict},
91
+ tags=self.tags,
92
+ )
93
+ def get_corporation_ledger(
94
+ request: WSGIRequest, corporation_id: int, division_id: int, year: int
95
+ ):
96
+ """Get the ledger for a character for a specific year. Admin Endpoint."""
97
+ return self._ledger_api_response(
98
+ request=request,
99
+ corporation_id=corporation_id,
100
+ division_id=division_id,
101
+ year=year,
102
+ )
103
+
104
+ @api.get(
105
+ "corporation/{corporation_id}/division/{division_id}/date/{year}/{month}/",
106
+ response={200: CorporationLedgerResponse, 403: dict, 404: dict},
107
+ tags=self.tags,
108
+ )
109
+ def get_corporation_ledger(
110
+ request: WSGIRequest,
111
+ corporation_id: int,
112
+ division_id: int,
113
+ year: int,
114
+ month: int,
115
+ ):
116
+ """Get the ledger for a character for a specific year. Admin Endpoint."""
117
+ return self._ledger_api_response(
118
+ request=request,
119
+ corporation_id=corporation_id,
120
+ division_id=division_id,
121
+ year=year,
122
+ month=month,
123
+ )
124
+
125
+ @api.get(
126
+ "corporation/{corporation_id}/division/{division_id}/date/{year}/{month}/{day}/",
127
+ response={200: CorporationLedgerResponse, 403: dict, 404: dict},
128
+ tags=self.tags,
129
+ )
130
+ def get_corporation_ledger(
131
+ request: WSGIRequest,
132
+ corporation_id: int,
133
+ division_id: int,
134
+ year: int,
135
+ month: int,
136
+ day: int,
137
+ ):
138
+ """Get the ledger for a character for a specific year. Admin Endpoint."""
139
+ return self._ledger_api_response(
140
+ request=request,
141
+ corporation_id=corporation_id,
142
+ division_id=division_id,
143
+ year=year,
144
+ month=month,
145
+ day=day,
146
+ )
147
+
148
+ @api.get(
149
+ "corporation/{corporation_id}/date/{year}/",
150
+ response={200: CorporationLedgerResponse, 403: dict, 404: dict},
151
+ tags=self.tags,
152
+ )
153
+ def get_corporation_ledger(
154
+ request: WSGIRequest, corporation_id: int, year: int
155
+ ):
156
+ """Get the ledger for a character for a specific year. Admin Endpoint."""
157
+ return self._ledger_api_response(
158
+ request=request,
159
+ corporation_id=corporation_id,
160
+ year=year,
161
+ )
162
+
163
+ @api.get(
164
+ "corporation/{corporation_id}/date/{year}/{month}/",
165
+ response={200: CorporationLedgerResponse, 403: dict, 404: dict},
166
+ tags=self.tags,
167
+ )
168
+ def get_corporation_ledger(
169
+ request: WSGIRequest, corporation_id: int, year: int, month: int
170
+ ):
171
+ """Get the ledger for a character for a specific year. Admin Endpoint."""
172
+ return self._ledger_api_response(
173
+ request=request,
174
+ corporation_id=corporation_id,
175
+ year=year,
176
+ month=month,
177
+ )
178
+
179
+ @api.get(
180
+ "corporation/{corporation_id}/date/{year}/{month}/{day}/",
181
+ response={200: CorporationLedgerResponse, 403: dict, 404: dict},
182
+ tags=self.tags,
183
+ )
184
+ def get_corporation_ledger(
185
+ request: WSGIRequest,
186
+ corporation_id: int,
187
+ year: int,
188
+ month: int,
189
+ day: int,
190
+ ):
191
+ """Get the ledger for a character for a specific year. Admin Endpoint."""
192
+ return self._ledger_api_response(
193
+ request=request,
194
+ corporation_id=corporation_id,
195
+ year=year,
196
+ month=month,
197
+ day=day,
198
+ )
199
+
200
+ # pylint: disable=duplicate-code
201
+ def _create_datatable_footer(
202
+ self,
203
+ entities: list[LedgerEntitySchema],
204
+ request_info: CorporationLedgerRequestInfo,
205
+ ) -> CorporationLedgerRequestInfo:
206
+ """
207
+ Create the footer HTML for the Ledger datatable.
208
+
209
+ This Helper function creates the footer HTML for the Ledger datatable
210
+ by summing up the respective fields from the list of entities.
211
+
212
+ Args:
213
+ entities (list[LedgerEntitySchema]): The list of entity ledger data.
214
+
215
+ Returns:
216
+ str: The generated footer HTML.
217
+ """
218
+ total_bounty = sum(entity.ledger.bounty for entity in entities)
219
+ total_ess = sum(entity.ledger.ess for entity in entities)
220
+ total_costs = sum(entity.ledger.costs for entity in entities)
221
+ total_miscellaneous = sum(entity.ledger.miscellaneous for entity in entities)
222
+ total_total = sum(entity.ledger.total for entity in entities)
223
+
224
+ # Generate Details Link
225
+ url = get_corporation_details_info_button(
226
+ entity_id=request_info.owner_id,
227
+ request_info=request_info,
228
+ )
229
+
230
+ # Skip Footer if no Totals
231
+ if total_total == 0:
232
+ return ""
233
+
234
+ footer_html = f"""
235
+ <tr>
236
+ <th class="border-top">{_("Summary")}</th>
237
+ <th class="border-top text-end {get_footer_text_class(total_bounty)}">{intcomma(value=int(total_bounty), use_l10n=True)} ISK</th>
238
+ <th class="border-top text-end {get_footer_text_class(total_ess)}">{intcomma(value=int(total_ess), use_l10n=True)} ISK</th>
239
+ <th class="border-top text-end {get_footer_text_class(total_miscellaneous)}">{intcomma(value=int(total_miscellaneous), use_l10n=True)} ISK</th>
240
+ <th class="border-top text-end {get_footer_text_class(total_costs)}">{intcomma(value=int(total_costs), use_l10n=True)} ISK</th>
241
+ <th class="border-start border-top text-end {get_footer_text_class(total_total)}">{intcomma(value=int(total_total), use_l10n=True)} ISK</th>
242
+ <th class="border-top">{url}</th>
243
+ </tr>
244
+ """
245
+ request_info.footer_html = footer_html
246
+ return request_info
247
+
248
+ def _sum_by_ref_types(
249
+ self, entry_list: list[dict], ref_types: list[str], sign: str | None = None
250
+ ) -> Decimal:
251
+ """Helper function to sum amounts in entry_list filtered by ref_types and sign.
252
+
253
+ Args:
254
+ entry_list (list[dict]): List of ledger entry dicts.
255
+ ref_types (list[str]): Reference types to include.
256
+ sign (str|None): If "positive", include only positive amounts; if "negative", only negative amounts.
257
+
258
+ Returns:
259
+ Decimal: The summed amount.
260
+ """
261
+ total = Decimal("0.00")
262
+ for r in entry_list:
263
+ if r.get("ref_type") in ref_types:
264
+ amt = r.get("amount") or Decimal("0.00")
265
+ if sign == "positive" and amt <= 0:
266
+ continue
267
+ if sign == "negative" and amt >= 0:
268
+ continue
269
+ total += amt
270
+ return total
271
+
272
+ # pylint: disable=too-many-locals
273
+ def _process_entity_entries(
274
+ self,
275
+ entity: EntitySchema,
276
+ request_info: CorporationLedgerRequestInfo,
277
+ entries_by_entity: dict[int, list[dict]],
278
+ processed_entry_ids: set[int] | None = None,
279
+ ) -> LedgerEntitySchema | None:
280
+ """
281
+ Process the entity entries for the corporation owner.
282
+
283
+ This Helper function processes the entity entries for the corporation
284
+ based on the provided date query.
285
+
286
+ Args:
287
+ owner (CorporationOwner): The corporation owner object.
288
+ request_info (LedgerRequestInfo): The request information object.
289
+ Returns:
290
+ list[EntitySchema]: A list of entity schemas for each processed entity.
291
+ """
292
+ # Build combined entry list for primary entity and any alt_ids
293
+ combined_entries: list[dict] = []
294
+ combined_entries.extend(entries_by_entity.get(entity.entity_id, []))
295
+
296
+ # Initialize processed ids set if not provided
297
+ if processed_entry_ids is None:
298
+ processed_entry_ids = set()
299
+
300
+ # If this EntitySchema carries alt_ids (members), include those rows too
301
+ alt_ids = getattr(entity, "alt_ids", None) or []
302
+ for aid in alt_ids:
303
+ combined_entries.extend(entries_by_entity.get(aid, []))
304
+
305
+ # Filter out already processed entries
306
+ entry_list = [
307
+ r for r in combined_entries if r.get("entry_id") not in processed_entry_ids
308
+ ]
309
+
310
+ # Skip Entity if no Ledger Entries
311
+ if not entry_list:
312
+ # logger.debug(f"Skipping Entity {entity} - No Ledger Entries")
313
+ return None
314
+
315
+ # Deduplicate by entry_id (an entry may appear under multiple alt_ids)
316
+ unique: dict[int, dict] = {}
317
+ for r in entry_list:
318
+ unique[r["entry_id"]] = r
319
+
320
+ # Collect Entry IDs to Mark as Processed
321
+ entry_ids = list(unique.keys())
322
+ entry_list = list(unique.values())
323
+
324
+ # Aggregate Data using in-memory rows
325
+ entity_bounty = self._sum_by_ref_types(entry_list, RefTypeManager.BOUNTY_PRIZES)
326
+ entity_ess = self._sum_by_ref_types(entry_list, RefTypeManager.ESS_TRANSFER)
327
+ entity_costs = self._sum_by_ref_types(
328
+ entry_list, RefTypeManager.ledger_ref_types(), sign="negative"
329
+ )
330
+ entity_miscellaneous = self._sum_by_ref_types(
331
+ entry_list, RefTypeManager.ledger_ref_types(), sign="positive"
332
+ )
333
+
334
+ total = sum(
335
+ [
336
+ entity_bounty,
337
+ entity_ess,
338
+ entity_miscellaneous,
339
+ entity_costs,
340
+ ]
341
+ )
342
+
343
+ response_entity = LedgerEntitySchema(
344
+ entity=entity,
345
+ ledger=LedgerSchema(
346
+ bounty=entity_bounty,
347
+ ess=entity_ess,
348
+ costs=entity_costs,
349
+ miscellaneous=entity_miscellaneous,
350
+ total=total,
351
+ ),
352
+ actions=get_corporation_details_info_button(
353
+ entity_id=entity.entity_id, request_info=request_info, section="single"
354
+ ),
355
+ )
356
+ # Mark these entries as processed so they won't be used again
357
+ processed_entry_ids.update(entry_ids)
358
+ return response_entity
359
+
360
+ def process_member_ledger_data(
361
+ self,
362
+ entity_ids: set[int],
363
+ request_info: CorporationLedgerRequestInfo,
364
+ entries_by_entity: dict[int, list[dict]],
365
+ processed_entry_ids: set[int],
366
+ entity_ledger_list: list[LedgerEntitySchema],
367
+ ) -> list[EntitySchema]:
368
+ """
369
+ Process the ledger data for auth member entities.
370
+
371
+ This Helper function processes the ledger data for auth member entities
372
+ based on the provided date query.
373
+
374
+ Args:
375
+ entity_ids (set[int]): The set of entity IDs to process.
376
+ entries_by_entity (dict[int, list[dict]]): The mapping of entity IDs to their ledger entries.
377
+ processed_entry_ids (set[int]): The set of already processed ledger entry IDs.
378
+ entity_ledger_list (list[LedgerEntitySchema]): The list to append processed ledger data to.
379
+ Returns:
380
+ list[int]: A list of processed entity IDs.
381
+ """
382
+
383
+ accounts = UserProfile.objects.filter(
384
+ main_character__isnull=False,
385
+ ).order_by(
386
+ "user__profile__main_character__character_name",
387
+ )
388
+
389
+ auth_entity_ids = []
390
+ for account in accounts:
391
+ alts = account.user.character_ownerships.all()
392
+ existings_alts = alts.filter(
393
+ character__character_id__in=entity_ids.intersection(
394
+ alts.values_list("character__character_id", flat=True)
395
+ )
396
+ )
397
+ alt_ids = list(
398
+ existings_alts.values_list("character__character_id", flat=True)
399
+ )
400
+
401
+ # Skip if no characters in Corporation
402
+ if not alt_ids:
403
+ continue
404
+
405
+ response_ledger = self._process_entity_entries(
406
+ entity=EntitySchema(
407
+ entity_id=account.main_character.character_id,
408
+ entity_name=account.main_character.character_name,
409
+ alt_ids=alt_ids,
410
+ icon=get_character_portrait_url(
411
+ character_id=account.main_character.character_id,
412
+ character_name=account.main_character.character_name,
413
+ size=32,
414
+ as_html=True,
415
+ ),
416
+ popover=get_corporation_ledger_popover_button(alts=existings_alts),
417
+ ),
418
+ request_info=request_info,
419
+ entries_by_entity=entries_by_entity,
420
+ processed_entry_ids=processed_entry_ids,
421
+ )
422
+ auth_entity_ids.extend(alt_ids)
423
+ if response_ledger is None:
424
+ continue
425
+ # Add Entity Ledger to List
426
+ entity_ledger_list.append(response_ledger)
427
+ return auth_entity_ids
428
+
429
+ def _process_ledger_data(
430
+ self,
431
+ entities: list[EveEntity],
432
+ request_info: CorporationLedgerRequestInfo,
433
+ entries_by_entity: dict[int, list[dict]],
434
+ processed_entry_ids: set[int],
435
+ entity_ledger_list: list[LedgerEntitySchema],
436
+ ) -> list[LedgerEntitySchema]:
437
+ """
438
+ Process the ledger data for the given entity IDs.
439
+
440
+ This Helper function processes the ledger data for the given entity IDs
441
+ based on the provided date query.
442
+
443
+ Args:
444
+ entity_ids (list[int]): The list of entity IDs to process.
445
+ entries_by_entity (dict[int, list[dict]]): The mapping of entity IDs to their ledger entries.
446
+ processed_entry_ids (set[int]): The set of already processed ledger entry IDs.
447
+ entity_ledger_list (list[LedgerEntitySchema]): The list to append processed ledger data to.
448
+ Returns:
449
+ list[LedgerEntitySchema]: A list of ledger responses for each entity.
450
+ """
451
+ for entity in entities:
452
+ response_ledger = self._process_entity_entries(
453
+ entity=EntitySchema(
454
+ entity_id=entity.eve_id,
455
+ entity_name=entity.name,
456
+ icon=entity.get_portrait(size=32, as_html=True),
457
+ ),
458
+ request_info=request_info,
459
+ entries_by_entity=entries_by_entity,
460
+ processed_entry_ids=processed_entry_ids,
461
+ )
462
+ if response_ledger is None:
463
+ continue
464
+ # Add Entity Ledger to List
465
+ entity_ledger_list.append(response_ledger)
466
+
467
+ return entity_ledger_list
468
+
469
+ # pylint: disable=too-many-locals
470
+ def generate_entity_data(
471
+ self, owner: CorporationOwner, request_info: CorporationLedgerRequestInfo
472
+ ) -> list[CorporationLedgerResponse]:
473
+ """
474
+ Generate the ledger data for a corporation owner.
475
+
476
+ This Helper function generates the ledger data for a entity
477
+ based on the provided date query.
478
+
479
+ Args:
480
+ owner (CorporationOwner): The corporation owner object.
481
+ request_info (LedgerRequestInfo): The request information object.
482
+ Returns:
483
+ list[CorporationLedgerResponse]: A list of ledger responses for each entity.
484
+ """
485
+ # Get Corporation Wallet Journal Entries
486
+ corp_journal_values = (
487
+ CorporationWalletJournalEntry.objects.filter(
488
+ division__corporation=owner,
489
+ **request_info.to_date_query(),
490
+ **request_info.to_division_query(),
491
+ )
492
+ # Exclude Zero Amount Entries
493
+ .exclude(amount=Decimal("0.00"))
494
+ # Exclude Internal Transfers
495
+ .exclude(
496
+ first_party_id=owner.eve_corporation.corporation_id,
497
+ second_party_id=owner.eve_corporation.corporation_id,
498
+ )
499
+ .values(
500
+ "entry_id",
501
+ "amount",
502
+ "ref_type",
503
+ "first_party_id",
504
+ "second_party_id",
505
+ "date",
506
+ )
507
+ .order_by("-date")
508
+ )
509
+
510
+ header_ids = list(corp_journal_values.values_list("entry_id", flat=True))
511
+
512
+ # Skip Corporation if no Ledger Entries
513
+ if len(header_ids) == 0:
514
+ return []
515
+
516
+ # Add Owner ID to header ids to ensure uniqueness
517
+ header_ids.append(owner.eve_corporation.corporation_id)
518
+
519
+ # Create Ledger Hash
520
+ wallet_journal_hash = self.cache_manager.create_ledger_hash(header_ids)
521
+
522
+ # Get Cached Ledger if Available
523
+ entity_ledger_list = self.cache_manager.get_cache_key(
524
+ key="corporation", ledger_hash=wallet_journal_hash
525
+ )
526
+ if entity_ledger_list is False:
527
+ entity_ids = set()
528
+ entity_ledger_list: list[LedgerEntitySchema] = []
529
+ processed_entry_ids: set[int] = set()
530
+ entries_by_entity: dict[int, list[dict]] = defaultdict(list)
531
+
532
+ for row in corp_journal_values:
533
+ a = row.get("first_party_id")
534
+ b = row.get("second_party_id")
535
+ if a:
536
+ entries_by_entity[a].append(row)
537
+ entity_ids.add(a)
538
+
539
+ # Only append second party if different from first to avoid double-counting
540
+ if b and b != a:
541
+ entries_by_entity[b].append(row)
542
+ entity_ids.add(b)
543
+
544
+ # Process Auth Entities (Members) First
545
+ auth_entity_ids = self.process_member_ledger_data(
546
+ entity_ids=entity_ids,
547
+ request_info=request_info,
548
+ entries_by_entity=entries_by_entity,
549
+ processed_entry_ids=processed_entry_ids,
550
+ entity_ledger_list=entity_ledger_list,
551
+ )
552
+
553
+ # Process Remaining Entities
554
+ entities = (
555
+ EveEntity.objects.filter(eve_id__in=entity_ids)
556
+ # Exclude Auth Entities
557
+ .exclude(eve_id__in=auth_entity_ids)
558
+ # Exclude NPC Entities
559
+ .exclude(eve_id__in=NPC_ENTITIES)
560
+ # Exclude Corporation Itself
561
+ .exclude(eve_id=owner.eve_corporation.corporation_id).order_by("name")
562
+ )
563
+
564
+ # Process NPC Entities Last
565
+ npc_entities = EveEntity.objects.filter(eve_id__in=NPC_ENTITIES).order_by(
566
+ "name"
567
+ )
568
+
569
+ # Process each Entity
570
+ self._process_ledger_data(
571
+ entities=list(entities),
572
+ request_info=request_info,
573
+ entries_by_entity=entries_by_entity,
574
+ processed_entry_ids=processed_entry_ids,
575
+ entity_ledger_list=entity_ledger_list,
576
+ )
577
+
578
+ # Process NPC Entities
579
+ self._process_ledger_data(
580
+ entities=list(npc_entities),
581
+ request_info=request_info,
582
+ entries_by_entity=entries_by_entity,
583
+ processed_entry_ids=processed_entry_ids,
584
+ entity_ledger_list=entity_ledger_list,
585
+ )
586
+ # Cache Ledger Response
587
+ self.cache_manager.set_cache_key(
588
+ key="corporation",
589
+ ledger_hash=wallet_journal_hash,
590
+ ledger_data=entity_ledger_list,
591
+ )
592
+ return entity_ledger_list
593
+
594
+ def generate_billboard_data(
595
+ self,
596
+ owner: CorporationOwner,
597
+ entity_ledger_list: list[LedgerEntitySchema],
598
+ request_info: CorporationLedgerRequestInfo,
599
+ ) -> BillboardSchema:
600
+ """
601
+ Generate the billboard data for the corporation ledger.
602
+
603
+ This Helper function generates the billboard data for the corporation
604
+ based on the provided character ledger data.
605
+
606
+ Args:
607
+ owner (CorporationOwner): The corporation owner object.
608
+ character_ledger_list (list[LedgerCharacterSchema]): The list of character ledger data.
609
+ request_info (LedgerRequestInfo): The request information object.
610
+ Returns:
611
+ BillboardSchema: The generated billboard data.
612
+ """
613
+ # Get Wallet Journal Entries
614
+ corp_wallet_journal = (
615
+ CorporationWalletJournalEntry.objects.filter(
616
+ division__corporation=owner,
617
+ **request_info.to_date_query(),
618
+ **request_info.to_division_query(),
619
+ )
620
+ # Exclude Zero Amount Entries
621
+ .exclude(amount=Decimal("0.00"))
622
+ # Exclude Internal Transfers
623
+ .exclude(
624
+ first_party_id=owner.eve_corporation.corporation_id,
625
+ second_party_id=owner.eve_corporation.corporation_id,
626
+ )
627
+ )
628
+
629
+ # Get IDs for Hashing
630
+ header_ids = list(corp_wallet_journal.values_list("entry_id", flat=True))
631
+
632
+ # Skip Corporation if no Ledger Entries
633
+ if len(header_ids) == 0:
634
+ return BillboardSchema()
635
+
636
+ # add corporation id to header ids to ensure uniqueness
637
+ header_ids.append(owner.eve_corporation.corporation_id)
638
+
639
+ # Create Ledger Hash
640
+ wallet_journal_hash = self.cache_manager.create_ledger_hash(header_ids)
641
+
642
+ # Get Cached Billboard if Available
643
+ response_billboard = self.cache_manager.get_cache_key(
644
+ key="corporation-billboard", ledger_hash=wallet_journal_hash
645
+ )
646
+ if response_billboard is False:
647
+ logger.debug(f"Billboard Cache for: {owner}")
648
+ # Create Timelines
649
+ wallet_timeline = (
650
+ self.billboard.create_timeline(
651
+ journal=corp_wallet_journal, request_info=request_info
652
+ )
653
+ .annotate_bounty_income()
654
+ .annotate_ess_income()
655
+ .annotate_miscellaneous()
656
+ )
657
+
658
+ # Generate XY Billboard
659
+ xy_results = self.billboard.create_or_update_results(wallet_timeline)
660
+ xy_billboard = self.billboard.create_xy_billboard(
661
+ results=xy_results, request_info=request_info
662
+ )
663
+ # Initialize Chord Billboard
664
+ chord_billboard = self.billboard.create_chord_billboard(entity_ledger_list)
665
+
666
+ response_billboard = BillboardSchema(
667
+ xy_chart=xy_billboard, chord_chart=chord_billboard
668
+ )
669
+ # Cache Billboard Response
670
+ self.cache_manager.set_cache_key(
671
+ key="corporation-billboard",
672
+ ledger_hash=wallet_journal_hash,
673
+ ledger_data=response_billboard,
674
+ )
675
+ # Billboard Data Generation Logic Here
676
+ return response_billboard
677
+
678
+ # pylint: disable=too-many-positional-arguments, duplicate-code
679
+ def _ledger_api_response(
680
+ self,
681
+ request,
682
+ corporation_id: int,
683
+ year: int,
684
+ division_id: int = None,
685
+ month: int = None,
686
+ day: int = None,
687
+ ) -> CorporationLedgerResponse | tuple[int, dict]:
688
+ """
689
+ Helper function to generate ledger response for various date parameters.
690
+
691
+ This function consolidates the common logic for generating the ledger response
692
+ based on the provided date parameters (year, month, day).
693
+
694
+ Args:
695
+ request (WSGIRequest): The incoming request object.
696
+ corporation_id (int): The corporation ID.
697
+ year (int): The year for the ledger data.
698
+ month (int, optional): The month for the ledger data. Defaults to None.
699
+ day (int, optional): The day for the ledger data. Defaults to None.
700
+
701
+ Returns:
702
+ CorporationLedgerResponse | tuple[int, dict]: The ledger response or error tuple.
703
+ """
704
+ perms, owner = get_corporationowner_or_none(
705
+ request=request, corporation_id=corporation_id
706
+ )
707
+
708
+ if owner is None:
709
+ return 404, {"error": _("Corporation not found in Ledger.")}
710
+
711
+ if perms is False:
712
+ return 403, {
713
+ "error": _("You do not have permission to view this corporation.")
714
+ }
715
+
716
+ # Build Request Info
717
+ request_info = CorporationLedgerRequestInfo(
718
+ owner_id=owner.eve_corporation.corporation_id,
719
+ division_id=division_id,
720
+ year=year,
721
+ month=month,
722
+ day=day,
723
+ )
724
+
725
+ # Generate Entity Ledger Data
726
+ entity_ledger_list = self.generate_entity_data(
727
+ owner=owner,
728
+ request_info=request_info,
729
+ )
730
+
731
+ # Generate Billboard Data
732
+ billboard = self.generate_billboard_data(
733
+ owner=owner,
734
+ entity_ledger_list=entity_ledger_list,
735
+ request_info=request_info,
736
+ )
737
+
738
+ # Update Request Info with Available Data
739
+ self._create_datatable_footer(
740
+ entities=entity_ledger_list, request_info=request_info
741
+ )
742
+
743
+ response_ledger = CorporationLedgerResponse(
744
+ owner=OwnerSchema(
745
+ character_id=owner.eve_corporation.corporation_id,
746
+ character_name=owner.eve_corporation.corporation_name,
747
+ icon=owner.get_portrait(as_html=True),
748
+ ),
749
+ information=request_info,
750
+ entities=entity_ledger_list,
751
+ billboard=billboard,
752
+ actions=get_corporation_details_info_button(
753
+ entity_id=owner.eve_corporation.corporation_id,
754
+ request_info=request_info,
755
+ ),
756
+ )
757
+
758
+ return response_ledger
759
+
760
+
761
+ class CorporationDetailsApiEndpoints:
762
+ tags = ["Corporation Details"]
763
+
764
+ # pylint: disable=too-many-statements, function-redefined, too-many-arguments
765
+ def __init__(self, api: NinjaAPI):
766
+ @api.get(
767
+ "corporation/{corporation_id}/division/{division_id}/date/{year}/section/{section}/view/details/{entity_id}/",
768
+ response={200: LedgerDetailsResponse, 403: dict, 404: dict},
769
+ tags=self.tags,
770
+ )
771
+ def get_corporation_ledger_details(
772
+ request: WSGIRequest,
773
+ corporation_id: int,
774
+ division_id: int,
775
+ year: int,
776
+ section: str,
777
+ entity_id: int,
778
+ ):
779
+ return self._ledger_details_api_response(
780
+ request=request,
781
+ corporation_id=corporation_id,
782
+ division_id=division_id,
783
+ entity_id=entity_id,
784
+ year=year,
785
+ section=section,
786
+ )
787
+
788
+ @api.get(
789
+ "corporation/{corporation_id}/division/{division_id}/date/{year}/{month}/section/{section}/view/details/{entity_id}/",
790
+ response={200: LedgerDetailsResponse, 403: dict, 404: dict},
791
+ tags=self.tags,
792
+ )
793
+ # pylint: disable=too-many-positional-arguments
794
+ def get_corporation_ledger_details(
795
+ request: WSGIRequest,
796
+ corporation_id: int,
797
+ division_id: int,
798
+ year: int,
799
+ month: int,
800
+ section: str,
801
+ entity_id: int,
802
+ ):
803
+ return self._ledger_details_api_response(
804
+ request=request,
805
+ corporation_id=corporation_id,
806
+ division_id=division_id,
807
+ entity_id=entity_id,
808
+ year=year,
809
+ month=month,
810
+ section=section,
811
+ )
812
+
813
+ @api.get(
814
+ "corporation/{corporation_id}/division/{division_id}/date/{year}/{month}/{day}/section/{section}/view/details/{entity_id}/",
815
+ response={200: LedgerDetailsResponse, 403: dict, 404: dict},
816
+ tags=self.tags,
817
+ )
818
+ # pylint: disable=too-many-positional-arguments
819
+ def get_corporation_ledger_details(
820
+ request: WSGIRequest,
821
+ corporation_id: int,
822
+ division_id: int,
823
+ year: int,
824
+ month: int,
825
+ day: int,
826
+ section: str,
827
+ entity_id: int,
828
+ ):
829
+ return self._ledger_details_api_response(
830
+ request=request,
831
+ corporation_id=corporation_id,
832
+ division_id=division_id,
833
+ entity_id=entity_id,
834
+ year=year,
835
+ month=month,
836
+ day=day,
837
+ section=section,
838
+ )
839
+
840
+ @api.get(
841
+ "corporation/{corporation_id}/date/{year}/section/{section}/view/details/{entity_id}/",
842
+ response={200: LedgerDetailsResponse, 403: dict, 404: dict},
843
+ tags=self.tags,
844
+ )
845
+ def get_corporation_ledger_details(
846
+ request: WSGIRequest,
847
+ corporation_id: int,
848
+ year: int,
849
+ section: str,
850
+ entity_id: int,
851
+ ):
852
+ return self._ledger_details_api_response(
853
+ request=request,
854
+ corporation_id=corporation_id,
855
+ entity_id=entity_id,
856
+ year=year,
857
+ section=section,
858
+ )
859
+
860
+ @api.get(
861
+ "corporation/{corporation_id}/date/{year}/{month}/section/{section}/view/details/{entity_id}/",
862
+ response={200: LedgerDetailsResponse, 403: dict, 404: dict},
863
+ tags=self.tags,
864
+ )
865
+ def get_corporation_ledger_details(
866
+ request: WSGIRequest,
867
+ corporation_id: int,
868
+ year: int,
869
+ month: int,
870
+ section: str,
871
+ entity_id: int,
872
+ ):
873
+ return self._ledger_details_api_response(
874
+ request=request,
875
+ corporation_id=corporation_id,
876
+ entity_id=entity_id,
877
+ year=year,
878
+ month=month,
879
+ section=section,
880
+ )
881
+
882
+ @api.get(
883
+ "corporation/{corporation_id}/date/{year}/{month}/{day}/section/{section}/view/details/{entity_id}/",
884
+ response={200: LedgerDetailsResponse, 403: dict, 404: dict},
885
+ tags=self.tags,
886
+ )
887
+ # pylint: disable=too-many-positional-arguments
888
+ def get_corporation_ledger_details(
889
+ request: WSGIRequest,
890
+ corporation_id: int,
891
+ year: int,
892
+ month: int,
893
+ day: int,
894
+ section: str,
895
+ entity_id: int,
896
+ ):
897
+ return self._ledger_details_api_response(
898
+ request=request,
899
+ corporation_id=corporation_id,
900
+ entity_id=entity_id,
901
+ year=year,
902
+ month=month,
903
+ day=day,
904
+ section=section,
905
+ )
906
+
907
+ # pylint: disable=duplicate-code
908
+ def _create_datatable_footer(self, value: float) -> str:
909
+ """Create the footer HTML for the datatable."""
910
+ footer_html = f"""
911
+ <tr>
912
+ <th>{_('Summary')}</th>
913
+ <th class="text-end {get_footer_text_class(value)}">{intcomma(value=int(value), use_l10n=True)} ISK</th>
914
+ <th></th>
915
+ </tr>
916
+ """
917
+ return footer_html
918
+
919
+ # pylint: disable=too-many-locals
920
+ def _create_ledger_details(
921
+ self,
922
+ journal: QuerySet[CorporationWalletJournalEntry],
923
+ request_info: CorporationLedgerRequestInfo,
924
+ ) -> LedgerDetailsResponse:
925
+ """
926
+ Generate the detailed ledger data for a character.
927
+ This Helper function generates the detailed ledger data for a character
928
+ based on the provided date query.
929
+
930
+ Args:
931
+ journal (QuerySet): The wallet journal entries.
932
+ mining (QuerySet): The mining ledger entries. Defaults to None.
933
+ request_info (LedgerRequestInfo): The request information containing date and section details.
934
+ Returns:
935
+ LedgerDetailsResponse: The generated ledger details response.
936
+ """
937
+ ref_types = RefTypeManager.get_all_categories()
938
+
939
+ avg = request_info.day if request_info.day else timezone.now().day
940
+ if request_info.section == "summary":
941
+ avg = 365
942
+
943
+ monthly_list = []
944
+ daily_list = []
945
+ hourly_list = []
946
+ summary = 0
947
+ # Income/Cost Ref Types
948
+ for category in RefTypeManager.CategoryChoice:
949
+ category_ref_types = ref_types.get(category.value, [])
950
+ if not category_ref_types:
951
+ continue
952
+ for __, income_flag in (("income", True), ("cost", False)):
953
+ kind_label = _("Income from") if income_flag else _("Cost from")
954
+ name = _("%(kind)s %(category)s") % {
955
+ "category": category.label,
956
+ "kind": kind_label,
957
+ }
958
+ kwargs = {"ref_type": category_ref_types, "income": income_flag}
959
+ amount = journal.aggregate_ref_type(**kwargs)
960
+ if (income_flag and amount > 0) or (not income_flag and amount < 0):
961
+ monthly = CategorySchema(
962
+ name=name,
963
+ amount=amount,
964
+ average=amount / avg / 30,
965
+ average_tick=amount / avg / 30 / 20,
966
+ ref_types=get_ref_type_details_popover_button(
967
+ ref_types=category_ref_types
968
+ ),
969
+ )
970
+
971
+ daily = CategorySchema(
972
+ name=name,
973
+ amount=amount / avg,
974
+ average=amount / avg / 30,
975
+ average_tick=amount / avg / 20,
976
+ ref_types=get_ref_type_details_popover_button(
977
+ ref_types=category_ref_types
978
+ ),
979
+ )
980
+
981
+ hourly = CategorySchema(
982
+ name=name,
983
+ amount=amount / avg / 24,
984
+ average=amount / avg / 24 / 30,
985
+ average_tick=amount / avg / 24 / 20,
986
+ ref_types=get_ref_type_details_popover_button(
987
+ ref_types=category_ref_types
988
+ ),
989
+ )
990
+ # Add Amounts
991
+ summary += amount
992
+ monthly_list.append(monthly)
993
+ daily_list.append(daily)
994
+ hourly_list.append(hourly)
995
+
996
+ if summary == 0:
997
+ return None
998
+
999
+ return LedgerDetailsResponse(
1000
+ summary=monthly_list,
1001
+ daily=daily_list,
1002
+ hourly=hourly_list,
1003
+ total=LedgerDetailsSummary(
1004
+ summary=self._create_datatable_footer(summary),
1005
+ daily=self._create_datatable_footer(
1006
+ summary / avg,
1007
+ ),
1008
+ hourly=self._create_datatable_footer(
1009
+ summary / avg / 24,
1010
+ ),
1011
+ ),
1012
+ )
1013
+
1014
+ def _check_auth_account(
1015
+ self,
1016
+ owner: CorporationOwner,
1017
+ entity_id: int,
1018
+ ) -> list[int] | None:
1019
+ """
1020
+ Check if the entity_id belongs to a auth account of the corporation.
1021
+ """
1022
+ for member in owner.auth_accounts:
1023
+ alt_ids = list(
1024
+ member.user.character_ownerships.all().values_list(
1025
+ "character__character_id", flat=True
1026
+ )
1027
+ )
1028
+ if entity_id in alt_ids:
1029
+ return alt_ids
1030
+ return None
1031
+
1032
+ def create_entity_details(
1033
+ self,
1034
+ owner: CorporationOwner,
1035
+ entity_id: int,
1036
+ request_info: CorporationLedgerRequestInfo,
1037
+ ) -> dict:
1038
+ """
1039
+ Create the entity amounts for the Information View.
1040
+ """
1041
+ # Check if Entity is a Member
1042
+ alt_ids = self._check_auth_account(owner=owner, entity_id=entity_id)
1043
+
1044
+ # Build Entity Query
1045
+ entity_query = Q(first_party_id=entity_id) | Q(second_party_id=entity_id)
1046
+ if alt_ids is not None:
1047
+ # If Member, query all alt_ids
1048
+ entity_query = Q(first_party_id__in=alt_ids) | Q(
1049
+ second_party_id__in=alt_ids
1050
+ )
1051
+ elif entity_id == owner.eve_corporation.corporation_id:
1052
+ # If Corporation itself query all entries
1053
+ entity_query = Q()
1054
+
1055
+ # Get Wallet Journal Entries
1056
+ wallet_journal = (
1057
+ CorporationWalletJournalEntry.objects.filter(
1058
+ entity_query,
1059
+ division__corporation=owner,
1060
+ **request_info.to_date_query(),
1061
+ **request_info.to_division_query(),
1062
+ )
1063
+ # Exclude Zero Amount Entries
1064
+ .exclude(amount=Decimal("0.00"))
1065
+ # Exclude Internal Transfers
1066
+ .exclude(
1067
+ first_party_id=owner.eve_corporation.corporation_id,
1068
+ second_party_id=owner.eve_corporation.corporation_id,
1069
+ )
1070
+ )
1071
+
1072
+ # If Member, Exclude Corporation Contracts (will count in Corporation itself)
1073
+ if alt_ids is not None:
1074
+ wallet_journal = wallet_journal.exclude(
1075
+ ref_type="contract_price_payment_corp",
1076
+ second_party_id__in=alt_ids,
1077
+ )
1078
+
1079
+ response_ledger_details: LedgerDetailsResponse = self._create_ledger_details(
1080
+ journal=wallet_journal,
1081
+ request_info=request_info,
1082
+ )
1083
+ return response_ledger_details
1084
+
1085
+ # pylint: disable=too-many-arguments, too-many-positional-arguments
1086
+ def _ledger_details_api_response(
1087
+ self,
1088
+ request: WSGIRequest,
1089
+ corporation_id: int,
1090
+ entity_id: int,
1091
+ year: int,
1092
+ month: int = None,
1093
+ day: int = None,
1094
+ division_id: int = None,
1095
+ section: str = "summary",
1096
+ ):
1097
+ """
1098
+ Helper function to generate ledger details response for various date parameters.
1099
+
1100
+ This function consolidates the common logic for generating the ledger details response
1101
+ based on the provided date parameters character_id, (year, month, day) and section.
1102
+
1103
+ Args:
1104
+ request (WSGIRequest): The incoming request object.
1105
+ corporation_id (int): The corporation ID.
1106
+ entity_id (int): The entity ID.
1107
+ year (int): The year for the ledger data.
1108
+ month (int, optional): The month for the ledger data. Defaults to None.
1109
+ day (int, optional): The day for the ledger data. Defaults to None.
1110
+ division_id (int, optional): The division ID for the ledger data. Defaults to None.
1111
+ section (str): The section type ('single' or 'summary').
1112
+ Returns:
1113
+ LedgerDetailsResponse | tuple[int, dict]: The ledger details response or error tuple.
1114
+ """
1115
+ perms, owner = get_corporationowner_or_none(
1116
+ request=request, corporation_id=corporation_id
1117
+ )
1118
+
1119
+ if owner is None:
1120
+ return 404, {"error": _("Corporation not found in Ledger.")}
1121
+
1122
+ if perms is False:
1123
+ return 403, {
1124
+ "error": _("You do not have permission to view this corporation.")
1125
+ }
1126
+
1127
+ request_info = CorporationLedgerRequestInfo(
1128
+ owner_id=owner.eve_corporation.corporation_id,
1129
+ entity_id=entity_id,
1130
+ division_id=division_id,
1131
+ year=year,
1132
+ month=month,
1133
+ day=day,
1134
+ section=section,
1135
+ )
1136
+
1137
+ return self.create_entity_details(
1138
+ owner=owner,
1139
+ entity_id=entity_id,
1140
+ request_info=request_info,
1141
+ )