From 2de3b892a5f9c85be40cf5f9c1ff74f58c5338ac Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 26 Apr 2021 21:33:39 +0200 Subject: [PATCH 01/14] More syntax for adding serial numbers #1519 --- InvenTree/InvenTree/helpers.py | 39 ++++++++++++++++++++++++++++++---- InvenTree/InvenTree/tests.py | 8 +++++++ 2 files changed, 43 insertions(+), 4 deletions(-) diff --git a/InvenTree/InvenTree/helpers.py b/InvenTree/InvenTree/helpers.py index 6f6953ccb5..d5508b7db2 100644 --- a/InvenTree/InvenTree/helpers.py +++ b/InvenTree/InvenTree/helpers.py @@ -357,6 +357,8 @@ def extract_serial_numbers(serials, expected_quantity): - Serial numbers must be positive - Serial numbers can be split by whitespace / newline / commma chars - Serial numbers can be supplied as an inclusive range using hyphen char e.g. 10-20 + - Serial numbers can be supplied as + for getting all expecteded numbers starting from + - Serial numbers can be supplied as + for getting numbers starting from Args: expected_quantity: The number of (unique) serial numbers we expect @@ -369,6 +371,13 @@ def extract_serial_numbers(serials, expected_quantity): numbers = [] errors = [] + # helpers + def number_add(n): + if n in numbers: + errors.append(_('Duplicate serial: {n}').format(n=n)) + else: + numbers.append(n) + try: expected_quantity = int(expected_quantity) except ValueError: @@ -395,10 +404,7 @@ def extract_serial_numbers(serials, expected_quantity): if a < b: for n in range(a, b + 1): - if n in numbers: - errors.append(_('Duplicate serial: {n}').format(n=n)) - else: - numbers.append(n) + number_add(n) else: errors.append(_("Invalid group: {g}").format(g=group)) @@ -409,6 +415,31 @@ def extract_serial_numbers(serials, expected_quantity): errors.append(_("Invalid group: {g}").format(g=group)) continue + # plus signals either + # 1: 'start+': expected number of serials, starting at start + # 2: 'start+number': number of serials, starting at start + elif '+' in group: + items = group.split('+') + + # case 1, 2 + if len(items) == 2: + start = int(items[0]) + + # case 2 + if bool(items[1]): + end = start + int(items[1]) + 1 + + # case 1 + else: + end = start + expected_quantity + + for n in range(start, end): + number_add(n) + # no case + else: + errors.append(_("Invalid group: {g}").format(g=group)) + continue + else: if group in numbers: errors.append(_("Duplicate serial: {g}".format(g=group))) diff --git a/InvenTree/InvenTree/tests.py b/InvenTree/InvenTree/tests.py index 8465473901..af812fe8a3 100644 --- a/InvenTree/InvenTree/tests.py +++ b/InvenTree/InvenTree/tests.py @@ -244,6 +244,14 @@ class TestSerialNumberExtraction(TestCase): self.assertIn(3, sn) self.assertIn(13, sn) + sn = e("1+", 10) + self.assertEqual(len(sn), 10) + self.assertEqual(sn, [_ for _ in range(1, 11)]) + + sn = e("4, 1+2", 4) + self.assertEqual(len(sn), 4) + self.assertEqual(sn, ["4", 1, 2, 3]) + def test_failures(self): e = helpers.extract_serial_numbers From c5ce5c533bee53f1f5065ae9ef4a91d3fc6173ab Mon Sep 17 00:00:00 2001 From: eeintech Date: Tue, 27 Apr 2021 11:10:46 -0400 Subject: [PATCH 02/14] Add owner model to imports ignore list --- tasks.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tasks.py b/tasks.py index c4eda5e5bf..3065d97243 100644 --- a/tasks.py +++ b/tasks.py @@ -248,6 +248,7 @@ def content_excludes(): "django_q.schedule", "django_q.task", "django_q.ormq", + "users.owner", ] output = "" From f0b44450fb9f790519aee30bba37d8cb8c93512d Mon Sep 17 00:00:00 2001 From: eeintech Date: Tue, 27 Apr 2021 12:48:36 -0400 Subject: [PATCH 03/14] Updated 'Required for Build Orders' API queryset --- InvenTree/build/models.py | 18 ++++++++++++++++-- InvenTree/part/api.py | 30 ++++++++---------------------- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/InvenTree/build/models.py b/InvenTree/build/models.py index 16c0e5bb7f..c5da505f43 100644 --- a/InvenTree/build/models.py +++ b/InvenTree/build/models.py @@ -996,14 +996,28 @@ class Build(MPTTModel): @property def required_parts(self): - """ Returns a dict of parts required to build this part (BOM) """ + """ Returns a list of parts required to build this part (BOM) """ parts = [] - for item in self.part.bom_items.all().prefetch_related('sub_part'): + for item in self.bom_items: parts.append(item.sub_part) return parts + @property + def required_parts_to_complete_build(self): + """ Returns a list of parts required to complete the full build """ + parts = [] + + for bom_item in self.bom_items: + # Get remaining quantity needed + required_quantity_to_complete_build = self.remaining * bom_item.quantity + # Compare to net stock + if bom_item.sub_part.net_stock < required_quantity_to_complete_build: + parts.append(bom_item.sub_part) + + return parts + def availableStockItems(self, part, output): """ Returns stock items which are available for allocation to this build. diff --git a/InvenTree/part/api.py b/InvenTree/part/api.py index a2d7609bed..6b26365b27 100644 --- a/InvenTree/part/api.py +++ b/InvenTree/part/api.py @@ -7,7 +7,7 @@ from __future__ import unicode_literals from django_filters.rest_framework import DjangoFilterBackend from django.http import JsonResponse -from django.db.models import Q, F, Count, Prefetch, Sum +from django.db.models import Q, F, Count from django.utils.translation import ugettext_lazy as _ from rest_framework import status @@ -635,29 +635,15 @@ class PartList(generics.ListCreateAPIView): # TODO: Need to figure out a cheaper way of making this filter query if stock_to_build is not None: - # Filter only active parts - queryset = queryset.filter(active=True) - # Prefetch current active builds - build_active_queryset = Build.objects.filter(status__in=BuildStatus.ACTIVE_CODES) - build_active_prefetch = Prefetch('builds', - queryset=build_active_queryset, - to_attr='current_builds') - parts = queryset.prefetch_related(build_active_prefetch) - + # Get active builds + builds = Build.objects.filter(status__in=BuildStatus.ACTIVE_CODES) # Store parts with builds needing stock - parts_need_stock = [] + parts_needed_to_complete_builds = [] + # Filter required parts + for build in builds: + parts_needed_to_complete_builds += [part.pk for part in build.required_parts_to_complete_build] - # Find parts with active builds - # where any subpart's stock is lower than quantity being built - for part in parts: - if part.current_builds: - builds_ids = [build.id for build in part.current_builds] - total_build_quantity = build_active_queryset.filter(pk__in=builds_ids).aggregate(quantity=Sum('quantity'))['quantity'] - - if part.can_build < total_build_quantity: - parts_need_stock.append(part.pk) - - queryset = queryset.filter(pk__in=parts_need_stock) + queryset = queryset.filter(pk__in=parts_needed_to_complete_builds) # Optionally limit the maximum number of returned results # e.g. for displaying "recent part" list From fc429708f1422e751253846c312677d10fd39adf Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 27 Apr 2021 21:38:06 +0200 Subject: [PATCH 04/14] [BUG] no attribute MPN Fixes #1524 --- InvenTree/company/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/InvenTree/company/models.py b/InvenTree/company/models.py index 3ea50b1622..c743bf4434 100644 --- a/InvenTree/company/models.py +++ b/InvenTree/company/models.py @@ -675,4 +675,4 @@ class SupplierPriceBreak(common.models.PriceBreak): db_table = 'part_supplierpricebreak' def __str__(self): - return f'{self.part.MPN} - {self.price} @ {self.quantity}' + return f'{self.part.SKU} - {self.price} @ {self.quantity}' From 292944179e2310132c41afed1ffeebbb5e3f6783 Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 28 Apr 2021 13:57:39 +0200 Subject: [PATCH 05/14] fixed wrong i18n-code for polish --- InvenTree/InvenTree/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/InvenTree/InvenTree/settings.py b/InvenTree/InvenTree/settings.py index 4fd8efae1a..cc61748372 100644 --- a/InvenTree/InvenTree/settings.py +++ b/InvenTree/InvenTree/settings.py @@ -491,7 +491,7 @@ LANGUAGES = [ ('en', _('English')), ('fr', _('French')), ('de', _('German')), - ('pk', _('Polish')), + ('pl', _('Polish')), ('tr', _('Turkish')), ] From 660f2673ac9029a36447aa1f51b6259aadd707b1 Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 28 Apr 2021 14:26:55 +0200 Subject: [PATCH 06/14] added in language-change form --- InvenTree/InvenTree/urls.py | 2 +- .../templates/InvenTree/settings/theme.html | 33 ++++++++++++++++++- 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/InvenTree/InvenTree/urls.py b/InvenTree/InvenTree/urls.py index da7799397e..003214cd02 100644 --- a/InvenTree/InvenTree/urls.py +++ b/InvenTree/InvenTree/urls.py @@ -79,7 +79,7 @@ apipatterns = [ settings_urls = [ url(r'^user/?', SettingsView.as_view(template_name='InvenTree/settings/user.html'), name='settings-user'), - url(r'^theme/?', ColorThemeSelectView.as_view(), name='settings-theme'), + url(r'^i18n/?', include('django.conf.urls.i18n')), url(r'^global/?', SettingsView.as_view(template_name='InvenTree/settings/global.html'), name='settings-global'), url(r'^report/?', SettingsView.as_view(template_name='InvenTree/settings/report.html'), name='settings-report'), diff --git a/InvenTree/templates/InvenTree/settings/theme.html b/InvenTree/templates/InvenTree/settings/theme.html index d7b006d13c..f0f9872774 100644 --- a/InvenTree/templates/InvenTree/settings/theme.html +++ b/InvenTree/templates/InvenTree/settings/theme.html @@ -16,7 +16,7 @@

{% trans "Color Themes" %}

- +
{% csrf_token %} @@ -33,4 +33,35 @@ {% endif %} + +
+
+

{% trans "Language" %}

+
+
+ + + +
+ {% csrf_token %} + +
+ +
+
+ +
+ +
+ + {% endblock %} From c5640fe6c6e439899c5594f55c4739bf946f2170 Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 28 Apr 2021 14:37:11 +0200 Subject: [PATCH 07/14] renamed theme to appearance --- InvenTree/InvenTree/urls.py | 3 ++- InvenTree/InvenTree/views.py | 8 ++++---- .../InvenTree/settings/{theme.html => appearance.html} | 3 +-- InvenTree/templates/InvenTree/settings/tabs.html | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) rename InvenTree/templates/InvenTree/settings/{theme.html => appearance.html} (94%) diff --git a/InvenTree/InvenTree/urls.py b/InvenTree/InvenTree/urls.py index 003214cd02..c57e82addc 100644 --- a/InvenTree/InvenTree/urls.py +++ b/InvenTree/InvenTree/urls.py @@ -39,7 +39,7 @@ from rest_framework.documentation import include_docs_urls from .views import IndexView, SearchView, DatabaseStatsView from .views import SettingsView, EditUserView, SetPasswordView -from .views import ColorThemeSelectView, SettingCategorySelectView +from .views import AppearanceSelectView, SettingCategorySelectView from .views import DynamicJsView from common.views import SettingEdit @@ -79,6 +79,7 @@ apipatterns = [ settings_urls = [ url(r'^user/?', SettingsView.as_view(template_name='InvenTree/settings/user.html'), name='settings-user'), + url(r'^appearance/?', AppearanceSelectView.as_view(), name='settings-appearance'), url(r'^i18n/?', include('django.conf.urls.i18n')), url(r'^global/?', SettingsView.as_view(template_name='InvenTree/settings/global.html'), name='settings-global'), diff --git a/InvenTree/InvenTree/views.py b/InvenTree/InvenTree/views.py index 5a080d7cdc..def4b34781 100644 --- a/InvenTree/InvenTree/views.py +++ b/InvenTree/InvenTree/views.py @@ -769,12 +769,12 @@ class SettingsView(TemplateView): return ctx -class ColorThemeSelectView(FormView): +class AppearanceSelectView(FormView): """ View for selecting a color theme """ form_class = ColorThemeSelectForm - success_url = reverse_lazy('settings-theme') - template_name = "InvenTree/settings/theme.html" + success_url = reverse_lazy('settings-appearance') + template_name = "InvenTree/settings/appearance.html" def get_user_theme(self): """ Get current user color theme """ @@ -788,7 +788,7 @@ class ColorThemeSelectView(FormView): def get_initial(self): """ Select current user color theme as initial choice """ - initial = super(ColorThemeSelectView, self).get_initial() + initial = super(AppearanceSelectView, self).get_initial() user_theme = self.get_user_theme() if user_theme: diff --git a/InvenTree/templates/InvenTree/settings/theme.html b/InvenTree/templates/InvenTree/settings/appearance.html similarity index 94% rename from InvenTree/templates/InvenTree/settings/theme.html rename to InvenTree/templates/InvenTree/settings/appearance.html index f0f9872774..e02af976ec 100644 --- a/InvenTree/templates/InvenTree/settings/theme.html +++ b/InvenTree/templates/InvenTree/settings/appearance.html @@ -18,7 +18,7 @@ -
+ {% csrf_token %} {% load crispy_forms_tags %} {% crispy form %} @@ -44,7 +44,6 @@
{% csrf_token %} -