diff --git a/InvenTree/InvenTree/api_version.py b/InvenTree/InvenTree/api_version.py
index c89fb1b799..88ffd0c4a4 100644
--- a/InvenTree/InvenTree/api_version.py
+++ b/InvenTree/InvenTree/api_version.py
@@ -2,17 +2,23 @@
# InvenTree API version
-INVENTREE_API_VERSION = 120
+INVENTREE_API_VERSION = 121
"""
Increment this API version number whenever there is a significant change to the API that any clients need to know about
+v121 -> 2023-06-14 : https://github.com/inventree/InvenTree/pull/4808
+ - Adds "ProjectCode" link to Build model
+
v120 -> 2023-06-07 : https://github.com/inventree/InvenTree/pull/4855
- Major overhaul of the build order API
- Adds new BuildLine model
+v120 -> 2023-06-12 : https://github.com/inventree/InvenTree/pull/4804
+ - Adds 'project_code' field to build order API endpoints
+
v119 -> 2023-06-01 : https://github.com/inventree/InvenTree/pull/4898
- - Add Metadata to: Part test templates, Part parameters, Part category parameter templates, BOM item substitute, Part relateds, Stock item test result
+ - Add Metadata to: Part test templates, Part parameters, Part category parameter templates, BOM item substitute, Related Parts, Stock item test result
v118 -> 2023-06-01 : https://github.com/inventree/InvenTree/pull/4935
- Adds extra fields for the PartParameterTemplate model
@@ -30,6 +36,7 @@ v115 - > 2023-05-18 : https://github.com/inventree/InvenTree/pull/4846
v114 -> 2023-05-16 : https://github.com/inventree/InvenTree/pull/4825
- Adds "delivery_date" to shipments
+>>>>>>> inventree/master
v113 -> 2023-05-13 : https://github.com/inventree/InvenTree/pull/4800
- Adds API endpoints for scrapping a build output
diff --git a/InvenTree/InvenTree/middleware.py b/InvenTree/InvenTree/middleware.py
index 540748a4e8..b31f6c96f0 100644
--- a/InvenTree/InvenTree/middleware.py
+++ b/InvenTree/InvenTree/middleware.py
@@ -14,7 +14,6 @@ from allauth_2fa.middleware import (AllauthTwoFactorMiddleware,
from error_report.middleware import ExceptionProcessor
from rest_framework.authtoken.models import Token
-from common.models import InvenTreeSetting
from InvenTree.urls import frontendpatterns
logger = logging.getLogger("inventree")
@@ -123,6 +122,9 @@ class Check2FAMiddleware(BaseRequire2FAMiddleware):
"""Check if user is required to have MFA enabled."""
def require_2fa(self, request):
"""Use setting to check if MFA should be enforced for frontend page."""
+
+ from common.models import InvenTreeSetting
+
try:
if url_matcher.resolve(request.path[1:]):
return InvenTreeSetting.get_setting('LOGIN_ENFORCE_MFA')
diff --git a/InvenTree/InvenTree/serializers.py b/InvenTree/InvenTree/serializers.py
index b59b2ddf04..8260fb05ef 100644
--- a/InvenTree/InvenTree/serializers.py
+++ b/InvenTree/InvenTree/serializers.py
@@ -21,7 +21,7 @@ from rest_framework.serializers import DecimalField
from rest_framework.utils import model_meta
from taggit.serializers import TaggitSerializer
-from common.models import InvenTreeSetting
+import common.models as common_models
from common.settings import currency_code_default, currency_code_mappings
from InvenTree.fields import InvenTreeRestURLField, InvenTreeURLField
from InvenTree.helpers_model import download_image_from_url
@@ -724,7 +724,7 @@ class RemoteImageMixin(metaclass=serializers.SerializerMetaclass):
if not url:
return
- if not InvenTreeSetting.get_setting('INVENTREE_DOWNLOAD_FROM_URL'):
+ if not common_models.InvenTreeSetting.get_setting('INVENTREE_DOWNLOAD_FROM_URL'):
raise ValidationError(_("Downloading images from remote URL is not enabled"))
try:
diff --git a/InvenTree/InvenTree/unit_test.py b/InvenTree/InvenTree/unit_test.py
index 448ca90427..d609dc88f8 100644
--- a/InvenTree/InvenTree/unit_test.py
+++ b/InvenTree/InvenTree/unit_test.py
@@ -233,6 +233,11 @@ class ExchangeRateMixin:
Rate.objects.bulk_create(items)
+class InvenTreeTestCase(ExchangeRateMixin, UserMixin, TestCase):
+ """Testcase with user setup buildin."""
+ pass
+
+
class InvenTreeAPITestCase(ExchangeRateMixin, UserMixin, APITestCase):
"""Base class for running InvenTree API tests."""
@@ -408,8 +413,3 @@ class InvenTreeAPITestCase(ExchangeRateMixin, UserMixin, APITestCase):
data.append(entry)
return data
-
-
-class InvenTreeTestCase(ExchangeRateMixin, UserMixin, TestCase):
- """Testcase with user setup buildin."""
- pass
diff --git a/InvenTree/InvenTree/views.py b/InvenTree/InvenTree/views.py
index d4e364fcf4..9cb04e09d1 100644
--- a/InvenTree/InvenTree/views.py
+++ b/InvenTree/InvenTree/views.py
@@ -31,8 +31,8 @@ from allauth_2fa.views import TwoFactorRemove
from djmoney.contrib.exchange.models import ExchangeBackend, Rate
from user_sessions.views import SessionDeleteOtherView, SessionDeleteView
-from common.models import ColorTheme, InvenTreeSetting
-from common.settings import currency_code_default, currency_codes
+import common.models as common_models
+import common.settings as common_settings
from part.models import PartCategory
from users.models import RuleSet, check_user_role
@@ -514,10 +514,10 @@ class SettingsView(TemplateView):
"""Add data for template."""
ctx = super().get_context_data(**kwargs).copy()
- ctx['settings'] = InvenTreeSetting.objects.all().order_by('key')
+ ctx['settings'] = common_models.InvenTreeSetting.objects.all().order_by('key')
- ctx["base_currency"] = currency_code_default()
- ctx["currencies"] = currency_codes
+ ctx["base_currency"] = common_settings.currency_code_default()
+ ctx["currencies"] = common_settings.currency_codes
ctx["rates"] = Rate.objects.filter(backend="InvenTreeExchange")
@@ -622,8 +622,8 @@ class AppearanceSelectView(RedirectView):
def get_user_theme(self):
"""Get current user color theme."""
try:
- user_theme = ColorTheme.objects.filter(user=self.request.user).get()
- except ColorTheme.DoesNotExist:
+ user_theme = common_models.ColorTheme.objects.filter(user=self.request.user).get()
+ except common_models.ColorTheme.DoesNotExist:
user_theme = None
return user_theme
@@ -637,7 +637,7 @@ class AppearanceSelectView(RedirectView):
# Create theme entry if user did not select one yet
if not user_theme:
- user_theme = ColorTheme()
+ user_theme = common_models.ColorTheme()
user_theme.user = request.user
user_theme.name = theme
diff --git a/InvenTree/build/api.py b/InvenTree/build/api.py
index c973884c9d..01c55f5e00 100644
--- a/InvenTree/build/api.py
+++ b/InvenTree/build/api.py
@@ -16,6 +16,7 @@ from InvenTree.helpers import str2bool, isNull, DownloadFile
from InvenTree.status_codes import BuildStatus, BuildStatusGroups
from InvenTree.mixins import CreateAPI, RetrieveUpdateDestroyAPI, ListCreateAPI
+import common.models
import build.admin
import build.serializers
from build.models import Build, BuildLine, BuildItem, BuildOrderAttachment
@@ -89,6 +90,21 @@ class BuildFilter(rest_filters.FilterSet):
lookup_expr="iexact"
)
+ project_code = rest_filters.ModelChoiceFilter(
+ queryset=common.models.ProjectCode.objects.all(),
+ field_name='project_code'
+ )
+
+ has_project_code = rest_filters.BooleanFilter(label='has_project_code', method='filter_has_project_code')
+
+ def filter_has_project_code(self, queryset, name, value):
+ """Filter by whether or not the order has a project code"""
+
+ if str2bool(value):
+ return queryset.exclude(project_code=None)
+ else:
+ return queryset.filter(project_code=None)
+
class BuildList(APIDownloadMixin, ListCreateAPI):
"""API endpoint for accessing a list of Build objects.
@@ -114,11 +130,13 @@ class BuildList(APIDownloadMixin, ListCreateAPI):
'completed',
'issued_by',
'responsible',
+ 'project_code',
'priority',
]
ordering_field_aliases = {
'reference': ['reference_int', 'reference'],
+ 'project_code': ['project_code__code'],
}
ordering = '-reference'
@@ -129,6 +147,7 @@ class BuildList(APIDownloadMixin, ListCreateAPI):
'part__name',
'part__IPN',
'part__description',
+ 'project_code__code',
'priority',
]
diff --git a/InvenTree/build/migrations/0048_build_project_code.py b/InvenTree/build/migrations/0048_build_project_code.py
new file mode 100644
index 0000000000..fcc7153bc7
--- /dev/null
+++ b/InvenTree/build/migrations/0048_build_project_code.py
@@ -0,0 +1,20 @@
+# Generated by Django 3.2.19 on 2023-05-14 09:22
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('common', '0019_projectcode_metadata'),
+ ('build', '0047_auto_20230606_1058'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='build',
+ name='project_code',
+ field=models.ForeignKey(blank=True, help_text='Project code for this build order', null=True, on_delete=django.db.models.deletion.SET_NULL, to='common.projectcode', verbose_name='Project Code'),
+ ),
+ ]
diff --git a/InvenTree/build/models.py b/InvenTree/build/models.py
index b382e65bda..b4ee51f80e 100644
--- a/InvenTree/build/models.py
+++ b/InvenTree/build/models.py
@@ -32,9 +32,10 @@ import InvenTree.models
import InvenTree.ready
import InvenTree.tasks
+import common.models
+from common.notifications import trigger_notification
from plugin.events import trigger_event
-import common.notifications
import part.models
import stock.models
import users.models
@@ -301,6 +302,14 @@ class Build(MPTTModel, InvenTree.models.InvenTreeBarcodeMixin, InvenTree.models.
help_text=_('Priority of this build order')
)
+ project_code = models.ForeignKey(
+ common.models.ProjectCode,
+ on_delete=models.SET_NULL,
+ blank=True, null=True,
+ verbose_name=_('Project Code'),
+ help_text=_('Project code for this build order'),
+ )
+
def sub_builds(self, cascade=True):
"""Return all Build Order objects under this one."""
if cascade:
@@ -547,7 +556,7 @@ class Build(MPTTModel, InvenTree.models.InvenTreeBarcodeMixin, InvenTree.models.
}
}
- common.notifications.trigger_notification(
+ trigger_notification(
build,
'build.completed',
targets=targets,
diff --git a/InvenTree/build/serializers.py b/InvenTree/build/serializers.py
index 1bc881d629..4ea3a1bb18 100644
--- a/InvenTree/build/serializers.py
+++ b/InvenTree/build/serializers.py
@@ -23,6 +23,7 @@ from InvenTree.status_codes import StockStatus
from stock.models import generate_batch_code, StockItem, StockLocation
from stock.serializers import StockItemSerializerBrief, LocationSerializer
+from common.serializers import ProjectCodeSerializer
import part.filters
from part.serializers import BomItemSerializer, PartSerializer, PartBriefSerializer
from users.serializers import OwnerSerializer
@@ -49,6 +50,8 @@ class BuildSerializer(InvenTreeModelSerializer):
'parent',
'part',
'part_detail',
+ 'project_code',
+ 'project_code_detail',
'overdue',
'reference',
'sales_order',
@@ -90,6 +93,8 @@ class BuildSerializer(InvenTreeModelSerializer):
barcode_hash = serializers.CharField(read_only=True)
+ project_code_detail = ProjectCodeSerializer(source='project_code', many=False, read_only=True)
+
@staticmethod
def annotate_queryset(queryset):
"""Add custom annotations to the BuildSerializer queryset, performing database queries as efficiently as possible.
diff --git a/InvenTree/build/templates/build/build_base.html b/InvenTree/build/templates/build/build_base.html
index fc692408c6..2f1ea421b6 100644
--- a/InvenTree/build/templates/build/build_base.html
+++ b/InvenTree/build/templates/build/build_base.html
@@ -108,6 +108,7 @@ src="{% static 'img/blank_image.png' %}"
{% trans "Build Description" %} |
{{ build.title }} |
+ {% include "project_code_data.html" with instance=build %}
{% include "barcode_data.html" with instance=build %}
diff --git a/InvenTree/build/test_migrations.py b/InvenTree/build/test_migrations.py
index af094db7f2..440074d4d7 100644
--- a/InvenTree/build/test_migrations.py
+++ b/InvenTree/build/test_migrations.py
@@ -19,22 +19,15 @@ class TestForwardMigrations(MigratorTestCase):
name='Widget',
description='Buildable Part',
active=True,
+ level=0, lft=0, rght=0, tree_id=0,
)
- with self.assertRaises(TypeError):
- # Cannot set the 'assembly' field as it hasn't been added to the db schema
- Part.objects.create(
- name='Blorb',
- description='ABCDE',
- assembly=True
- )
-
Build = self.old_state.apps.get_model('build', 'build')
Build.objects.create(
part=buildable_part,
title='A build of some stuff',
- quantity=50
+ quantity=50,
)
def test_items_exist(self):
@@ -67,7 +60,8 @@ class TestReferenceMigration(MigratorTestCase):
part = Part.objects.create(
name='Part',
- description='A test part'
+ description='A test part',
+ level=0, lft=0, rght=0, tree_id=0,
)
Build = self.old_state.apps.get_model('build', 'build')
diff --git a/InvenTree/common/api.py b/InvenTree/common/api.py
index 030630778f..abde58501f 100644
--- a/InvenTree/common/api.py
+++ b/InvenTree/common/api.py
@@ -525,7 +525,7 @@ settings_api_urls = [
path(r'/', NotificationUserSettingsDetail.as_view(), name='api-notification-setting-detail'),
# Notification Settings List
- re_path(r'^.*$', NotificationUserSettingsList.as_view(), name='api-notifcation-setting-list'),
+ re_path(r'^.*$', NotificationUserSettingsList.as_view(), name='api-notification-setting-list'),
])),
# Global settings
diff --git a/InvenTree/common/notifications.py b/InvenTree/common/notifications.py
index 07148e6267..6abf8999d8 100644
--- a/InvenTree/common/notifications.py
+++ b/InvenTree/common/notifications.py
@@ -8,8 +8,8 @@ from django.contrib.auth import get_user_model
from django.contrib.auth.models import Group
from django.utils.translation import gettext_lazy as _
+import common.models
import InvenTree.helpers
-from common.models import NotificationEntry, NotificationMessage
from InvenTree.ready import isImportingData
from plugin import registry
from plugin.models import NotificationUserSetting, PluginConfig
@@ -247,7 +247,7 @@ class UIMessageNotification(SingleNotificationMethod):
def send(self, target):
"""Send a UI notification to a user."""
- NotificationMessage.objects.create(
+ common.models.NotificationMessage.objects.create(
target_object=self.obj,
source_object=target,
user=target,
@@ -338,7 +338,7 @@ def trigger_notification(obj, category=None, obj_ref='pk', **kwargs):
# Check if we have notified recently...
delta = timedelta(days=1)
- if NotificationEntry.check_recent(category, obj_ref_value, delta):
+ if common.models.NotificationEntry.check_recent(category, obj_ref_value, delta):
logger.info(f"Notification '{category}' has recently been sent for '{str(obj)}' - SKIPPING")
return
@@ -398,7 +398,7 @@ def trigger_notification(obj, category=None, obj_ref='pk', **kwargs):
logger.error(error)
# Set delivery flag
- NotificationEntry.notify(category, obj_ref_value)
+ common.models.NotificationEntry.notify(category, obj_ref_value)
else:
logger.info(f"No possible users for notification '{category}'")
diff --git a/InvenTree/common/serializers.py b/InvenTree/common/serializers.py
index 8dc911f506..10616c251a 100644
--- a/InvenTree/common/serializers.py
+++ b/InvenTree/common/serializers.py
@@ -6,9 +6,7 @@ from django.urls import reverse
from flags.state import flag_state
from rest_framework import serializers
-from common.models import (InvenTreeSetting, InvenTreeUserSetting,
- NewsFeedEntry, NotesImage, NotificationMessage,
- ProjectCode)
+import common.models as common_models
from InvenTree.helpers import get_objectreference
from InvenTree.helpers_model import construct_absolute_url
from InvenTree.serializers import (InvenTreeImageSerializerField,
@@ -64,7 +62,7 @@ class GlobalSettingsSerializer(SettingsSerializer):
class Meta:
"""Meta options for GlobalSettingsSerializer."""
- model = InvenTreeSetting
+ model = common_models.InvenTreeSetting
fields = [
'pk',
'key',
@@ -85,7 +83,7 @@ class UserSettingsSerializer(SettingsSerializer):
class Meta:
"""Meta options for UserSettingsSerializer."""
- model = InvenTreeUserSetting
+ model = common_models.InvenTreeUserSetting
fields = [
'pk',
'key',
@@ -148,7 +146,7 @@ class NotificationMessageSerializer(InvenTreeModelSerializer):
class Meta:
"""Meta options for NotificationMessageSerializer."""
- model = NotificationMessage
+ model = common_models.NotificationMessage
fields = [
'pk',
'target',
@@ -209,7 +207,7 @@ class NewsFeedEntrySerializer(InvenTreeModelSerializer):
class Meta:
"""Meta options for NewsFeedEntrySerializer."""
- model = NewsFeedEntry
+ model = common_models.NewsFeedEntry
fields = [
'pk',
'feed_id',
@@ -243,7 +241,7 @@ class NotesImageSerializer(InvenTreeModelSerializer):
class Meta:
"""Meta options for NotesImageSerializer."""
- model = NotesImage
+ model = common_models.NotesImage
fields = [
'pk',
'image',
@@ -265,7 +263,7 @@ class ProjectCodeSerializer(InvenTreeModelSerializer):
class Meta:
"""Meta options for ProjectCodeSerializer."""
- model = ProjectCode
+ model = common_models.ProjectCode
fields = [
'pk',
'code',
diff --git a/InvenTree/common/tests.py b/InvenTree/common/tests.py
index f854b63514..4402a16933 100644
--- a/InvenTree/common/tests.py
+++ b/InvenTree/common/tests.py
@@ -226,7 +226,7 @@ class SettingsTest(InvenTreeTestCase):
cache.clear()
- # Generate a number of new usesr
+ # Generate a number of new users
for idx in range(5):
get_user_model().objects.create(
username=f"User_{idx}",
@@ -417,7 +417,7 @@ class UserSettingsApiTest(InvenTreeAPITestCase):
self.assertTrue(str2bool(response.data['value']))
- # Assign some falsey values
+ # Assign some false(ish) values
for v in ['false', False, '0', 'n', 'FalSe']:
self.patch(
url,
@@ -535,7 +535,7 @@ class NotificationUserSettingsApiTest(InvenTreeAPITestCase):
def test_api_list(self):
"""Test list URL."""
- url = reverse('api-notifcation-setting-list')
+ url = reverse('api-notification-setting-list')
self.get(url, expected_code=200)
@@ -583,7 +583,7 @@ class PluginSettingsApiTest(PluginMixin, InvenTreeAPITestCase):
# Failure mode tests
- # Non - exsistant plugin
+ # Non-existent plugin
url = reverse('api-plugin-setting-detail', kwargs={'plugin': 'doesnotexist', 'key': 'doesnotmatter'})
response = self.get(url, expected_code=404)
self.assertIn("Plugin 'doesnotexist' not installed", str(response.data))
@@ -729,7 +729,7 @@ class WebhookMessageTests(TestCase):
class NotificationTest(InvenTreeAPITestCase):
- """Tests for NotificationEntriy."""
+ """Tests for NotificationEntry."""
fixtures = [
'users',
@@ -785,7 +785,7 @@ class NotificationTest(InvenTreeAPITestCase):
messages = NotificationMessage.objects.all()
# As there are three staff users (including the 'test' user) we expect 30 notifications
- # However, one user is marked as i nactive
+ # However, one user is marked as inactive
self.assertEqual(messages.count(), 20)
# Only 10 messages related to *this* user
diff --git a/InvenTree/company/test_api.py b/InvenTree/company/test_api.py
index 4f98686111..4fa3ba595a 100644
--- a/InvenTree/company/test_api.py
+++ b/InvenTree/company/test_api.py
@@ -468,7 +468,7 @@ class SupplierPartTest(InvenTreeAPITestCase):
self.assertIsNone(sp.availability_updated)
self.assertEqual(sp.available, 0)
- # Now, *update* the availabile quantity via the API
+ # Now, *update* the available quantity via the API
self.patch(
reverse('api-supplier-part-detail', kwargs={'pk': sp.pk}),
{
diff --git a/InvenTree/company/test_migrations.py b/InvenTree/company/test_migrations.py
index fb38e9e695..1d9a9f88cf 100644
--- a/InvenTree/company/test_migrations.py
+++ b/InvenTree/company/test_migrations.py
@@ -48,7 +48,8 @@ class TestManufacturerField(MigratorTestCase):
# Create an initial part
part = Part.objects.create(
name='Screw',
- description='A single screw'
+ description='A single screw',
+ level=0, tree_id=0, lft=0, rght=0
)
# Create a company to act as the supplier
diff --git a/InvenTree/order/api.py b/InvenTree/order/api.py
index 0e6071ebab..b4a60a95ae 100644
--- a/InvenTree/order/api.py
+++ b/InvenTree/order/api.py
@@ -13,7 +13,7 @@ from rest_framework import status
from rest_framework.exceptions import ValidationError
from rest_framework.response import Response
-from common.models import InvenTreeSetting, ProjectCode
+import common.models as common_models
from common.settings import settings
from company.models import SupplierPart
from generic.states import StatusView
@@ -139,7 +139,7 @@ class OrderFilter(rest_filters.FilterSet):
return queryset.exclude(status__in=self.Meta.model.get_status_class().OPEN)
project_code = rest_filters.ModelChoiceFilter(
- queryset=ProjectCode.objects.all(),
+ queryset=common_models.ProjectCode.objects.all(),
field_name='project_code'
)
@@ -1457,7 +1457,7 @@ class OrderCalendarExport(ICalFeed):
else:
ordertype_title = _('Unknown')
- return f'{InvenTreeSetting.get_setting("INVENTREE_COMPANY_NAME")} {ordertype_title}'
+ return f'{common_models.InvenTreeSetting.get_setting("INVENTREE_COMPANY_NAME")} {ordertype_title}'
def product_id(self, obj):
"""Return calendar product id."""
diff --git a/InvenTree/order/models.py b/InvenTree/order/models.py
index f9992b4d83..93f431d43f 100644
--- a/InvenTree/order/models.py
+++ b/InvenTree/order/models.py
@@ -22,6 +22,7 @@ from djmoney.contrib.exchange.models import convert_money
from djmoney.money import Money
from mptt.models import TreeForeignKey
+import common.models as common_models
import InvenTree.helpers
import InvenTree.ready
import InvenTree.tasks
@@ -29,7 +30,6 @@ import InvenTree.validators
import order.validators
import stock.models
import users.models as UserModels
-from common.models import ProjectCode
from common.notifications import InvenTreeNotificationBodies
from common.settings import currency_code_default
from company.models import Company, Contact, SupplierPart
@@ -231,7 +231,11 @@ class Order(InvenTreeBarcodeMixin, InvenTreeNotesMixin, MetadataMixin, Reference
description = models.CharField(max_length=250, blank=True, verbose_name=_('Description'), help_text=_('Order description (optional)'))
- project_code = models.ForeignKey(ProjectCode, on_delete=models.SET_NULL, blank=True, null=True, verbose_name=_('Project Code'), help_text=_('Select project code for this order'))
+ project_code = models.ForeignKey(
+ common_models.ProjectCode, on_delete=models.SET_NULL,
+ blank=True, null=True,
+ verbose_name=_('Project Code'), help_text=_('Select project code for this order')
+ )
link = InvenTreeURLField(blank=True, verbose_name=_('Link'), help_text=_('Link to external page'))
diff --git a/InvenTree/order/serializers.py b/InvenTree/order/serializers.py
index d0dee4740b..116f0fb7b0 100644
--- a/InvenTree/order/serializers.py
+++ b/InvenTree/order/serializers.py
@@ -13,11 +13,11 @@ from rest_framework import serializers
from rest_framework.serializers import ValidationError
from sql_util.utils import SubqueryCount
-import common.serializers
import order.models
import part.filters
import stock.models
import stock.serializers
+from common.serializers import ProjectCodeSerializer
from company.serializers import (CompanyBriefSerializer, ContactSerializer,
SupplierPartSerializer)
from InvenTree.helpers import (extract_serial_numbers, hash_barcode, normalize,
@@ -73,7 +73,7 @@ class AbstractOrderSerializer(serializers.Serializer):
responsible_detail = OwnerSerializer(source='responsible', read_only=True, many=False)
# Detail for project code field
- project_code_detail = common.serializers.ProjectCodeSerializer(source='project_code', read_only=True, many=False)
+ project_code_detail = ProjectCodeSerializer(source='project_code', read_only=True, many=False)
# Boolean field indicating if this order is overdue (Note: must be annotated)
overdue = serializers.BooleanField(required=False, read_only=True)
diff --git a/InvenTree/plugin/test_api.py b/InvenTree/plugin/test_api.py
index 817995de4d..bb4d5b3290 100644
--- a/InvenTree/plugin/test_api.py
+++ b/InvenTree/plugin/test_api.py
@@ -54,7 +54,7 @@ class PluginDetailAPITest(PluginMixin, InvenTreeAPITestCase):
).data
self.assertEqual(data['success'], True)
- # valid - github url and packagename
+ # valid - github url and package name
data = self.post(
url,
{
diff --git a/InvenTree/templates/js/translated/build.js b/InvenTree/templates/js/translated/build.js
index 9b40bcd87b..3d70cb9347 100644
--- a/InvenTree/templates/js/translated/build.js
+++ b/InvenTree/templates/js/translated/build.js
@@ -14,6 +14,7 @@
FullCalendar,
getFormFieldValue,
getTableData,
+ global_settings,
handleFormErrors,
handleFormSuccess,
imageHoverIcon,
@@ -64,7 +65,7 @@
function buildFormFields() {
- return {
+ let fields = {
reference: {
icon: 'fa-hashtag',
},
@@ -76,6 +77,9 @@ function buildFormFields() {
},
title: {},
quantity: {},
+ project_code: {
+ icon: 'fa-list',
+ },
priority: {},
parent: {
filters: {
@@ -111,6 +115,12 @@ function buildFormFields() {
icon: 'fa-users',
},
};
+
+ if (!global_settings.PROJECT_CODES_ENABLED) {
+ delete fields.project_code;
+ }
+
+ return fields;
}
/*
@@ -2020,6 +2030,18 @@ function loadBuildTable(table, options) {
title: '{% trans "Description" %}',
switchable: true,
},
+ {
+ field: 'project_code',
+ title: '{% trans "Project Code" %}',
+ sortable: true,
+ switchable: global_settings.PROJECT_CODES_ENABLED,
+ visible: global_settings.PROJECT_CODES_ENABLED,
+ formatter: function(value, row) {
+ if (row.project_code_detail) {
+ return `${row.project_code_detail.code}`;
+ }
+ }
+ },
{
field: 'priority',
title: '{% trans "Priority" %}',
diff --git a/InvenTree/templates/js/translated/table_filters.js b/InvenTree/templates/js/translated/table_filters.js
index c24b90c15c..6b12133a09 100644
--- a/InvenTree/templates/js/translated/table_filters.js
+++ b/InvenTree/templates/js/translated/table_filters.js
@@ -440,7 +440,7 @@ function getPluginTableFilters() {
// Return a dictionary of filters for the "build" table
function getBuildTableFilters() {
- return {
+ let filters = {
status: {
title: '{% trans "Build status" %}',
options: buildCodes,
@@ -477,6 +477,13 @@ function getBuildTableFilters() {
},
},
};
+
+ if (global_settings.PROJECT_CODES_ENABLED) {
+ filters['has_project_code'] = constructHasProjectCodeFilter();
+ filters['project_code'] = constructProjectCodeFilter();
+ }
+
+ return filters;
}