aa-ledger 1.0.4__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.4.dist-info → aa_ledger-2.0.0.dist-info}/METADATA +5 -6
  2. aa_ledger-2.0.0.dist-info/RECORD +267 -0
  3. ledger/__init__.py +2 -2
  4. ledger/admin.py +23 -18
  5. ledger/api/__init__.py +23 -7
  6. ledger/api/{ledger/admin.py → admin.py} +25 -31
  7. ledger/api/alliance.py +755 -0
  8. ledger/api/character.py +786 -0
  9. ledger/api/corporation.py +1141 -0
  10. ledger/api/{helpers.py → helpers/core.py} +33 -33
  11. ledger/api/helpers/icons.py +372 -0
  12. ledger/api/helpers/planetary_helper.py +354 -0
  13. ledger/api/planetary.py +354 -0
  14. ledger/api/schema.py +240 -15
  15. ledger/app_settings.py +11 -27
  16. ledger/auth_hooks.py +2 -2
  17. ledger/constants.py +50 -177
  18. ledger/decorators.py +2 -46
  19. ledger/forms.py +133 -39
  20. ledger/helpers/billboard.py +194 -144
  21. ledger/helpers/cache.py +105 -0
  22. ledger/helpers/discord.py +2 -4
  23. ledger/helpers/eveonline.py +160 -0
  24. ledger/helpers/ledger_data.py +23 -0
  25. ledger/helpers/ref_type.py +53 -78
  26. ledger/locale/cs_CZ/LC_MESSAGES/django.mo +0 -0
  27. ledger/locale/cs_CZ/LC_MESSAGES/django.po +349 -193
  28. ledger/locale/de/LC_MESSAGES/django.mo +0 -0
  29. ledger/locale/de/LC_MESSAGES/django.po +528 -379
  30. ledger/locale/django.pot +721 -546
  31. ledger/locale/es/LC_MESSAGES/django.mo +0 -0
  32. ledger/locale/es/LC_MESSAGES/django.po +349 -194
  33. ledger/locale/fr_FR/LC_MESSAGES/django.mo +0 -0
  34. ledger/locale/fr_FR/LC_MESSAGES/django.po +349 -193
  35. ledger/locale/it_IT/LC_MESSAGES/django.mo +0 -0
  36. ledger/locale/it_IT/LC_MESSAGES/django.po +349 -193
  37. ledger/locale/ja/LC_MESSAGES/django.mo +0 -0
  38. ledger/locale/ja/LC_MESSAGES/django.po +348 -193
  39. ledger/locale/ko_KR/LC_MESSAGES/django.mo +0 -0
  40. ledger/locale/ko_KR/LC_MESSAGES/django.po +349 -193
  41. ledger/locale/nl_NL/LC_MESSAGES/django.mo +0 -0
  42. ledger/locale/nl_NL/LC_MESSAGES/django.po +349 -193
  43. ledger/locale/pl_PL/LC_MESSAGES/django.mo +0 -0
  44. ledger/locale/pl_PL/LC_MESSAGES/django.po +350 -193
  45. ledger/locale/ru/LC_MESSAGES/django.mo +0 -0
  46. ledger/locale/ru/LC_MESSAGES/django.po +348 -193
  47. ledger/locale/sk/LC_MESSAGES/django.mo +0 -0
  48. ledger/locale/sk/LC_MESSAGES/django.po +348 -193
  49. ledger/locale/uk/LC_MESSAGES/django.mo +0 -0
  50. ledger/locale/uk/LC_MESSAGES/django.po +348 -193
  51. ledger/locale/zh_Hans/LC_MESSAGES/django.mo +0 -0
  52. ledger/locale/zh_Hans/LC_MESSAGES/django.po +348 -193
  53. ledger/managers/character_audit_manager.py +28 -20
  54. ledger/managers/character_journal_manager.py +185 -357
  55. ledger/managers/character_mining_manager.py +52 -26
  56. ledger/managers/character_planetary_manager.py +178 -136
  57. ledger/managers/corporation_audit_manager.py +36 -27
  58. ledger/managers/corporation_journal_manager.py +92 -56
  59. ledger/managers/general_manager.py +8 -7
  60. ledger/migrations/0018_remove_characterplanet_ledger_char_planet__58a5b6_idx_and_more.py +44 -0
  61. ledger/migrations/0019_rename_characteraudit_characterowner_and_more.py +48 -0
  62. ledger/models/__init__.py +5 -11
  63. ledger/models/characteraudit.py +101 -109
  64. ledger/models/corporationaudit.py +94 -49
  65. ledger/models/general.py +105 -211
  66. ledger/models/helpers/update_manager.py +302 -0
  67. ledger/models/planetary.py +60 -205
  68. ledger/providers.py +101 -0
  69. ledger/static/ledger/css/{ledger.css → aa-ledger.css} +54 -28
  70. ledger/static/ledger/js/aa-ledger.js +124 -0
  71. ledger/static/ledger/js/charts.js +25 -1
  72. ledger/static/ledger/js/view-alliance-ledger.js +383 -0
  73. ledger/static/ledger/js/view-character-ledger.js +388 -0
  74. ledger/static/ledger/js/view-corporation-ledger.js +402 -0
  75. ledger/static/ledger/js/view-planetary.js +492 -0
  76. ledger/static/ledger/libs/amCharts/5.14.4/js/flow.js +2 -0
  77. ledger/static/ledger/libs/amCharts/5.14.4/js/index.js +2 -0
  78. ledger/static/ledger/libs/amCharts/5.14.4/js/percent.js +2 -0
  79. ledger/static/ledger/libs/amCharts/5.14.4/js/themes/Animated.js +2 -0
  80. ledger/static/ledger/libs/amCharts/5.14.4/js/themes/Dark.js +2 -0
  81. ledger/static/ledger/libs/amCharts/5.14.4/js/xy.js +2 -0
  82. ledger/static/ledger/libs/datatables/2.3.5/css/dataTables.bootstrap5.css +610 -0
  83. ledger/static/ledger/libs/datatables/2.3.5/js/dataTables.bootstrap5.js +122 -0
  84. ledger/static/ledger/libs/datatables/2.3.5/js/dataTables.js +14127 -0
  85. ledger/static/ledger/libs/datatables/Extensions/ColumnControl/1.1.1/css/columnControl.bootstrap5.css +516 -0
  86. ledger/static/ledger/libs/datatables/Extensions/ColumnControl/1.1.1/css/columnControl.dataTables.css +529 -0
  87. ledger/static/ledger/libs/datatables/Extensions/ColumnControl/1.1.1/js/columnControl.bootstrap5.js +73 -0
  88. ledger/static/ledger/libs/datatables/Extensions/ColumnControl/1.1.1/js/dataTables.columnControl.js +3090 -0
  89. ledger/static/ledger/libs/datatables/Extensions/FixedHeader/4.0.4/css/fixedHeader.bootstrap5.css +20 -0
  90. ledger/static/ledger/libs/datatables/Extensions/FixedHeader/4.0.4/js/dataTables.fixedHeader.js +1203 -0
  91. ledger/static/ledger/libs/datatables/Extensions/FixedHeader/4.0.4/js/fixedHeader.bootstrap5.js +59 -0
  92. ledger/tasks.py +157 -141
  93. ledger/templates/ledger/base.html +59 -21
  94. ledger/templates/ledger/bundles/aa-ledger-css.html +3 -0
  95. ledger/templates/ledger/bundles/aa-ledger-js.html +3 -0
  96. ledger/templates/ledger/bundles/view-alliance-ledger-js.html +14 -0
  97. ledger/templates/ledger/bundles/view-character-ledger-js.html +15 -0
  98. ledger/templates/ledger/bundles/view-character-planetary-css.html +3 -0
  99. ledger/templates/ledger/bundles/view-character-planetary-js.html +4 -0
  100. ledger/templates/ledger/bundles/view-corporation-ledger-js.html +15 -0
  101. ledger/templates/ledger/partials/modal/confirm.html +0 -1
  102. ledger/templates/ledger/partials/modal/request-accept-delete-alliance.html +38 -0
  103. ledger/templates/ledger/partials/modal/request-accept-delete-character.html +38 -0
  104. ledger/templates/ledger/partials/modal/request-accept-delete-corporation.html +38 -0
  105. ledger/templates/ledger/partials/modal/request-accept-switch-notification.html +38 -0
  106. ledger/templates/ledger/partials/modal/request-view-alliance-details.html +26 -0
  107. ledger/templates/ledger/partials/modal/request-view-character-details.html +26 -0
  108. ledger/templates/ledger/partials/modal/request-view-corporation-details.html +26 -0
  109. ledger/templates/ledger/partials/modal/request-view-extractor.html +32 -0
  110. ledger/templates/ledger/partials/modal/request-view-factory.html +31 -0
  111. ledger/templates/ledger/partials/{menu → navigation}/administration.html +8 -0
  112. ledger/templates/ledger/partials/{menu → navigation}/navigation.html +2 -2
  113. ledger/templates/ledger/partials/{administration → view-alliance-administration}/alliance_corporations.html +3 -3
  114. ledger/templates/ledger/partials/view-alliance-administration/dashboard.html +81 -0
  115. ledger/templates/ledger/partials/view-alliance-ledger/alliance-billboard.html +25 -0
  116. ledger/templates/ledger/partials/view-alliance-ledger/alliance-ledger-details.html +21 -0
  117. ledger/templates/ledger/partials/view-alliance-ledger/alliance-table.html +24 -0
  118. ledger/templates/ledger/partials/view-alliance-ledger/information/daily.html +18 -0
  119. ledger/templates/ledger/partials/view-alliance-ledger/information/hourly.html +18 -0
  120. ledger/templates/ledger/partials/view-alliance-ledger/information/summary.html +19 -0
  121. ledger/templates/ledger/partials/{administration → view-character-administration}/character.html +1 -9
  122. ledger/templates/ledger/partials/{administration → view-character-administration}/dashboard.html +0 -34
  123. ledger/templates/ledger/partials/view-character-ledger/character-billboard.html +25 -0
  124. ledger/templates/ledger/partials/view-character-ledger/character-ledger-details.html +21 -0
  125. ledger/templates/ledger/partials/view-character-ledger/character-table.html +25 -0
  126. ledger/templates/ledger/partials/view-character-ledger/information/daily.html +18 -0
  127. ledger/templates/ledger/partials/view-character-ledger/information/hourly.html +18 -0
  128. ledger/templates/ledger/partials/view-character-ledger/information/summary.html +19 -0
  129. ledger/templates/ledger/partials/view-character-planetary/extractor-table.html +24 -0
  130. ledger/templates/ledger/partials/view-character-planetary/factory-table.html +24 -0
  131. ledger/templates/ledger/partials/view-character-planetary/planetary-table.html +22 -0
  132. ledger/templates/ledger/partials/view-character-planetary/storage-table.html +23 -0
  133. ledger/templates/ledger/partials/{administration → view-corporation-administration}/corporation.html +5 -13
  134. ledger/templates/ledger/partials/{administration → view-corporation-administration}/corporation_characters.html +1 -1
  135. ledger/templates/ledger/partials/view-corporation-administration/dashboard.html +81 -0
  136. ledger/templates/ledger/partials/view-corporation-ledger/corporation-billboard.html +25 -0
  137. ledger/templates/ledger/partials/view-corporation-ledger/corporation-ledger-details.html +21 -0
  138. ledger/templates/ledger/partials/view-corporation-ledger/corporation-table.html +26 -0
  139. ledger/templates/ledger/partials/view-corporation-ledger/information/daily.html +18 -0
  140. ledger/templates/ledger/partials/view-corporation-ledger/information/hourly.html +18 -0
  141. ledger/templates/ledger/partials/view-corporation-ledger/information/summary.html +19 -0
  142. ledger/templates/ledger/view-administration.html +62 -0
  143. ledger/templates/ledger/view-alliance-administration.html +49 -0
  144. ledger/templates/ledger/view-alliance-ledger.html +72 -0
  145. ledger/templates/ledger/view-alliance-overview.html +131 -0
  146. ledger/templates/ledger/view-character-administration.html +42 -0
  147. ledger/templates/ledger/view-character-ledger.html +73 -0
  148. ledger/templates/ledger/view-character-overview.html +135 -0
  149. ledger/templates/ledger/view-character-planetary-overview.html +135 -0
  150. ledger/templates/ledger/view-character-planetary.html +73 -0
  151. ledger/templates/ledger/view-corporation-administration.html +42 -0
  152. ledger/templates/ledger/view-corporation-ledger.html +73 -0
  153. ledger/templates/ledger/view-corporation-overview.html +131 -0
  154. ledger/templatetags/ledger.py +3 -5
  155. ledger/tests/__init__.py +187 -0
  156. ledger/tests/test_admin.py +164 -68
  157. ledger/tests/test_auth_hook.py +31 -13
  158. ledger/tests/test_decarators.py +14 -79
  159. ledger/tests/test_discord_installed.py +0 -1
  160. ledger/tests/test_helpers/test_ledger_data.py +19 -0
  161. ledger/tests/test_managers/test_character_audit_manager.py +111 -69
  162. ledger/tests/test_managers/test_character_journal_manager.py +48 -208
  163. ledger/tests/test_managers/test_character_mining_manager.py +37 -16
  164. ledger/tests/test_managers/test_corporation_division_manager.py +66 -28
  165. ledger/tests/test_managers/test_corporation_journal_manager.py +39 -42
  166. ledger/tests/test_managers/test_general_manager.py +78 -18
  167. ledger/tests/test_managers/test_planetary_manager.py +73 -32
  168. ledger/tests/test_models/test_characteraudit.py +58 -74
  169. ledger/tests/test_models/test_characterminingledger.py +20 -26
  170. ledger/tests/test_models/test_characterwalletjournal.py +10 -33
  171. ledger/tests/test_models/test_corporationaudit.py +41 -35
  172. ledger/tests/test_models/test_corporationwalletjournal.py +35 -32
  173. ledger/tests/test_models/test_general.py +44 -11
  174. ledger/tests/test_models/test_planetary.py +14 -80
  175. ledger/tests/test_templatetags.py +2 -7
  176. ledger/tests/test_views/corporation/test_add_corp.py +16 -35
  177. ledger/tests/test_views/corporation/test_delete_corporation.py +66 -42
  178. ledger/tests/test_views/test_access.py +512 -545
  179. ledger/tests/test_views/test_add_ally.py +57 -46
  180. ledger/tests/test_views/test_add_char.py +21 -33
  181. ledger/tests/test_views/test_delete_character.py +24 -21
  182. ledger/tests/testdata/README_ESI_STUB.md +430 -0
  183. ledger/tests/testdata/esi_stub_openapi.py +511 -0
  184. ledger/tests/testdata/integrations/__init__.py +0 -0
  185. ledger/tests/testdata/{load_eveuniverse.py → integrations/eveuniverse.py} +0 -1
  186. ledger/tests/testdata/integrations/planetary.py +13 -0
  187. ledger/tests/testdata/json/factory.json +281 -0
  188. ledger/tests/testdata/json/inactive.json +281 -0
  189. ledger/tests/testdata/json/pins.json +175 -272
  190. ledger/tests/testdata/json/route.json +95 -528
  191. ledger/tests/testdata/test_esi_stub.py +468 -0
  192. ledger/tests/testdata/utils.py +601 -0
  193. ledger/thirdparty/charlink_hook.py +60 -30
  194. ledger/urls.py +0 -135
  195. ledger/views/alliance/add_ally.py +2 -4
  196. ledger/views/alliance/alliance_ledger.py +64 -147
  197. ledger/views/character/add_char.py +8 -10
  198. ledger/views/character/character_ledger.py +60 -126
  199. ledger/views/character/planetary.py +5 -98
  200. ledger/views/corporation/add_corp.py +10 -12
  201. ledger/views/corporation/corporation_ledger.py +65 -327
  202. ledger/views/index.py +92 -30
  203. aa_ledger-1.0.4.dist-info/RECORD +0 -236
  204. ledger/api/api_helper/planetary_helper.py +0 -107
  205. ledger/api/ledger/__init__.py +0 -7
  206. ledger/api/ledger/planetary.py +0 -231
  207. ledger/helpers/alliance.py +0 -317
  208. ledger/helpers/character.py +0 -251
  209. ledger/helpers/core.py +0 -665
  210. ledger/helpers/corporation.py +0 -427
  211. ledger/helpers/data_exporter.py +0 -452
  212. ledger/static/ledger/js/planetary-confirm.js +0 -66
  213. ledger/static/ledger/js/planetary.js +0 -143
  214. ledger/templates/ledger/admin.html +0 -43
  215. ledger/templates/ledger/allyledger/admin/alliance_administration.html +0 -46
  216. ledger/templates/ledger/allyledger/admin/alliance_overview.html +0 -108
  217. ledger/templates/ledger/allyledger/alliance_ledger.html +0 -86
  218. ledger/templates/ledger/bundles/character-ledger-bundles.html +0 -66
  219. ledger/templates/ledger/bundles/corporation-ledger-bundles.html +0 -75
  220. ledger/templates/ledger/bundles/ledger-bundles.html +0 -23
  221. ledger/templates/ledger/bundles/ledger-css.html +0 -3
  222. ledger/templates/ledger/bundles/planetary-bundles.html +0 -50
  223. ledger/templates/ledger/bundles/table-css.html +0 -3
  224. ledger/templates/ledger/charledger/admin/character_administration.html +0 -39
  225. ledger/templates/ledger/charledger/admin/character_overview.html +0 -106
  226. ledger/templates/ledger/charledger/character_ledger.html +0 -94
  227. ledger/templates/ledger/charledger/planetary/admin/planetary_overview.html +0 -123
  228. ledger/templates/ledger/charledger/planetary/planetary_ledger.html +0 -54
  229. ledger/templates/ledger/corpledger/admin/corporation_administration.html +0 -39
  230. ledger/templates/ledger/corpledger/admin/corporation_overview.html +0 -108
  231. ledger/templates/ledger/corpledger/corporation_ledger.html +0 -129
  232. ledger/templates/ledger/data-export.html +0 -78
  233. ledger/templates/ledger/error.html +0 -31
  234. ledger/templates/ledger/partials/form/error-message.html +0 -1
  235. ledger/templates/ledger/partials/information/daily.html +0 -56
  236. ledger/templates/ledger/partials/information/day.html +0 -48
  237. ledger/templates/ledger/partials/information/error.html +0 -8
  238. ledger/templates/ledger/partials/information/hourly.html +0 -53
  239. ledger/templates/ledger/partials/information/summary.html +0 -88
  240. ledger/templates/ledger/partials/information/view_character_content.html +0 -35
  241. ledger/templates/ledger/partials/modal/switchalarm_confirm.html +0 -39
  242. ledger/templates/ledger/partials/modal/view_extractor.html +0 -48
  243. ledger/templates/ledger/partials/modal/view_factory.html +0 -123
  244. ledger/templates/ledger/partials/table/char-ledger.html +0 -85
  245. ledger/templates/ledger/partials/table/corp-ledger.html +0 -66
  246. ledger/templates/ledger/partials/table/planetary.html +0 -18
  247. ledger/templates/ledger/partials/thirdparty/billboard.html +0 -22
  248. ledger/templates/ledger/partials/view/card.html +0 -160
  249. ledger/templates/ledger/permission.html +0 -2
  250. ledger/tests/test_helpers/test_billboard.py +0 -11
  251. ledger/tests/test_helpers/test_data_exporter.py +0 -207
  252. ledger/tests/test_tasks.py +0 -282
  253. ledger/tests/test_view_helpers/test_core.py +0 -47
  254. ledger/tests/test_views/corporation/test_corporation.py +0 -267
  255. ledger/tests/test_views/test_planetary.py +0 -137
  256. ledger/tests/testdata/esi_stub.py +0 -109
  257. ledger/tests/testdata/esi_stub_migration.py +0 -80
  258. ledger/tests/testdata/generate_characteraudit.py +0 -106
  259. ledger/tests/testdata/generate_corporationaudit.py +0 -74
  260. ledger/tests/testdata/generate_events.py +0 -31
  261. ledger/tests/testdata/generate_miningledger.py +0 -13
  262. ledger/tests/testdata/generate_planets.py +0 -48
  263. ledger/tests/testdata/generate_walletjournal.py +0 -42
  264. ledger/tests/testdata/json/czarno-pins.json +0 -240
  265. ledger/tests/testdata/json/czarno-routes.json +0 -165
  266. ledger/tests/testdata/json/pins2.json +0 -538
  267. {aa_ledger-1.0.4.dist-info → aa_ledger-2.0.0.dist-info}/WHEEL +0 -0
  268. {aa_ledger-1.0.4.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
@@ -1,4 +1,5 @@
1
1
  # Standard Library
2
+ from decimal import Decimal
2
3
  from typing import TYPE_CHECKING
3
4
 
4
5
  # Django
@@ -10,15 +11,13 @@ from django.db.models.functions import Coalesce
10
11
  from allianceauth.services.hooks import get_extension_logger
11
12
  from esi.errors import TokenError
12
13
 
13
- # Alliance Auth (External Libs)
14
- from app_utils.logging import LoggerAddTag
15
-
16
14
  # AA Ledger
17
15
  from ledger import __title__
18
16
  from ledger.app_settings import LEDGER_BULK_BATCH_SIZE
19
17
  from ledger.decorators import log_timing
20
18
  from ledger.helpers.ref_type import RefTypeManager
21
- from ledger.providers import esi
19
+ from ledger.models.helpers.update_manager import CharacterUpdateSection
20
+ from ledger.providers import AppLogger, esi
22
21
 
23
22
  if TYPE_CHECKING:
24
23
  # Alliance Auth
@@ -26,12 +25,15 @@ if TYPE_CHECKING:
26
25
 
27
26
  # AA Ledger
28
27
  from ledger.models.characteraudit import (
29
- CharacterAudit,
28
+ CharacterOwner,
29
+ )
30
+ from ledger.models.characteraudit import (
31
+ CharacterWalletJournalEntry as CharacterWalletJournalEntryContext,
30
32
  )
31
33
  from ledger.models.general import UpdateSectionResult
32
34
 
33
35
 
34
- logger = LoggerAddTag(get_extension_logger(__name__), __title__)
36
+ logger = AppLogger(get_extension_logger(__name__), __title__)
35
37
 
36
38
 
37
39
  class CharWalletIncomeFilter(models.QuerySet):
@@ -60,308 +62,244 @@ class CharWalletIncomeFilter(models.QuerySet):
60
62
  )
61
63
  )
62
64
 
63
- def annotate_mission_income(self) -> models.QuerySet:
64
- return self.annotate(
65
- mission_income=Coalesce(
66
- Sum(
67
- "amount",
68
- filter=Q(ref_type__in=RefTypeManager.MISSION_REWARD, amount__gt=0),
69
- ),
70
- Value(0),
71
- output_field=DecimalField(),
72
- )
73
- )
74
65
 
75
- def annotate_incursion_income(self) -> models.QuerySet:
66
+ class CharWalletOutSideFilter(CharWalletIncomeFilter):
67
+ def annotate_miscellaneous(self) -> models.QuerySet:
76
68
  return self.annotate(
77
- incursion_income=Coalesce(
69
+ miscellaneous=Coalesce(
78
70
  Sum(
79
71
  "amount",
80
- filter=Q(ref_type__in=RefTypeManager.INCURSION, amount__gt=0),
72
+ filter=Q(
73
+ ref_type__in=RefTypeManager.ledger_ref_types(), amount__gt=0
74
+ ),
81
75
  ),
82
76
  Value(0),
83
77
  output_field=DecimalField(),
84
78
  )
85
79
  )
86
80
 
87
- # Trading - Income
88
- def annotate_market_income(self) -> models.QuerySet:
81
+ def annotate_costs(self) -> models.QuerySet:
89
82
  return self.annotate(
90
- market_income=Coalesce(
83
+ costs=Coalesce(
91
84
  Sum(
92
85
  "amount",
93
- filter=Q(ref_type__in=RefTypeManager.MARKET, amount__gt=0),
86
+ filter=Q(
87
+ ref_type__in=RefTypeManager.ledger_ref_types(), amount__lt=0
88
+ ),
94
89
  ),
95
90
  Value(0),
96
91
  output_field=DecimalField(),
97
92
  )
98
93
  )
99
94
 
100
- def annotate_contract_income(self) -> models.QuerySet:
101
- contract_types = list(RefTypeManager.CONTRACT) + list(
102
- RefTypeManager.CORPORATION_CONTRACT
103
- )
104
- return self.annotate(
105
- contract_income=Coalesce(
106
- Sum(
107
- "amount",
108
- filter=Q(ref_type__in=contract_types, amount__gt=0),
109
- ),
110
- Value(0),
111
- output_field=DecimalField(),
112
- )
113
- )
114
95
 
115
- def annotate_donation_income(self, exclude: list = None) -> models.QuerySet:
116
- if exclude is None:
117
- exclude = []
96
+ class CharWalletCostQueryFilter(CharWalletOutSideFilter):
97
+ pass
118
98
 
119
- return self.annotate(
120
- donation_income=Coalesce(
121
- Sum(
122
- "amount",
123
- filter=Q(ref_type__in=RefTypeManager.DONATION, amount__gt=0)
124
- & ~Q(first_party_id__in=exclude),
125
- ),
126
- Value(0),
127
- output_field=DecimalField(),
128
- )
129
- )
130
99
 
131
- def annotate_insurance_income(self) -> models.QuerySet:
132
- return self.annotate(
133
- insurance_income=Coalesce(
134
- Sum(
135
- "amount",
136
- filter=Q(ref_type__in=RefTypeManager.INSURANCE, amount__gt=0),
137
- ),
138
- Value(0),
139
- output_field=DecimalField(),
140
- )
100
+ # pylint: disable=used-before-assignment
101
+ class CharWalletQuerySet(CharWalletCostQueryFilter):
102
+ def aggregate_bounty(self) -> dict:
103
+ """Aggregate bounty income."""
104
+ return Decimal(
105
+ self.filter(ref_type__in=RefTypeManager.BOUNTY_PRIZES).aggregate(
106
+ total_bounty=Coalesce(
107
+ Sum("amount"), Value(0), output_field=DecimalField()
108
+ )
109
+ )["total_bounty"]
141
110
  )
142
111
 
143
- def annotate_milestone_income(self) -> models.QuerySet:
144
- return self.annotate(
145
- milestone_income=Coalesce(
146
- Sum(
147
- "amount",
148
- filter=Q(
149
- ref_type__in=RefTypeManager.MILESTONE_REWARD, amount__gt=0
150
- ),
151
- ),
152
- Value(0),
153
- output_field=DecimalField(),
154
- )
112
+ def aggregate_ess(self) -> dict:
113
+ """Aggregate ESS income."""
114
+ return Decimal(
115
+ self.filter(ref_type__in=RefTypeManager.ESS_TRANSFER).aggregate(
116
+ total_ess=Coalesce(Sum("amount"), Value(0), output_field=DecimalField())
117
+ )["total_ess"]
155
118
  )
156
119
 
157
- def annotate_daily_goal_income(self) -> models.QuerySet:
158
- """Annotate daily goal income."""
159
- return self.annotate(
160
- daily_goal_income=Coalesce(
161
- Sum(
162
- "amount",
163
- filter=Q(
164
- ref_type__in=RefTypeManager.DAILY_GOAL_REWARD, amount__gt=0
165
- ),
166
- ),
167
- Value(0),
168
- output_field=DecimalField(),
169
- )
170
- )
120
+ def aggregate_costs(self, first_party=None, second_party=None) -> dict:
121
+ """
122
+ Aggregate costs.
123
+
124
+ Args:
125
+ first_party (int | list[int], optional): Filter by first party ID(s).
126
+ second_party (int | list[int], optional): Filter by second party ID(s).
127
+ Returns:
128
+ dict: Aggregated total costs.
129
+ """
130
+ qs = self
131
+ cost_types = RefTypeManager.ledger_ref_types()
171
132
 
133
+ if first_party is not None:
134
+ if isinstance(first_party, int):
135
+ first_party = [first_party]
136
+ qs = qs.filter(first_party__in=first_party)
172
137
 
173
- class CharWalletOutSideFilter(CharWalletIncomeFilter):
174
- def annotate_miscellaneous(self) -> models.QuerySet:
175
- return self.annotate(
176
- miscellaneous=Coalesce(
177
- Sum(
178
- "amount",
179
- filter=Q(ref_type__in=RefTypeManager.all_ref_types(), amount__gt=0),
180
- ),
181
- Value(0),
182
- output_field=DecimalField(),
183
- )
184
- )
138
+ if second_party is not None:
139
+ if isinstance(second_party, int):
140
+ second_party = [second_party]
141
+ qs = qs.filter(Q(ref_type__in=cost_types))
142
+ else:
143
+ qs = qs.filter(Q(ref_type__in=cost_types))
185
144
 
186
- def annotate_miscellaneous_exclude_donations(self, exclude=None) -> models.QuerySet:
187
- """Allow excluding donations by first_party_id from the miscellaneous sum."""
188
- # Normalize single int to list for compatibility with Q lookups
189
- if isinstance(exclude, int):
190
- exclude = [exclude]
145
+ qs = qs.filter(amount__lt=0)
146
+ return Decimal(
147
+ qs.aggregate(
148
+ total_costs=Coalesce(
149
+ Sum("amount"), Value(0), output_field=DecimalField()
150
+ )
151
+ )["total_costs"]
152
+ )
191
153
 
192
- # Ensure we have a mutable list (remove may not exist on other iterables)
193
- all_types = RefTypeManager.all_ref_types()
194
- donation_types = RefTypeManager.DONATION
154
+ def aggregate_miscellaneous(self, first_party=None, second_party=None) -> dict:
155
+ """
156
+ Aggregate miscellaneous income.
157
+
158
+ Args:
159
+ first_party (int | list[int], optional): Filter by first party ID(s).
160
+ second_party (int | list[int], optional): Filter by second party ID(s).
161
+ Returns:
162
+ dict: Aggregated total miscellaneous income.
163
+ """
164
+ qs = self
195
165
 
196
- if exclude is not None:
197
- try:
198
- all_types.remove("player_donation")
199
- except ValueError:
200
- pass
166
+ misc_types = RefTypeManager.ledger_ref_types()
201
167
 
202
- # Base filter: all reference types and only positive amounts (income)
203
- filter_q = Q(ref_type__in=all_types, amount__gt=0)
168
+ if first_party is not None:
169
+ if isinstance(first_party, int):
170
+ first_party = [first_party]
171
+ qs = qs.filter(Q(ref_type__in=misc_types))
172
+ else:
173
+ qs = qs.filter(Q(ref_type__in=misc_types))
204
174
 
205
- if exclude:
206
- filter_q = filter_q & ~(
207
- Q(ref_type__in=donation_types) & Q(first_party_id__in=exclude)
208
- )
175
+ if second_party is not None:
176
+ if isinstance(second_party, int):
177
+ second_party = [second_party]
178
+ qs = qs.filter(second_party__in=second_party)
209
179
 
210
- return self.annotate(
211
- miscellaneous=Coalesce(
212
- Sum(
213
- "amount",
214
- filter=filter_q,
215
- ),
216
- Value(0),
217
- output_field=DecimalField(),
218
- )
180
+ qs = qs.filter(amount__gt=0)
181
+ return Decimal(
182
+ qs.aggregate(
183
+ total_misc=Coalesce(
184
+ Sum("amount"), Value(0), output_field=DecimalField()
185
+ )
186
+ )["total_misc"]
219
187
  )
220
188
 
221
- def annotate_costs(self) -> models.QuerySet:
222
- return self.annotate(
223
- costs=Coalesce(
224
- Sum(
225
- "amount",
226
- filter=Q(ref_type__in=RefTypeManager.all_ref_types(), amount__lt=0),
227
- ),
228
- Value(0),
229
- output_field=DecimalField(),
230
- )
231
- )
189
+ def aggregate_ref_type(
190
+ self,
191
+ ref_type: list,
192
+ first_party=None,
193
+ second_party=None,
194
+ income: bool = False,
195
+ ) -> dict:
196
+ """Aggregate income by ref_type."""
197
+ qs = self.filter(ref_type__in=ref_type)
232
198
 
199
+ if first_party is not None:
200
+ if isinstance(first_party, int):
201
+ first_party = [first_party]
202
+ qs = qs.filter(first_party__in=first_party)
233
203
 
234
- class CharWalletCostQueryFilter(CharWalletOutSideFilter):
235
- # Costs
236
- def annotate_contract_cost(self) -> models.QuerySet:
237
- return self.annotate(
238
- contract_cost=Coalesce(
239
- Sum(
240
- "amount",
241
- filter=Q(ref_type__in=RefTypeManager.CONTRACT, amount__lt=0),
242
- ),
243
- Value(0),
244
- output_field=DecimalField(),
245
- )
246
- )
204
+ if second_party is not None:
205
+ if isinstance(second_party, int):
206
+ second_party = [second_party]
207
+ qs = qs.filter(second_party__in=second_party)
247
208
 
248
- def annotate_market_cost(self) -> models.QuerySet:
249
- return self.annotate(
250
- market_cost=Coalesce(
251
- Sum(
252
- "amount",
253
- filter=Q(ref_type__in=RefTypeManager.MARKET, amount__lt=0),
254
- ),
255
- Value(0),
256
- output_field=DecimalField(),
257
- )
258
- )
209
+ if income:
210
+ qs = qs.filter(amount__gt=0)
211
+ else:
212
+ qs = qs.filter(amount__lt=0)
259
213
 
260
- def annotate_asset_cost(self) -> models.QuerySet:
261
- return self.annotate(
262
- asset_cost=Coalesce(
263
- Sum(
264
- "amount",
265
- filter=Q(ref_type__in=RefTypeManager.ASSETS, amount__lt=0),
266
- ),
267
- Value(0),
268
- output_field=DecimalField(),
269
- )
214
+ return Decimal(
215
+ qs.aggregate(
216
+ total=Coalesce(Sum("amount"), Value(0), output_field=DecimalField())
217
+ )["total"]
270
218
  )
271
219
 
272
- def annotate_traveling_cost(self) -> models.QuerySet:
273
- return self.annotate(
274
- traveling_cost=Coalesce(
275
- Sum(
276
- "amount",
277
- filter=Q(ref_type__in=RefTypeManager.TRAVELING, amount__lt=0),
278
- ),
279
- Value(0),
280
- output_field=DecimalField(),
281
- )
282
- )
283
220
 
284
- def annotate_production_cost(self) -> models.QuerySet:
285
- return self.annotate(
286
- production_cost=Coalesce(
287
- Sum(
288
- "amount",
289
- filter=Q(ref_type__in=RefTypeManager.PRODUCTION, amount__lt=0),
290
- ),
291
- Value(0),
292
- output_field=DecimalField(),
293
- )
294
- )
221
+ class CharWalletManager(models.Manager["CharacterWalletJournalEntryContext"]):
222
+ def get_queryset(self) -> CharWalletQuerySet:
223
+ return CharWalletQuerySet(self.model, using=self._db)
295
224
 
296
- def annotate_skill_cost(self) -> models.QuerySet:
297
- return self.annotate(
298
- skill_cost=Coalesce(
299
- Sum(
300
- "amount",
301
- filter=Q(ref_type__in=RefTypeManager.SKILL, amount__lt=0),
302
- ),
303
- Value(0),
304
- output_field=DecimalField(),
305
- )
306
- )
225
+ # pylint: disable=duplicate-code
226
+ def annotate_bounty_income(self) -> models.QuerySet:
227
+ """Annotate bounty income."""
228
+ return self.get_queryset().annotate_bounty_income()
307
229
 
308
- def annotate_insurance_cost(self) -> models.QuerySet:
309
- return self.annotate(
310
- insurance_cost=Coalesce(
311
- Sum(
312
- "amount",
313
- filter=Q(ref_type__in=RefTypeManager.INSURANCE, amount__lt=0),
314
- ),
315
- Value(0),
316
- output_field=DecimalField(),
317
- )
318
- )
230
+ # pylint: disable=duplicate-code
231
+ def annotate_ess_income(self) -> models.QuerySet:
232
+ """Annotate ess income."""
233
+ return self.get_queryset().annotate_ess_income()
319
234
 
320
- def annotate_planetary_cost(self) -> models.QuerySet:
321
- return self.annotate(
322
- planetary_cost=Coalesce(
323
- Sum(
324
- "amount",
325
- filter=Q(ref_type__in=RefTypeManager.PLANETARY, amount__lt=0),
326
- ),
327
- Value(0),
328
- output_field=DecimalField(),
329
- )
235
+ # pylint: disable=duplicate-code
236
+ def annotate_miscellaneous(self) -> models.QuerySet:
237
+ """Annotate miscellaneous income."""
238
+ return self.get_queryset().annotate_miscellaneous()
239
+
240
+ # pylint: disable=duplicate-code
241
+ def annotate_costs(self) -> models.QuerySet:
242
+ """Annotate costs."""
243
+ return self.get_queryset().annotate_costs()
244
+
245
+ # pylint: disable=duplicate-code
246
+ def aggregate_bounty(self) -> dict:
247
+ """Aggregate bounty income."""
248
+ return self.get_queryset().aggregate_bounty()
249
+
250
+ # pylint: disable=duplicate-code
251
+ def aggregate_ess(self) -> dict:
252
+ """Aggregate ess income."""
253
+ return self.get_queryset().aggregate_ess()
254
+
255
+ # pylint: disable=duplicate-code
256
+ def aggregate_costs(self, first_party=None, second_party=None) -> dict:
257
+ """Aggregate costs."""
258
+ return self.get_queryset().aggregate_costs(
259
+ first_party=first_party, second_party=second_party
330
260
  )
331
261
 
332
- def annotate_lp_cost(self) -> models.QuerySet:
333
- return self.annotate(
334
- lp_cost=Coalesce(
335
- Sum(
336
- "amount",
337
- filter=Q(ref_type__in=RefTypeManager.LP, amount__lt=0),
338
- ),
339
- Value(0),
340
- output_field=DecimalField(),
341
- )
262
+ # pylint: disable=duplicate-code
263
+ def aggregate_miscellaneous(self, first_party=None, second_party=None) -> dict:
264
+ """Aggregate miscellaneous income."""
265
+ return self.get_queryset().aggregate_miscellaneous(
266
+ first_party=first_party, second_party=second_party
342
267
  )
343
268
 
269
+ # pylint: disable=duplicate-code
270
+ def aggregate_ref_type(
271
+ self,
272
+ ref_type: list,
273
+ first_party=None,
274
+ second_party=None,
275
+ income: bool = False,
276
+ ) -> dict:
277
+ """Aggregate income by ref_type."""
278
+ return self.get_queryset().aggregate_ref_type(
279
+ ref_type=ref_type,
280
+ first_party=first_party,
281
+ second_party=second_party,
282
+ income=income,
283
+ )
344
284
 
345
- # pylint: disable=used-before-assignment
346
- class CharWalletQuerySet(CharWalletCostQueryFilter):
347
285
  @log_timing(logger)
348
286
  def update_or_create_esi(
349
- self, character: "CharacterAudit", force_refresh: bool = False
287
+ self, owner: "CharacterOwner", force_refresh: bool = False
350
288
  ) -> "UpdateSectionResult":
351
289
  """Update or Create a wallet journal entry from ESI data."""
352
- return character.update_section_if_changed(
353
- section=character.UpdateSection.WALLET_JOURNAL,
290
+ return owner.update_manager.update_section_if_changed(
291
+ section=CharacterUpdateSection.WALLET_JOURNAL,
354
292
  fetch_func=self._fetch_esi_data,
355
293
  force_refresh=force_refresh,
356
294
  )
357
295
 
358
- def _fetch_esi_data(self, audit: "CharacterAudit", force_refresh: bool) -> None:
296
+ def _fetch_esi_data(self, owner: "CharacterOwner", force_refresh: bool) -> None:
359
297
  """Fetch wallet journal entries from ESI data."""
360
298
  req_scopes = ["esi-wallet.read_character_wallet.v1"]
361
- token = audit.get_token(scopes=req_scopes)
299
+ token = owner.get_token(scopes=req_scopes)
362
300
 
363
301
  operation = esi.client.Wallet.GetCharactersCharacterIdWalletJournal(
364
- character_id=audit.eve_character.character_id,
302
+ character_id=owner.eve_character.character_id,
365
303
  token=token,
366
304
  )
367
305
 
@@ -369,12 +307,12 @@ class CharWalletQuerySet(CharWalletCostQueryFilter):
369
307
  force_refresh=force_refresh,
370
308
  )
371
309
 
372
- self._update_or_create_objs(character=audit, objs=journal_items)
310
+ self._update_or_create_objs(character=owner, objs=journal_items)
373
311
 
374
312
  @transaction.atomic()
375
313
  def _update_or_create_objs(
376
314
  self,
377
- character: "CharacterAudit",
315
+ character: "CharacterOwner",
378
316
  objs: list["CharactersCharacterIdWalletJournalGetItem"],
379
317
  ) -> None:
380
318
  """Update or Create wallet journal entries from objs data."""
@@ -425,113 +363,3 @@ class CharWalletQuerySet(CharWalletCostQueryFilter):
425
363
  self.bulk_create(items, batch_size=LEDGER_BULK_BATCH_SIZE)
426
364
  else:
427
365
  raise TokenError("ESI Fail")
428
-
429
- def aggregate_bounty(self) -> dict:
430
- """Aggregate bounty income."""
431
- return self.filter(ref_type__in=RefTypeManager.BOUNTY_PRIZES).aggregate(
432
- total_bounty=Coalesce(Sum("amount"), Value(0), output_field=DecimalField())
433
- )["total_bounty"]
434
-
435
- def aggregate_ess(self) -> dict:
436
- """Aggregate ESS income."""
437
- return self.filter(ref_type__in=RefTypeManager.ESS_TRANSFER).aggregate(
438
- total_ess=Coalesce(Sum("amount"), Value(0), output_field=DecimalField())
439
- )["total_ess"]
440
-
441
- def aggregate_costs(self, first_party=None, second_party=None) -> dict:
442
- """Aggregate costs. first_party wird für donation exkludiert."""
443
- qs = self
444
- cost_types = RefTypeManager.all_ref_types()
445
- donation_types = RefTypeManager.DONATION
446
-
447
- if first_party is not None:
448
- if isinstance(first_party, int):
449
- first_party = [first_party]
450
- qs = qs.filter(first_party__in=first_party)
451
-
452
- if second_party is not None:
453
- if isinstance(second_party, int):
454
- second_party = [second_party]
455
- # Exclude: alle donation mit first_party_id in Liste, Rest wie gehabt
456
- qs = qs.exclude(
457
- Q(ref_type__in=donation_types, first_party_id__in=second_party)
458
- )
459
- qs = qs.filter(Q(ref_type__in=cost_types))
460
- else:
461
- qs = qs.filter(Q(ref_type__in=cost_types))
462
-
463
- qs = qs.filter(amount__lt=0)
464
- return qs.aggregate(
465
- total_costs=Coalesce(Sum("amount"), Value(0), output_field=DecimalField())
466
- )["total_costs"]
467
-
468
- def aggregate_miscellaneous(self, first_party=None, second_party=None) -> dict:
469
- """Aggregate miscellaneous income. first_party wird nur für donation angewandt."""
470
- qs = self
471
-
472
- misc_types = RefTypeManager.all_ref_types()
473
- donation_types = RefTypeManager.DONATION
474
-
475
- if first_party is not None:
476
- if isinstance(first_party, int):
477
- first_party = [first_party]
478
- # Exclude: alle donation mit first_party_id in Liste, Rest wie gehabt
479
- qs = qs.exclude(
480
- Q(ref_type__in=donation_types, first_party_id__in=first_party)
481
- )
482
- qs = qs.filter(Q(ref_type__in=misc_types))
483
- else:
484
- qs = qs.filter(Q(ref_type__in=misc_types))
485
-
486
- if second_party is not None:
487
- if isinstance(second_party, int):
488
- second_party = [second_party]
489
- qs = qs.filter(second_party__in=second_party)
490
-
491
- qs = qs.filter(amount__gt=0)
492
- return qs.aggregate(
493
- total_misc=Coalesce(Sum("amount"), Value(0), output_field=DecimalField())
494
- )["total_misc"]
495
-
496
- # pylint: disable=too-many-positional-arguments
497
- def aggregate_ref_type(
498
- self,
499
- ref_type: list,
500
- first_party=None,
501
- second_party=None,
502
- exclude=None,
503
- income: bool = False,
504
- ) -> dict:
505
- """Aggregate income by ref_type."""
506
- qs = self.filter(ref_type__in=ref_type)
507
-
508
- if first_party is not None:
509
- if isinstance(first_party, int):
510
- first_party = [first_party]
511
- qs = qs.filter(first_party__in=first_party)
512
-
513
- if second_party is not None:
514
- if isinstance(second_party, int):
515
- second_party = [second_party]
516
- qs = qs.filter(second_party__in=second_party)
517
-
518
- if exclude is not None:
519
- if isinstance(exclude, int):
520
- exclude = [exclude]
521
- qs = qs.exclude(first_party__in=exclude)
522
-
523
- if income:
524
- qs = qs.filter(amount__gt=0)
525
- else:
526
- qs = qs.filter(amount__lt=0)
527
-
528
- return qs.aggregate(
529
- total=Coalesce(Sum("amount"), Value(0), output_field=DecimalField())
530
- )["total"]
531
-
532
-
533
- class CharWalletManagerBase(models.Manager):
534
- pass
535
-
536
-
537
- CharWalletManager = CharWalletManagerBase.from_queryset(CharWalletQuerySet)