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
@@ -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,14 +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__
16
+ from ledger.app_settings import LEDGER_BULK_BATCH_SIZE
18
17
  from ledger.decorators import log_timing
19
18
  from ledger.helpers.ref_type import RefTypeManager
20
- from ledger.providers import esi
19
+ from ledger.models.helpers.update_manager import CharacterUpdateSection
20
+ from ledger.providers import AppLogger, esi
21
21
 
22
22
  if TYPE_CHECKING:
23
23
  # Alliance Auth
@@ -25,12 +25,15 @@ if TYPE_CHECKING:
25
25
 
26
26
  # AA Ledger
27
27
  from ledger.models.characteraudit import (
28
- CharacterAudit,
28
+ CharacterOwner,
29
+ )
30
+ from ledger.models.characteraudit import (
31
+ CharacterWalletJournalEntry as CharacterWalletJournalEntryContext,
29
32
  )
30
33
  from ledger.models.general import UpdateSectionResult
31
34
 
32
35
 
33
- logger = LoggerAddTag(get_extension_logger(__name__), __title__)
36
+ logger = AppLogger(get_extension_logger(__name__), __title__)
34
37
 
35
38
 
36
39
  class CharWalletIncomeFilter(models.QuerySet):
@@ -59,308 +62,244 @@ class CharWalletIncomeFilter(models.QuerySet):
59
62
  )
60
63
  )
61
64
 
62
- def annotate_mission_income(self) -> models.QuerySet:
63
- return self.annotate(
64
- mission_income=Coalesce(
65
- Sum(
66
- "amount",
67
- filter=Q(ref_type__in=RefTypeManager.MISSION_REWARD, amount__gt=0),
68
- ),
69
- Value(0),
70
- output_field=DecimalField(),
71
- )
72
- )
73
65
 
74
- def annotate_incursion_income(self) -> models.QuerySet:
66
+ class CharWalletOutSideFilter(CharWalletIncomeFilter):
67
+ def annotate_miscellaneous(self) -> models.QuerySet:
75
68
  return self.annotate(
76
- incursion_income=Coalesce(
69
+ miscellaneous=Coalesce(
77
70
  Sum(
78
71
  "amount",
79
- 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
+ ),
80
75
  ),
81
76
  Value(0),
82
77
  output_field=DecimalField(),
83
78
  )
84
79
  )
85
80
 
86
- # Trading - Income
87
- def annotate_market_income(self) -> models.QuerySet:
81
+ def annotate_costs(self) -> models.QuerySet:
88
82
  return self.annotate(
89
- market_income=Coalesce(
83
+ costs=Coalesce(
90
84
  Sum(
91
85
  "amount",
92
- 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
+ ),
93
89
  ),
94
90
  Value(0),
95
91
  output_field=DecimalField(),
96
92
  )
97
93
  )
98
94
 
99
- def annotate_contract_income(self) -> models.QuerySet:
100
- contract_types = list(RefTypeManager.CONTRACT) + list(
101
- RefTypeManager.CORPORATION_CONTRACT
102
- )
103
- return self.annotate(
104
- contract_income=Coalesce(
105
- Sum(
106
- "amount",
107
- filter=Q(ref_type__in=contract_types, amount__gt=0),
108
- ),
109
- Value(0),
110
- output_field=DecimalField(),
111
- )
112
- )
113
95
 
114
- def annotate_donation_income(self, exclude: list = None) -> models.QuerySet:
115
- if exclude is None:
116
- exclude = []
96
+ class CharWalletCostQueryFilter(CharWalletOutSideFilter):
97
+ pass
117
98
 
118
- return self.annotate(
119
- donation_income=Coalesce(
120
- Sum(
121
- "amount",
122
- filter=Q(ref_type__in=RefTypeManager.DONATION, amount__gt=0)
123
- & ~Q(first_party_id__in=exclude),
124
- ),
125
- Value(0),
126
- output_field=DecimalField(),
127
- )
128
- )
129
99
 
130
- def annotate_insurance_income(self) -> models.QuerySet:
131
- return self.annotate(
132
- insurance_income=Coalesce(
133
- Sum(
134
- "amount",
135
- filter=Q(ref_type__in=RefTypeManager.INSURANCE, amount__gt=0),
136
- ),
137
- Value(0),
138
- output_field=DecimalField(),
139
- )
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"]
140
110
  )
141
111
 
142
- def annotate_milestone_income(self) -> models.QuerySet:
143
- return self.annotate(
144
- milestone_income=Coalesce(
145
- Sum(
146
- "amount",
147
- filter=Q(
148
- ref_type__in=RefTypeManager.MILESTONE_REWARD, amount__gt=0
149
- ),
150
- ),
151
- Value(0),
152
- output_field=DecimalField(),
153
- )
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"]
154
118
  )
155
119
 
156
- def annotate_daily_goal_income(self) -> models.QuerySet:
157
- """Annotate daily goal income."""
158
- return self.annotate(
159
- daily_goal_income=Coalesce(
160
- Sum(
161
- "amount",
162
- filter=Q(
163
- ref_type__in=RefTypeManager.DAILY_GOAL_REWARD, amount__gt=0
164
- ),
165
- ),
166
- Value(0),
167
- output_field=DecimalField(),
168
- )
169
- )
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()
170
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)
171
137
 
172
- class CharWalletOutSideFilter(CharWalletIncomeFilter):
173
- def annotate_miscellaneous(self) -> models.QuerySet:
174
- return self.annotate(
175
- miscellaneous=Coalesce(
176
- Sum(
177
- "amount",
178
- filter=Q(ref_type__in=RefTypeManager.all_ref_types(), amount__gt=0),
179
- ),
180
- Value(0),
181
- output_field=DecimalField(),
182
- )
183
- )
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))
184
144
 
185
- def annotate_miscellaneous_exclude_donations(self, exclude=None) -> models.QuerySet:
186
- """Allow excluding donations by first_party_id from the miscellaneous sum."""
187
- # Normalize single int to list for compatibility with Q lookups
188
- if isinstance(exclude, int):
189
- 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
+ )
190
153
 
191
- # Ensure we have a mutable list (remove may not exist on other iterables)
192
- all_types = RefTypeManager.all_ref_types()
193
- 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
194
165
 
195
- if exclude is not None:
196
- try:
197
- all_types.remove("player_donation")
198
- except ValueError:
199
- pass
166
+ misc_types = RefTypeManager.ledger_ref_types()
200
167
 
201
- # Base filter: all reference types and only positive amounts (income)
202
- 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))
203
174
 
204
- if exclude:
205
- filter_q = filter_q & ~(
206
- Q(ref_type__in=donation_types) & Q(first_party_id__in=exclude)
207
- )
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)
208
179
 
209
- return self.annotate(
210
- miscellaneous=Coalesce(
211
- Sum(
212
- "amount",
213
- filter=filter_q,
214
- ),
215
- Value(0),
216
- output_field=DecimalField(),
217
- )
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"]
218
187
  )
219
188
 
220
- def annotate_costs(self) -> models.QuerySet:
221
- return self.annotate(
222
- costs=Coalesce(
223
- Sum(
224
- "amount",
225
- filter=Q(ref_type__in=RefTypeManager.all_ref_types(), amount__lt=0),
226
- ),
227
- Value(0),
228
- output_field=DecimalField(),
229
- )
230
- )
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)
231
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)
232
203
 
233
- class CharWalletCostQueryFilter(CharWalletOutSideFilter):
234
- # Costs
235
- def annotate_contract_cost(self) -> models.QuerySet:
236
- return self.annotate(
237
- contract_cost=Coalesce(
238
- Sum(
239
- "amount",
240
- filter=Q(ref_type__in=RefTypeManager.CONTRACT, amount__lt=0),
241
- ),
242
- Value(0),
243
- output_field=DecimalField(),
244
- )
245
- )
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)
246
208
 
247
- def annotate_market_cost(self) -> models.QuerySet:
248
- return self.annotate(
249
- market_cost=Coalesce(
250
- Sum(
251
- "amount",
252
- filter=Q(ref_type__in=RefTypeManager.MARKET, amount__lt=0),
253
- ),
254
- Value(0),
255
- output_field=DecimalField(),
256
- )
257
- )
209
+ if income:
210
+ qs = qs.filter(amount__gt=0)
211
+ else:
212
+ qs = qs.filter(amount__lt=0)
258
213
 
259
- def annotate_asset_cost(self) -> models.QuerySet:
260
- return self.annotate(
261
- asset_cost=Coalesce(
262
- Sum(
263
- "amount",
264
- filter=Q(ref_type__in=RefTypeManager.ASSETS, amount__lt=0),
265
- ),
266
- Value(0),
267
- output_field=DecimalField(),
268
- )
214
+ return Decimal(
215
+ qs.aggregate(
216
+ total=Coalesce(Sum("amount"), Value(0), output_field=DecimalField())
217
+ )["total"]
269
218
  )
270
219
 
271
- def annotate_traveling_cost(self) -> models.QuerySet:
272
- return self.annotate(
273
- traveling_cost=Coalesce(
274
- Sum(
275
- "amount",
276
- filter=Q(ref_type__in=RefTypeManager.TRAVELING, amount__lt=0),
277
- ),
278
- Value(0),
279
- output_field=DecimalField(),
280
- )
281
- )
282
220
 
283
- def annotate_production_cost(self) -> models.QuerySet:
284
- return self.annotate(
285
- production_cost=Coalesce(
286
- Sum(
287
- "amount",
288
- filter=Q(ref_type__in=RefTypeManager.PRODUCTION, amount__lt=0),
289
- ),
290
- Value(0),
291
- output_field=DecimalField(),
292
- )
293
- )
221
+ class CharWalletManager(models.Manager["CharacterWalletJournalEntryContext"]):
222
+ def get_queryset(self) -> CharWalletQuerySet:
223
+ return CharWalletQuerySet(self.model, using=self._db)
294
224
 
295
- def annotate_skill_cost(self) -> models.QuerySet:
296
- return self.annotate(
297
- skill_cost=Coalesce(
298
- Sum(
299
- "amount",
300
- filter=Q(ref_type__in=RefTypeManager.SKILL, amount__lt=0),
301
- ),
302
- Value(0),
303
- output_field=DecimalField(),
304
- )
305
- )
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()
306
229
 
307
- def annotate_insurance_cost(self) -> models.QuerySet:
308
- return self.annotate(
309
- insurance_cost=Coalesce(
310
- Sum(
311
- "amount",
312
- filter=Q(ref_type__in=RefTypeManager.INSURANCE, amount__lt=0),
313
- ),
314
- Value(0),
315
- output_field=DecimalField(),
316
- )
317
- )
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()
318
234
 
319
- def annotate_planetary_cost(self) -> models.QuerySet:
320
- return self.annotate(
321
- planetary_cost=Coalesce(
322
- Sum(
323
- "amount",
324
- filter=Q(ref_type__in=RefTypeManager.PLANETARY, amount__lt=0),
325
- ),
326
- Value(0),
327
- output_field=DecimalField(),
328
- )
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
329
260
  )
330
261
 
331
- def annotate_lp_cost(self) -> models.QuerySet:
332
- return self.annotate(
333
- lp_cost=Coalesce(
334
- Sum(
335
- "amount",
336
- filter=Q(ref_type__in=RefTypeManager.LP, amount__lt=0),
337
- ),
338
- Value(0),
339
- output_field=DecimalField(),
340
- )
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
341
267
  )
342
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
+ )
343
284
 
344
- # pylint: disable=used-before-assignment
345
- class CharWalletQuerySet(CharWalletCostQueryFilter):
346
285
  @log_timing(logger)
347
286
  def update_or_create_esi(
348
- self, character: "CharacterAudit", force_refresh: bool = False
287
+ self, owner: "CharacterOwner", force_refresh: bool = False
349
288
  ) -> "UpdateSectionResult":
350
289
  """Update or Create a wallet journal entry from ESI data."""
351
- return character.update_section_if_changed(
352
- section=character.UpdateSection.WALLET_JOURNAL,
290
+ return owner.update_manager.update_section_if_changed(
291
+ section=CharacterUpdateSection.WALLET_JOURNAL,
353
292
  fetch_func=self._fetch_esi_data,
354
293
  force_refresh=force_refresh,
355
294
  )
356
295
 
357
- def _fetch_esi_data(self, audit: "CharacterAudit", force_refresh: bool) -> None:
296
+ def _fetch_esi_data(self, owner: "CharacterOwner", force_refresh: bool) -> None:
358
297
  """Fetch wallet journal entries from ESI data."""
359
298
  req_scopes = ["esi-wallet.read_character_wallet.v1"]
360
- token = audit.get_token(scopes=req_scopes)
299
+ token = owner.get_token(scopes=req_scopes)
361
300
 
362
301
  operation = esi.client.Wallet.GetCharactersCharacterIdWalletJournal(
363
- character_id=audit.eve_character.character_id,
302
+ character_id=owner.eve_character.character_id,
364
303
  token=token,
365
304
  )
366
305
 
@@ -368,12 +307,12 @@ class CharWalletQuerySet(CharWalletCostQueryFilter):
368
307
  force_refresh=force_refresh,
369
308
  )
370
309
 
371
- self._update_or_create_objs(character=audit, objs=journal_items)
310
+ self._update_or_create_objs(character=owner, objs=journal_items)
372
311
 
373
312
  @transaction.atomic()
374
313
  def _update_or_create_objs(
375
314
  self,
376
- character: "CharacterAudit",
315
+ character: "CharacterOwner",
377
316
  objs: list["CharactersCharacterIdWalletJournalGetItem"],
378
317
  ) -> None:
379
318
  """Update or Create wallet journal entries from objs data."""
@@ -421,116 +360,6 @@ class CharWalletQuerySet(CharWalletCostQueryFilter):
421
360
  created_names = EveEntity.objects.create_bulk_from_esi(_new_names)
422
361
 
423
362
  if created_names:
424
- self.bulk_create(items)
363
+ self.bulk_create(items, batch_size=LEDGER_BULK_BATCH_SIZE)
425
364
  else:
426
365
  raise TokenError("ESI Fail")
427
-
428
- def aggregate_bounty(self) -> dict:
429
- """Aggregate bounty income."""
430
- return self.filter(ref_type__in=RefTypeManager.BOUNTY_PRIZES).aggregate(
431
- total_bounty=Coalesce(Sum("amount"), Value(0), output_field=DecimalField())
432
- )["total_bounty"]
433
-
434
- def aggregate_ess(self) -> dict:
435
- """Aggregate ESS income."""
436
- return self.filter(ref_type__in=RefTypeManager.ESS_TRANSFER).aggregate(
437
- total_ess=Coalesce(Sum("amount"), Value(0), output_field=DecimalField())
438
- )["total_ess"]
439
-
440
- def aggregate_costs(self, first_party=None, second_party=None) -> dict:
441
- """Aggregate costs. first_party wird für donation exkludiert."""
442
- qs = self
443
- cost_types = RefTypeManager.all_ref_types()
444
- donation_types = RefTypeManager.DONATION
445
-
446
- if first_party is not None:
447
- if isinstance(first_party, int):
448
- first_party = [first_party]
449
- qs = qs.filter(first_party__in=first_party)
450
-
451
- if second_party is not None:
452
- if isinstance(second_party, int):
453
- second_party = [second_party]
454
- # Exclude: alle donation mit first_party_id in Liste, Rest wie gehabt
455
- qs = qs.exclude(
456
- Q(ref_type__in=donation_types, first_party_id__in=second_party)
457
- )
458
- qs = qs.filter(Q(ref_type__in=cost_types))
459
- else:
460
- qs = qs.filter(Q(ref_type__in=cost_types))
461
-
462
- qs = qs.filter(amount__lt=0)
463
- return qs.aggregate(
464
- total_costs=Coalesce(Sum("amount"), Value(0), output_field=DecimalField())
465
- )["total_costs"]
466
-
467
- def aggregate_miscellaneous(self, first_party=None, second_party=None) -> dict:
468
- """Aggregate miscellaneous income. first_party wird nur für donation angewandt."""
469
- qs = self
470
-
471
- misc_types = RefTypeManager.all_ref_types()
472
- donation_types = RefTypeManager.DONATION
473
-
474
- if first_party is not None:
475
- if isinstance(first_party, int):
476
- first_party = [first_party]
477
- # Exclude: alle donation mit first_party_id in Liste, Rest wie gehabt
478
- qs = qs.exclude(
479
- Q(ref_type__in=donation_types, first_party_id__in=first_party)
480
- )
481
- qs = qs.filter(Q(ref_type__in=misc_types))
482
- else:
483
- qs = qs.filter(Q(ref_type__in=misc_types))
484
-
485
- if second_party is not None:
486
- if isinstance(second_party, int):
487
- second_party = [second_party]
488
- qs = qs.filter(second_party__in=second_party)
489
-
490
- qs = qs.filter(amount__gt=0)
491
- return qs.aggregate(
492
- total_misc=Coalesce(Sum("amount"), Value(0), output_field=DecimalField())
493
- )["total_misc"]
494
-
495
- # pylint: disable=too-many-positional-arguments
496
- def aggregate_ref_type(
497
- self,
498
- ref_type: list,
499
- first_party=None,
500
- second_party=None,
501
- exclude=None,
502
- income: bool = False,
503
- ) -> dict:
504
- """Aggregate income by ref_type."""
505
- qs = self.filter(ref_type__in=ref_type)
506
-
507
- if first_party is not None:
508
- if isinstance(first_party, int):
509
- first_party = [first_party]
510
- qs = qs.filter(first_party__in=first_party)
511
-
512
- if second_party is not None:
513
- if isinstance(second_party, int):
514
- second_party = [second_party]
515
- qs = qs.filter(second_party__in=second_party)
516
-
517
- if exclude is not None:
518
- if isinstance(exclude, int):
519
- exclude = [exclude]
520
- qs = qs.exclude(first_party__in=exclude)
521
-
522
- if income:
523
- qs = qs.filter(amount__gt=0)
524
- else:
525
- qs = qs.filter(amount__lt=0)
526
-
527
- return qs.aggregate(
528
- total=Coalesce(Sum("amount"), Value(0), output_field=DecimalField())
529
- )["total"]
530
-
531
-
532
- class CharWalletManagerBase(models.Manager):
533
- pass
534
-
535
-
536
- CharWalletManager = CharWalletManagerBase.from_queryset(CharWalletQuerySet)