From 8eea8812e410666d868628f6896c505c1708a20f Mon Sep 17 00:00:00 2001 From: Matthias Mair Date: Sat, 10 Aug 2024 00:12:58 +0200 Subject: [PATCH 01/47] Add more liniting rules (mostly for imports) (#7846) * remove unused imports * enable pyflake checks * various fixes * fix assert --- pyproject.toml | 2 +- src/backend/InvenTree/InvenTree/api.py | 3 --- src/backend/InvenTree/InvenTree/apps.py | 2 +- src/backend/InvenTree/InvenTree/conversion.py | 4 ++-- src/backend/InvenTree/InvenTree/exceptions.py | 1 - src/backend/InvenTree/InvenTree/helpers.py | 2 -- .../InvenTree/InvenTree/helpers_model.py | 6 ------ src/backend/InvenTree/InvenTree/models.py | 2 +- src/backend/InvenTree/InvenTree/settings.py | 3 +-- src/backend/InvenTree/InvenTree/status_codes.py | 6 +++--- src/backend/InvenTree/InvenTree/test_sso.py | 1 - src/backend/InvenTree/InvenTree/tests.py | 1 - src/backend/InvenTree/InvenTree/version.py | 2 +- src/backend/InvenTree/common/tests.py | 3 --- src/backend/InvenTree/common/validators.py | 2 +- src/backend/InvenTree/importer/admin.py | 1 - src/backend/InvenTree/machine/admin.py | 1 - src/backend/InvenTree/machine/registry.py | 4 ++-- src/backend/InvenTree/machine/tests.py | 2 +- src/backend/InvenTree/order/models.py | 1 - src/backend/InvenTree/part/api.py | 6 +++--- src/backend/InvenTree/part/stocktake.py | 1 - src/backend/InvenTree/part/test_api.py | 8 +++----- src/backend/InvenTree/part/test_bom_export.py | 17 +---------------- src/backend/InvenTree/part/test_pricing.py | 2 +- .../InvenTree/plugin/base/label/mixins.py | 2 -- src/backend/InvenTree/plugin/base/locate/api.py | 1 - .../plugin/builtin/labels/label_sheet.py | 1 - src/backend/InvenTree/plugin/registry.py | 2 +- .../samples/integration/report_plugin_sample.py | 1 - src/backend/InvenTree/plugin/staticfiles.py | 2 +- src/backend/InvenTree/report/api.py | 2 +- src/backend/InvenTree/report/models.py | 1 - src/backend/InvenTree/report/tests.py | 2 +- src/backend/InvenTree/stock/api.py | 2 -- src/backend/InvenTree/stock/filters.py | 2 -- src/backend/InvenTree/stock/generators.py | 2 -- src/backend/InvenTree/stock/models.py | 2 +- src/backend/InvenTree/users/api.py | 2 +- 39 files changed, 28 insertions(+), 79 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 0946691589..a8c23e75d8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -20,7 +20,7 @@ src = ["src/backend/InvenTree"] "__init__.py" = ["D104"] [tool.ruff.lint] -select = ["A", "B", "C4", "D", "I", "N"] +select = ["A", "B", "C4", "D", "I", "N", "F"] # Things that should be enabled in the future: # - LOG # - DJ # for Django stuff diff --git a/src/backend/InvenTree/InvenTree/api.py b/src/backend/InvenTree/InvenTree/api.py index cba3727c9e..952ddb3ff4 100644 --- a/src/backend/InvenTree/InvenTree/api.py +++ b/src/backend/InvenTree/InvenTree/api.py @@ -8,7 +8,6 @@ from pathlib import Path from django.conf import settings from django.db import transaction from django.http import JsonResponse -from django.urls import include, path from django.utils.translation import gettext_lazy as _ from django_q.models import OrmQ @@ -21,9 +20,7 @@ from rest_framework.views import APIView import InvenTree.version import users.models -from InvenTree.filters import SEARCH_ORDER_FILTER from InvenTree.mixins import ListCreateAPI -from InvenTree.permissions import RolePermission from InvenTree.templatetags.inventree_extras import plugins_info from part.models import Part from plugin.serializers import MetadataSerializer diff --git a/src/backend/InvenTree/InvenTree/apps.py b/src/backend/InvenTree/InvenTree/apps.py index 0cd448361f..accd545980 100644 --- a/src/backend/InvenTree/InvenTree/apps.py +++ b/src/backend/InvenTree/InvenTree/apps.py @@ -11,7 +11,7 @@ from django.core.exceptions import AppRegistryNotReady from django.db import transaction from django.db.utils import IntegrityError, OperationalError -from allauth.socialaccount.signals import social_account_added, social_account_updated +from allauth.socialaccount.signals import social_account_updated import InvenTree.conversion import InvenTree.ready diff --git a/src/backend/InvenTree/InvenTree/conversion.py b/src/backend/InvenTree/InvenTree/conversion.py index b2d15c16df..61f1850899 100644 --- a/src/backend/InvenTree/InvenTree/conversion.py +++ b/src/backend/InvenTree/InvenTree/conversion.py @@ -153,7 +153,7 @@ def convert_physical_value(value: str, unit: str = None, strip_units=True): if unit: try: valid = unit in ureg - except Exception as exc: + except Exception: valid = False if not valid: @@ -196,7 +196,7 @@ def convert_physical_value(value: str, unit: str = None, strip_units=True): try: value = convert_value(attempt, unit) break - except Exception as exc: + except Exception: value = None if value is None: diff --git a/src/backend/InvenTree/InvenTree/exceptions.py b/src/backend/InvenTree/InvenTree/exceptions.py index f6fca172a3..12328994e7 100644 --- a/src/backend/InvenTree/InvenTree/exceptions.py +++ b/src/backend/InvenTree/InvenTree/exceptions.py @@ -9,7 +9,6 @@ import traceback from django.conf import settings from django.core.exceptions import ValidationError as DjangoValidationError -from django.db.utils import IntegrityError, OperationalError from django.utils.translation import gettext_lazy as _ import rest_framework.views as drfviews diff --git a/src/backend/InvenTree/InvenTree/helpers.py b/src/backend/InvenTree/InvenTree/helpers.py index 4558594436..63a1c43f8f 100644 --- a/src/backend/InvenTree/InvenTree/helpers.py +++ b/src/backend/InvenTree/InvenTree/helpers.py @@ -3,7 +3,6 @@ import datetime import hashlib import io -import json import logging import os import os.path @@ -27,7 +26,6 @@ from bleach import clean from djmoney.money import Money from PIL import Image -import InvenTree.version from common.currency import currency_code_default from .settings import MEDIA_URL, STATIC_URL diff --git a/src/backend/InvenTree/InvenTree/helpers_model.py b/src/backend/InvenTree/InvenTree/helpers_model.py index bf814dc12a..810c44c633 100644 --- a/src/backend/InvenTree/InvenTree/helpers_model.py +++ b/src/backend/InvenTree/InvenTree/helpers_model.py @@ -36,8 +36,6 @@ def get_base_url(request=None): 3. If settings.SITE_URL is set (e.g. in the Django settings), use that 4. If the InvenTree setting INVENTREE_BASE_URL is set, use that """ - import common.models - # Check if a request is provided if request: return request.build_absolute_uri('/') @@ -104,8 +102,6 @@ def download_image_from_url(remote_url, timeout=2.5): ValueError: Server responded with invalid 'Content-Length' value TypeError: Response is not a valid image """ - import common.models - # Check that the provided URL at least looks valid validator = URLValidator() validator(remote_url) @@ -203,8 +199,6 @@ def render_currency( max_decimal_places: The maximum number of decimal places to render to. If unspecified, uses the PRICING_DECIMAL_PLACES setting. include_symbol: If True, include the currency symbol in the output """ - import common.models - if money in [None, '']: return '-' diff --git a/src/backend/InvenTree/InvenTree/models.py b/src/backend/InvenTree/InvenTree/models.py index c9f7a31253..7430ee5c9f 100644 --- a/src/backend/InvenTree/InvenTree/models.py +++ b/src/backend/InvenTree/InvenTree/models.py @@ -93,7 +93,7 @@ class PluginValidationMixin(DiffMixin): return except ValidationError as exc: raise exc - except Exception as exc: + except Exception: # Log the exception to the database import InvenTree.exceptions diff --git a/src/backend/InvenTree/InvenTree/settings.py b/src/backend/InvenTree/InvenTree/settings.py index 0d7b306ff2..8112ea0f19 100644 --- a/src/backend/InvenTree/InvenTree/settings.py +++ b/src/backend/InvenTree/InvenTree/settings.py @@ -18,7 +18,6 @@ import django.conf.locale import django.core.exceptions from django.core.validators import URLValidator from django.http import Http404 -from django.utils.translation import gettext_lazy as _ import pytz from dotenv import load_dotenv @@ -301,7 +300,7 @@ if ( and get_boolean_setting('INVENTREE_DEBUG_SHELL', 'debug_shell', False) ): # noqa try: - import django_admin_shell + import django_admin_shell # noqa: F401 INSTALLED_APPS.append('django_admin_shell') ADMIN_SHELL_ENABLE = True diff --git a/src/backend/InvenTree/InvenTree/status_codes.py b/src/backend/InvenTree/InvenTree/status_codes.py index 81d713f59c..adb5a359d8 100644 --- a/src/backend/InvenTree/InvenTree/status_codes.py +++ b/src/backend/InvenTree/InvenTree/status_codes.py @@ -4,6 +4,6 @@ This file remains here for backwards compatibility, as external plugins may import status codes from this file. """ -from build.status_codes import * -from order.status_codes import * -from stock.status_codes import * +from build.status_codes import * # noqa: F403 +from order.status_codes import * # noqa: F403 +from stock.status_codes import * # noqa: F403 diff --git a/src/backend/InvenTree/InvenTree/test_sso.py b/src/backend/InvenTree/InvenTree/test_sso.py index 60a5619c0b..e20f11d3ae 100644 --- a/src/backend/InvenTree/InvenTree/test_sso.py +++ b/src/backend/InvenTree/InvenTree/test_sso.py @@ -9,7 +9,6 @@ from allauth.socialaccount.models import SocialAccount, SocialLogin from common.models import InvenTreeSetting from InvenTree import sso from InvenTree.forms import RegistratonMixin -from InvenTree.unit_test import InvenTreeTestCase class Dummy: diff --git a/src/backend/InvenTree/InvenTree/tests.py b/src/backend/InvenTree/InvenTree/tests.py index b4ff159c90..5f2093a8fe 100644 --- a/src/backend/InvenTree/InvenTree/tests.py +++ b/src/backend/InvenTree/InvenTree/tests.py @@ -1,6 +1,5 @@ """Test general functions and helpers.""" -import json import os import time from datetime import datetime, timedelta diff --git a/src/backend/InvenTree/InvenTree/version.py b/src/backend/InvenTree/InvenTree/version.py index db8c25f603..a33bec5e93 100644 --- a/src/backend/InvenTree/InvenTree/version.py +++ b/src/backend/InvenTree/InvenTree/version.py @@ -117,7 +117,7 @@ def inventreeDocUrl(): def inventreeAppUrl(): """Return URL for InvenTree app site.""" - return f'https://docs.inventree.org/app/' + return 'https://docs.inventree.org/app/' def inventreeCreditsUrl(): diff --git a/src/backend/InvenTree/common/tests.py b/src/backend/InvenTree/common/tests.py index 0c4d236bf9..c0d46bd9c2 100644 --- a/src/backend/InvenTree/common/tests.py +++ b/src/backend/InvenTree/common/tests.py @@ -407,8 +407,6 @@ class SettingsTest(InvenTreeTestCase): @override_settings(SITE_URL=None, PLUGIN_TESTING=True, PLUGIN_TESTING_SETUP=True) def test_defaults(self): """Populate the settings with default values.""" - N = len(InvenTreeSetting.SETTINGS.keys()) - for key in InvenTreeSetting.SETTINGS.keys(): value = InvenTreeSetting.get_setting_default(key) @@ -1103,7 +1101,6 @@ class CommonTest(InvenTreeAPITestCase): def test_restart_flag(self): """Test that the restart flag is reset on start.""" - import common.models from plugin import registry # set flag true diff --git a/src/backend/InvenTree/common/validators.py b/src/backend/InvenTree/common/validators.py index c8b2b18762..efb82e692e 100644 --- a/src/backend/InvenTree/common/validators.py +++ b/src/backend/InvenTree/common/validators.py @@ -45,7 +45,7 @@ def validate_attachment_model_type(value): """Ensure that the provided attachment model is valid.""" model_names = [el[0] for el in attachment_model_options()] if value not in model_names: - raise ValidationError(f'Model type does not support attachments') + raise ValidationError('Model type does not support attachments') def validate_notes_model_type(value): diff --git a/src/backend/InvenTree/importer/admin.py b/src/backend/InvenTree/importer/admin.py index 01a12799aa..a33f2e7b50 100644 --- a/src/backend/InvenTree/importer/admin.py +++ b/src/backend/InvenTree/importer/admin.py @@ -1,7 +1,6 @@ """Admin site specification for the 'importer' app.""" from django.contrib import admin -from django.urls import path import importer.models import importer.registry diff --git a/src/backend/InvenTree/machine/admin.py b/src/backend/InvenTree/machine/admin.py index 3437c7a38a..929f913a5e 100755 --- a/src/backend/InvenTree/machine/admin.py +++ b/src/backend/InvenTree/machine/admin.py @@ -1,7 +1,6 @@ """Django admin interface for the machine app.""" from django.contrib import admin -from django.http.request import HttpRequest from machine import models diff --git a/src/backend/InvenTree/machine/registry.py b/src/backend/InvenTree/machine/registry.py index c896546c54..b9cdf1e0c9 100644 --- a/src/backend/InvenTree/machine/registry.py +++ b/src/backend/InvenTree/machine/registry.py @@ -13,7 +13,7 @@ logger = logging.getLogger('inventree') class MachineRegistry( - get_shared_class_instance_state_mixin(lambda _x: f'machine:registry') + get_shared_class_instance_state_mixin(lambda _x: 'machine:registry') ): """Machine registry class.""" @@ -44,7 +44,7 @@ class MachineRegistry( """Initialize the machine registry.""" # clear cache for machines (only needed for global redis cache) if main and hasattr(cache, 'delete_pattern'): # pragma: no cover - cache.delete_pattern(f'machine:*') + cache.delete_pattern('machine:*') self.discover_machine_types() self.discover_drivers() diff --git a/src/backend/InvenTree/machine/tests.py b/src/backend/InvenTree/machine/tests.py index 0293ca2bb9..6075ee7118 100755 --- a/src/backend/InvenTree/machine/tests.py +++ b/src/backend/InvenTree/machine/tests.py @@ -292,7 +292,7 @@ class TestLabelPrinterMachineType(TestMachineRegistryMixin, InvenTreeAPITestCase # test the single print label method calls self.assertEqual(self.print_label.call_count, 2) self.assertEqual(self.print_label.call_args.args[0], self.machine.machine) - self.assertEqual(self.print_label.call_args.args[1], label) + self.assertEqual(self.print_label.call_args.args[1], template) self.assertEqual(self.print_label.call_args.args[2], parts[1]) self.assertIn('printing_options', self.print_labels.call_args.kwargs) self.assertEqual( diff --git a/src/backend/InvenTree/order/models.py b/src/backend/InvenTree/order/models.py index 3d2039a84c..f79c615321 100644 --- a/src/backend/InvenTree/order/models.py +++ b/src/backend/InvenTree/order/models.py @@ -1,7 +1,6 @@ """Order model definitions.""" import logging -import os import sys from datetime import datetime from decimal import Decimal diff --git a/src/backend/InvenTree/part/api.py b/src/backend/InvenTree/part/api.py index 49be598d88..513048a03c 100644 --- a/src/backend/InvenTree/part/api.py +++ b/src/backend/InvenTree/part/api.py @@ -1126,9 +1126,9 @@ class PartFilter(rest_filters.FilterSet): # TODO: We should cache BOM checksums to make this process more efficient pks = [] - for part in queryset: - if part.is_bom_valid() == value: - pks.append(part.pk) + for item in queryset: + if item.is_bom_valid() == value: + pks.append(item.pk) return queryset.filter(pk__in=pks) diff --git a/src/backend/InvenTree/part/stocktake.py b/src/backend/InvenTree/part/stocktake.py index 3705d84ae0..b26c9a62cb 100644 --- a/src/backend/InvenTree/part/stocktake.py +++ b/src/backend/InvenTree/part/stocktake.py @@ -3,7 +3,6 @@ import io import logging import time -from datetime import datetime from django.contrib.auth.models import User from django.core.files.base import ContentFile diff --git a/src/backend/InvenTree/part/test_api.py b/src/backend/InvenTree/part/test_api.py index 85af447556..038dc9baf6 100644 --- a/src/backend/InvenTree/part/test_api.py +++ b/src/backend/InvenTree/part/test_api.py @@ -4,7 +4,6 @@ import os from datetime import datetime from decimal import Decimal from enum import IntEnum -from pathlib import Path from random import randint from django.core.exceptions import ValidationError @@ -13,7 +12,6 @@ from django.test.utils import CaptureQueriesContext from django.urls import reverse import PIL -from rest_framework import status from rest_framework.test import APIClient import build.models @@ -371,7 +369,7 @@ class PartCategoryAPITest(InvenTreeAPITestCase): params['delete_parts'] = '1' if delete_child_categories: params['delete_child_categories'] = '1' - response = self.delete(url, params, expected_code=204) + self.delete(url, params, expected_code=204) if delete_parts: if i == Target.delete_subcategories_delete_parts: @@ -539,7 +537,7 @@ class PartCategoryAPITest(InvenTreeAPITestCase): ) sub4 = PartCategory.objects.create(name='sub4', parent=sub3) sub5 = PartCategory.objects.create(name='sub5', parent=sub2) - part = Part.objects.create(name='test', category=sub4) + Part.objects.create(name='test', category=sub4) PartCategory.objects.rebuild() # This query will trigger an internal server error if annotation results are not limited to 1 @@ -973,7 +971,7 @@ class PartAPITest(PartAPITestBase): """Return list of part thumbnails.""" url = reverse('api-part-thumbs') - response = self.get(url) + self.get(url) def test_paginate(self): """Test pagination of the Part list API.""" diff --git a/src/backend/InvenTree/part/test_bom_export.py b/src/backend/InvenTree/part/test_bom_export.py index 7702e94210..fc2e2ca3cf 100644 --- a/src/backend/InvenTree/part/test_bom_export.py +++ b/src/backend/InvenTree/part/test_bom_export.py @@ -151,22 +151,7 @@ class BomExportTest(InvenTreeTestCase): self.assertEqual(response.status_code, 200) content = response.headers['Content-Disposition'] - self.assertEqual(content, 'attachment; filename="BOB | Bob | A2_BOM.xls"') - - def test_export_xlsx(self): - """Test BOM download in XLSX format.""" - params = { - 'format': 'xlsx', - 'cascade': True, - 'parameter_data': True, - 'stock_data': True, - 'supplier_data': True, - 'manufacturer_data': True, - } - - response = self.client.get(self.url, data=params) - - self.assertEqual(response.status_code, 200) + self.assertEqual(content, 'attachment; filename="BOB | Bob | A2_BOM.xlsx"') def test_export_json(self): """Test BOM download in JSON format.""" diff --git a/src/backend/InvenTree/part/test_pricing.py b/src/backend/InvenTree/part/test_pricing.py index bf1b6764cc..212d086c5a 100644 --- a/src/backend/InvenTree/part/test_pricing.py +++ b/src/backend/InvenTree/part/test_pricing.py @@ -12,7 +12,7 @@ import company.models import order.models import part.models import stock.models -from common.settings import get_global_setting, set_global_setting +from common.settings import set_global_setting from InvenTree.unit_test import InvenTreeTestCase from order.status_codes import PurchaseOrderStatus diff --git a/src/backend/InvenTree/plugin/base/label/mixins.py b/src/backend/InvenTree/plugin/base/label/mixins.py index a60c1b0d71..16660dc6c3 100644 --- a/src/backend/InvenTree/plugin/base/label/mixins.py +++ b/src/backend/InvenTree/plugin/base/label/mixins.py @@ -3,8 +3,6 @@ from typing import Union from django.core.exceptions import ValidationError -from django.db.models.query import QuerySet -from django.http import JsonResponse from django.utils.translation import gettext_lazy as _ import pdf2image diff --git a/src/backend/InvenTree/plugin/base/locate/api.py b/src/backend/InvenTree/plugin/base/locate/api.py index 8d37505b3a..948d67bb61 100644 --- a/src/backend/InvenTree/plugin/base/locate/api.py +++ b/src/backend/InvenTree/plugin/base/locate/api.py @@ -1,6 +1,5 @@ """API for location plugins.""" -from drf_spectacular.utils import OpenApiResponse, extend_schema from rest_framework import permissions, serializers from rest_framework.exceptions import NotFound, ParseError from rest_framework.generics import GenericAPIView diff --git a/src/backend/InvenTree/plugin/builtin/labels/label_sheet.py b/src/backend/InvenTree/plugin/builtin/labels/label_sheet.py index c11f06d111..c58f4d6cb2 100644 --- a/src/backend/InvenTree/plugin/builtin/labels/label_sheet.py +++ b/src/backend/InvenTree/plugin/builtin/labels/label_sheet.py @@ -5,7 +5,6 @@ import math from django.core.exceptions import ValidationError from django.core.files.base import ContentFile -from django.http import JsonResponse from django.utils.translation import gettext_lazy as _ import weasyprint diff --git a/src/backend/InvenTree/plugin/registry.py b/src/backend/InvenTree/plugin/registry.py index 92f72bc0f1..4b49eb5433 100644 --- a/src/backend/InvenTree/plugin/registry.py +++ b/src/backend/InvenTree/plugin/registry.py @@ -572,7 +572,7 @@ class PluginsRegistry: try: self._init_plugin(plg, plugin_configs) break - except IntegrationPluginError as error: + except IntegrationPluginError: # Error has been handled downstream pass except Exception as error: diff --git a/src/backend/InvenTree/plugin/samples/integration/report_plugin_sample.py b/src/backend/InvenTree/plugin/samples/integration/report_plugin_sample.py index d81fcd1873..b67c8e96f1 100644 --- a/src/backend/InvenTree/plugin/samples/integration/report_plugin_sample.py +++ b/src/backend/InvenTree/plugin/samples/integration/report_plugin_sample.py @@ -4,7 +4,6 @@ import random from plugin import InvenTreePlugin from plugin.mixins import ReportMixin -from report.models import ReportTemplate class SampleReportPlugin(ReportMixin, InvenTreePlugin): diff --git a/src/backend/InvenTree/plugin/staticfiles.py b/src/backend/InvenTree/plugin/staticfiles.py index 6e08051fc4..0ece4bcc7e 100644 --- a/src/backend/InvenTree/plugin/staticfiles.py +++ b/src/backend/InvenTree/plugin/staticfiles.py @@ -92,4 +92,4 @@ def copy_plugin_static_files(slug, check_reload=True): logger.debug('- copied %s to %s', str(item), str(destination_path)) copied += 1 - logger.info(f"Copied %s static files for plugin '%s'.", copied, slug) + logger.info("Copied %s static files for plugin '%s'.", copied, slug) diff --git a/src/backend/InvenTree/report/api.py b/src/backend/InvenTree/report/api.py index c4e566067b..75959557f7 100644 --- a/src/backend/InvenTree/report/api.py +++ b/src/backend/InvenTree/report/api.py @@ -354,7 +354,7 @@ class ReportPrint(GenericAPIView): for plugin in registry.with_mixin('report'): try: plugin.report_callback(self, instance, output, request) - except Exception as e: + except Exception: InvenTree.exceptions.log_error( f'plugins.{plugin.slug}.report_callback' ) diff --git a/src/backend/InvenTree/report/models.py b/src/backend/InvenTree/report/models.py index 56a1ffe2fd..58cb2a0998 100644 --- a/src/backend/InvenTree/report/models.py +++ b/src/backend/InvenTree/report/models.py @@ -15,7 +15,6 @@ from django.template.loader import render_to_string from django.urls import reverse from django.utils.translation import gettext_lazy as _ -import common.models import InvenTree.exceptions import InvenTree.helpers import InvenTree.models diff --git a/src/backend/InvenTree/report/tests.py b/src/backend/InvenTree/report/tests.py index 68120a01a2..8f05fa0041 100644 --- a/src/backend/InvenTree/report/tests.py +++ b/src/backend/InvenTree/report/tests.py @@ -583,7 +583,7 @@ class TestReportTest(PrintTestMixins, ReportTest): InvenTreeSetting.set_setting('REPORT_ATTACH_TEST_REPORT', True, None) response = self.post( - url, {'template': report.pk, 'items': [item.pk]}, expected_code=201 + url, {'template': template.pk, 'items': [item.pk]}, expected_code=201 ) # There should be a link to the generated PDF diff --git a/src/backend/InvenTree/stock/api.py b/src/backend/InvenTree/stock/api.py index 13aea93e63..9fbf257b71 100644 --- a/src/backend/InvenTree/stock/api.py +++ b/src/backend/InvenTree/stock/api.py @@ -1,6 +1,5 @@ """JSON API for the Stock app.""" -import json from collections import OrderedDict from datetime import timedelta @@ -59,7 +58,6 @@ from order.serializers import ( ) from part.models import BomItem, Part, PartCategory from part.serializers import PartBriefSerializer -from stock.admin import LocationResource, StockItemResource from stock.generators import generate_batch_code, generate_serial_number from stock.models import ( StockItem, diff --git a/src/backend/InvenTree/stock/filters.py b/src/backend/InvenTree/stock/filters.py index 4257bd49b9..ac3098a632 100644 --- a/src/backend/InvenTree/stock/filters.py +++ b/src/backend/InvenTree/stock/filters.py @@ -3,8 +3,6 @@ from django.db.models import F, Func, IntegerField, OuterRef, Q, Subquery from django.db.models.functions import Coalesce -from sql_util.utils import SubqueryCount - import stock.models diff --git a/src/backend/InvenTree/stock/generators.py b/src/backend/InvenTree/stock/generators.py index f08ec0ab05..37accce6e7 100644 --- a/src/backend/InvenTree/stock/generators.py +++ b/src/backend/InvenTree/stock/generators.py @@ -76,8 +76,6 @@ def generate_batch_code(**kwargs): def generate_serial_number(part=None, quantity=1, **kwargs) -> str: """Generate a default 'serial number' for a new StockItem.""" - from plugin.registry import registry - quantity = quantity or 1 if part is None: diff --git a/src/backend/InvenTree/stock/models.py b/src/backend/InvenTree/stock/models.py index 35ce61a99a..de8be1ce79 100644 --- a/src/backend/InvenTree/stock/models.py +++ b/src/backend/InvenTree/stock/models.py @@ -9,7 +9,7 @@ from decimal import Decimal, InvalidOperation from django.conf import settings from django.contrib.auth.models import User -from django.core.exceptions import FieldError, ValidationError +from django.core.exceptions import ValidationError from django.core.validators import MinValueValidator from django.db import models, transaction from django.db.models import Q, Sum diff --git a/src/backend/InvenTree/users/api.py b/src/backend/InvenTree/users/api.py index e784f81393..c8cfb12807 100644 --- a/src/backend/InvenTree/users/api.py +++ b/src/backend/InvenTree/users/api.py @@ -19,7 +19,7 @@ from rest_framework import exceptions, permissions from rest_framework.authentication import BasicAuthentication from rest_framework.decorators import authentication_classes from rest_framework.generics import DestroyAPIView -from rest_framework.permissions import AllowAny, IsAuthenticated +from rest_framework.permissions import IsAuthenticated from rest_framework.response import Response from rest_framework.views import APIView From 3733e8a41758ff2fc02ebe87a577a2421802e2f6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 10 Aug 2024 08:13:43 +1000 Subject: [PATCH 02/47] Bump the dependencies group across 1 directory with 5 updates (#7811) Bumps the dependencies group with 5 updates in the / directory: | Package | From | To | | --- | --- | --- | | [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) | `3.5.0` | `3.6.1` | | [actions/upload-artifact](https://github.com/actions/upload-artifact) | `4.3.4` | `4.3.5` | | [oasdiff/oasdiff-action](https://github.com/oasdiff/oasdiff-action) | `205ce7e2c5ae1511e720cbd307cae79fd7d4a909` | `a2ff6682b27d175162a74c09ace8771bd3d512f8` | | [ossf/scorecard-action](https://github.com/ossf/scorecard-action) | `2.3.3` | `2.4.0` | | [github/codeql-action](https://github.com/github/codeql-action) | `3.25.13` | `3.25.15` | Updates `docker/setup-buildx-action` from 3.5.0 to 3.6.1 - [Release notes](https://github.com/docker/setup-buildx-action/releases) - [Commits](https://github.com/docker/setup-buildx-action/compare/aa33708b10e362ff993539393ff100fa93ed6a27...988b5a0280414f521da01fcc63a27aeeb4b104db) Updates `actions/upload-artifact` from 4.3.4 to 4.3.5 - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/0b2256b8c012f0828dc542b3febcab082c67f72b...89ef406dd8d7e03cfd12d9e0a4a378f454709029) Updates `oasdiff/oasdiff-action` from 205ce7e2c5ae1511e720cbd307cae79fd7d4a909 to a2ff6682b27d175162a74c09ace8771bd3d512f8 - [Release notes](https://github.com/oasdiff/oasdiff-action/releases) - [Commits](https://github.com/oasdiff/oasdiff-action/compare/205ce7e2c5ae1511e720cbd307cae79fd7d4a909...a2ff6682b27d175162a74c09ace8771bd3d512f8) Updates `ossf/scorecard-action` from 2.3.3 to 2.4.0 - [Release notes](https://github.com/ossf/scorecard-action/releases) - [Changelog](https://github.com/ossf/scorecard-action/blob/main/RELEASE.md) - [Commits](https://github.com/ossf/scorecard-action/compare/dc50aa9510b46c811795eb24b2f1ba02a914e534...62b2cac7ed8198b15735ed49ab1e5cf35480ba46) Updates `github/codeql-action` from 3.25.13 to 3.25.15 - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/2d790406f505036ef40ecba973cc774a50395aac...afb54ba388a7dca6ecae48f608c4ff05ff4cc77a) --- updated-dependencies: - dependency-name: docker/setup-buildx-action dependency-type: direct:production update-type: version-update:semver-minor dependency-group: dependencies - dependency-name: actions/upload-artifact dependency-type: direct:production update-type: version-update:semver-patch dependency-group: dependencies - dependency-name: oasdiff/oasdiff-action dependency-type: direct:production dependency-group: dependencies - dependency-name: ossf/scorecard-action dependency-type: direct:production update-type: version-update:semver-minor dependency-group: dependencies - dependency-name: github/codeql-action dependency-type: direct:production update-type: version-update:semver-patch dependency-group: dependencies ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Matthias Mair --- .github/workflows/docker.yaml | 2 +- .github/workflows/qc_checks.yaml | 8 ++++---- .github/workflows/scorecard.yaml | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/docker.yaml b/.github/workflows/docker.yaml index 860e494073..cba819ce1e 100644 --- a/.github/workflows/docker.yaml +++ b/.github/workflows/docker.yaml @@ -127,7 +127,7 @@ jobs: uses: docker/setup-qemu-action@49b3bc8e6bdd4a60e6116a5414239cba5943d3cf # pin@v3.2.0 - name: Set up Docker Buildx if: github.event_name != 'pull_request' - uses: docker/setup-buildx-action@aa33708b10e362ff993539393ff100fa93ed6a27 # pin@v3.5.0 + uses: docker/setup-buildx-action@988b5a0280414f521da01fcc63a27aeeb4b104db # pin@v3.6.1 - name: Set up cosign if: github.event_name != 'pull_request' uses: sigstore/cosign-installer@59acb6260d9c0ba8f4a2f9d9b48431a222b68e20 # pin@v3.5.0 diff --git a/.github/workflows/qc_checks.yaml b/.github/workflows/qc_checks.yaml index a128af69c8..da3d43478d 100644 --- a/.github/workflows/qc_checks.yaml +++ b/.github/workflows/qc_checks.yaml @@ -159,7 +159,7 @@ jobs: - name: Export API Documentation run: invoke schema --ignore-warnings --filename src/backend/InvenTree/schema.yml - name: Upload schema - uses: actions/upload-artifact@0b2256b8c012f0828dc542b3febcab082c67f72b # pin@v4.3.4 + uses: actions/upload-artifact@89ef406dd8d7e03cfd12d9e0a4a378f454709029 # pin@v4.3.5 with: name: schema.yml path: src/backend/InvenTree/schema.yml @@ -177,7 +177,7 @@ jobs: echo "Downloaded api.yaml" - name: Running OpenAPI Spec diff action id: breaking_changes - uses: oasdiff/oasdiff-action/diff@205ce7e2c5ae1511e720cbd307cae79fd7d4a909 # pin@main + uses: oasdiff/oasdiff-action/diff@a2ff6682b27d175162a74c09ace8771bd3d512f8 # pin@main with: base: 'api.yaml' revision: 'src/backend/InvenTree/schema.yml' @@ -535,7 +535,7 @@ jobs: - name: Run Playwright tests id: tests run: cd src/frontend && npx nyc playwright test - - uses: actions/upload-artifact@0b2256b8c012f0828dc542b3febcab082c67f72b # pin@v4 + - uses: actions/upload-artifact@89ef406dd8d7e03cfd12d9e0a4a378f454709029 # pin@v4 if: ${{ !cancelled() && steps.tests.outcome == 'failure' }} with: name: playwright-report @@ -573,7 +573,7 @@ jobs: run: | cd src/backend/InvenTree/web/static zip -r frontend-build.zip web/ web/.vite - - uses: actions/upload-artifact@0b2256b8c012f0828dc542b3febcab082c67f72b # pin@v4.3.4 + - uses: actions/upload-artifact@89ef406dd8d7e03cfd12d9e0a4a378f454709029 # pin@v4.3.5 with: name: frontend-build path: src/backend/InvenTree/web/static/web diff --git a/.github/workflows/scorecard.yaml b/.github/workflows/scorecard.yaml index 86d474fe44..31c997a4c8 100644 --- a/.github/workflows/scorecard.yaml +++ b/.github/workflows/scorecard.yaml @@ -37,7 +37,7 @@ jobs: persist-credentials: false - name: "Run analysis" - uses: ossf/scorecard-action@dc50aa9510b46c811795eb24b2f1ba02a914e534 # v2.3.3 + uses: ossf/scorecard-action@62b2cac7ed8198b15735ed49ab1e5cf35480ba46 # v2.4.0 with: results_file: results.sarif results_format: sarif @@ -59,7 +59,7 @@ jobs: # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF # format to the repository Actions tab. - name: "Upload artifact" - uses: actions/upload-artifact@0b2256b8c012f0828dc542b3febcab082c67f72b # v4.3.4 + uses: actions/upload-artifact@89ef406dd8d7e03cfd12d9e0a4a378f454709029 # v4.3.5 with: name: SARIF file path: results.sarif @@ -67,6 +67,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard. - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@2d790406f505036ef40ecba973cc774a50395aac # v3.25.13 + uses: github/codeql-action/upload-sarif@afb54ba388a7dca6ecae48f608c4ff05ff4cc77a # v3.25.15 with: sarif_file: results.sarif From 556a3161e88658c49903dcb993dcdf1474732911 Mon Sep 17 00:00:00 2001 From: Oliver Date: Sat, 10 Aug 2024 09:19:32 +1000 Subject: [PATCH 03/47] [PUI] Part po table (#7844) * Implement table for part purchase orders * Add filters * Improve table * Adjust PO actions based on status * Bump API version --- .../InvenTree/InvenTree/api_version.py | 5 +- src/backend/InvenTree/order/serializers.py | 5 + src/frontend/src/pages/part/PartDetail.tsx | 3 +- .../tables/part/PartPurchaseOrdersTable.tsx | 152 ++++++++++++++++++ .../purchasing/PurchaseOrderLineItemTable.tsx | 24 ++- 5 files changed, 181 insertions(+), 8 deletions(-) create mode 100644 src/frontend/src/tables/part/PartPurchaseOrdersTable.tsx diff --git a/src/backend/InvenTree/InvenTree/api_version.py b/src/backend/InvenTree/InvenTree/api_version.py index a0f0d02acd..0f0a6a6bde 100644 --- a/src/backend/InvenTree/InvenTree/api_version.py +++ b/src/backend/InvenTree/InvenTree/api_version.py @@ -1,13 +1,16 @@ """InvenTree API version information.""" # InvenTree API version -INVENTREE_API_VERSION = 235 +INVENTREE_API_VERSION = 236 """Increment this API version number whenever there is a significant change to the API that any clients need to know about.""" INVENTREE_API_TEXT = """ +v236 - 2024-08-10 : https://github.com/inventree/InvenTree/pull/7844 + - Adds "supplier_name" to the PurchaseOrder API serializer + v235 - 2024-08-08 : https://github.com/inventree/InvenTree/pull/7837 - Adds "on_order" quantity to SalesOrderLineItem serializer - Adds "building" quantity to SalesOrderLineItem serializer diff --git a/src/backend/InvenTree/order/serializers.py b/src/backend/InvenTree/order/serializers.py index f24b009e38..f94ed0c293 100644 --- a/src/backend/InvenTree/order/serializers.py +++ b/src/backend/InvenTree/order/serializers.py @@ -231,6 +231,7 @@ class PurchaseOrderSerializer( 'supplier', 'supplier_detail', 'supplier_reference', + 'supplier_name', 'total_price', 'order_currency', ]) @@ -278,6 +279,10 @@ class PurchaseOrderSerializer( return queryset + supplier_name = serializers.CharField( + source='supplier.name', read_only=True, label=_('Supplier Name') + ) + supplier_detail = CompanyBriefSerializer( source='supplier', many=False, read_only=True ) diff --git a/src/frontend/src/pages/part/PartDetail.tsx b/src/frontend/src/pages/part/PartDetail.tsx index 821766af5f..638f156e5d 100644 --- a/src/frontend/src/pages/part/PartDetail.tsx +++ b/src/frontend/src/pages/part/PartDetail.tsx @@ -89,6 +89,7 @@ import BuildAllocatedStockTable from '../../tables/build/BuildAllocatedStockTabl import { BuildOrderTable } from '../../tables/build/BuildOrderTable'; import { AttachmentTable } from '../../tables/general/AttachmentTable'; import { PartParameterTable } from '../../tables/part/PartParameterTable'; +import PartPurchaseOrdersTable from '../../tables/part/PartPurchaseOrdersTable'; import PartTestTemplateTable from '../../tables/part/PartTestTemplateTable'; import { PartVariantTable } from '../../tables/part/PartVariantTable'; import { RelatedPartTable } from '../../tables/part/RelatedPartTable'; @@ -648,7 +649,7 @@ export default function PartDetail() { label: t`Purchase Orders`, icon: , hidden: !part.purchaseable, - content: + content: }, { name: 'sales_orders', diff --git a/src/frontend/src/tables/part/PartPurchaseOrdersTable.tsx b/src/frontend/src/tables/part/PartPurchaseOrdersTable.tsx new file mode 100644 index 0000000000..4857f75dcf --- /dev/null +++ b/src/frontend/src/tables/part/PartPurchaseOrdersTable.tsx @@ -0,0 +1,152 @@ +import { t } from '@lingui/macro'; +import { Text } from '@mantine/core'; +import { useMemo } from 'react'; + +import { ProgressBar } from '../../components/items/ProgressBar'; +import { formatCurrency } from '../../defaults/formatters'; +import { ApiEndpoints } from '../../enums/ApiEndpoints'; +import { ModelType } from '../../enums/ModelType'; +import { useTable } from '../../hooks/UseTable'; +import { apiUrl } from '../../states/ApiState'; +import { useUserState } from '../../states/UserState'; +import { TableColumn } from '../Column'; +import { DateColumn, ReferenceColumn, StatusColumn } from '../ColumnRenderers'; +import { StatusFilterOptions, TableFilter } from '../Filter'; +import { InvenTreeTable } from '../InvenTreeTable'; +import { TableHoverCard } from '../TableHoverCard'; + +export default function PartPurchaseOrdersTable({ + partId +}: { + partId: number; +}) { + const table = useTable('partpurchaseorders'); + const user = useUserState(); + + const tableColumns: TableColumn[] = useMemo(() => { + return [ + ReferenceColumn({ + accessor: 'order_detail.reference', + sortable: true, + switchable: false, + title: t`Purchase Order` + }), + StatusColumn({ + accessor: 'order_detail.status', + sortable: true, + title: t`Status`, + model: ModelType.purchaseorder + }), + { + accessor: 'order_detail.supplier_name', + title: t`Supplier`, + sortable: false, + switchable: true + }, + { + accessor: 'supplier_part_detail.SKU', + ordering: 'sku', + title: t`Supplier Part`, + sortable: true + }, + { + accessor: 'supplier_part_detail.manufacturer_part_detail.MPN', + ordering: 'mpn', + title: t`Manufacturer Part`, + sortable: true + }, + { + accessor: 'quantity', + switchable: false, + render: (record: any) => { + let supplier_part = record?.supplier_part_detail ?? {}; + let part = record?.part_detail ?? supplier_part?.part_detail ?? {}; + let extra = []; + + if (supplier_part.pack_quantity_native != 1) { + let total = record.quantity * supplier_part.pack_quantity_native; + + extra.push( + + {t`Pack Quantity`}: {supplier_part.pack_quantity} + + ); + + extra.push( + + {t`Total Quantity`}: {total} {part?.units} + + ); + } + + return ( + + } + extra={extra} + title={t`Quantity`} + /> + ); + } + }, + DateColumn({ + accessor: 'target_date', + title: t`Target Date` + }), + { + accessor: 'purchase_price', + render: (record: any) => + formatCurrency(record.purchase_price, { + currency: record.purchase_price_currency + }) + } + ]; + }, []); + + const tableFilters: TableFilter[] = useMemo(() => { + return [ + { + name: 'pending', + label: t`Pending`, + description: t`Show pending orders` + }, + { + name: 'received', + label: t`Received`, + description: t`Show received items` + }, + { + name: 'order_status', + label: t`Order Status`, + description: t`Filter by order status`, + choiceFunction: StatusFilterOptions(ModelType.purchaseorder) + } + ]; + }, []); + + return ( + <> + + + ); +} diff --git a/src/frontend/src/tables/purchasing/PurchaseOrderLineItemTable.tsx b/src/frontend/src/tables/purchasing/PurchaseOrderLineItemTable.tsx index 5d10c1484c..895a40beff 100644 --- a/src/frontend/src/tables/purchasing/PurchaseOrderLineItemTable.tsx +++ b/src/frontend/src/tables/purchasing/PurchaseOrderLineItemTable.tsx @@ -21,6 +21,7 @@ import { useDeleteApiFormModal, useEditApiFormModal } from '../../hooks/UseForm'; +import useStatusCodes from '../../hooks/UseStatusCodes'; import { useTable } from '../../hooks/UseTable'; import { apiUrl } from '../../states/ApiState'; import { useUserState } from '../../states/UserState'; @@ -273,13 +274,23 @@ export function PurchaseOrderLineItemTable({ table: table }); + const poStatus = useStatusCodes({ modelType: ModelType.purchaseorder }); + + const orderOpen: boolean = useMemo(() => { + return ( + order.status == poStatus.PENDING || + order.status == poStatus.PLACED || + order.status == poStatus.ON_HOLD + ); + }, [order, poStatus]); + const rowActions = useCallback( (record: any) => { let received = (record?.received ?? 0) >= (record?.quantity ?? 0); return [ { - hidden: received, + hidden: received || !orderOpen, title: t`Receive line item`, icon: , color: 'green', @@ -296,7 +307,7 @@ export function PurchaseOrderLineItemTable({ } }), RowDuplicateAction({ - hidden: !user.hasAddRole(UserRoles.purchase_order), + hidden: !orderOpen || !user.hasAddRole(UserRoles.purchase_order), onClick: () => { setInitialData({ ...record }); newLine.open(); @@ -311,14 +322,14 @@ export function PurchaseOrderLineItemTable({ }) ]; }, - [orderId, user] + [orderId, user, orderOpen] ); // Custom table actions const tableActions = useMemo(() => { return [