mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +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:
parent
676bb02f6e
commit
d36cf358f8
@ -591,11 +591,6 @@ db_options = db_config.get('OPTIONS', db_config.get('options', {}))
|
||||
|
||||
# Specific options for postgres backend
|
||||
if 'postgres' in db_engine: # pragma: no cover
|
||||
from psycopg2.extensions import (
|
||||
ISOLATION_LEVEL_READ_COMMITTED,
|
||||
ISOLATION_LEVEL_SERIALIZABLE,
|
||||
)
|
||||
|
||||
# Connection timeout
|
||||
if 'connect_timeout' not in db_options:
|
||||
# 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(
|
||||
'INVENTREE_DB_ISOLATION_SERIALIZABLE', 'database.serializable', False
|
||||
)
|
||||
db_options['isolation_level'] = (
|
||||
ISOLATION_LEVEL_SERIALIZABLE
|
||||
if serializable
|
||||
else ISOLATION_LEVEL_READ_COMMITTED
|
||||
)
|
||||
db_options['isolation_level'] = 4 if serializable else 2
|
||||
|
||||
# Specific options for MySql / MariaDB backend
|
||||
elif 'mysql' in db_engine: # pragma: no cover
|
||||
@ -963,7 +954,6 @@ TIME_ZONE = get_setting('INVENTREE_TIMEZONE', 'timezone', 'UTC')
|
||||
|
||||
USE_I18N = True
|
||||
|
||||
USE_L10N = True
|
||||
|
||||
# Do not use native timezone support in "test" mode
|
||||
# It generates a *lot* of cruft in the logs
|
||||
|
@ -25,7 +25,7 @@ class HTMLAPITests(InvenTreeTestCase):
|
||||
url = reverse('api-part-list')
|
||||
|
||||
# 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)
|
||||
|
||||
def test_build_api(self):
|
||||
@ -33,7 +33,7 @@ class HTMLAPITests(InvenTreeTestCase):
|
||||
url = reverse('api-build-list')
|
||||
|
||||
# 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)
|
||||
|
||||
def test_stock_api(self):
|
||||
@ -41,7 +41,7 @@ class HTMLAPITests(InvenTreeTestCase):
|
||||
url = reverse('api-stock-list')
|
||||
|
||||
# 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)
|
||||
|
||||
def test_company_list(self):
|
||||
@ -49,7 +49,7 @@ class HTMLAPITests(InvenTreeTestCase):
|
||||
url = reverse('api-company-list')
|
||||
|
||||
# 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)
|
||||
|
||||
def test_not_found(self):
|
||||
|
@ -15,7 +15,9 @@ class MiddlewareTests(InvenTreeTestCase):
|
||||
|
||||
def check_path(self, url, code=200, **kwargs):
|
||||
"""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)
|
||||
return response
|
||||
|
||||
|
@ -51,6 +51,7 @@ class BuildResource(InvenTreeResource):
|
||||
notes = Field(attribute='notes')
|
||||
|
||||
|
||||
@admin.register(Build)
|
||||
class BuildAdmin(ImportExportModelAdmin):
|
||||
"""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 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 for managing the BuildLine model via the admin interface"""
|
||||
|
||||
@ -112,8 +115,3 @@ class BuildLineAdmin(admin.ModelAdmin):
|
||||
'build__reference',
|
||||
'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
|
||||
import django.db.models.deletion
|
||||
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):
|
||||
@ -49,5 +43,4 @@ class Migration(migrations.Migration):
|
||||
field=models.PositiveIntegerField(db_index=True, default=0, editable=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'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(assign_bom_items, reverse_code=migrations.RunPython.noop),
|
||||
]
|
||||
operations = []
|
||||
|
@ -13,7 +13,7 @@ import math
|
||||
import os
|
||||
import re
|
||||
import uuid
|
||||
from datetime import datetime, timedelta
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from enum import Enum
|
||||
from secrets import compare_digest
|
||||
from typing import Any, Callable, Dict, List, Tuple, TypedDict, Union
|
||||
@ -2850,7 +2850,12 @@ class NotificationMessage(models.Model):
|
||||
|
||||
def age(self):
|
||||
"""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
|
||||
|
||||
def age_human(self):
|
||||
|
@ -33,6 +33,7 @@ class CompanyResource(InvenTreeResource):
|
||||
clean_model_instances = True
|
||||
|
||||
|
||||
@admin.register(Company)
|
||||
class CompanyAdmin(ImportExportModelAdmin):
|
||||
"""Admin class for the Company model."""
|
||||
|
||||
@ -69,6 +70,7 @@ class SupplierPriceBreakInline(admin.TabularInline):
|
||||
model = SupplierPriceBreak
|
||||
|
||||
|
||||
@admin.register(SupplierPart)
|
||||
class SupplierPartAdmin(ImportExportModelAdmin):
|
||||
"""Admin class for the SupplierPart model."""
|
||||
|
||||
@ -105,6 +107,7 @@ class ManufacturerPartResource(InvenTreeResource):
|
||||
manufacturer_name = Field(attribute='manufacturer__name', readonly=True)
|
||||
|
||||
|
||||
@admin.register(ManufacturerPart)
|
||||
class ManufacturerPartAdmin(ImportExportModelAdmin):
|
||||
"""Admin class for ManufacturerPart model."""
|
||||
|
||||
@ -117,6 +120,7 @@ class ManufacturerPartAdmin(ImportExportModelAdmin):
|
||||
autocomplete_fields = ('part', 'manufacturer')
|
||||
|
||||
|
||||
@admin.register(ManufacturerPartAttachment)
|
||||
class ManufacturerPartAttachmentAdmin(ImportExportModelAdmin):
|
||||
"""Admin class for ManufacturerPartAttachment model."""
|
||||
|
||||
@ -137,6 +141,7 @@ class ManufacturerPartParameterResource(InvenTreeResource):
|
||||
clean_model_instance = True
|
||||
|
||||
|
||||
@admin.register(ManufacturerPartParameter)
|
||||
class ManufacturerPartParameterAdmin(ImportExportModelAdmin):
|
||||
"""Admin class for ManufacturerPartParameter model."""
|
||||
|
||||
@ -173,6 +178,7 @@ class SupplierPriceBreakResource(InvenTreeResource):
|
||||
MPN = Field(attribute='part__MPN', readonly=True)
|
||||
|
||||
|
||||
@admin.register(SupplierPriceBreak)
|
||||
class SupplierPriceBreakAdmin(ImportExportModelAdmin):
|
||||
"""Admin class for the SupplierPriceBreak model."""
|
||||
|
||||
@ -197,6 +203,7 @@ class AddressResource(InvenTreeResource):
|
||||
company = Field(attribute='company', widget=widgets.ForeignKeyWidget(Company))
|
||||
|
||||
|
||||
@admin.register(Address)
|
||||
class AddressAdmin(ImportExportModelAdmin):
|
||||
"""Admin class for the Address model."""
|
||||
|
||||
@ -221,6 +228,7 @@ class ContactResource(InvenTreeResource):
|
||||
company = Field(attribute='company', widget=widgets.ForeignKeyWidget(Company))
|
||||
|
||||
|
||||
@admin.register(Contact)
|
||||
class ContactAdmin(ImportExportModelAdmin):
|
||||
"""Admin class for the Contact model."""
|
||||
|
||||
@ -229,15 +237,3 @@ class ContactAdmin(ImportExportModelAdmin):
|
||||
list_display = ('company', 'name', 'role', 'email', 'phone')
|
||||
|
||||
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)
|
||||
|
||||
# No login yet - check in headers
|
||||
if 'HTTP_AUTHORIZATION' in request.META:
|
||||
auth = request.META['HTTP_AUTHORIZATION'].split()
|
||||
if 'authorization' in request.headers:
|
||||
auth = request.headers['authorization'].split()
|
||||
if len(auth) == 2:
|
||||
# NOTE: We are only support basic authentication for now.
|
||||
#
|
||||
|
@ -78,7 +78,14 @@ class TotalPriceMixin(models.Model):
|
||||
"""Update the total_price field when saved."""
|
||||
# Recalculate total_price for this order
|
||||
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(
|
||||
null=True,
|
||||
@ -136,6 +143,10 @@ class TotalPriceMixin(models.Model):
|
||||
|
||||
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
|
||||
for line in self.lines.all():
|
||||
if not line.price:
|
||||
|
@ -634,7 +634,7 @@ class PurchaseOrderTest(OrderTest):
|
||||
response = self.client.get(
|
||||
reverse('api-po-so-calendar', kwargs={'ordertype': 'purchase-order'}),
|
||||
format='json',
|
||||
HTTP_AUTHORIZATION=f'basic {base64_token}',
|
||||
headers={'authorization': f'basic {base64_token}'},
|
||||
)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
|
@ -57,7 +57,8 @@ class PurchaseOrderTests(OrderViewTestCase):
|
||||
def test_po_export(self):
|
||||
"""Export PurchaseOrder."""
|
||||
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)
|
||||
|
@ -245,7 +245,9 @@ def annotate_variant_quantity(subquery: Q, reference: str = 'quantity'):
|
||||
Subquery(
|
||||
subquery.annotate(
|
||||
total=Func(F(reference), function='SUM', output_field=FloatField())
|
||||
).values('total')
|
||||
)
|
||||
.values('total')
|
||||
.order_by()
|
||||
),
|
||||
0,
|
||||
output_field=FloatField(),
|
||||
@ -270,7 +272,9 @@ def annotate_category_parts():
|
||||
Subquery(
|
||||
subquery.annotate(
|
||||
total=Func(F('pk'), function='COUNT', output_field=IntegerField())
|
||||
).values('total')
|
||||
)
|
||||
.values('total')
|
||||
.order_by()
|
||||
),
|
||||
0,
|
||||
output_field=IntegerField(),
|
||||
|
@ -1,13 +1,6 @@
|
||||
# Generated by Django 2.2.5 on 2019-09-08 04:04
|
||||
|
||||
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):
|
||||
@ -18,6 +11,4 @@ class Migration(migrations.Migration):
|
||||
('part', '0019_auto_20190908_0404'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(update_tree)
|
||||
]
|
||||
operations = []
|
||||
|
@ -2,13 +2,6 @@
|
||||
|
||||
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):
|
||||
|
||||
@ -43,6 +36,4 @@ class Migration(migrations.Migration):
|
||||
field=models.PositiveIntegerField(db_index=True, default=0, editable=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 not, it is considered "orphaned" and will be deleted.
|
||||
"""
|
||||
_new = False
|
||||
if self.pk:
|
||||
try:
|
||||
previous = Part.objects.get(pk=self.pk)
|
||||
@ -470,6 +471,8 @@ class Part(InvenTreeBarcodeMixin, InvenTreeNotesMixin, MetadataMixin, MPTTModel)
|
||||
previous.image.delete(save=False)
|
||||
except Part.DoesNotExist:
|
||||
pass
|
||||
else:
|
||||
_new = True
|
||||
|
||||
self.full_clean()
|
||||
|
||||
@ -478,6 +481,10 @@ class Part(InvenTreeBarcodeMixin, InvenTreeNotesMixin, MetadataMixin, MPTTModel)
|
||||
except InvalidMove:
|
||||
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):
|
||||
"""Return a string representation of the Part (for use in the admin interface)."""
|
||||
return f'{self.full_name} - {self.description}'
|
||||
@ -827,6 +834,12 @@ class Part(InvenTreeBarcodeMixin, InvenTreeNotesMixin, MetadataMixin, MPTTModel)
|
||||
# Run custom validation for the name field
|
||||
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:
|
||||
for part in self.get_used_in():
|
||||
if not part.trackable:
|
||||
|
@ -108,7 +108,7 @@ class PartDetailTest(PartViewTestCase):
|
||||
"""Test downloading a BOM for a valid part."""
|
||||
response = self.client.get(
|
||||
reverse('api-bom-download', args=(1,)),
|
||||
HTTP_X_REQUESTED_WITH='XMLHttpRequest',
|
||||
headers={'x-requested-with': 'XMLHttpRequest'},
|
||||
)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertIn('streaming_content', dir(response))
|
||||
|
@ -106,7 +106,6 @@ class InvenTreePluginTests(TestCase):
|
||||
LICENSE = 'MIT'
|
||||
|
||||
cls.plugin_name = NameInvenTreePlugin()
|
||||
cls.plugin_sample = SampleIntegrationPlugin()
|
||||
|
||||
class VersionInvenTreePlugin(InvenTreePlugin):
|
||||
NAME = 'Version'
|
||||
@ -140,7 +139,7 @@ class InvenTreePluginTests(TestCase):
|
||||
|
||||
# is_sample
|
||||
self.assertEqual(self.plugin.is_sample, False)
|
||||
self.assertEqual(self.plugin_sample.is_sample, True)
|
||||
self.assertEqual(SampleIntegrationPlugin().is_sample, True)
|
||||
|
||||
# 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):
|
||||
"""Admin class for the various reporting models."""
|
||||
|
||||
list_display = ('name', 'description', 'template', 'filters', 'enabled', 'revision')
|
||||
|
||||
|
||||
@admin.register(ReportSnippet)
|
||||
class ReportSnippetAdmin(admin.ModelAdmin):
|
||||
"""Admin class for the ReportSnippet model."""
|
||||
|
||||
list_display = ('id', 'snippet', 'description')
|
||||
|
||||
|
||||
@admin.register(ReportAsset)
|
||||
class ReportAssetAdmin(admin.ModelAdmin):
|
||||
"""Admin class for the ReportAsset model."""
|
||||
|
||||
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
|
||||
|
||||
|
||||
@admin.register(StockLocation)
|
||||
class LocationAdmin(ImportExportModelAdmin):
|
||||
"""Admin class for Location."""
|
||||
|
||||
@ -99,6 +100,7 @@ class LocationAdmin(ImportExportModelAdmin):
|
||||
autocomplete_fields = ['parent']
|
||||
|
||||
|
||||
@admin.register(StockLocationType)
|
||||
class LocationTypeAdmin(admin.ModelAdmin):
|
||||
"""Admin class for StockLocationType."""
|
||||
|
||||
@ -268,6 +270,7 @@ class StockItemResource(InvenTreeResource):
|
||||
StockItem.objects.rebuild()
|
||||
|
||||
|
||||
@admin.register(StockItem)
|
||||
class StockItemAdmin(ImportExportModelAdmin):
|
||||
"""Admin class for StockItem."""
|
||||
|
||||
@ -292,6 +295,7 @@ class StockItemAdmin(ImportExportModelAdmin):
|
||||
]
|
||||
|
||||
|
||||
@admin.register(StockItemAttachment)
|
||||
class StockAttachmentAdmin(admin.ModelAdmin):
|
||||
"""Admin class for StockAttachment."""
|
||||
|
||||
@ -300,6 +304,7 @@ class StockAttachmentAdmin(admin.ModelAdmin):
|
||||
autocomplete_fields = ['stock_item']
|
||||
|
||||
|
||||
@admin.register(StockItemTracking)
|
||||
class StockTrackingAdmin(ImportExportModelAdmin):
|
||||
"""Admin class for StockTracking."""
|
||||
|
||||
@ -308,17 +313,10 @@ class StockTrackingAdmin(ImportExportModelAdmin):
|
||||
autocomplete_fields = ['item']
|
||||
|
||||
|
||||
@admin.register(StockItemTestResult)
|
||||
class StockItemTestResultAdmin(admin.ModelAdmin):
|
||||
"""Admin class for StockItemTestResult."""
|
||||
|
||||
list_display = ('stock_item', 'test', 'result', 'value')
|
||||
|
||||
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.annotate(
|
||||
total=Func(F('pk'), function='COUNT', output_field=IntegerField())
|
||||
).values('total')
|
||||
)
|
||||
.values('total')
|
||||
.order_by()
|
||||
),
|
||||
0,
|
||||
output_field=IntegerField(),
|
||||
@ -50,7 +52,9 @@ def annotate_child_items():
|
||||
Subquery(
|
||||
child_stock_query.annotate(
|
||||
count=Func(F('pk'), function='COUNT', output_field=IntegerField())
|
||||
).values('count')
|
||||
)
|
||||
.values('count')
|
||||
.order_by()
|
||||
),
|
||||
0,
|
||||
output_field=IntegerField(),
|
||||
|
@ -2,13 +2,6 @@
|
||||
|
||||
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):
|
||||
@ -19,6 +12,4 @@ class Migration(migrations.Migration):
|
||||
('stock', '0011_auto_20190908_0404'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(update_tree)
|
||||
]
|
||||
operations = []
|
||||
|
@ -1,13 +1,6 @@
|
||||
# Generated by Django 2.2.9 on 2020-02-17 11:09
|
||||
|
||||
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):
|
||||
@ -18,6 +11,4 @@ class Migration(migrations.Migration):
|
||||
('stock', '0021_auto_20200215_2232'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(update_stock_item_tree)
|
||||
]
|
||||
operations = []
|
||||
|
@ -107,7 +107,8 @@ class StockLocationManager(TreeManager):
|
||||
|
||||
- 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):
|
||||
|
@ -13,6 +13,7 @@ from users.models import ApiToken, Owner, RuleSet
|
||||
User = get_user_model()
|
||||
|
||||
|
||||
@admin.register(ApiToken)
|
||||
class ApiTokenAdmin(admin.ModelAdmin):
|
||||
"""Admin class for the ApiToken model."""
|
||||
|
||||
@ -288,6 +289,7 @@ class InvenTreeUserAdmin(UserAdmin):
|
||||
)
|
||||
|
||||
|
||||
@admin.register(Owner)
|
||||
class OwnerAdmin(admin.ModelAdmin):
|
||||
"""Custom admin interface for the Owner model."""
|
||||
|
||||
@ -299,7 +301,3 @@ admin.site.register(Group, RoleGroupAdmin)
|
||||
|
||||
admin.site.unregister(User)
|
||||
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
|
||||
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_host', request.META.get('REMOTE_HOST', ''))
|
||||
token.set_metadata('remote_user', request.META.get('REMOTE_USER', ''))
|
||||
|
@ -10,7 +10,7 @@ asgiref==3.7.2
|
||||
# django
|
||||
build==1.0.3
|
||||
# via pip-tools
|
||||
certifi==2023.7.22
|
||||
certifi==2023.11.17
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# requests
|
||||
@ -33,13 +33,13 @@ coverage[toml]==5.5
|
||||
# coveralls
|
||||
coveralls==2.1.2
|
||||
# via -r requirements-dev.in
|
||||
cryptography==41.0.6
|
||||
cryptography==41.0.7
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# pdfminer-six
|
||||
distlib==0.3.7
|
||||
distlib==0.3.8
|
||||
# via virtualenv
|
||||
django==3.2.23
|
||||
django==4.2.9
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# django-slowtests
|
||||
@ -53,7 +53,7 @@ filelock==3.13.1
|
||||
# via virtualenv
|
||||
identify==2.5.31
|
||||
# via pre-commit
|
||||
idna==3.4
|
||||
idna==3.6
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# requests
|
||||
@ -61,7 +61,7 @@ importlib-metadata==6.8.0
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# build
|
||||
isort==5.12.0
|
||||
isort==5.13.2
|
||||
# via -r requirements-dev.in
|
||||
nodeenv==1.8.0
|
||||
# via pre-commit
|
||||
@ -69,13 +69,13 @@ packaging==23.2
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# build
|
||||
pdfminer-six==20221105
|
||||
pdfminer-six==20231228
|
||||
# via -r requirements-dev.in
|
||||
pip-tools==7.3.0
|
||||
# via -r requirements-dev.in
|
||||
platformdirs==3.11.0
|
||||
platformdirs==4.1.0
|
||||
# via virtualenv
|
||||
pre-commit==3.5.0
|
||||
pre-commit==3.6.0
|
||||
# via -r requirements-dev.in
|
||||
pycparser==2.21
|
||||
# via
|
||||
@ -83,10 +83,6 @@ pycparser==2.21
|
||||
# cffi
|
||||
pyproject-hooks==1.0.0
|
||||
# via build
|
||||
pytz==2023.3.post1
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# django
|
||||
pyyaml==6.0.1
|
||||
# via
|
||||
# -c requirements.txt
|
||||
@ -106,20 +102,20 @@ tomli==2.0.1
|
||||
# build
|
||||
# pip-tools
|
||||
# pyproject-hooks
|
||||
typing-extensions==4.8.0
|
||||
typing-extensions==4.9.0
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# asgiref
|
||||
# django-test-migrations
|
||||
urllib3==2.0.7
|
||||
urllib3==2.1.0
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# requests
|
||||
virtualenv==20.24.6
|
||||
virtualenv==20.25.0
|
||||
# via pre-commit
|
||||
wheel==0.41.3
|
||||
wheel==0.42.0
|
||||
# via pip-tools
|
||||
zipp==3.16.0
|
||||
zipp==3.16.2
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# importlib-metadata
|
||||
|
@ -1,5 +1,5 @@
|
||||
# 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
|
||||
cryptography>=40.0.0,!=40.0.2 # Core cryptographic functionality
|
||||
django-allauth # SSO for external providers via OpenID
|
||||
@ -13,11 +13,13 @@ django-filter # Extended filtering options
|
||||
django-flags # Feature flags
|
||||
django-formtools # Form wizard tools
|
||||
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-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-mptt==0.11.0 # Modified Preorder Tree Traversal
|
||||
django-mptt # Modified Preorder Tree Traversal
|
||||
django-redis>=5.0.0 # Redis integration
|
||||
django-q2 # Background task scheduling
|
||||
django-q-sentry # sentry.io integration for django-q
|
||||
|
@ -5,14 +5,16 @@
|
||||
# pip-compile --output-file=requirements.txt requirements.in
|
||||
#
|
||||
asgiref==3.7.2
|
||||
# via django
|
||||
# via
|
||||
# django
|
||||
# django-cors-headers
|
||||
async-timeout==4.0.3
|
||||
# via redis
|
||||
attrs==23.1.0
|
||||
attrs==23.2.0
|
||||
# via
|
||||
# jsonschema
|
||||
# referencing
|
||||
babel==2.13.1
|
||||
babel==2.14.0
|
||||
# via py-moneyed
|
||||
backoff==2.2.1
|
||||
# via
|
||||
@ -25,7 +27,7 @@ bleach[css]==6.1.0
|
||||
# django-markdownify
|
||||
brotli==1.1.0
|
||||
# via fonttools
|
||||
certifi==2023.7.22
|
||||
certifi==2023.11.17
|
||||
# via
|
||||
# requests
|
||||
# sentry-sdk
|
||||
@ -39,7 +41,7 @@ coreapi==2.3.3
|
||||
# via -r requirements.in
|
||||
coreschema==0.0.4
|
||||
# via coreapi
|
||||
cryptography==41.0.6
|
||||
cryptography==41.0.7
|
||||
# via
|
||||
# -r requirements.in
|
||||
# djangorestframework-simplejwt
|
||||
@ -59,7 +61,7 @@ diff-match-patch==20230430
|
||||
# via django-import-export
|
||||
dj-rest-auth==5.0.2
|
||||
# via -r requirements.in
|
||||
django==3.2.23
|
||||
django==4.2.9
|
||||
# via
|
||||
# -r requirements.in
|
||||
# dj-rest-auth
|
||||
@ -76,7 +78,6 @@ django==3.2.23
|
||||
# django-js-asset
|
||||
# django-markdownify
|
||||
# django-money
|
||||
# django-mptt
|
||||
# django-otp
|
||||
# django-picklefield
|
||||
# django-q2
|
||||
@ -101,7 +102,7 @@ django-allauth-2fa==0.11.1
|
||||
# via -r requirements.in
|
||||
django-cleanup==8.0.0
|
||||
# via -r requirements.in
|
||||
django-cors-headers==4.3.0
|
||||
django-cors-headers==4.3.1
|
||||
# via -r requirements.in
|
||||
django-crispy-forms==1.14.0
|
||||
# via -r requirements.in
|
||||
@ -109,17 +110,17 @@ django-dbbackup==4.0.2
|
||||
# via -r requirements.in
|
||||
django-error-report-2==0.4.2
|
||||
# via -r requirements.in
|
||||
django-filter==23.3
|
||||
django-filter==23.5
|
||||
# via -r requirements.in
|
||||
django-flags==5.0.13
|
||||
# via -r requirements.in
|
||||
django-formtools==2.4.1
|
||||
django-formtools==2.5.1
|
||||
# via -r requirements.in
|
||||
django-ical==1.9.2
|
||||
# via -r requirements.in
|
||||
django-import-export==3.3.1
|
||||
django-import-export==3.3.5
|
||||
# via -r requirements.in
|
||||
django-js-asset==2.1.0
|
||||
django-js-asset==2.2.0
|
||||
# via django-mptt
|
||||
django-maintenance-mode==0.21.0
|
||||
# via -r requirements.in
|
||||
@ -127,9 +128,9 @@ django-markdownify==0.9.3
|
||||
# via -r requirements.in
|
||||
django-money==3.2.0
|
||||
# via -r requirements.in
|
||||
django-mptt==0.11.0
|
||||
django-mptt==0.16.0
|
||||
# via -r requirements.in
|
||||
django-otp==1.2.4
|
||||
django-otp==1.3.0
|
||||
# via django-allauth-2fa
|
||||
django-picklefield==3.1
|
||||
# via django-q2
|
||||
@ -141,7 +142,7 @@ django-recurrence==1.11.1
|
||||
# via django-ical
|
||||
django-redis==5.4.0
|
||||
# via -r requirements.in
|
||||
django-sesame==3.2.1
|
||||
django-sesame==3.2.2
|
||||
# via -r requirements.in
|
||||
django-sql-utils==0.7.0
|
||||
# via -r requirements.in
|
||||
@ -149,11 +150,11 @@ django-sslserver==0.22
|
||||
# via -r requirements.in
|
||||
django-stdimage==6.0.2
|
||||
# via -r requirements.in
|
||||
django-taggit==4.0.0
|
||||
django-taggit==5.0.1
|
||||
# via -r requirements.in
|
||||
django-user-sessions==2.0.0
|
||||
# via -r requirements.in
|
||||
django-weasyprint==2.2.1
|
||||
django-weasyprint==2.2.2
|
||||
# via -r requirements.in
|
||||
django-xforwardedfor-middleware==2.0
|
||||
# via -r requirements.in
|
||||
@ -163,15 +164,15 @@ djangorestframework==3.14.0
|
||||
# dj-rest-auth
|
||||
# djangorestframework-simplejwt
|
||||
# drf-spectacular
|
||||
djangorestframework-simplejwt[crypto]==5.3.0
|
||||
djangorestframework-simplejwt[crypto]==5.3.1
|
||||
# via -r requirements.in
|
||||
drf-spectacular==0.26.5
|
||||
drf-spectacular==0.27.0
|
||||
# via -r requirements.in
|
||||
dulwich==0.21.6
|
||||
dulwich==0.21.7
|
||||
# via -r requirements.in
|
||||
et-xmlfile==1.1.0
|
||||
# via openpyxl
|
||||
feedparser==6.0.10
|
||||
feedparser==6.0.11
|
||||
# via -r requirements.in
|
||||
fonttools[woff]==4.44.0
|
||||
# via
|
||||
@ -189,7 +190,7 @@ html5lib==1.1
|
||||
# via weasyprint
|
||||
icalendar==5.0.11
|
||||
# via django-ical
|
||||
idna==3.4
|
||||
idna==3.6
|
||||
# via requests
|
||||
importlib-metadata==6.8.0
|
||||
# via
|
||||
@ -202,9 +203,9 @@ itypes==1.2.0
|
||||
# via coreapi
|
||||
jinja2==3.1.3
|
||||
# via coreschema
|
||||
jsonschema==4.19.2
|
||||
jsonschema==4.20.0
|
||||
# via drf-spectacular
|
||||
jsonschema-specifications==2023.7.1
|
||||
jsonschema-specifications==2023.12.1
|
||||
# via jsonschema
|
||||
markdown==3.5.1
|
||||
# via django-markdownify
|
||||
@ -277,7 +278,7 @@ opentelemetry-util-http==0.43b0
|
||||
# opentelemetry-instrumentation-wsgi
|
||||
packaging==23.2
|
||||
# via gunicorn
|
||||
pdf2image==1.16.3
|
||||
pdf2image==1.17.0
|
||||
# via -r requirements.in
|
||||
pillow==10.2.0
|
||||
# via
|
||||
@ -316,13 +317,12 @@ python-dateutil==2.8.2
|
||||
# icalendar
|
||||
python-dotenv==1.0.0
|
||||
# via -r requirements.in
|
||||
python-fsutil==0.12.0
|
||||
python-fsutil==0.13.0
|
||||
# via django-maintenance-mode
|
||||
python3-openid==3.2.0
|
||||
# via django-allauth
|
||||
pytz==2023.3.post1
|
||||
# via
|
||||
# django
|
||||
# django-dbbackup
|
||||
# djangorestframework
|
||||
# icalendar
|
||||
@ -339,11 +339,11 @@ rapidfuzz==0.7.6
|
||||
# via -r requirements.in
|
||||
redis==5.0.1
|
||||
# via django-redis
|
||||
referencing==0.30.2
|
||||
referencing==0.32.1
|
||||
# via
|
||||
# jsonschema
|
||||
# jsonschema-specifications
|
||||
regex==2023.10.3
|
||||
regex==2023.12.25
|
||||
# via -r requirements.in
|
||||
requests==2.31.0
|
||||
# via
|
||||
@ -353,11 +353,11 @@ requests==2.31.0
|
||||
# requests-oauthlib
|
||||
requests-oauthlib==1.3.1
|
||||
# via django-allauth
|
||||
rpds-py==0.12.0
|
||||
rpds-py==0.16.2
|
||||
# via
|
||||
# jsonschema
|
||||
# referencing
|
||||
sentry-sdk==1.34.0
|
||||
sentry-sdk==1.39.2
|
||||
# via
|
||||
# -r requirements.in
|
||||
# django-q-sentry
|
||||
@ -381,9 +381,10 @@ tinycss2==1.2.1
|
||||
# bleach
|
||||
# cssselect2
|
||||
# weasyprint
|
||||
typing-extensions==4.8.0
|
||||
typing-extensions==4.9.0
|
||||
# via
|
||||
# asgiref
|
||||
# drf-spectacular
|
||||
# opentelemetry-sdk
|
||||
# py-moneyed
|
||||
# qrcode
|
||||
@ -391,12 +392,12 @@ uritemplate==4.1.1
|
||||
# via
|
||||
# coreapi
|
||||
# drf-spectacular
|
||||
urllib3==2.0.7
|
||||
urllib3==2.1.0
|
||||
# via
|
||||
# dulwich
|
||||
# requests
|
||||
# sentry-sdk
|
||||
weasyprint==60.1
|
||||
weasyprint==60.2
|
||||
# via
|
||||
# -r requirements.in
|
||||
# django-weasyprint
|
||||
@ -415,7 +416,7 @@ xlrd==2.0.1
|
||||
# via tablib
|
||||
xlwt==1.3.0
|
||||
# via tablib
|
||||
zipp==3.16.0
|
||||
zipp==3.16.2
|
||||
# via importlib-metadata
|
||||
zopfli==0.2.3
|
||||
# via fonttools
|
||||
|
Loading…
Reference in New Issue
Block a user