mirror of
				https://github.com/inventree/InvenTree.git
				synced 2025-10-30 20:55:42 +00:00 
			
		
		
		
	Bump to Dj 4.x (#6173)
* bump to dj >4.2 * switch to experimental git release * bump django-import_export * bump mptt * replace is_ajax, which was removed https://docs.djangoproject.com/en/3.1/releases/3.1/#id2 * Save before accessing values in m2m/fk fields * move plugin init * use dev version of django for fix * update deps * fix deps * use django smaller 4.2 * fix reqs * fix merge * remove moved code * another merge fix * fix ajax call * fix refs * change python min v * fix deps * bump deps * fix deps * pin pillow * dj 4.1 upgrades * make diff smaller * bump all deps * drop down to py3.9 * bump versions * merge fix * fix diff * more bumping * diff cleanup * bump deps * fix reqs * use accurate state for model migrations using apps the historically correct state is used * try import * added more logs * add try here too * clean up rebuilds * Dj 4.2 (#161) * autochanges * bump * fix diff * fix diff * bump deps * fix req * remove select_related to test error influence * switch to mptt fork * fix reqs for upstream * move tracking ensureance into save * optimize check frequency * use psycopg instead of psycopg2 * fix header * just use the values * switch to dj < 4.2 * fix req * another req fix * switch to 4.2 again * fix merge error * Check for null pk in calculate_total_price Cannot access self.lines if pk is Null * use patched mptt * try psycopg2 again * Remove tree rebuild from migrations * Prevent notify_users if importing or migrating * Add order_by() to subquery annotations - Ref: https://stackoverflow.com/a/629691 * Update stock filters - Append order_by() * fix error if running without timezones in testing * add logging to figure this out * remove tz from self.creation if TZ is off * add tz? * move around? * only run the test i am trying to figure out not reproducible on my machine * only run the test i am trying to figure out not reproducible on my machine * run all tests again --------- Co-authored-by: Oliver <oliver.henry.walters@gmail.com>
This commit is contained in:
		| @@ -591,11 +591,6 @@ db_options = db_config.get('OPTIONS', db_config.get('options', {})) | |||||||
|  |  | ||||||
| # Specific options for postgres backend | # Specific options for postgres backend | ||||||
| if 'postgres' in db_engine:  # pragma: no cover | if 'postgres' in db_engine:  # pragma: no cover | ||||||
|     from psycopg2.extensions import ( |  | ||||||
|         ISOLATION_LEVEL_READ_COMMITTED, |  | ||||||
|         ISOLATION_LEVEL_SERIALIZABLE, |  | ||||||
|     ) |  | ||||||
|  |  | ||||||
|     # Connection timeout |     # Connection timeout | ||||||
|     if 'connect_timeout' not in db_options: |     if 'connect_timeout' not in db_options: | ||||||
|         # The DB server is in the same data center, it should not take very |         # The DB server is in the same data center, it should not take very | ||||||
| @@ -659,11 +654,7 @@ if 'postgres' in db_engine:  # pragma: no cover | |||||||
|         serializable = get_boolean_setting( |         serializable = get_boolean_setting( | ||||||
|             'INVENTREE_DB_ISOLATION_SERIALIZABLE', 'database.serializable', False |             'INVENTREE_DB_ISOLATION_SERIALIZABLE', 'database.serializable', False | ||||||
|         ) |         ) | ||||||
|         db_options['isolation_level'] = ( |         db_options['isolation_level'] = 4 if serializable else 2 | ||||||
|             ISOLATION_LEVEL_SERIALIZABLE |  | ||||||
|             if serializable |  | ||||||
|             else ISOLATION_LEVEL_READ_COMMITTED |  | ||||||
|         ) |  | ||||||
|  |  | ||||||
| # Specific options for MySql / MariaDB backend | # Specific options for MySql / MariaDB backend | ||||||
| elif 'mysql' in db_engine:  # pragma: no cover | elif 'mysql' in db_engine:  # pragma: no cover | ||||||
| @@ -963,7 +954,6 @@ TIME_ZONE = get_setting('INVENTREE_TIMEZONE', 'timezone', 'UTC') | |||||||
|  |  | ||||||
| USE_I18N = True | USE_I18N = True | ||||||
|  |  | ||||||
| USE_L10N = True |  | ||||||
|  |  | ||||||
| # Do not use native timezone support in "test" mode | # Do not use native timezone support in "test" mode | ||||||
| # It generates a *lot* of cruft in the logs | # It generates a *lot* of cruft in the logs | ||||||
|   | |||||||
| @@ -25,7 +25,7 @@ class HTMLAPITests(InvenTreeTestCase): | |||||||
|         url = reverse('api-part-list') |         url = reverse('api-part-list') | ||||||
|  |  | ||||||
|         # Check JSON response |         # Check JSON response | ||||||
|         response = self.client.get(url, HTTP_ACCEPT='application/json') |         response = self.client.get(url, headers={'accept': 'application/json'}) | ||||||
|         self.assertEqual(response.status_code, 200) |         self.assertEqual(response.status_code, 200) | ||||||
|  |  | ||||||
|     def test_build_api(self): |     def test_build_api(self): | ||||||
| @@ -33,7 +33,7 @@ class HTMLAPITests(InvenTreeTestCase): | |||||||
|         url = reverse('api-build-list') |         url = reverse('api-build-list') | ||||||
|  |  | ||||||
|         # Check JSON response |         # Check JSON response | ||||||
|         response = self.client.get(url, HTTP_ACCEPT='application/json') |         response = self.client.get(url, headers={'accept': 'application/json'}) | ||||||
|         self.assertEqual(response.status_code, 200) |         self.assertEqual(response.status_code, 200) | ||||||
|  |  | ||||||
|     def test_stock_api(self): |     def test_stock_api(self): | ||||||
| @@ -41,7 +41,7 @@ class HTMLAPITests(InvenTreeTestCase): | |||||||
|         url = reverse('api-stock-list') |         url = reverse('api-stock-list') | ||||||
|  |  | ||||||
|         # Check JSON response |         # Check JSON response | ||||||
|         response = self.client.get(url, HTTP_ACCEPT='application/json') |         response = self.client.get(url, headers={'accept': 'application/json'}) | ||||||
|         self.assertEqual(response.status_code, 200) |         self.assertEqual(response.status_code, 200) | ||||||
|  |  | ||||||
|     def test_company_list(self): |     def test_company_list(self): | ||||||
| @@ -49,7 +49,7 @@ class HTMLAPITests(InvenTreeTestCase): | |||||||
|         url = reverse('api-company-list') |         url = reverse('api-company-list') | ||||||
|  |  | ||||||
|         # Check JSON response |         # Check JSON response | ||||||
|         response = self.client.get(url, HTTP_ACCEPT='application/json') |         response = self.client.get(url, headers={'accept': 'application/json'}) | ||||||
|         self.assertEqual(response.status_code, 200) |         self.assertEqual(response.status_code, 200) | ||||||
|  |  | ||||||
|     def test_not_found(self): |     def test_not_found(self): | ||||||
|   | |||||||
| @@ -15,7 +15,9 @@ class MiddlewareTests(InvenTreeTestCase): | |||||||
|  |  | ||||||
|     def check_path(self, url, code=200, **kwargs): |     def check_path(self, url, code=200, **kwargs): | ||||||
|         """Helper function to run a request.""" |         """Helper function to run a request.""" | ||||||
|         response = self.client.get(url, HTTP_ACCEPT='application/json', **kwargs) |         response = self.client.get( | ||||||
|  |             url, headers={'accept': 'application/json'}, **kwargs | ||||||
|  |         ) | ||||||
|         self.assertEqual(response.status_code, code) |         self.assertEqual(response.status_code, code) | ||||||
|         return response |         return response | ||||||
|  |  | ||||||
|   | |||||||
| @@ -51,6 +51,7 @@ class BuildResource(InvenTreeResource): | |||||||
|     notes = Field(attribute='notes') |     notes = Field(attribute='notes') | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @admin.register(Build) | ||||||
| class BuildAdmin(ImportExportModelAdmin): | class BuildAdmin(ImportExportModelAdmin): | ||||||
|     """Class for managing the Build model via the admin interface""" |     """Class for managing the Build model via the admin interface""" | ||||||
|  |  | ||||||
| @@ -83,6 +84,7 @@ class BuildAdmin(ImportExportModelAdmin): | |||||||
|     ] |     ] | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @admin.register(BuildItem) | ||||||
| class BuildItemAdmin(admin.ModelAdmin): | class BuildItemAdmin(admin.ModelAdmin): | ||||||
|     """Class for managing the BuildItem model via the admin interface.""" |     """Class for managing the BuildItem model via the admin interface.""" | ||||||
|  |  | ||||||
| @@ -98,6 +100,7 @@ class BuildItemAdmin(admin.ModelAdmin): | |||||||
|     ] |     ] | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @admin.register(BuildLine) | ||||||
| class BuildLineAdmin(admin.ModelAdmin): | class BuildLineAdmin(admin.ModelAdmin): | ||||||
|     """Class for managing the BuildLine model via the admin interface""" |     """Class for managing the BuildLine model via the admin interface""" | ||||||
|  |  | ||||||
| @@ -112,8 +115,3 @@ class BuildLineAdmin(admin.ModelAdmin): | |||||||
|         'build__reference', |         'build__reference', | ||||||
|         'bom_item__sub_part__name', |         'bom_item__sub_part__name', | ||||||
|     ] |     ] | ||||||
|  |  | ||||||
|  |  | ||||||
| admin.site.register(Build, BuildAdmin) |  | ||||||
| admin.site.register(BuildItem, BuildItemAdmin) |  | ||||||
| admin.site.register(BuildLine, BuildLineAdmin) |  | ||||||
|   | |||||||
| @@ -3,12 +3,6 @@ | |||||||
| from django.db import migrations, models | from django.db import migrations, models | ||||||
| import django.db.models.deletion | import django.db.models.deletion | ||||||
| import mptt.fields | import mptt.fields | ||||||
| from build.models import Build |  | ||||||
|  |  | ||||||
|  |  | ||||||
| def update_tree(apps, schema_editor): |  | ||||||
|     # Update the Build MPTT model |  | ||||||
|     Build.objects.rebuild() |  | ||||||
|  |  | ||||||
|  |  | ||||||
| class Migration(migrations.Migration): | class Migration(migrations.Migration): | ||||||
| @@ -49,5 +43,4 @@ class Migration(migrations.Migration): | |||||||
|             field=models.PositiveIntegerField(db_index=True, default=0, editable=False), |             field=models.PositiveIntegerField(db_index=True, default=0, editable=False), | ||||||
|             preserve_default=False, |             preserve_default=False, | ||||||
|         ), |         ), | ||||||
|         migrations.RunPython(update_tree, reverse_code=migrations.RunPython.noop), |  | ||||||
|     ] |     ] | ||||||
|   | |||||||
| @@ -57,6 +57,4 @@ class Migration(migrations.Migration): | |||||||
|         ('build', '0028_builditem_bom_item'), |         ('build', '0028_builditem_bom_item'), | ||||||
|     ] |     ] | ||||||
|  |  | ||||||
|     operations = [ |     operations = [] | ||||||
|         migrations.RunPython(assign_bom_items, reverse_code=migrations.RunPython.noop), |  | ||||||
|     ] |  | ||||||
|   | |||||||
| @@ -13,7 +13,7 @@ import math | |||||||
| import os | import os | ||||||
| import re | import re | ||||||
| import uuid | import uuid | ||||||
| from datetime import datetime, timedelta | from datetime import datetime, timedelta, timezone | ||||||
| from enum import Enum | from enum import Enum | ||||||
| from secrets import compare_digest | from secrets import compare_digest | ||||||
| from typing import Any, Callable, Dict, List, Tuple, TypedDict, Union | from typing import Any, Callable, Dict, List, Tuple, TypedDict, Union | ||||||
| @@ -2850,7 +2850,12 @@ class NotificationMessage(models.Model): | |||||||
|  |  | ||||||
|     def age(self): |     def age(self): | ||||||
|         """Age of the message in seconds.""" |         """Age of the message in seconds.""" | ||||||
|         delta = now() - self.creation |         # Add timezone information if TZ is enabled (in production mode mostly) | ||||||
|  |         delta = now() - ( | ||||||
|  |             self.creation.replace(tzinfo=timezone.utc) | ||||||
|  |             if settings.USE_TZ | ||||||
|  |             else self.creation | ||||||
|  |         ) | ||||||
|         return delta.seconds |         return delta.seconds | ||||||
|  |  | ||||||
|     def age_human(self): |     def age_human(self): | ||||||
|   | |||||||
| @@ -33,6 +33,7 @@ class CompanyResource(InvenTreeResource): | |||||||
|         clean_model_instances = True |         clean_model_instances = True | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @admin.register(Company) | ||||||
| class CompanyAdmin(ImportExportModelAdmin): | class CompanyAdmin(ImportExportModelAdmin): | ||||||
|     """Admin class for the Company model.""" |     """Admin class for the Company model.""" | ||||||
|  |  | ||||||
| @@ -69,6 +70,7 @@ class SupplierPriceBreakInline(admin.TabularInline): | |||||||
|     model = SupplierPriceBreak |     model = SupplierPriceBreak | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @admin.register(SupplierPart) | ||||||
| class SupplierPartAdmin(ImportExportModelAdmin): | class SupplierPartAdmin(ImportExportModelAdmin): | ||||||
|     """Admin class for the SupplierPart model.""" |     """Admin class for the SupplierPart model.""" | ||||||
|  |  | ||||||
| @@ -105,6 +107,7 @@ class ManufacturerPartResource(InvenTreeResource): | |||||||
|     manufacturer_name = Field(attribute='manufacturer__name', readonly=True) |     manufacturer_name = Field(attribute='manufacturer__name', readonly=True) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @admin.register(ManufacturerPart) | ||||||
| class ManufacturerPartAdmin(ImportExportModelAdmin): | class ManufacturerPartAdmin(ImportExportModelAdmin): | ||||||
|     """Admin class for ManufacturerPart model.""" |     """Admin class for ManufacturerPart model.""" | ||||||
|  |  | ||||||
| @@ -117,6 +120,7 @@ class ManufacturerPartAdmin(ImportExportModelAdmin): | |||||||
|     autocomplete_fields = ('part', 'manufacturer') |     autocomplete_fields = ('part', 'manufacturer') | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @admin.register(ManufacturerPartAttachment) | ||||||
| class ManufacturerPartAttachmentAdmin(ImportExportModelAdmin): | class ManufacturerPartAttachmentAdmin(ImportExportModelAdmin): | ||||||
|     """Admin class for ManufacturerPartAttachment model.""" |     """Admin class for ManufacturerPartAttachment model.""" | ||||||
|  |  | ||||||
| @@ -137,6 +141,7 @@ class ManufacturerPartParameterResource(InvenTreeResource): | |||||||
|         clean_model_instance = True |         clean_model_instance = True | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @admin.register(ManufacturerPartParameter) | ||||||
| class ManufacturerPartParameterAdmin(ImportExportModelAdmin): | class ManufacturerPartParameterAdmin(ImportExportModelAdmin): | ||||||
|     """Admin class for ManufacturerPartParameter model.""" |     """Admin class for ManufacturerPartParameter model.""" | ||||||
|  |  | ||||||
| @@ -173,6 +178,7 @@ class SupplierPriceBreakResource(InvenTreeResource): | |||||||
|     MPN = Field(attribute='part__MPN', readonly=True) |     MPN = Field(attribute='part__MPN', readonly=True) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @admin.register(SupplierPriceBreak) | ||||||
| class SupplierPriceBreakAdmin(ImportExportModelAdmin): | class SupplierPriceBreakAdmin(ImportExportModelAdmin): | ||||||
|     """Admin class for the SupplierPriceBreak model.""" |     """Admin class for the SupplierPriceBreak model.""" | ||||||
|  |  | ||||||
| @@ -197,6 +203,7 @@ class AddressResource(InvenTreeResource): | |||||||
|     company = Field(attribute='company', widget=widgets.ForeignKeyWidget(Company)) |     company = Field(attribute='company', widget=widgets.ForeignKeyWidget(Company)) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @admin.register(Address) | ||||||
| class AddressAdmin(ImportExportModelAdmin): | class AddressAdmin(ImportExportModelAdmin): | ||||||
|     """Admin class for the Address model.""" |     """Admin class for the Address model.""" | ||||||
|  |  | ||||||
| @@ -221,6 +228,7 @@ class ContactResource(InvenTreeResource): | |||||||
|     company = Field(attribute='company', widget=widgets.ForeignKeyWidget(Company)) |     company = Field(attribute='company', widget=widgets.ForeignKeyWidget(Company)) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @admin.register(Contact) | ||||||
| class ContactAdmin(ImportExportModelAdmin): | class ContactAdmin(ImportExportModelAdmin): | ||||||
|     """Admin class for the Contact model.""" |     """Admin class for the Contact model.""" | ||||||
|  |  | ||||||
| @@ -229,15 +237,3 @@ class ContactAdmin(ImportExportModelAdmin): | |||||||
|     list_display = ('company', 'name', 'role', 'email', 'phone') |     list_display = ('company', 'name', 'role', 'email', 'phone') | ||||||
|  |  | ||||||
|     search_fields = ['company', 'name', 'email'] |     search_fields = ['company', 'name', 'email'] | ||||||
|  |  | ||||||
|  |  | ||||||
| admin.site.register(Company, CompanyAdmin) |  | ||||||
| admin.site.register(SupplierPart, SupplierPartAdmin) |  | ||||||
| admin.site.register(SupplierPriceBreak, SupplierPriceBreakAdmin) |  | ||||||
|  |  | ||||||
| admin.site.register(ManufacturerPart, ManufacturerPartAdmin) |  | ||||||
| admin.site.register(ManufacturerPartAttachment, ManufacturerPartAttachmentAdmin) |  | ||||||
| admin.site.register(ManufacturerPartParameter, ManufacturerPartParameterAdmin) |  | ||||||
|  |  | ||||||
| admin.site.register(Address, AddressAdmin) |  | ||||||
| admin.site.register(Contact, ContactAdmin) |  | ||||||
|   | |||||||
| @@ -1362,8 +1362,8 @@ class OrderCalendarExport(ICalFeed): | |||||||
|             return super().__call__(request, *args, **kwargs) |             return super().__call__(request, *args, **kwargs) | ||||||
|  |  | ||||||
|         # No login yet - check in headers |         # No login yet - check in headers | ||||||
|         if 'HTTP_AUTHORIZATION' in request.META: |         if 'authorization' in request.headers: | ||||||
|             auth = request.META['HTTP_AUTHORIZATION'].split() |             auth = request.headers['authorization'].split() | ||||||
|             if len(auth) == 2: |             if len(auth) == 2: | ||||||
|                 # NOTE: We are only support basic authentication for now. |                 # NOTE: We are only support basic authentication for now. | ||||||
|                 # |                 # | ||||||
|   | |||||||
| @@ -78,7 +78,14 @@ class TotalPriceMixin(models.Model): | |||||||
|         """Update the total_price field when saved.""" |         """Update the total_price field when saved.""" | ||||||
|         # Recalculate total_price for this order |         # Recalculate total_price for this order | ||||||
|         self.update_total_price(commit=False) |         self.update_total_price(commit=False) | ||||||
|         super().save(*args, **kwargs) |  | ||||||
|  |         if hasattr(self, '_SAVING_TOTAL_PRICE') and self._SAVING_TOTAL_PRICE: | ||||||
|  |             # Avoid recursion on save | ||||||
|  |             return super().save(*args, **kwargs) | ||||||
|  |         self._SAVING_TOTAL_PRICE = True | ||||||
|  |  | ||||||
|  |         # Save the object as we can not access foreign/m2m fields before saving | ||||||
|  |         self.update_total_price(commit=True) | ||||||
|  |  | ||||||
|     total_price = InvenTreeModelMoneyField( |     total_price = InvenTreeModelMoneyField( | ||||||
|         null=True, |         null=True, | ||||||
| @@ -136,6 +143,10 @@ class TotalPriceMixin(models.Model): | |||||||
|  |  | ||||||
|         total = Money(0, target_currency) |         total = Money(0, target_currency) | ||||||
|  |  | ||||||
|  |         # Check if the order has been saved (otherwise we can't calculate the total price) | ||||||
|  |         if self.pk is None: | ||||||
|  |             return total | ||||||
|  |  | ||||||
|         # order items |         # order items | ||||||
|         for line in self.lines.all(): |         for line in self.lines.all(): | ||||||
|             if not line.price: |             if not line.price: | ||||||
|   | |||||||
| @@ -634,7 +634,7 @@ class PurchaseOrderTest(OrderTest): | |||||||
|         response = self.client.get( |         response = self.client.get( | ||||||
|             reverse('api-po-so-calendar', kwargs={'ordertype': 'purchase-order'}), |             reverse('api-po-so-calendar', kwargs={'ordertype': 'purchase-order'}), | ||||||
|             format='json', |             format='json', | ||||||
|             HTTP_AUTHORIZATION=f'basic {base64_token}', |             headers={'authorization': f'basic {base64_token}'}, | ||||||
|         ) |         ) | ||||||
|         self.assertEqual(response.status_code, 200) |         self.assertEqual(response.status_code, 200) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -57,7 +57,8 @@ class PurchaseOrderTests(OrderViewTestCase): | |||||||
|     def test_po_export(self): |     def test_po_export(self): | ||||||
|         """Export PurchaseOrder.""" |         """Export PurchaseOrder.""" | ||||||
|         response = self.client.get( |         response = self.client.get( | ||||||
|             reverse('po-export', args=(1,)), HTTP_X_REQUESTED_WITH='XMLHttpRequest' |             reverse('po-export', args=(1,)), | ||||||
|  |             headers={'x-requested-with': 'XMLHttpRequest'}, | ||||||
|         ) |         ) | ||||||
|  |  | ||||||
|         # Response should be streaming-content (file download) |         # Response should be streaming-content (file download) | ||||||
|   | |||||||
| @@ -245,7 +245,9 @@ def annotate_variant_quantity(subquery: Q, reference: str = 'quantity'): | |||||||
|         Subquery( |         Subquery( | ||||||
|             subquery.annotate( |             subquery.annotate( | ||||||
|                 total=Func(F(reference), function='SUM', output_field=FloatField()) |                 total=Func(F(reference), function='SUM', output_field=FloatField()) | ||||||
|             ).values('total') |             ) | ||||||
|  |             .values('total') | ||||||
|  |             .order_by() | ||||||
|         ), |         ), | ||||||
|         0, |         0, | ||||||
|         output_field=FloatField(), |         output_field=FloatField(), | ||||||
| @@ -270,7 +272,9 @@ def annotate_category_parts(): | |||||||
|         Subquery( |         Subquery( | ||||||
|             subquery.annotate( |             subquery.annotate( | ||||||
|                 total=Func(F('pk'), function='COUNT', output_field=IntegerField()) |                 total=Func(F('pk'), function='COUNT', output_field=IntegerField()) | ||||||
|             ).values('total') |             ) | ||||||
|  |             .values('total') | ||||||
|  |             .order_by() | ||||||
|         ), |         ), | ||||||
|         0, |         0, | ||||||
|         output_field=IntegerField(), |         output_field=IntegerField(), | ||||||
|   | |||||||
| @@ -1,13 +1,6 @@ | |||||||
| # Generated by Django 2.2.5 on 2019-09-08 04:04 | # Generated by Django 2.2.5 on 2019-09-08 04:04 | ||||||
|  |  | ||||||
| from django.db import migrations | from django.db import migrations | ||||||
| from part import models |  | ||||||
|  |  | ||||||
|  |  | ||||||
| def update_tree(apps, schema_editor): |  | ||||||
|     # Update the PartCategory MPTT model |  | ||||||
|  |  | ||||||
|     models.PartCategory.objects.rebuild() |  | ||||||
|  |  | ||||||
|  |  | ||||||
| class Migration(migrations.Migration): | class Migration(migrations.Migration): | ||||||
| @@ -18,6 +11,4 @@ class Migration(migrations.Migration): | |||||||
|         ('part', '0019_auto_20190908_0404'), |         ('part', '0019_auto_20190908_0404'), | ||||||
|     ] |     ] | ||||||
|  |  | ||||||
|     operations = [ |     operations = [] | ||||||
|         migrations.RunPython(update_tree) |  | ||||||
|     ] |  | ||||||
|   | |||||||
| @@ -2,13 +2,6 @@ | |||||||
|  |  | ||||||
| from django.db import migrations, models | from django.db import migrations, models | ||||||
|  |  | ||||||
| from part.models import Part |  | ||||||
|  |  | ||||||
|  |  | ||||||
| def update_tree(apps, schema_editor): |  | ||||||
|     # Update the MPTT for Part model |  | ||||||
|     Part.objects.rebuild() |  | ||||||
|  |  | ||||||
|  |  | ||||||
| class Migration(migrations.Migration): | class Migration(migrations.Migration): | ||||||
|  |  | ||||||
| @@ -43,6 +36,4 @@ class Migration(migrations.Migration): | |||||||
|             field=models.PositiveIntegerField(db_index=True, default=0, editable=False), |             field=models.PositiveIntegerField(db_index=True, default=0, editable=False), | ||||||
|             preserve_default=False, |             preserve_default=False, | ||||||
|         ), |         ), | ||||||
|  |  | ||||||
|         migrations.RunPython(update_tree, reverse_code=migrations.RunPython.noop) |  | ||||||
|     ] |     ] | ||||||
|   | |||||||
| @@ -452,6 +452,7 @@ class Part(InvenTreeBarcodeMixin, InvenTreeNotesMixin, MetadataMixin, MPTTModel) | |||||||
|         If the part image has been updated, then check if the "old" (previous) image is still used by another part. |         If the part image has been updated, then check if the "old" (previous) image is still used by another part. | ||||||
|         If not, it is considered "orphaned" and will be deleted. |         If not, it is considered "orphaned" and will be deleted. | ||||||
|         """ |         """ | ||||||
|  |         _new = False | ||||||
|         if self.pk: |         if self.pk: | ||||||
|             try: |             try: | ||||||
|                 previous = Part.objects.get(pk=self.pk) |                 previous = Part.objects.get(pk=self.pk) | ||||||
| @@ -470,6 +471,8 @@ class Part(InvenTreeBarcodeMixin, InvenTreeNotesMixin, MetadataMixin, MPTTModel) | |||||||
|                         previous.image.delete(save=False) |                         previous.image.delete(save=False) | ||||||
|             except Part.DoesNotExist: |             except Part.DoesNotExist: | ||||||
|                 pass |                 pass | ||||||
|  |         else: | ||||||
|  |             _new = True | ||||||
|  |  | ||||||
|         self.full_clean() |         self.full_clean() | ||||||
|  |  | ||||||
| @@ -478,6 +481,10 @@ class Part(InvenTreeBarcodeMixin, InvenTreeNotesMixin, MetadataMixin, MPTTModel) | |||||||
|         except InvalidMove: |         except InvalidMove: | ||||||
|             raise ValidationError({'variant_of': _('Invalid choice for parent part')}) |             raise ValidationError({'variant_of': _('Invalid choice for parent part')}) | ||||||
|  |  | ||||||
|  |         if _new: | ||||||
|  |             # Only run if the check was not run previously (due to not existing in the database) | ||||||
|  |             self.ensure_trackable() | ||||||
|  |  | ||||||
|     def __str__(self): |     def __str__(self): | ||||||
|         """Return a string representation of the Part (for use in the admin interface).""" |         """Return a string representation of the Part (for use in the admin interface).""" | ||||||
|         return f'{self.full_name} - {self.description}' |         return f'{self.full_name} - {self.description}' | ||||||
| @@ -827,6 +834,12 @@ class Part(InvenTreeBarcodeMixin, InvenTreeNotesMixin, MetadataMixin, MPTTModel) | |||||||
|         # Run custom validation for the name field |         # Run custom validation for the name field | ||||||
|         self.validate_name() |         self.validate_name() | ||||||
|  |  | ||||||
|  |         if self.pk: | ||||||
|  |             # Only run if the part already exists in the database | ||||||
|  |             self.ensure_trackable() | ||||||
|  |  | ||||||
|  |     def ensure_trackable(self): | ||||||
|  |         """Ensure that trackable is set correctly downline.""" | ||||||
|         if self.trackable: |         if self.trackable: | ||||||
|             for part in self.get_used_in(): |             for part in self.get_used_in(): | ||||||
|                 if not part.trackable: |                 if not part.trackable: | ||||||
|   | |||||||
| @@ -108,7 +108,7 @@ class PartDetailTest(PartViewTestCase): | |||||||
|         """Test downloading a BOM for a valid part.""" |         """Test downloading a BOM for a valid part.""" | ||||||
|         response = self.client.get( |         response = self.client.get( | ||||||
|             reverse('api-bom-download', args=(1,)), |             reverse('api-bom-download', args=(1,)), | ||||||
|             HTTP_X_REQUESTED_WITH='XMLHttpRequest', |             headers={'x-requested-with': 'XMLHttpRequest'}, | ||||||
|         ) |         ) | ||||||
|         self.assertEqual(response.status_code, 200) |         self.assertEqual(response.status_code, 200) | ||||||
|         self.assertIn('streaming_content', dir(response)) |         self.assertIn('streaming_content', dir(response)) | ||||||
|   | |||||||
| @@ -106,7 +106,6 @@ class InvenTreePluginTests(TestCase): | |||||||
|             LICENSE = 'MIT' |             LICENSE = 'MIT' | ||||||
|  |  | ||||||
|         cls.plugin_name = NameInvenTreePlugin() |         cls.plugin_name = NameInvenTreePlugin() | ||||||
|         cls.plugin_sample = SampleIntegrationPlugin() |  | ||||||
|  |  | ||||||
|         class VersionInvenTreePlugin(InvenTreePlugin): |         class VersionInvenTreePlugin(InvenTreePlugin): | ||||||
|             NAME = 'Version' |             NAME = 'Version' | ||||||
| @@ -140,7 +139,7 @@ class InvenTreePluginTests(TestCase): | |||||||
|  |  | ||||||
|         # is_sample |         # is_sample | ||||||
|         self.assertEqual(self.plugin.is_sample, False) |         self.assertEqual(self.plugin.is_sample, False) | ||||||
|         self.assertEqual(self.plugin_sample.is_sample, True) |         self.assertEqual(SampleIntegrationPlugin().is_sample, True) | ||||||
|  |  | ||||||
|         # slug |         # slug | ||||||
|         self.assertEqual(self.plugin.slug, '') |         self.assertEqual(self.plugin.slug, '') | ||||||
|   | |||||||
| @@ -15,31 +15,30 @@ from .models import ( | |||||||
| ) | ) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @admin.register( | ||||||
|  |     BillOfMaterialsReport, | ||||||
|  |     BuildReport, | ||||||
|  |     PurchaseOrderReport, | ||||||
|  |     ReturnOrderReport, | ||||||
|  |     SalesOrderReport, | ||||||
|  |     StockLocationReport, | ||||||
|  |     TestReport, | ||||||
|  | ) | ||||||
| class ReportTemplateAdmin(admin.ModelAdmin): | class ReportTemplateAdmin(admin.ModelAdmin): | ||||||
|     """Admin class for the various reporting models.""" |     """Admin class for the various reporting models.""" | ||||||
|  |  | ||||||
|     list_display = ('name', 'description', 'template', 'filters', 'enabled', 'revision') |     list_display = ('name', 'description', 'template', 'filters', 'enabled', 'revision') | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @admin.register(ReportSnippet) | ||||||
| class ReportSnippetAdmin(admin.ModelAdmin): | class ReportSnippetAdmin(admin.ModelAdmin): | ||||||
|     """Admin class for the ReportSnippet model.""" |     """Admin class for the ReportSnippet model.""" | ||||||
|  |  | ||||||
|     list_display = ('id', 'snippet', 'description') |     list_display = ('id', 'snippet', 'description') | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @admin.register(ReportAsset) | ||||||
| class ReportAssetAdmin(admin.ModelAdmin): | class ReportAssetAdmin(admin.ModelAdmin): | ||||||
|     """Admin class for the ReportAsset model.""" |     """Admin class for the ReportAsset model.""" | ||||||
|  |  | ||||||
|     list_display = ('id', 'asset', 'description') |     list_display = ('id', 'asset', 'description') | ||||||
|  |  | ||||||
|  |  | ||||||
| admin.site.register(ReportSnippet, ReportSnippetAdmin) |  | ||||||
| admin.site.register(ReportAsset, ReportAssetAdmin) |  | ||||||
|  |  | ||||||
| admin.site.register(StockLocationReport, ReportTemplateAdmin) |  | ||||||
| admin.site.register(TestReport, ReportTemplateAdmin) |  | ||||||
| admin.site.register(BuildReport, ReportTemplateAdmin) |  | ||||||
| admin.site.register(BillOfMaterialsReport, ReportTemplateAdmin) |  | ||||||
| admin.site.register(PurchaseOrderReport, ReportTemplateAdmin) |  | ||||||
| admin.site.register(ReturnOrderReport, ReportTemplateAdmin) |  | ||||||
| admin.site.register(SalesOrderReport, ReportTemplateAdmin) |  | ||||||
|   | |||||||
| @@ -85,6 +85,7 @@ class LocationInline(admin.TabularInline): | |||||||
|     model = StockLocation |     model = StockLocation | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @admin.register(StockLocation) | ||||||
| class LocationAdmin(ImportExportModelAdmin): | class LocationAdmin(ImportExportModelAdmin): | ||||||
|     """Admin class for Location.""" |     """Admin class for Location.""" | ||||||
|  |  | ||||||
| @@ -99,6 +100,7 @@ class LocationAdmin(ImportExportModelAdmin): | |||||||
|     autocomplete_fields = ['parent'] |     autocomplete_fields = ['parent'] | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @admin.register(StockLocationType) | ||||||
| class LocationTypeAdmin(admin.ModelAdmin): | class LocationTypeAdmin(admin.ModelAdmin): | ||||||
|     """Admin class for StockLocationType.""" |     """Admin class for StockLocationType.""" | ||||||
|  |  | ||||||
| @@ -268,6 +270,7 @@ class StockItemResource(InvenTreeResource): | |||||||
|         StockItem.objects.rebuild() |         StockItem.objects.rebuild() | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @admin.register(StockItem) | ||||||
| class StockItemAdmin(ImportExportModelAdmin): | class StockItemAdmin(ImportExportModelAdmin): | ||||||
|     """Admin class for StockItem.""" |     """Admin class for StockItem.""" | ||||||
|  |  | ||||||
| @@ -292,6 +295,7 @@ class StockItemAdmin(ImportExportModelAdmin): | |||||||
|     ] |     ] | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @admin.register(StockItemAttachment) | ||||||
| class StockAttachmentAdmin(admin.ModelAdmin): | class StockAttachmentAdmin(admin.ModelAdmin): | ||||||
|     """Admin class for StockAttachment.""" |     """Admin class for StockAttachment.""" | ||||||
|  |  | ||||||
| @@ -300,6 +304,7 @@ class StockAttachmentAdmin(admin.ModelAdmin): | |||||||
|     autocomplete_fields = ['stock_item'] |     autocomplete_fields = ['stock_item'] | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @admin.register(StockItemTracking) | ||||||
| class StockTrackingAdmin(ImportExportModelAdmin): | class StockTrackingAdmin(ImportExportModelAdmin): | ||||||
|     """Admin class for StockTracking.""" |     """Admin class for StockTracking.""" | ||||||
|  |  | ||||||
| @@ -308,17 +313,10 @@ class StockTrackingAdmin(ImportExportModelAdmin): | |||||||
|     autocomplete_fields = ['item'] |     autocomplete_fields = ['item'] | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @admin.register(StockItemTestResult) | ||||||
| class StockItemTestResultAdmin(admin.ModelAdmin): | class StockItemTestResultAdmin(admin.ModelAdmin): | ||||||
|     """Admin class for StockItemTestResult.""" |     """Admin class for StockItemTestResult.""" | ||||||
|  |  | ||||||
|     list_display = ('stock_item', 'test', 'result', 'value') |     list_display = ('stock_item', 'test', 'result', 'value') | ||||||
|  |  | ||||||
|     autocomplete_fields = ['stock_item'] |     autocomplete_fields = ['stock_item'] | ||||||
|  |  | ||||||
|  |  | ||||||
| admin.site.register(StockLocation, LocationAdmin) |  | ||||||
| admin.site.register(StockLocationType, LocationTypeAdmin) |  | ||||||
| admin.site.register(StockItem, StockItemAdmin) |  | ||||||
| admin.site.register(StockItemTracking, StockTrackingAdmin) |  | ||||||
| admin.site.register(StockItemAttachment, StockAttachmentAdmin) |  | ||||||
| admin.site.register(StockItemTestResult, StockItemTestResultAdmin) |  | ||||||
|   | |||||||
| @@ -30,7 +30,9 @@ def annotate_location_items(filter: Q = None): | |||||||
|         Subquery( |         Subquery( | ||||||
|             subquery.annotate( |             subquery.annotate( | ||||||
|                 total=Func(F('pk'), function='COUNT', output_field=IntegerField()) |                 total=Func(F('pk'), function='COUNT', output_field=IntegerField()) | ||||||
|             ).values('total') |             ) | ||||||
|  |             .values('total') | ||||||
|  |             .order_by() | ||||||
|         ), |         ), | ||||||
|         0, |         0, | ||||||
|         output_field=IntegerField(), |         output_field=IntegerField(), | ||||||
| @@ -50,7 +52,9 @@ def annotate_child_items(): | |||||||
|         Subquery( |         Subquery( | ||||||
|             child_stock_query.annotate( |             child_stock_query.annotate( | ||||||
|                 count=Func(F('pk'), function='COUNT', output_field=IntegerField()) |                 count=Func(F('pk'), function='COUNT', output_field=IntegerField()) | ||||||
|             ).values('count') |             ) | ||||||
|  |             .values('count') | ||||||
|  |             .order_by() | ||||||
|         ), |         ), | ||||||
|         0, |         0, | ||||||
|         output_field=IntegerField(), |         output_field=IntegerField(), | ||||||
|   | |||||||
| @@ -2,13 +2,6 @@ | |||||||
|  |  | ||||||
| from django.db import migrations | from django.db import migrations | ||||||
|  |  | ||||||
| from stock import models |  | ||||||
|  |  | ||||||
|  |  | ||||||
| def update_tree(apps, schema_editor): |  | ||||||
|     # Update the StockLocation MPTT model |  | ||||||
|  |  | ||||||
|     models.StockLocation.objects.rebuild() |  | ||||||
|  |  | ||||||
|  |  | ||||||
| class Migration(migrations.Migration): | class Migration(migrations.Migration): | ||||||
| @@ -19,6 +12,4 @@ class Migration(migrations.Migration): | |||||||
|         ('stock', '0011_auto_20190908_0404'), |         ('stock', '0011_auto_20190908_0404'), | ||||||
|     ] |     ] | ||||||
|  |  | ||||||
|     operations = [ |     operations = [] | ||||||
|         migrations.RunPython(update_tree) |  | ||||||
|     ] |  | ||||||
|   | |||||||
| @@ -1,13 +1,6 @@ | |||||||
| # Generated by Django 2.2.9 on 2020-02-17 11:09 | # Generated by Django 2.2.9 on 2020-02-17 11:09 | ||||||
|  |  | ||||||
| from django.db import migrations | from django.db import migrations | ||||||
| from stock import models |  | ||||||
|  |  | ||||||
|  |  | ||||||
| def update_stock_item_tree(apps, schema_editor): |  | ||||||
|     # Update the StockItem MPTT model |  | ||||||
|  |  | ||||||
|     models.StockItem.objects.rebuild() |  | ||||||
|  |  | ||||||
|  |  | ||||||
| class Migration(migrations.Migration): | class Migration(migrations.Migration): | ||||||
| @@ -18,6 +11,4 @@ class Migration(migrations.Migration): | |||||||
|         ('stock', '0021_auto_20200215_2232'), |         ('stock', '0021_auto_20200215_2232'), | ||||||
|     ] |     ] | ||||||
|  |  | ||||||
|     operations = [ |     operations = [] | ||||||
|         migrations.RunPython(update_stock_item_tree) |  | ||||||
|     ] |  | ||||||
|   | |||||||
| @@ -107,7 +107,8 @@ class StockLocationManager(TreeManager): | |||||||
|  |  | ||||||
|         - Joins the StockLocationType by default for speedier icon access |         - Joins the StockLocationType by default for speedier icon access | ||||||
|         """ |         """ | ||||||
|         return super().get_queryset().select_related('location_type') |         # return super().get_queryset().select_related("location_type") | ||||||
|  |         return super().get_queryset() | ||||||
|  |  | ||||||
|  |  | ||||||
| class StockLocation(InvenTreeBarcodeMixin, MetadataMixin, InvenTreeTree): | class StockLocation(InvenTreeBarcodeMixin, MetadataMixin, InvenTreeTree): | ||||||
|   | |||||||
| @@ -13,6 +13,7 @@ from users.models import ApiToken, Owner, RuleSet | |||||||
| User = get_user_model() | User = get_user_model() | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @admin.register(ApiToken) | ||||||
| class ApiTokenAdmin(admin.ModelAdmin): | class ApiTokenAdmin(admin.ModelAdmin): | ||||||
|     """Admin class for the ApiToken model.""" |     """Admin class for the ApiToken model.""" | ||||||
|  |  | ||||||
| @@ -288,6 +289,7 @@ class InvenTreeUserAdmin(UserAdmin): | |||||||
|     ) |     ) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @admin.register(Owner) | ||||||
| class OwnerAdmin(admin.ModelAdmin): | class OwnerAdmin(admin.ModelAdmin): | ||||||
|     """Custom admin interface for the Owner model.""" |     """Custom admin interface for the Owner model.""" | ||||||
|  |  | ||||||
| @@ -299,7 +301,3 @@ admin.site.register(Group, RoleGroupAdmin) | |||||||
|  |  | ||||||
| admin.site.unregister(User) | admin.site.unregister(User) | ||||||
| admin.site.register(User, InvenTreeUserAdmin) | admin.site.register(User, InvenTreeUserAdmin) | ||||||
|  |  | ||||||
| admin.site.register(Owner, OwnerAdmin) |  | ||||||
|  |  | ||||||
| admin.site.register(ApiToken, ApiTokenAdmin) |  | ||||||
|   | |||||||
| @@ -262,7 +262,7 @@ class GetAuthToken(APIView): | |||||||
|                 ) |                 ) | ||||||
|  |  | ||||||
|             # Add some metadata about the request |             # Add some metadata about the request | ||||||
|             token.set_metadata('user_agent', request.META.get('HTTP_USER_AGENT', '')) |             token.set_metadata('user_agent', request.headers.get('user-agent', '')) | ||||||
|             token.set_metadata('remote_addr', request.META.get('REMOTE_ADDR', '')) |             token.set_metadata('remote_addr', request.META.get('REMOTE_ADDR', '')) | ||||||
|             token.set_metadata('remote_host', request.META.get('REMOTE_HOST', '')) |             token.set_metadata('remote_host', request.META.get('REMOTE_HOST', '')) | ||||||
|             token.set_metadata('remote_user', request.META.get('REMOTE_USER', '')) |             token.set_metadata('remote_user', request.META.get('REMOTE_USER', '')) | ||||||
|   | |||||||
| @@ -10,7 +10,7 @@ asgiref==3.7.2 | |||||||
|     #   django |     #   django | ||||||
| build==1.0.3 | build==1.0.3 | ||||||
|     # via pip-tools |     # via pip-tools | ||||||
| certifi==2023.7.22 | certifi==2023.11.17 | ||||||
|     # via |     # via | ||||||
|     #   -c requirements.txt |     #   -c requirements.txt | ||||||
|     #   requests |     #   requests | ||||||
| @@ -33,13 +33,13 @@ coverage[toml]==5.5 | |||||||
|     #   coveralls |     #   coveralls | ||||||
| coveralls==2.1.2 | coveralls==2.1.2 | ||||||
|     # via -r requirements-dev.in |     # via -r requirements-dev.in | ||||||
| cryptography==41.0.6 | cryptography==41.0.7 | ||||||
|     # via |     # via | ||||||
|     #   -c requirements.txt |     #   -c requirements.txt | ||||||
|     #   pdfminer-six |     #   pdfminer-six | ||||||
| distlib==0.3.7 | distlib==0.3.8 | ||||||
|     # via virtualenv |     # via virtualenv | ||||||
| django==3.2.23 | django==4.2.9 | ||||||
|     # via |     # via | ||||||
|     #   -c requirements.txt |     #   -c requirements.txt | ||||||
|     #   django-slowtests |     #   django-slowtests | ||||||
| @@ -53,7 +53,7 @@ filelock==3.13.1 | |||||||
|     # via virtualenv |     # via virtualenv | ||||||
| identify==2.5.31 | identify==2.5.31 | ||||||
|     # via pre-commit |     # via pre-commit | ||||||
| idna==3.4 | idna==3.6 | ||||||
|     # via |     # via | ||||||
|     #   -c requirements.txt |     #   -c requirements.txt | ||||||
|     #   requests |     #   requests | ||||||
| @@ -61,7 +61,7 @@ importlib-metadata==6.8.0 | |||||||
|     # via |     # via | ||||||
|     #   -c requirements.txt |     #   -c requirements.txt | ||||||
|     #   build |     #   build | ||||||
| isort==5.12.0 | isort==5.13.2 | ||||||
|     # via -r requirements-dev.in |     # via -r requirements-dev.in | ||||||
| nodeenv==1.8.0 | nodeenv==1.8.0 | ||||||
|     # via pre-commit |     # via pre-commit | ||||||
| @@ -69,13 +69,13 @@ packaging==23.2 | |||||||
|     # via |     # via | ||||||
|     #   -c requirements.txt |     #   -c requirements.txt | ||||||
|     #   build |     #   build | ||||||
| pdfminer-six==20221105 | pdfminer-six==20231228 | ||||||
|     # via -r requirements-dev.in |     # via -r requirements-dev.in | ||||||
| pip-tools==7.3.0 | pip-tools==7.3.0 | ||||||
|     # via -r requirements-dev.in |     # via -r requirements-dev.in | ||||||
| platformdirs==3.11.0 | platformdirs==4.1.0 | ||||||
|     # via virtualenv |     # via virtualenv | ||||||
| pre-commit==3.5.0 | pre-commit==3.6.0 | ||||||
|     # via -r requirements-dev.in |     # via -r requirements-dev.in | ||||||
| pycparser==2.21 | pycparser==2.21 | ||||||
|     # via |     # via | ||||||
| @@ -83,10 +83,6 @@ pycparser==2.21 | |||||||
|     #   cffi |     #   cffi | ||||||
| pyproject-hooks==1.0.0 | pyproject-hooks==1.0.0 | ||||||
|     # via build |     # via build | ||||||
| pytz==2023.3.post1 |  | ||||||
|     # via |  | ||||||
|     #   -c requirements.txt |  | ||||||
|     #   django |  | ||||||
| pyyaml==6.0.1 | pyyaml==6.0.1 | ||||||
|     # via |     # via | ||||||
|     #   -c requirements.txt |     #   -c requirements.txt | ||||||
| @@ -106,20 +102,20 @@ tomli==2.0.1 | |||||||
|     #   build |     #   build | ||||||
|     #   pip-tools |     #   pip-tools | ||||||
|     #   pyproject-hooks |     #   pyproject-hooks | ||||||
| typing-extensions==4.8.0 | typing-extensions==4.9.0 | ||||||
|     # via |     # via | ||||||
|     #   -c requirements.txt |     #   -c requirements.txt | ||||||
|     #   asgiref |     #   asgiref | ||||||
|     #   django-test-migrations |     #   django-test-migrations | ||||||
| urllib3==2.0.7 | urllib3==2.1.0 | ||||||
|     # via |     # via | ||||||
|     #   -c requirements.txt |     #   -c requirements.txt | ||||||
|     #   requests |     #   requests | ||||||
| virtualenv==20.24.6 | virtualenv==20.25.0 | ||||||
|     # via pre-commit |     # via pre-commit | ||||||
| wheel==0.41.3 | wheel==0.42.0 | ||||||
|     # via pip-tools |     # via pip-tools | ||||||
| zipp==3.16.0 | zipp==3.16.2 | ||||||
|     # via |     # via | ||||||
|     #   -c requirements.txt |     #   -c requirements.txt | ||||||
|     #   importlib-metadata |     #   importlib-metadata | ||||||
|   | |||||||
| @@ -1,5 +1,5 @@ | |||||||
| # Please keep this list sorted - if you pin a version provide a reason | # Please keep this list sorted - if you pin a version provide a reason | ||||||
| Django>=3.2.14,<4                       # Django package | Django<5.0                              # Django package | ||||||
| coreapi                                 # API documentation for djangorestframework | coreapi                                 # API documentation for djangorestframework | ||||||
| cryptography>=40.0.0,!=40.0.2           # Core cryptographic functionality | cryptography>=40.0.0,!=40.0.2           # Core cryptographic functionality | ||||||
| django-allauth                          # SSO for external providers via OpenID | django-allauth                          # SSO for external providers via OpenID | ||||||
| @@ -13,11 +13,13 @@ django-filter                           # Extended filtering options | |||||||
| django-flags                            # Feature flags | django-flags                            # Feature flags | ||||||
| django-formtools                        # Form wizard tools | django-formtools                        # Form wizard tools | ||||||
| django-ical                             # iCal export for calendar views | django-ical                             # iCal export for calendar views | ||||||
| django-import-export>=3.3.1             # Data import / export for admin interface | django-import-export                    # Data import / export for admin interface | ||||||
| django-maintenance-mode                 # Shut down application while reloading etc. | django-maintenance-mode                 # Shut down application while reloading etc. | ||||||
| django-markdownify                      # Markdown rendering | django-markdownify                      # Markdown rendering | ||||||
|  | django-mptt                             # Modified Preorder Tree Traversal | ||||||
|  | django-markdownify                      # Markdown rendering | ||||||
| django-money>=3.0.0,<3.3.0              # Django app for currency management # FIXED 2023-10-31 3.3.0 breaks due to https://github.com/django-money/django-money/issues/731 | django-money>=3.0.0,<3.3.0              # Django app for currency management # FIXED 2023-10-31 3.3.0 breaks due to https://github.com/django-money/django-money/issues/731 | ||||||
| django-mptt==0.11.0                     # Modified Preorder Tree Traversal | django-mptt                             # Modified Preorder Tree Traversal | ||||||
| django-redis>=5.0.0                     # Redis integration | django-redis>=5.0.0                     # Redis integration | ||||||
| django-q2                               # Background task scheduling | django-q2                               # Background task scheduling | ||||||
| django-q-sentry                         # sentry.io integration for django-q | django-q-sentry                         # sentry.io integration for django-q | ||||||
|   | |||||||
| @@ -5,14 +5,16 @@ | |||||||
| #    pip-compile --output-file=requirements.txt requirements.in | #    pip-compile --output-file=requirements.txt requirements.in | ||||||
| # | # | ||||||
| asgiref==3.7.2 | asgiref==3.7.2 | ||||||
|     # via django |     # via | ||||||
|  |     #   django | ||||||
|  |     #   django-cors-headers | ||||||
| async-timeout==4.0.3 | async-timeout==4.0.3 | ||||||
|     # via redis |     # via redis | ||||||
| attrs==23.1.0 | attrs==23.2.0 | ||||||
|     # via |     # via | ||||||
|     #   jsonschema |     #   jsonschema | ||||||
|     #   referencing |     #   referencing | ||||||
| babel==2.13.1 | babel==2.14.0 | ||||||
|     # via py-moneyed |     # via py-moneyed | ||||||
| backoff==2.2.1 | backoff==2.2.1 | ||||||
|     # via |     # via | ||||||
| @@ -25,7 +27,7 @@ bleach[css]==6.1.0 | |||||||
|     #   django-markdownify |     #   django-markdownify | ||||||
| brotli==1.1.0 | brotli==1.1.0 | ||||||
|     # via fonttools |     # via fonttools | ||||||
| certifi==2023.7.22 | certifi==2023.11.17 | ||||||
|     # via |     # via | ||||||
|     #   requests |     #   requests | ||||||
|     #   sentry-sdk |     #   sentry-sdk | ||||||
| @@ -39,7 +41,7 @@ coreapi==2.3.3 | |||||||
|     # via -r requirements.in |     # via -r requirements.in | ||||||
| coreschema==0.0.4 | coreschema==0.0.4 | ||||||
|     # via coreapi |     # via coreapi | ||||||
| cryptography==41.0.6 | cryptography==41.0.7 | ||||||
|     # via |     # via | ||||||
|     #   -r requirements.in |     #   -r requirements.in | ||||||
|     #   djangorestframework-simplejwt |     #   djangorestframework-simplejwt | ||||||
| @@ -59,7 +61,7 @@ diff-match-patch==20230430 | |||||||
|     # via django-import-export |     # via django-import-export | ||||||
| dj-rest-auth==5.0.2 | dj-rest-auth==5.0.2 | ||||||
|     # via -r requirements.in |     # via -r requirements.in | ||||||
| django==3.2.23 | django==4.2.9 | ||||||
|     # via |     # via | ||||||
|     #   -r requirements.in |     #   -r requirements.in | ||||||
|     #   dj-rest-auth |     #   dj-rest-auth | ||||||
| @@ -76,7 +78,6 @@ django==3.2.23 | |||||||
|     #   django-js-asset |     #   django-js-asset | ||||||
|     #   django-markdownify |     #   django-markdownify | ||||||
|     #   django-money |     #   django-money | ||||||
|     #   django-mptt |  | ||||||
|     #   django-otp |     #   django-otp | ||||||
|     #   django-picklefield |     #   django-picklefield | ||||||
|     #   django-q2 |     #   django-q2 | ||||||
| @@ -101,7 +102,7 @@ django-allauth-2fa==0.11.1 | |||||||
|     # via -r requirements.in |     # via -r requirements.in | ||||||
| django-cleanup==8.0.0 | django-cleanup==8.0.0 | ||||||
|     # via -r requirements.in |     # via -r requirements.in | ||||||
| django-cors-headers==4.3.0 | django-cors-headers==4.3.1 | ||||||
|     # via -r requirements.in |     # via -r requirements.in | ||||||
| django-crispy-forms==1.14.0 | django-crispy-forms==1.14.0 | ||||||
|     # via -r requirements.in |     # via -r requirements.in | ||||||
| @@ -109,17 +110,17 @@ django-dbbackup==4.0.2 | |||||||
|     # via -r requirements.in |     # via -r requirements.in | ||||||
| django-error-report-2==0.4.2 | django-error-report-2==0.4.2 | ||||||
|     # via -r requirements.in |     # via -r requirements.in | ||||||
| django-filter==23.3 | django-filter==23.5 | ||||||
|     # via -r requirements.in |     # via -r requirements.in | ||||||
| django-flags==5.0.13 | django-flags==5.0.13 | ||||||
|     # via -r requirements.in |     # via -r requirements.in | ||||||
| django-formtools==2.4.1 | django-formtools==2.5.1 | ||||||
|     # via -r requirements.in |     # via -r requirements.in | ||||||
| django-ical==1.9.2 | django-ical==1.9.2 | ||||||
|     # via -r requirements.in |     # via -r requirements.in | ||||||
| django-import-export==3.3.1 | django-import-export==3.3.5 | ||||||
|     # via -r requirements.in |     # via -r requirements.in | ||||||
| django-js-asset==2.1.0 | django-js-asset==2.2.0 | ||||||
|     # via django-mptt |     # via django-mptt | ||||||
| django-maintenance-mode==0.21.0 | django-maintenance-mode==0.21.0 | ||||||
|     # via -r requirements.in |     # via -r requirements.in | ||||||
| @@ -127,9 +128,9 @@ django-markdownify==0.9.3 | |||||||
|     # via -r requirements.in |     # via -r requirements.in | ||||||
| django-money==3.2.0 | django-money==3.2.0 | ||||||
|     # via -r requirements.in |     # via -r requirements.in | ||||||
| django-mptt==0.11.0 | django-mptt==0.16.0 | ||||||
|     # via -r requirements.in |     # via -r requirements.in | ||||||
| django-otp==1.2.4 | django-otp==1.3.0 | ||||||
|     # via django-allauth-2fa |     # via django-allauth-2fa | ||||||
| django-picklefield==3.1 | django-picklefield==3.1 | ||||||
|     # via django-q2 |     # via django-q2 | ||||||
| @@ -141,7 +142,7 @@ django-recurrence==1.11.1 | |||||||
|     # via django-ical |     # via django-ical | ||||||
| django-redis==5.4.0 | django-redis==5.4.0 | ||||||
|     # via -r requirements.in |     # via -r requirements.in | ||||||
| django-sesame==3.2.1 | django-sesame==3.2.2 | ||||||
|     # via -r requirements.in |     # via -r requirements.in | ||||||
| django-sql-utils==0.7.0 | django-sql-utils==0.7.0 | ||||||
|     # via -r requirements.in |     # via -r requirements.in | ||||||
| @@ -149,11 +150,11 @@ django-sslserver==0.22 | |||||||
|     # via -r requirements.in |     # via -r requirements.in | ||||||
| django-stdimage==6.0.2 | django-stdimage==6.0.2 | ||||||
|     # via -r requirements.in |     # via -r requirements.in | ||||||
| django-taggit==4.0.0 | django-taggit==5.0.1 | ||||||
|     # via -r requirements.in |     # via -r requirements.in | ||||||
| django-user-sessions==2.0.0 | django-user-sessions==2.0.0 | ||||||
|     # via -r requirements.in |     # via -r requirements.in | ||||||
| django-weasyprint==2.2.1 | django-weasyprint==2.2.2 | ||||||
|     # via -r requirements.in |     # via -r requirements.in | ||||||
| django-xforwardedfor-middleware==2.0 | django-xforwardedfor-middleware==2.0 | ||||||
|     # via -r requirements.in |     # via -r requirements.in | ||||||
| @@ -163,15 +164,15 @@ djangorestframework==3.14.0 | |||||||
|     #   dj-rest-auth |     #   dj-rest-auth | ||||||
|     #   djangorestframework-simplejwt |     #   djangorestframework-simplejwt | ||||||
|     #   drf-spectacular |     #   drf-spectacular | ||||||
| djangorestframework-simplejwt[crypto]==5.3.0 | djangorestframework-simplejwt[crypto]==5.3.1 | ||||||
|     # via -r requirements.in |     # via -r requirements.in | ||||||
| drf-spectacular==0.26.5 | drf-spectacular==0.27.0 | ||||||
|     # via -r requirements.in |     # via -r requirements.in | ||||||
| dulwich==0.21.6 | dulwich==0.21.7 | ||||||
|     # via -r requirements.in |     # via -r requirements.in | ||||||
| et-xmlfile==1.1.0 | et-xmlfile==1.1.0 | ||||||
|     # via openpyxl |     # via openpyxl | ||||||
| feedparser==6.0.10 | feedparser==6.0.11 | ||||||
|     # via -r requirements.in |     # via -r requirements.in | ||||||
| fonttools[woff]==4.44.0 | fonttools[woff]==4.44.0 | ||||||
|     # via |     # via | ||||||
| @@ -189,7 +190,7 @@ html5lib==1.1 | |||||||
|     # via weasyprint |     # via weasyprint | ||||||
| icalendar==5.0.11 | icalendar==5.0.11 | ||||||
|     # via django-ical |     # via django-ical | ||||||
| idna==3.4 | idna==3.6 | ||||||
|     # via requests |     # via requests | ||||||
| importlib-metadata==6.8.0 | importlib-metadata==6.8.0 | ||||||
|     # via |     # via | ||||||
| @@ -202,9 +203,9 @@ itypes==1.2.0 | |||||||
|     # via coreapi |     # via coreapi | ||||||
| jinja2==3.1.3 | jinja2==3.1.3 | ||||||
|     # via coreschema |     # via coreschema | ||||||
| jsonschema==4.19.2 | jsonschema==4.20.0 | ||||||
|     # via drf-spectacular |     # via drf-spectacular | ||||||
| jsonschema-specifications==2023.7.1 | jsonschema-specifications==2023.12.1 | ||||||
|     # via jsonschema |     # via jsonschema | ||||||
| markdown==3.5.1 | markdown==3.5.1 | ||||||
|     # via django-markdownify |     # via django-markdownify | ||||||
| @@ -277,7 +278,7 @@ opentelemetry-util-http==0.43b0 | |||||||
|     #   opentelemetry-instrumentation-wsgi |     #   opentelemetry-instrumentation-wsgi | ||||||
| packaging==23.2 | packaging==23.2 | ||||||
|     # via gunicorn |     # via gunicorn | ||||||
| pdf2image==1.16.3 | pdf2image==1.17.0 | ||||||
|     # via -r requirements.in |     # via -r requirements.in | ||||||
| pillow==10.2.0 | pillow==10.2.0 | ||||||
|     # via |     # via | ||||||
| @@ -316,13 +317,12 @@ python-dateutil==2.8.2 | |||||||
|     #   icalendar |     #   icalendar | ||||||
| python-dotenv==1.0.0 | python-dotenv==1.0.0 | ||||||
|     # via -r requirements.in |     # via -r requirements.in | ||||||
| python-fsutil==0.12.0 | python-fsutil==0.13.0 | ||||||
|     # via django-maintenance-mode |     # via django-maintenance-mode | ||||||
| python3-openid==3.2.0 | python3-openid==3.2.0 | ||||||
|     # via django-allauth |     # via django-allauth | ||||||
| pytz==2023.3.post1 | pytz==2023.3.post1 | ||||||
|     # via |     # via | ||||||
|     #   django |  | ||||||
|     #   django-dbbackup |     #   django-dbbackup | ||||||
|     #   djangorestframework |     #   djangorestframework | ||||||
|     #   icalendar |     #   icalendar | ||||||
| @@ -339,11 +339,11 @@ rapidfuzz==0.7.6 | |||||||
|     # via -r requirements.in |     # via -r requirements.in | ||||||
| redis==5.0.1 | redis==5.0.1 | ||||||
|     # via django-redis |     # via django-redis | ||||||
| referencing==0.30.2 | referencing==0.32.1 | ||||||
|     # via |     # via | ||||||
|     #   jsonschema |     #   jsonschema | ||||||
|     #   jsonschema-specifications |     #   jsonschema-specifications | ||||||
| regex==2023.10.3 | regex==2023.12.25 | ||||||
|     # via -r requirements.in |     # via -r requirements.in | ||||||
| requests==2.31.0 | requests==2.31.0 | ||||||
|     # via |     # via | ||||||
| @@ -353,11 +353,11 @@ requests==2.31.0 | |||||||
|     #   requests-oauthlib |     #   requests-oauthlib | ||||||
| requests-oauthlib==1.3.1 | requests-oauthlib==1.3.1 | ||||||
|     # via django-allauth |     # via django-allauth | ||||||
| rpds-py==0.12.0 | rpds-py==0.16.2 | ||||||
|     # via |     # via | ||||||
|     #   jsonschema |     #   jsonschema | ||||||
|     #   referencing |     #   referencing | ||||||
| sentry-sdk==1.34.0 | sentry-sdk==1.39.2 | ||||||
|     # via |     # via | ||||||
|     #   -r requirements.in |     #   -r requirements.in | ||||||
|     #   django-q-sentry |     #   django-q-sentry | ||||||
| @@ -381,9 +381,10 @@ tinycss2==1.2.1 | |||||||
|     #   bleach |     #   bleach | ||||||
|     #   cssselect2 |     #   cssselect2 | ||||||
|     #   weasyprint |     #   weasyprint | ||||||
| typing-extensions==4.8.0 | typing-extensions==4.9.0 | ||||||
|     # via |     # via | ||||||
|     #   asgiref |     #   asgiref | ||||||
|  |     #   drf-spectacular | ||||||
|     #   opentelemetry-sdk |     #   opentelemetry-sdk | ||||||
|     #   py-moneyed |     #   py-moneyed | ||||||
|     #   qrcode |     #   qrcode | ||||||
| @@ -391,12 +392,12 @@ uritemplate==4.1.1 | |||||||
|     # via |     # via | ||||||
|     #   coreapi |     #   coreapi | ||||||
|     #   drf-spectacular |     #   drf-spectacular | ||||||
| urllib3==2.0.7 | urllib3==2.1.0 | ||||||
|     # via |     # via | ||||||
|     #   dulwich |     #   dulwich | ||||||
|     #   requests |     #   requests | ||||||
|     #   sentry-sdk |     #   sentry-sdk | ||||||
| weasyprint==60.1 | weasyprint==60.2 | ||||||
|     # via |     # via | ||||||
|     #   -r requirements.in |     #   -r requirements.in | ||||||
|     #   django-weasyprint |     #   django-weasyprint | ||||||
| @@ -415,7 +416,7 @@ xlrd==2.0.1 | |||||||
|     # via tablib |     # via tablib | ||||||
| xlwt==1.3.0 | xlwt==1.3.0 | ||||||
|     # via tablib |     # via tablib | ||||||
| zipp==3.16.0 | zipp==3.16.2 | ||||||
|     # via importlib-metadata |     # via importlib-metadata | ||||||
| zopfli==0.2.3 | zopfli==0.2.3 | ||||||
|     # via fonttools |     # via fonttools | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user