aa-buybackprogram 3.0.0b3__tar.gz → 3.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (133) hide show
  1. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/PKG-INFO +33 -4
  2. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/README.md +32 -3
  3. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/__init__.py +1 -1
  4. aa_buybackprogram-3.1.0/buybackprogram/admin.py +194 -0
  5. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/forms.py +68 -4
  6. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/helpers.py +1 -2
  7. aa_buybackprogram-3.1.0/buybackprogram/migrations/0019_program_tracking_prefill_reverseprogram_stock_source_and_more.py +123 -0
  8. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/models.py +360 -94
  9. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/notification.py +1 -1
  10. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/tasks.py +47 -18
  11. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/templates/buybackprogram/contract_details.html +13 -2
  12. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/templates/buybackprogram/index.html +45 -5
  13. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/templates/buybackprogram/partials/stats/tabs/finished.html +9 -7
  14. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/templates/buybackprogram/partials/stats/tabs/outstanding.html +9 -7
  15. aa_buybackprogram-3.1.0/buybackprogram/templates/buybackprogram/partials/stats/tabs/untracked.html +48 -0
  16. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/templates/buybackprogram/reverse_program/reverse_calculate.html +197 -104
  17. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/templates/buybackprogram/reverse_program/reverse_index.html +45 -5
  18. aa_buybackprogram-3.1.0/buybackprogram/templates/buybackprogram/reverse_program/reverse_program_add.html +258 -0
  19. aa_buybackprogram-3.1.0/buybackprogram/templates/buybackprogram/reverse_program/reverse_program_edit.html +259 -0
  20. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/templates/buybackprogram/reverse_program/reverse_program_stats.html +129 -4
  21. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/templates/buybackprogram/reverse_program/reverse_stats.html +74 -5
  22. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/tests/test_helpers.py +18 -0
  23. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/tests/test_reverse_buyback.py +13 -4
  24. aa_buybackprogram-3.1.0/buybackprogram/tests/test_tasks.py +94 -0
  25. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/urls.py +20 -0
  26. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/views/calculate.py +21 -2
  27. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/views/programs.py +58 -4
  28. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/views/stats.py +130 -1
  29. aa_buybackprogram-3.0.0b3/buybackprogram/admin.py +0 -62
  30. aa_buybackprogram-3.0.0b3/buybackprogram/templates/buybackprogram/partials/stats/tabs/untracked.html +0 -55
  31. aa_buybackprogram-3.0.0b3/buybackprogram/templates/buybackprogram/reverse_program/reverse_program_add.html +0 -59
  32. aa_buybackprogram-3.0.0b3/buybackprogram/templates/buybackprogram/reverse_program/reverse_program_edit.html +0 -64
  33. aa_buybackprogram-3.0.0b3/buybackprogram/tests/test_data_migrations.log +0 -2
  34. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/LICENSE +0 -0
  35. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/app_settings.py +0 -0
  36. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/apps.py +0 -0
  37. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/auth_hooks.py +0 -0
  38. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/constants.py +0 -0
  39. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/decorators.py +0 -0
  40. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/management/__init__.py +0 -0
  41. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/management/commands/__init__.py +0 -0
  42. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/management/commands/buybackprogram_generate_test_data.py +0 -0
  43. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/management/commands/buybackprogram_link_contracts.py +0 -0
  44. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/management/commands/buybackprogram_load_data.py +0 -0
  45. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/management/commands/buybackprogram_load_prices.py +0 -0
  46. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/management/commands/generate_dummy_data.py +0 -0
  47. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/migrations/0001_initial.py +0 -0
  48. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/migrations/0002_contractnotification.py +0 -0
  49. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/migrations/0003_statics_performance_increase.py +0 -0
  50. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/migrations/0004_auto_20220323_1331.py +0 -0
  51. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/migrations/0005_program_compression_price_dencity_modifier.py +0 -0
  52. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/migrations/0006_program_bonds_npc_price_and_more.py +0 -0
  53. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/migrations/0007_program_expiration.py +0 -0
  54. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/migrations/0008_alter_general_options_alter_program_refining_rate.py +0 -0
  55. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/migrations/0009_contract_location_name.py +0 -0
  56. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/migrations/0010_contract_no_tracking_alter_tracking_tracking_number.py +0 -0
  57. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/migrations/0011_delete_duplicated_contracts.py +0 -0
  58. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/migrations/0012_make_contract_id_unique.py +0 -0
  59. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/migrations/0013_program_discord_show_item_list.py +0 -0
  60. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/migrations/0014_program_price_type.py +0 -0
  61. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/migrations/0015_faq_contractnotification_header_and_more.py +0 -0
  62. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/migrations/0016_alter_program_compression_price_dencity_modifier.py +0 -0
  63. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/migrations/0017_alter_contract_price_alter_contract_volume_and_more.py +0 -0
  64. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/migrations/0018_structurenamecache_alter_general_options_and_more.py +0 -0
  65. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/migrations/__init__.py +0 -0
  66. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/notes.py +0 -0
  67. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/providers.py +0 -0
  68. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/static/buybackprogram/css/billboards_dark.css +0 -0
  69. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/static/buybackprogram/css/billboards_light.css +0 -0
  70. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/static/buybackprogram/css/style_dark.css +0 -0
  71. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/static/buybackprogram/css/style_light.css +0 -0
  72. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/static/buybackprogram/images/help.png +0 -0
  73. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/static/buybackprogram/js/autocomplete.js +0 -0
  74. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/static/buybackprogram/js/bootstrap-autocomplete.min.js +0 -0
  75. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/swagger.json +0 -0
  76. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/templates/buybackprogram/base.html +0 -0
  77. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/templates/buybackprogram/bundles/buybackprogram-css-light.html +0 -0
  78. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/templates/buybackprogram/faq.html +0 -0
  79. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/templates/buybackprogram/leaderboards.html +0 -0
  80. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/templates/buybackprogram/location_add.html +0 -0
  81. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/templates/buybackprogram/page.html +0 -0
  82. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/templates/buybackprogram/partials/base/menu-management.html +0 -0
  83. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/templates/buybackprogram/partials/base/menu-reverse-management.html +0 -0
  84. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/templates/buybackprogram/partials/base/menu-reverse.html +0 -0
  85. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/templates/buybackprogram/partials/base/menu.html +0 -0
  86. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/templates/buybackprogram/partials/calculate/accepted_locations.html +0 -0
  87. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/templates/buybackprogram/partials/calculate/calculation_success.html +0 -0
  88. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/templates/buybackprogram/partials/calculate/contract_settings.html +0 -0
  89. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/templates/buybackprogram/partials/calculate/invoice.html +0 -0
  90. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/templates/buybackprogram/partials/calculate/item_details.html +0 -0
  91. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/templates/buybackprogram/partials/details/accepted-locations.html +0 -0
  92. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/templates/buybackprogram/partials/details/contract-info.html +0 -0
  93. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/templates/buybackprogram/partials/details/invoice.html +0 -0
  94. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/templates/buybackprogram/partials/details/tabs/contract-items.html +0 -0
  95. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/templates/buybackprogram/partials/details/tabs/original-items.html +0 -0
  96. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/templates/buybackprogram/partials/details/tabs/tabs-navigation.html +0 -0
  97. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/templates/buybackprogram/partials/details/warning.html +0 -0
  98. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/templates/buybackprogram/partials/stats/summary.html +0 -0
  99. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/templates/buybackprogram/partials/stats/tabs/tabs-navigation.html +0 -0
  100. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/templates/buybackprogram/partials/stats/tabs/wallet_overview.html +0 -0
  101. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/templates/buybackprogram/partials/stats/warning.html +0 -0
  102. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/templates/buybackprogram/performance.html +0 -0
  103. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/templates/buybackprogram/program_add.html +0 -0
  104. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/templates/buybackprogram/program_calculate.html +0 -0
  105. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/templates/buybackprogram/program_edit.html +0 -0
  106. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/templates/buybackprogram/program_edit_item.html +0 -0
  107. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/templates/buybackprogram/program_edit_marketgroup.html +0 -0
  108. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/templates/buybackprogram/program_edit_static_price.html +0 -0
  109. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/templates/buybackprogram/program_edit_watchlist.html +0 -0
  110. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/templates/buybackprogram/program_special_taxes.html +0 -0
  111. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/templates/buybackprogram/reverse_program/reverse_contract_details.html +0 -0
  112. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/templates/buybackprogram/stats.html +0 -0
  113. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/templates/buybackprogram/user_settings.html +0 -0
  114. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/templatetags/__init__.py +0 -0
  115. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/templatetags/bs_tab_color_class.py +0 -0
  116. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/templatetags/help_icons.py +0 -0
  117. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/templatetags/price_formats.py +0 -0
  118. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/templatetags/program_settings.py +0 -0
  119. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/tests/__init__.py +0 -0
  120. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/tests/test_data_migrations.py +0 -0
  121. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/tests/test_models.py +0 -0
  122. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/tests/test_reverse_contract_notifications.py +0 -0
  123. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/tests/test_views.py +0 -0
  124. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/tests/testdata/__init__.py +0 -0
  125. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/tests/testdata/create_eveuniverse.py +0 -0
  126. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/tests/testdata/eveuniverse.json +0 -0
  127. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/tests/testdata/factories.py +0 -0
  128. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/tests/testdata/load_eveuniverse.py +0 -0
  129. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/utils.py +0 -0
  130. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/views/__init__.py +0 -0
  131. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/views/common.py +0 -0
  132. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/buybackprogram/views/special_taxes.py +0 -0
  133. {aa_buybackprogram-3.0.0b3 → aa_buybackprogram-3.1.0}/pyproject.toml +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: aa-buybackprogram
3
- Version: 3.0.0b3
3
+ Version: 3.1.0
4
4
  Summary: Buyback program plugin app for Alliance Auth.
5
5
  Author-email: Ikarus Cesaille <contact@eve-linknet.com>
6
6
  Requires-Python: >=3.10
@@ -39,6 +39,7 @@ An Alliance Auth app for creating buyback programs and to allow users calculate
39
39
  - [Features](#features)
40
40
  - [Installation](#installation)
41
41
  - [Program Settings](#program-settings)
42
+ - [Troubleshooting](#troubleshooting)
42
43
  - [Change Log](CHANGELOG.md)
43
44
 
44
45
  ## Images
@@ -165,7 +166,7 @@ Buyback program requires a few periodic tasks to operate and update its data.
165
166
 
166
167
  Buybackprogram is designed to use locally stored prices to speed up the price calculations. It is important that you have the price update task in your periodic tasks so that your prices will update.
167
168
 
168
- By adding the following lines in your `local.py` setting file the program will update the stored item prices at midnight. This same task is also responsible for maintenance tasks such as removing unlinked tracking objects. It will also check for any new contracts and update the statistics page with them every 30 minutes:
169
+ By adding the following lines in your `local.py` setting file the program will update the stored item prices at midnight. This same task is also responsible for maintenance tasks such as removing unlinked tracking objects, including reverse buyback requests that were never followed through with an actual contract (which releases the stock they had reserved). It will also check for any new contracts and update the statistics page with them every 30 minutes:
169
170
 
170
171
  ```python
171
172
  # Buybackprogram price updates, updates prices at midnight
@@ -207,14 +208,14 @@ Name | Description | Default | Options
207
208
  BUYBACKPROGRAM_ENABLE_REVERSE_BUYBACK | Enable or disable the reverse buyback feature entirely. When disabled the menu item will not appear. | True | `True`, `False`
208
209
  BUYBACKPROGRAM_DISALLOW_CONTRACTS_WITH_ANY_DISALLOWED_ITEMS | Prevents price calculations if there are any disallowed items in the parsed list | False | `True`, `False`
209
210
  BUYBACKPROGRAM_SHOW_LOCATION_COUNT | How many locations we will show on buyback index page before we change it to a pop-up show all locations button | 4 | Integer
210
- BUYBACKPROGRAM_TRACKING_PREFILL | This is the prefill tag you will have on the tracking description for your contracts | aa-bbp. | Free text input
211
+ BUYBACKPROGRAM_TRACKING_PREFILL | Default prefill tag used at the start of tracking numbers for your contracts. Individual buyback and reverse buyback programs can override this from their program settings (Tracking prefill tag), e.g. to separate ore and manufacturing buybacks in reports. | aa-bbp. | Free text input
211
212
  BUYBACKPROGRAM_PRICE_SOURCE_ID | Station ID for fetching base prices. Supports IDs listed on [Fuzzwork API](https://market.fuzzwork.co.uk/api/). Does not work with Janice API!| 60003760 | See ID list
212
213
  BUYBACKPROGRAM_PRICE_SOURCE_NAME | Display name of your price source. Has no effect on the actual price fetch which uses the ID. | Jita | Free text input
213
214
  BUYBACKPROGRAM_PRICE_AGE_WARNING_LIMIT | Limit in hours when an item price is considered as outdated | 48
214
215
  BUYBACKPROGRAM_PRICE_METHOD | By default Fuzzwork API will be used for pricing, if this is set to "Janice" then the Janice API will be used. | Fuzzwork | `Fuzzwork`, `Janice`
215
216
  BUYBACKPROGRAM_PRICE_INSTANT_PRICES | On default we use top 5% average prices. Set to `true` to change to instant prices | False | `False`, `True`
216
217
  BUYBACKPROGRAM_PRICE_JANICE_API_KEY | The API key to access Janice API. | `null` | `G9KwKq3465588VPd6747t95Zh94q3W2E`
217
- BUYBACKPROGRAM_UNUSED_TRACKING_PURGE_LIMIT | Time limit to remove unlinked tracking objects from the database. Set to 0 to never purge any tracking objects. | 48 | Any number in hours
218
+ BUYBACKPROGRAM_UNUSED_TRACKING_PURGE_LIMIT | Time limit to remove unlinked tracking objects (both normal and reverse buyback requests) from the database. Purging an unlinked reverse buyback request also releases the hangar stock it had reserved. Set to 0 to never purge any tracking objects. | 48 | Any number in hours
218
219
  BUYBACKPROGRAM_TRACK_PREFILL_CONTRACTS | Determines if we will track and store contracts that starts with the prefill phrase but do not have any actual tracking hits in database | True | `True`, `False`
219
220
  BUYBACKPROGRAM_RESTRICT_TRACKING_DETAILS | When True, only the contract issuer and managers can view tracking detail pages via direct URL. Set to False to allow any authenticated basic_access user to view any tracking detail. | False | `True`, `False`
220
221
 
@@ -513,6 +514,20 @@ If ticked, contracts will be issued from the manager's corporation.
513
514
  ##### Location
514
515
  The structure where stock is held and contracts will be created. The structure ID must be set on the location for hangar sync to work.
515
516
 
517
+ ##### Stock Source
518
+ Choose where the program's stock comes from:
519
+
520
+ - **Whole hangar division** — everything in the selected Hangar Division at the program locations is for sale.
521
+ - **Selected containers only** — only the contents of the chosen containers are for sale. Use this when you cannot dedicate an entire division to buyback. Containers must be assembled and sitting directly in a hangar division at one of the program locations; nested containers are not supported. Give containers a name ingame so they are easy to pick from the list.
522
+
523
+ Containers are discovered during the hangar sync, so on a new manager the container list is empty until the first sync has run. Click **Refresh Stock** and reload the page if the list is empty. If a selected container is repackaged or moved away from the program locations it disappears from the program's stock and a warning is written to the log.
524
+
525
+ ##### Hangar Division
526
+ The corporation hangar division to sell from. Only used when the stock source is a whole hangar division.
527
+
528
+ ##### Containers
529
+ The containers to sell from. Only used when the stock source is selected containers.
530
+
516
531
  ##### Price Type
517
532
  The base price type used to value items. Default: Sell.
518
533
 
@@ -535,3 +550,17 @@ Once your program is created, click **Refresh Stock** in the management menu to
535
550
 
536
551
  Players navigate to **Reverse Buyback**, click **Buy** on a program, paste a list of items they want, and submit. The calculator checks availability against current stock and generates a price and tracking number. The player then waits for the owner to create a contract to them using the tracking number as the contract title.
537
552
 
553
+
554
+ ## Troubleshooting
555
+
556
+ ### Contract updates stop running after a worker restart
557
+
558
+ Symptom: the log shows `Starting all contract updates` and `Updating contracts for <owner>` for each owner, but no `Fetching all contracts for ...` line follows and no contracts are linked.
559
+
560
+ Cause: each owner's contract update runs as a run-once task. If a worker is restarted while the task is running (for example right after applying migrations), the lock is never released and every following run is silently skipped until the lock expires. The lock is stored in the Django cache and expires after `BUYBACKPROGRAM_TASKS_TIME_LIMIT` seconds (2 hours by default).
561
+
562
+ To clear the locks immediately:
563
+
564
+ ```bash
565
+ redis-cli -n 1 --scan --pattern "*qo_buybackprogram*" | xargs -r redis-cli -n 1 del
566
+
@@ -13,6 +13,7 @@ An Alliance Auth app for creating buyback programs and to allow users calculate
13
13
  - [Features](#features)
14
14
  - [Installation](#installation)
15
15
  - [Program Settings](#program-settings)
16
+ - [Troubleshooting](#troubleshooting)
16
17
  - [Change Log](CHANGELOG.md)
17
18
 
18
19
  ## Images
@@ -139,7 +140,7 @@ Buyback program requires a few periodic tasks to operate and update its data.
139
140
 
140
141
  Buybackprogram is designed to use locally stored prices to speed up the price calculations. It is important that you have the price update task in your periodic tasks so that your prices will update.
141
142
 
142
- By adding the following lines in your `local.py` setting file the program will update the stored item prices at midnight. This same task is also responsible for maintenance tasks such as removing unlinked tracking objects. It will also check for any new contracts and update the statistics page with them every 30 minutes:
143
+ By adding the following lines in your `local.py` setting file the program will update the stored item prices at midnight. This same task is also responsible for maintenance tasks such as removing unlinked tracking objects, including reverse buyback requests that were never followed through with an actual contract (which releases the stock they had reserved). It will also check for any new contracts and update the statistics page with them every 30 minutes:
143
144
 
144
145
  ```python
145
146
  # Buybackprogram price updates, updates prices at midnight
@@ -181,14 +182,14 @@ Name | Description | Default | Options
181
182
  BUYBACKPROGRAM_ENABLE_REVERSE_BUYBACK | Enable or disable the reverse buyback feature entirely. When disabled the menu item will not appear. | True | `True`, `False`
182
183
  BUYBACKPROGRAM_DISALLOW_CONTRACTS_WITH_ANY_DISALLOWED_ITEMS | Prevents price calculations if there are any disallowed items in the parsed list | False | `True`, `False`
183
184
  BUYBACKPROGRAM_SHOW_LOCATION_COUNT | How many locations we will show on buyback index page before we change it to a pop-up show all locations button | 4 | Integer
184
- BUYBACKPROGRAM_TRACKING_PREFILL | This is the prefill tag you will have on the tracking description for your contracts | aa-bbp. | Free text input
185
+ BUYBACKPROGRAM_TRACKING_PREFILL | Default prefill tag used at the start of tracking numbers for your contracts. Individual buyback and reverse buyback programs can override this from their program settings (Tracking prefill tag), e.g. to separate ore and manufacturing buybacks in reports. | aa-bbp. | Free text input
185
186
  BUYBACKPROGRAM_PRICE_SOURCE_ID | Station ID for fetching base prices. Supports IDs listed on [Fuzzwork API](https://market.fuzzwork.co.uk/api/). Does not work with Janice API!| 60003760 | See ID list
186
187
  BUYBACKPROGRAM_PRICE_SOURCE_NAME | Display name of your price source. Has no effect on the actual price fetch which uses the ID. | Jita | Free text input
187
188
  BUYBACKPROGRAM_PRICE_AGE_WARNING_LIMIT | Limit in hours when an item price is considered as outdated | 48
188
189
  BUYBACKPROGRAM_PRICE_METHOD | By default Fuzzwork API will be used for pricing, if this is set to "Janice" then the Janice API will be used. | Fuzzwork | `Fuzzwork`, `Janice`
189
190
  BUYBACKPROGRAM_PRICE_INSTANT_PRICES | On default we use top 5% average prices. Set to `true` to change to instant prices | False | `False`, `True`
190
191
  BUYBACKPROGRAM_PRICE_JANICE_API_KEY | The API key to access Janice API. | `null` | `G9KwKq3465588VPd6747t95Zh94q3W2E`
191
- BUYBACKPROGRAM_UNUSED_TRACKING_PURGE_LIMIT | Time limit to remove unlinked tracking objects from the database. Set to 0 to never purge any tracking objects. | 48 | Any number in hours
192
+ BUYBACKPROGRAM_UNUSED_TRACKING_PURGE_LIMIT | Time limit to remove unlinked tracking objects (both normal and reverse buyback requests) from the database. Purging an unlinked reverse buyback request also releases the hangar stock it had reserved. Set to 0 to never purge any tracking objects. | 48 | Any number in hours
192
193
  BUYBACKPROGRAM_TRACK_PREFILL_CONTRACTS | Determines if we will track and store contracts that starts with the prefill phrase but do not have any actual tracking hits in database | True | `True`, `False`
193
194
  BUYBACKPROGRAM_RESTRICT_TRACKING_DETAILS | When True, only the contract issuer and managers can view tracking detail pages via direct URL. Set to False to allow any authenticated basic_access user to view any tracking detail. | False | `True`, `False`
194
195
 
@@ -487,6 +488,20 @@ If ticked, contracts will be issued from the manager's corporation.
487
488
  ##### Location
488
489
  The structure where stock is held and contracts will be created. The structure ID must be set on the location for hangar sync to work.
489
490
 
491
+ ##### Stock Source
492
+ Choose where the program's stock comes from:
493
+
494
+ - **Whole hangar division** — everything in the selected Hangar Division at the program locations is for sale.
495
+ - **Selected containers only** — only the contents of the chosen containers are for sale. Use this when you cannot dedicate an entire division to buyback. Containers must be assembled and sitting directly in a hangar division at one of the program locations; nested containers are not supported. Give containers a name ingame so they are easy to pick from the list.
496
+
497
+ Containers are discovered during the hangar sync, so on a new manager the container list is empty until the first sync has run. Click **Refresh Stock** and reload the page if the list is empty. If a selected container is repackaged or moved away from the program locations it disappears from the program's stock and a warning is written to the log.
498
+
499
+ ##### Hangar Division
500
+ The corporation hangar division to sell from. Only used when the stock source is a whole hangar division.
501
+
502
+ ##### Containers
503
+ The containers to sell from. Only used when the stock source is selected containers.
504
+
490
505
  ##### Price Type
491
506
  The base price type used to value items. Default: Sell.
492
507
 
@@ -508,3 +523,17 @@ Once your program is created, click **Refresh Stock** in the management menu to
508
523
  #### How it works for players
509
524
 
510
525
  Players navigate to **Reverse Buyback**, click **Buy** on a program, paste a list of items they want, and submit. The calculator checks availability against current stock and generates a price and tracking number. The player then waits for the owner to create a contract to them using the tracking number as the contract title.
526
+
527
+
528
+ ## Troubleshooting
529
+
530
+ ### Contract updates stop running after a worker restart
531
+
532
+ Symptom: the log shows `Starting all contract updates` and `Updating contracts for <owner>` for each owner, but no `Fetching all contracts for ...` line follows and no contracts are linked.
533
+
534
+ Cause: each owner's contract update runs as a run-once task. If a worker is restarted while the task is running (for example right after applying migrations), the lock is never released and every following run is silently skipped until the lock expires. The lock is stored in the Django cache and expires after `BUYBACKPROGRAM_TASKS_TIME_LIMIT` seconds (2 hours by default).
535
+
536
+ To clear the locks immediately:
537
+
538
+ ```bash
539
+ redis-cli -n 1 --scan --pattern "*qo_buybackprogram*" | xargs -r redis-cli -n 1 del
@@ -2,5 +2,5 @@
2
2
 
3
3
  default_app_config = "buybackprogram.apps.BuybackProgramConfig"
4
4
 
5
- __version__ = "3.0.0b3"
5
+ __version__ = "3.1.0"
6
6
  __title__ = "Buyback Program"
@@ -0,0 +1,194 @@
1
+ from django.contrib import admin
2
+ from django.db.models import Exists, OuterRef
3
+
4
+ from .models import (
5
+ Contract,
6
+ Faq,
7
+ HangarContainer,
8
+ HangarDivision,
9
+ HangarStock,
10
+ Location,
11
+ Owner,
12
+ Program,
13
+ ReverseProgram,
14
+ ReverseTracking,
15
+ ReverseTrackingItem,
16
+ StaticItemPrice,
17
+ )
18
+
19
+ # Register your models here.
20
+
21
+
22
+ class ProgramAdmin(admin.ModelAdmin):
23
+ model = Program
24
+
25
+ list_display = (
26
+ "owner",
27
+ "_location",
28
+ "is_corporation",
29
+ "hauling_fuel_cost",
30
+ "tax",
31
+ "refining_rate",
32
+ "price_dencity_modifier",
33
+ "allow_all_items",
34
+ "use_refined_value",
35
+ "use_compressed_value",
36
+ "use_raw_ore_value",
37
+ "allow_unpacked_items",
38
+ )
39
+
40
+ @classmethod
41
+ def _location(cls, obj):
42
+ names = [x.name for x in obj.location.all().order_by("name")]
43
+
44
+ if names:
45
+ return ", ".join(names)
46
+ else:
47
+ return None
48
+
49
+ _location.short_description = "Location"
50
+ _location.admin_order_field = "location__name"
51
+
52
+
53
+ class FaqAdmin(admin.ModelAdmin):
54
+ model = Program
55
+
56
+ list_display = (
57
+ "header",
58
+ "body",
59
+ )
60
+
61
+
62
+ class ContractTypeFilter(admin.SimpleListFilter):
63
+ title = "contract type"
64
+ parameter_name = "contract_type"
65
+
66
+ def lookups(self, request, model_admin):
67
+ return (
68
+ ("reverse", "Reverse buyback"),
69
+ ("normal", "Normal buyback"),
70
+ )
71
+
72
+ def queryset(self, request, queryset):
73
+ if self.value() == "reverse":
74
+ return queryset.filter(reversetracking__isnull=False).distinct()
75
+ if self.value() == "normal":
76
+ return queryset.filter(reversetracking__isnull=True)
77
+ return None
78
+
79
+
80
+ @admin.register(Contract)
81
+ class ContractAdmin(admin.ModelAdmin):
82
+ list_display = (
83
+ "contract_id",
84
+ "status",
85
+ "price",
86
+ "date_issued",
87
+ "location_name",
88
+ "_is_reverse",
89
+ "no_tracking",
90
+ )
91
+ list_filter = (ContractTypeFilter, "status")
92
+ search_fields = ("contract_id", "title", "location_name")
93
+
94
+ def get_queryset(self, request):
95
+ return (
96
+ super()
97
+ .get_queryset(request)
98
+ .annotate(
99
+ is_reverse=Exists(
100
+ ReverseTracking.objects.filter(contract=OuterRef("pk"))
101
+ )
102
+ )
103
+ )
104
+
105
+ @admin.display(description="Reverse", boolean=True, ordering="is_reverse")
106
+ def _is_reverse(self, obj):
107
+ return obj.is_reverse
108
+
109
+
110
+ @admin.register(StaticItemPrice)
111
+ class StaticItemPriceAdmin(admin.ModelAdmin):
112
+ list_display = ("program", "eve_type", "price")
113
+ search_fields = ("program__name", "eve_type__name")
114
+
115
+
116
+ @admin.register(ReverseProgram)
117
+ class ReverseProgramAdmin(admin.ModelAdmin):
118
+ list_display = (
119
+ "name",
120
+ "owner",
121
+ "is_corporation",
122
+ "stock_source",
123
+ "price_type",
124
+ "markup",
125
+ "is_public",
126
+ )
127
+ list_filter = ("stock_source",)
128
+ search_fields = ("name",)
129
+ filter_horizontal = ("containers",)
130
+
131
+
132
+ class ReverseTrackingItemInline(admin.TabularInline):
133
+ model = ReverseTrackingItem
134
+ extra = 0
135
+ raw_id_fields = ("eve_type",)
136
+
137
+
138
+ @admin.register(ReverseTracking)
139
+ class ReverseTrackingAdmin(admin.ModelAdmin):
140
+ list_display = (
141
+ "tracking_number",
142
+ "program",
143
+ "issuer_user",
144
+ "net_price",
145
+ "created_at",
146
+ "_contract_status",
147
+ )
148
+ list_filter = (("contract", admin.EmptyFieldListFilter), "program")
149
+ search_fields = ("tracking_number",)
150
+ inlines = [ReverseTrackingItemInline]
151
+
152
+ @admin.display(description="Contract status")
153
+ def _contract_status(self, obj):
154
+ if obj.contract is None:
155
+ return "unlinked"
156
+ return obj.contract.status
157
+
158
+
159
+ @admin.register(HangarStock)
160
+ class HangarStockAdmin(admin.ModelAdmin):
161
+ list_display = ("program", "eve_type", "quantity", "updated_at")
162
+ search_fields = ("program__name", "eve_type__name")
163
+ raw_id_fields = ("eve_type",)
164
+
165
+
166
+ @admin.register(HangarDivision)
167
+ class HangarDivisionAdmin(admin.ModelAdmin):
168
+ list_display = ("name", "owner", "division_id", "location_flag", "updated_at")
169
+ search_fields = ("name",)
170
+ list_filter = ("owner",)
171
+
172
+
173
+ @admin.register(HangarContainer)
174
+ class HangarContainerAdmin(admin.ModelAdmin):
175
+ list_display = (
176
+ "name",
177
+ "type_name",
178
+ "owner",
179
+ "item_id",
180
+ "structure_id",
181
+ "location_flag",
182
+ "updated_at",
183
+ )
184
+ search_fields = ("name", "item_id")
185
+ list_filter = ("owner", "location_flag")
186
+
187
+
188
+ admin.site.register(Program, ProgramAdmin)
189
+
190
+ admin.site.register(Owner)
191
+
192
+ admin.site.register(Location)
193
+
194
+ admin.site.register(Faq, FaqAdmin)
@@ -3,7 +3,9 @@ from django.core.validators import MaxValueValidator, MinValueValidator
3
3
  from django.utils.translation import gettext as _
4
4
  from eveuniverse.models import EveGroup, EveMarketGroup, EveSolarSystem, EveType
5
5
 
6
+ from buybackprogram.app_settings import BUYBACKPROGRAM_TRACKING_PREFILL
6
7
  from buybackprogram.models import (
8
+ HangarContainer,
7
9
  HangarDivision,
8
10
  Location,
9
11
  Owner,
@@ -30,6 +32,9 @@ class ProgramForm(forms.ModelForm):
30
32
  self.user = kwargs.pop("user", None)
31
33
 
32
34
  super(ProgramForm, self).__init__(*args, **kwargs)
35
+ self.fields["tracking_prefill"].widget.attrs["placeholder"] = (
36
+ "%s (global default)" % BUYBACKPROGRAM_TRACKING_PREFILL
37
+ )
33
38
  self.fields["owner"].queryset = Owner.objects.filter(user=self.user)
34
39
  self.fields["location"].queryset = Location.objects.filter(
35
40
  owner__user=self.user
@@ -257,6 +262,9 @@ class ReverseProgramForm(forms.ModelForm):
257
262
  "is_public": "Public program",
258
263
  "is_corporation": "Use corporation as contract issuer",
259
264
  }
265
+ widgets = {
266
+ "stock_source": forms.RadioSelect,
267
+ }
260
268
 
261
269
  def __init__(self, *args, **kwargs):
262
270
  self.user = kwargs.pop("user", None)
@@ -267,17 +275,73 @@ class ReverseProgramForm(forms.ModelForm):
267
275
  )
268
276
 
269
277
  if is_admin:
270
- self.fields["owner"].queryset = Owner.objects.all()
278
+ owners = Owner.objects.all()
271
279
  self.fields["location"].queryset = Location.objects.all()
272
- self.fields["hangar_division"].queryset = HangarDivision.objects.all()
273
280
  else:
274
- self.fields["owner"].queryset = Owner.objects.filter(user=self.user)
281
+ owners = Owner.objects.filter(user=self.user)
275
282
  self.fields["location"].queryset = Location.objects.filter(
276
283
  owner__user=self.user
277
284
  )
285
+ self.fields["owner"].queryset = owners
286
+
287
+ # Hangar divisions and containers are only ever offered for the
288
+ # selected manager, so one manager's hangar layout is never rendered
289
+ # for another. Before a manager is picked the lists are empty and the
290
+ # page fills them via the autocomplete endpoints.
291
+ owner_id = self.data.get("owner") if self.is_bound else None
292
+ if not owner_id and self.instance.pk:
293
+ owner_id = self.instance.owner_id
294
+
295
+ if self.is_bound:
296
+ location_ids = self.data.getlist("location")
297
+ elif self.instance.pk:
298
+ location_ids = list(self.instance.location.values_list("pk", flat=True))
299
+ else:
300
+ location_ids = []
301
+
302
+ if owner_id and owners.filter(pk=owner_id).exists():
278
303
  self.fields["hangar_division"].queryset = HangarDivision.objects.filter(
279
- owner__user=self.user
304
+ owner_id=owner_id
305
+ )
306
+ # Only containers sitting at one of the program's locations can
307
+ # ever supply stock, so nothing else is offered or accepted
308
+ structure_ids = Location.objects.filter(
309
+ pk__in=location_ids, structure_id__isnull=False
310
+ ).values_list("structure_id", flat=True)
311
+ self.fields["containers"].queryset = HangarContainer.objects.filter(
312
+ owner_id=owner_id, structure_id__in=structure_ids
280
313
  )
314
+ else:
315
+ self.fields["hangar_division"].queryset = HangarDivision.objects.none()
316
+ self.fields["containers"].queryset = HangarContainer.objects.none()
317
+
318
+ def clean(self):
319
+ cleaned_data = super().clean()
320
+ stock_source = cleaned_data.get("stock_source")
321
+ owner = cleaned_data.get("owner")
322
+ containers = cleaned_data.get("containers")
323
+
324
+ if stock_source == ReverseProgram.StockSource.CONTAINERS:
325
+ locations = cleaned_data.get("location")
326
+ if (
327
+ locations is not None
328
+ and not locations.filter(structure_id__isnull=False).exists()
329
+ ):
330
+ self.add_error(
331
+ "location",
332
+ "Selected containers only needs at least one location with a structure ID set; containers are matched by structure.",
333
+ )
334
+ if not containers:
335
+ self.add_error(
336
+ "containers",
337
+ "Select at least one container, or change the stock source to a whole hangar division.",
338
+ )
339
+ elif owner and containers.exclude(owner=owner).exists():
340
+ self.add_error(
341
+ "containers", "All containers must belong to the selected manager."
342
+ )
343
+
344
+ return cleaned_data
281
345
 
282
346
 
283
347
  class ReverseCalculatorForm(forms.Form):
@@ -16,7 +16,6 @@ from buybackprogram.app_settings import (
16
16
  BUYBACKPROGRAM_PRICE_JANICE_API_KEY,
17
17
  BUYBACKPROGRAM_PRICE_METHOD,
18
18
  BUYBACKPROGRAM_PRICE_SOURCE_ID,
19
- BUYBACKPROGRAM_TRACKING_PREFILL,
20
19
  )
21
20
  from buybackprogram.constants import (
22
21
  BLUE_LOOT_TYPE_IDS,
@@ -1257,7 +1256,7 @@ def get_tracking_number(
1257
1256
  last_id = 0
1258
1257
 
1259
1258
  tracking_number = (
1260
- BUYBACKPROGRAM_TRACKING_PREFILL
1259
+ program.get_tracking_prefill()
1261
1260
  + "-"
1262
1261
  + str(last_id)
1263
1262
  + "-"
@@ -0,0 +1,123 @@
1
+ # Generated by Django 5.2.15 on 2026-09-14 04:26
2
+
3
+ import django.core.validators
4
+ import django.db.models.deletion
5
+ from django.db import migrations, models
6
+
7
+
8
+ class Migration(migrations.Migration):
9
+ dependencies = [
10
+ ("buybackprogram", "0018_structurenamecache_alter_general_options_and_more"),
11
+ ]
12
+
13
+ operations = [
14
+ migrations.AddField(
15
+ model_name="program",
16
+ name="tracking_prefill",
17
+ field=models.CharField(
18
+ blank=True,
19
+ default="",
20
+ help_text="Optional tag used as the start of tracking numbers for this program, for example aa-bbp-ore. Leave empty to use the global BUYBACKPROGRAM_TRACKING_PREFILL setting. Only letters, numbers, dots, dashes and underscores.",
21
+ max_length=16,
22
+ validators=[
23
+ django.core.validators.RegexValidator(
24
+ "^[A-Za-z0-9._-]*$",
25
+ "Only letters, numbers, dots, dashes and underscores are allowed.",
26
+ )
27
+ ],
28
+ verbose_name="Tracking prefill tag",
29
+ ),
30
+ ),
31
+ migrations.AddField(
32
+ model_name="reverseprogram",
33
+ name="stock_source",
34
+ field=models.CharField(
35
+ choices=[
36
+ ("division", "Whole hangar division"),
37
+ ("containers", "Selected containers only"),
38
+ ],
39
+ default="division",
40
+ help_text="Sell everything in a hangar division, or only the contents of specific containers at the program locations.",
41
+ max_length=10,
42
+ verbose_name="Stock source",
43
+ ),
44
+ ),
45
+ migrations.AddField(
46
+ model_name="reverseprogram",
47
+ name="tracking_prefill",
48
+ field=models.CharField(
49
+ blank=True,
50
+ default="",
51
+ help_text="Optional tag used as the start of tracking numbers for this program. Leave empty to use the global BUYBACKPROGRAM_TRACKING_PREFILL setting. Only letters, numbers, dots, dashes and underscores.",
52
+ max_length=16,
53
+ validators=[
54
+ django.core.validators.RegexValidator(
55
+ "^[A-Za-z0-9._-]*$",
56
+ "Only letters, numbers, dots, dashes and underscores are allowed.",
57
+ )
58
+ ],
59
+ verbose_name="Tracking prefill tag",
60
+ ),
61
+ ),
62
+ migrations.AlterField(
63
+ model_name="reverseprogram",
64
+ name="hangar_division",
65
+ field=models.ForeignKey(
66
+ blank=True,
67
+ help_text="The corporation hangar division where stock is stored. Used when stock source is a whole hangar division.",
68
+ null=True,
69
+ on_delete=django.db.models.deletion.SET_NULL,
70
+ to="buybackprogram.hangardivision",
71
+ ),
72
+ ),
73
+ migrations.CreateModel(
74
+ name="HangarContainer",
75
+ fields=[
76
+ (
77
+ "id",
78
+ models.AutoField(
79
+ auto_created=True,
80
+ primary_key=True,
81
+ serialize=False,
82
+ verbose_name="ID",
83
+ ),
84
+ ),
85
+ (
86
+ "item_id",
87
+ models.BigIntegerField(
88
+ help_text="ESI asset item id of the container"
89
+ ),
90
+ ),
91
+ ("name", models.CharField(max_length=128)),
92
+ ("type_name", models.CharField(blank=True, default="", max_length=128)),
93
+ ("structure_id", models.BigIntegerField(blank=True, null=True)),
94
+ (
95
+ "location_flag",
96
+ models.CharField(blank=True, default="", max_length=32),
97
+ ),
98
+ ("updated_at", models.DateTimeField(auto_now=True)),
99
+ (
100
+ "owner",
101
+ models.ForeignKey(
102
+ on_delete=django.db.models.deletion.CASCADE,
103
+ related_name="containers",
104
+ to="buybackprogram.owner",
105
+ ),
106
+ ),
107
+ ],
108
+ options={
109
+ "default_permissions": (),
110
+ "unique_together": {("owner", "item_id")},
111
+ },
112
+ ),
113
+ migrations.AddField(
114
+ model_name="reverseprogram",
115
+ name="containers",
116
+ field=models.ManyToManyField(
117
+ blank=True,
118
+ help_text="Containers whose contents are sold. Used when stock source is selected containers. Containers appear here after the first hangar sync; name them ingame to make them easy to identify.",
119
+ related_name="+",
120
+ to="buybackprogram.hangarcontainer",
121
+ ),
122
+ ),
123
+ ]