mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
Merge branch 'docupdates' of https://github.com/matmair/InvenTree into docupdates
This commit is contained in:
commit
6c25872f81
@ -1,3 +1,5 @@
|
||||
"""Admin functionality for the BuildOrder app"""
|
||||
|
||||
from django.contrib import admin
|
||||
|
||||
from import_export.admin import ImportExportModelAdmin
|
||||
@ -39,6 +41,7 @@ class BuildResource(ModelResource):
|
||||
notes = Field(attribute='notes')
|
||||
|
||||
class Meta:
|
||||
"""Metaclass options"""
|
||||
models = Build
|
||||
skip_unchanged = True
|
||||
report_skipped = False
|
||||
@ -50,6 +53,7 @@ class BuildResource(ModelResource):
|
||||
|
||||
|
||||
class BuildAdmin(ImportExportModelAdmin):
|
||||
"""Class for managing the Build model via the admin interface"""
|
||||
|
||||
exclude = [
|
||||
'reference_int',
|
||||
@ -81,6 +85,7 @@ class BuildAdmin(ImportExportModelAdmin):
|
||||
|
||||
|
||||
class BuildItemAdmin(admin.ModelAdmin):
|
||||
"""Class for managing the BuildItem model via the admin interface"""
|
||||
|
||||
list_display = (
|
||||
'build',
|
||||
|
@ -27,7 +27,7 @@ class BuildFilter(rest_filters.FilterSet):
|
||||
active = rest_filters.BooleanFilter(label='Build is active', method='filter_active')
|
||||
|
||||
def filter_active(self, queryset, name, value):
|
||||
|
||||
"""Filter the queryset to either include or exclude orders which are active"""
|
||||
if str2bool(value):
|
||||
queryset = queryset.filter(status__in=BuildStatus.ACTIVE_CODES)
|
||||
else:
|
||||
@ -38,7 +38,7 @@ class BuildFilter(rest_filters.FilterSet):
|
||||
overdue = rest_filters.BooleanFilter(label='Build is overdue', method='filter_overdue')
|
||||
|
||||
def filter_overdue(self, queryset, name, value):
|
||||
|
||||
"""Filter the queryset to either include or exclude orders which are overdue"""
|
||||
if str2bool(value):
|
||||
queryset = queryset.filter(Build.OVERDUE_FILTER)
|
||||
else:
|
||||
@ -112,6 +112,7 @@ class BuildList(APIDownloadMixin, generics.ListCreateAPIView):
|
||||
return queryset
|
||||
|
||||
def download_queryset(self, queryset, export_format):
|
||||
"""Download the queryset data as a file"""
|
||||
dataset = build.admin.BuildResource().export(queryset=queryset)
|
||||
|
||||
filedata = dataset.export(export_format)
|
||||
@ -120,7 +121,7 @@ class BuildList(APIDownloadMixin, generics.ListCreateAPIView):
|
||||
return DownloadFile(filedata, filename)
|
||||
|
||||
def filter_queryset(self, queryset):
|
||||
|
||||
"""Custom query filtering for the BuildList endpoint"""
|
||||
queryset = super().filter_queryset(queryset)
|
||||
|
||||
params = self.request.query_params
|
||||
@ -184,7 +185,7 @@ class BuildList(APIDownloadMixin, generics.ListCreateAPIView):
|
||||
return queryset
|
||||
|
||||
def get_serializer(self, *args, **kwargs):
|
||||
|
||||
"""Add extra context information to the endpoint serializer"""
|
||||
try:
|
||||
part_detail = str2bool(self.request.GET.get('part_detail', None))
|
||||
except AttributeError:
|
||||
@ -215,7 +216,7 @@ class BuildUnallocate(generics.CreateAPIView):
|
||||
serializer_class = build.serializers.BuildUnallocationSerializer
|
||||
|
||||
def get_serializer_context(self):
|
||||
|
||||
"""Add extra context information to the endpoint serializer"""
|
||||
ctx = super().get_serializer_context()
|
||||
|
||||
try:
|
||||
@ -232,6 +233,7 @@ class BuildOrderContextMixin:
|
||||
"""Mixin class which adds build order as serializer context variable."""
|
||||
|
||||
def get_serializer_context(self):
|
||||
"""Add extra context information to the endpoint serializer"""
|
||||
ctx = super().get_serializer_context()
|
||||
|
||||
ctx['request'] = self.request
|
||||
@ -265,6 +267,7 @@ class BuildOutputDelete(BuildOrderContextMixin, generics.CreateAPIView):
|
||||
"""API endpoint for deleting multiple build outputs."""
|
||||
|
||||
def get_serializer_context(self):
|
||||
"""Add extra context information to the endpoint serializer"""
|
||||
ctx = super().get_serializer_context()
|
||||
|
||||
ctx['to_complete'] = False
|
||||
@ -338,7 +341,7 @@ class BuildItemList(generics.ListCreateAPIView):
|
||||
serializer_class = build.serializers.BuildItemSerializer
|
||||
|
||||
def get_serializer(self, *args, **kwargs):
|
||||
|
||||
"""Returns a BuildItemSerializer instance based on the request"""
|
||||
try:
|
||||
params = self.request.query_params
|
||||
|
||||
@ -361,7 +364,7 @@ class BuildItemList(generics.ListCreateAPIView):
|
||||
return query
|
||||
|
||||
def filter_queryset(self, queryset):
|
||||
|
||||
"""Customm query filtering for the BuildItem list"""
|
||||
queryset = super().filter_queryset(queryset)
|
||||
|
||||
params = self.request.query_params
|
||||
|
@ -1,5 +1,8 @@
|
||||
"""Django app for the BuildOrder module"""
|
||||
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class BuildConfig(AppConfig):
|
||||
"""BuildOrder app config class"""
|
||||
name = 'build'
|
||||
|
@ -92,10 +92,11 @@ class Build(MPTTModel, ReferenceIndexingMixin):
|
||||
|
||||
@staticmethod
|
||||
def get_api_url():
|
||||
"""Return the API URL associated with the BuildOrder model"""
|
||||
return reverse('api-build-list')
|
||||
|
||||
def api_instance_filters(self):
|
||||
|
||||
"""Returns custom API filters for the particular BuildOrder instance"""
|
||||
return {
|
||||
'parent': {
|
||||
'exclude_tree': self.pk,
|
||||
@ -115,7 +116,7 @@ class Build(MPTTModel, ReferenceIndexingMixin):
|
||||
return defaults
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
|
||||
"""Custom save method for the BuildOrder model"""
|
||||
self.rebuild_reference_field()
|
||||
|
||||
try:
|
||||
@ -126,6 +127,7 @@ class Build(MPTTModel, ReferenceIndexingMixin):
|
||||
})
|
||||
|
||||
class Meta:
|
||||
"""Metaclass options for the BuildOrder model"""
|
||||
verbose_name = _("Build Order")
|
||||
verbose_name_plural = _("Build Orders")
|
||||
|
||||
@ -170,12 +172,13 @@ class Build(MPTTModel, ReferenceIndexingMixin):
|
||||
return queryset
|
||||
|
||||
def __str__(self):
|
||||
|
||||
"""String representation of a BuildOrder"""
|
||||
prefix = getSetting("BUILDORDER_REFERENCE_PREFIX")
|
||||
|
||||
return f"{prefix}{self.reference}"
|
||||
|
||||
def get_absolute_url(self):
|
||||
"""Return the web URL associated with this BuildOrder"""
|
||||
return reverse('build-detail', kwargs={'pk': self.id})
|
||||
|
||||
reference = models.CharField(
|
||||
@ -393,9 +396,11 @@ class Build(MPTTModel, ReferenceIndexingMixin):
|
||||
|
||||
@property
|
||||
def output_count(self):
|
||||
"""Return the number of build outputs (StockItem) associated with this build order"""
|
||||
return self.build_outputs.count()
|
||||
|
||||
def has_build_outputs(self):
|
||||
"""Returns True if this build has more than zero build outputs"""
|
||||
return self.output_count > 0
|
||||
|
||||
def get_build_outputs(self, **kwargs):
|
||||
@ -436,7 +441,7 @@ class Build(MPTTModel, ReferenceIndexingMixin):
|
||||
|
||||
@property
|
||||
def complete_count(self):
|
||||
|
||||
"""Return the total quantity of completed outputs"""
|
||||
quantity = 0
|
||||
|
||||
for output in self.complete_outputs:
|
||||
@ -1049,6 +1054,7 @@ class BuildOrderAttachment(InvenTreeAttachment):
|
||||
"""Model for storing file attachments against a BuildOrder object."""
|
||||
|
||||
def getSubdir(self):
|
||||
"""Return the media file subdirectory for storing BuildOrder attachments"""
|
||||
return os.path.join('bo_files', str(self.build.id))
|
||||
|
||||
build = models.ForeignKey(Build, on_delete=models.CASCADE, related_name='attachments')
|
||||
@ -1069,20 +1075,17 @@ class BuildItem(models.Model):
|
||||
|
||||
@staticmethod
|
||||
def get_api_url():
|
||||
"""Return the API URL used to access this model"""
|
||||
return reverse('api-build-item-list')
|
||||
|
||||
def get_absolute_url(self):
|
||||
# TODO - Fix!
|
||||
return '/build/item/{pk}/'.format(pk=self.id)
|
||||
# return reverse('build-detail', kwargs={'pk': self.id})
|
||||
|
||||
class Meta:
|
||||
"""Serializer metaclass"""
|
||||
unique_together = [
|
||||
('build', 'stock_item', 'install_into'),
|
||||
]
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
|
||||
"""Custom save method for the BuildItem model"""
|
||||
self.clean()
|
||||
|
||||
super().save()
|
||||
|
@ -66,6 +66,7 @@ class BuildSerializer(ReferenceIndexingSerializerMixin, InvenTreeModelSerializer
|
||||
return queryset
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
"""Determine if extra serializer fields are required"""
|
||||
part_detail = kwargs.pop('part_detail', True)
|
||||
|
||||
super().__init__(*args, **kwargs)
|
||||
@ -74,6 +75,7 @@ class BuildSerializer(ReferenceIndexingSerializerMixin, InvenTreeModelSerializer
|
||||
self.fields.pop('part_detail')
|
||||
|
||||
class Meta:
|
||||
"""Serializer metaclass"""
|
||||
model = Build
|
||||
fields = [
|
||||
'pk',
|
||||
@ -127,7 +129,7 @@ class BuildOutputSerializer(serializers.Serializer):
|
||||
)
|
||||
|
||||
def validate_output(self, output):
|
||||
|
||||
"""Perform validation for the output (StockItem) provided to the serializer"""
|
||||
build = self.context['build']
|
||||
|
||||
# As this serializer can be used in multiple contexts, we need to work out why we are here
|
||||
@ -159,6 +161,7 @@ class BuildOutputSerializer(serializers.Serializer):
|
||||
return output
|
||||
|
||||
class Meta:
|
||||
"""Serializer metaclass"""
|
||||
fields = [
|
||||
'output',
|
||||
]
|
||||
@ -182,13 +185,15 @@ class BuildOutputCreateSerializer(serializers.Serializer):
|
||||
)
|
||||
|
||||
def get_build(self):
|
||||
"""Return the Build instance associated with this serializer"""
|
||||
return self.context["build"]
|
||||
|
||||
def get_part(self):
|
||||
"""Return the Part instance associated with the build"""
|
||||
return self.get_build().part
|
||||
|
||||
def validate_quantity(self, quantity):
|
||||
|
||||
"""Validate the provided quantity field"""
|
||||
if quantity <= 0:
|
||||
raise ValidationError(_("Quantity must be greater than zero"))
|
||||
|
||||
@ -219,7 +224,7 @@ class BuildOutputCreateSerializer(serializers.Serializer):
|
||||
)
|
||||
|
||||
def validate_serial_numbers(self, serial_numbers):
|
||||
|
||||
"""Clean the provided serial number string"""
|
||||
serial_numbers = serial_numbers.strip()
|
||||
|
||||
return serial_numbers
|
||||
@ -292,6 +297,7 @@ class BuildOutputDeleteSerializer(serializers.Serializer):
|
||||
"""DRF serializer for deleting (cancelling) one or more build outputs."""
|
||||
|
||||
class Meta:
|
||||
"""Serializer metaclass"""
|
||||
fields = [
|
||||
'outputs',
|
||||
]
|
||||
@ -302,7 +308,7 @@ class BuildOutputDeleteSerializer(serializers.Serializer):
|
||||
)
|
||||
|
||||
def validate(self, data):
|
||||
|
||||
"""Perform data validation for this serializer"""
|
||||
data = super().validate(data)
|
||||
|
||||
outputs = data.get('outputs', [])
|
||||
@ -329,6 +335,7 @@ class BuildOutputCompleteSerializer(serializers.Serializer):
|
||||
"""DRF serializer for completing one or more build outputs."""
|
||||
|
||||
class Meta:
|
||||
"""Serializer metaclass"""
|
||||
fields = [
|
||||
'outputs',
|
||||
'location',
|
||||
@ -370,7 +377,7 @@ class BuildOutputCompleteSerializer(serializers.Serializer):
|
||||
)
|
||||
|
||||
def validate(self, data):
|
||||
|
||||
"""Perform data validation for this serializer"""
|
||||
super().validate(data)
|
||||
|
||||
outputs = data.get('outputs', [])
|
||||
@ -409,15 +416,17 @@ class BuildOutputCompleteSerializer(serializers.Serializer):
|
||||
|
||||
|
||||
class BuildCancelSerializer(serializers.Serializer):
|
||||
"""DRF serializer class for cancelling an active BuildOrder"""
|
||||
|
||||
class Meta:
|
||||
"""Serializer metaclass"""
|
||||
fields = [
|
||||
'remove_allocated_stock',
|
||||
'remove_incomplete_outputs',
|
||||
]
|
||||
|
||||
def get_context_data(self):
|
||||
|
||||
"""Retrieve extra context data from this serializer"""
|
||||
build = self.context['build']
|
||||
|
||||
return {
|
||||
@ -441,7 +450,7 @@ class BuildCancelSerializer(serializers.Serializer):
|
||||
)
|
||||
|
||||
def save(self):
|
||||
|
||||
"""Cancel the specified build"""
|
||||
build = self.context['build']
|
||||
request = self.context['request']
|
||||
|
||||
@ -465,7 +474,7 @@ class BuildCompleteSerializer(serializers.Serializer):
|
||||
)
|
||||
|
||||
def validate_accept_unallocated(self, value):
|
||||
|
||||
"""Check if the 'accept_unallocated' field is required"""
|
||||
build = self.context['build']
|
||||
|
||||
if not build.are_untracked_parts_allocated() and not value:
|
||||
@ -481,7 +490,7 @@ class BuildCompleteSerializer(serializers.Serializer):
|
||||
)
|
||||
|
||||
def validate_accept_incomplete(self, value):
|
||||
|
||||
"""Check if the 'accept_incomplete' field is required"""
|
||||
build = self.context['build']
|
||||
|
||||
if build.remaining > 0 and not value:
|
||||
@ -490,7 +499,7 @@ class BuildCompleteSerializer(serializers.Serializer):
|
||||
return value
|
||||
|
||||
def validate(self, data):
|
||||
|
||||
"""Perform validation of this serializer prior to saving"""
|
||||
build = self.context['build']
|
||||
|
||||
if build.incomplete_count > 0:
|
||||
@ -502,7 +511,7 @@ class BuildCompleteSerializer(serializers.Serializer):
|
||||
return data
|
||||
|
||||
def save(self):
|
||||
|
||||
"""Complete the specified build output"""
|
||||
request = self.context['request']
|
||||
build = self.context['build']
|
||||
|
||||
@ -537,8 +546,7 @@ class BuildUnallocationSerializer(serializers.Serializer):
|
||||
)
|
||||
|
||||
def validate_output(self, stock_item):
|
||||
|
||||
# Stock item must point to the same build order!
|
||||
"""Validation for the output StockItem instance. Stock item must point to the same build order!"""
|
||||
build = self.context['build']
|
||||
|
||||
if stock_item and stock_item.build != build:
|
||||
@ -573,7 +581,7 @@ class BuildAllocationItemSerializer(serializers.Serializer):
|
||||
)
|
||||
|
||||
def validate_bom_item(self, bom_item):
|
||||
"""Check if the parts match!"""
|
||||
"""Check if the parts match"""
|
||||
build = self.context['build']
|
||||
|
||||
# BomItem should point to the same 'part' as the parent build
|
||||
@ -596,7 +604,7 @@ class BuildAllocationItemSerializer(serializers.Serializer):
|
||||
)
|
||||
|
||||
def validate_stock_item(self, stock_item):
|
||||
|
||||
"""Perform validation of the stock_item field"""
|
||||
if not stock_item.in_stock:
|
||||
raise ValidationError(_("Item must be in stock"))
|
||||
|
||||
@ -610,7 +618,7 @@ class BuildAllocationItemSerializer(serializers.Serializer):
|
||||
)
|
||||
|
||||
def validate_quantity(self, quantity):
|
||||
|
||||
"""Perform validation of the 'quantity' field"""
|
||||
if quantity <= 0:
|
||||
raise ValidationError(_("Quantity must be greater than zero"))
|
||||
|
||||
@ -625,6 +633,7 @@ class BuildAllocationItemSerializer(serializers.Serializer):
|
||||
)
|
||||
|
||||
class Meta:
|
||||
"""Serializer metaclass"""
|
||||
fields = [
|
||||
'bom_item',
|
||||
'stock_item',
|
||||
@ -633,7 +642,7 @@ class BuildAllocationItemSerializer(serializers.Serializer):
|
||||
]
|
||||
|
||||
def validate(self, data):
|
||||
|
||||
"""Perfofrm data validation for this item"""
|
||||
super().validate(data)
|
||||
|
||||
build = self.context['build']
|
||||
@ -684,6 +693,7 @@ class BuildAllocationSerializer(serializers.Serializer):
|
||||
items = BuildAllocationItemSerializer(many=True)
|
||||
|
||||
class Meta:
|
||||
"""Serializer metaclass"""
|
||||
fields = [
|
||||
'items',
|
||||
]
|
||||
@ -700,7 +710,7 @@ class BuildAllocationSerializer(serializers.Serializer):
|
||||
return data
|
||||
|
||||
def save(self):
|
||||
|
||||
"""Perform the allocation"""
|
||||
data = self.validated_data
|
||||
|
||||
items = data.get('items', [])
|
||||
@ -732,6 +742,7 @@ class BuildAutoAllocationSerializer(serializers.Serializer):
|
||||
"""DRF serializer for auto allocating stock items against a build order."""
|
||||
|
||||
class Meta:
|
||||
"""Serializer metaclass"""
|
||||
fields = [
|
||||
'location',
|
||||
'exclude_location',
|
||||
@ -770,7 +781,7 @@ class BuildAutoAllocationSerializer(serializers.Serializer):
|
||||
)
|
||||
|
||||
def save(self):
|
||||
|
||||
"""Perform the auto-allocation step"""
|
||||
data = self.validated_data
|
||||
|
||||
build = self.context['build']
|
||||
@ -799,7 +810,7 @@ class BuildItemSerializer(InvenTreeModelSerializer):
|
||||
quantity = InvenTreeDecimalField()
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
|
||||
"""Determine which extra details fields should be included"""
|
||||
build_detail = kwargs.pop('build_detail', False)
|
||||
part_detail = kwargs.pop('part_detail', False)
|
||||
location_detail = kwargs.pop('location_detail', False)
|
||||
@ -816,6 +827,7 @@ class BuildItemSerializer(InvenTreeModelSerializer):
|
||||
self.fields.pop('location_detail')
|
||||
|
||||
class Meta:
|
||||
"""Serializer metaclass"""
|
||||
model = BuildItem
|
||||
fields = [
|
||||
'pk',
|
||||
@ -837,6 +849,7 @@ class BuildAttachmentSerializer(InvenTreeAttachmentSerializer):
|
||||
"""Serializer for a BuildAttachment."""
|
||||
|
||||
class Meta:
|
||||
"""Serializer metaclass"""
|
||||
model = BuildOrderAttachment
|
||||
|
||||
fields = [
|
||||
|
@ -1,3 +1,5 @@
|
||||
"""Background task definitions for the BuildOrder app"""
|
||||
|
||||
from decimal import Decimal
|
||||
import logging
|
||||
|
||||
|
@ -1,3 +1,5 @@
|
||||
"""Unit tests for the BuildOrder API"""
|
||||
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
from django.urls import reverse
|
||||
@ -91,16 +93,12 @@ class BuildAPITest(InvenTreeAPITestCase):
|
||||
'build.add'
|
||||
]
|
||||
|
||||
def setUp(self):
|
||||
|
||||
super().setUp()
|
||||
|
||||
|
||||
class BuildTest(BuildAPITest):
|
||||
"""Unit testing for the build complete API endpoint."""
|
||||
|
||||
def setUp(self):
|
||||
|
||||
"""Basic setup for this test suite"""
|
||||
super().setUp()
|
||||
|
||||
self.build = Build.objects.get(pk=1)
|
||||
@ -477,7 +475,7 @@ class BuildTest(BuildAPITest):
|
||||
self.assertIn('This build output has already been completed', str(response.data))
|
||||
|
||||
def test_download_build_orders(self):
|
||||
|
||||
"""Test that we can download a list of build orders via the API"""
|
||||
required_cols = [
|
||||
'reference',
|
||||
'status',
|
||||
@ -532,7 +530,7 @@ class BuildAllocationTest(BuildAPITest):
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
|
||||
"""Basic operation as part of test suite setup"""
|
||||
super().setUp()
|
||||
|
||||
self.assignRole('build.add')
|
||||
|
@ -1,3 +1,5 @@
|
||||
"""Unit tests for the BuildOrder app"""
|
||||
|
||||
from ctypes import Union
|
||||
from django.test import TestCase
|
||||
|
||||
@ -114,6 +116,7 @@ class BuildTestBase(TestCase):
|
||||
|
||||
|
||||
class BuildTest(BuildTestBase):
|
||||
"""Basic set of tests for the Build model"""
|
||||
|
||||
def test_ref_int(self):
|
||||
"""Test the "integer reference" field used for natural sorting."""
|
||||
@ -133,8 +136,7 @@ class BuildTest(BuildTestBase):
|
||||
self.assertEqual(build.reference_int, ii)
|
||||
|
||||
def test_init(self):
|
||||
# Perform some basic tests before we start the ball rolling
|
||||
|
||||
"""Perform some basic tests before we start the ball rolling"""
|
||||
self.assertEqual(StockItem.objects.count(), 10)
|
||||
|
||||
# Build is PENDING
|
||||
@ -158,8 +160,7 @@ class BuildTest(BuildTestBase):
|
||||
self.assertFalse(self.build.is_complete)
|
||||
|
||||
def test_build_item_clean(self):
|
||||
# Ensure that dodgy BuildItem objects cannot be created
|
||||
|
||||
"""Ensure that dodgy BuildItem objects cannot be created"""
|
||||
stock = StockItem.objects.create(part=self.assembly, quantity=99)
|
||||
|
||||
# Create a BuiltItem which points to an invalid StockItem
|
||||
@ -185,8 +186,7 @@ class BuildTest(BuildTestBase):
|
||||
b.save()
|
||||
|
||||
def test_duplicate_bom_line(self):
|
||||
# Try to add a duplicate BOM item - it should fail!
|
||||
|
||||
"""Try to add a duplicate BOM item - it should fail!"""
|
||||
with self.assertRaises(IntegrityError):
|
||||
BomItem.objects.create(
|
||||
part=self.assembly,
|
||||
@ -291,7 +291,7 @@ class BuildTest(BuildTestBase):
|
||||
|
||||
self.assertEqual(BuildItem.objects.count(), 0)
|
||||
"""
|
||||
pass
|
||||
...
|
||||
|
||||
def test_complete(self):
|
||||
"""Test completion of a build output."""
|
||||
@ -370,7 +370,7 @@ class AutoAllocationTests(BuildTestBase):
|
||||
"""Tests for auto allocating stock against a build order."""
|
||||
|
||||
def setUp(self):
|
||||
|
||||
"""Create some data as part of this test suite"""
|
||||
super().setUp()
|
||||
|
||||
# Add a "substitute" part for bom_item_2
|
||||
|
@ -38,7 +38,7 @@ class TestForwardMigrations(MigratorTestCase):
|
||||
)
|
||||
|
||||
def test_items_exist(self):
|
||||
|
||||
"""Test to ensure that the 'assembly' field is correctly configured"""
|
||||
Part = self.new_state.apps.get_model('part', 'part')
|
||||
|
||||
self.assertEqual(Part.objects.count(), 1)
|
||||
@ -96,7 +96,7 @@ class TestReferenceMigration(MigratorTestCase):
|
||||
print(build.reference)
|
||||
|
||||
def test_build_reference(self):
|
||||
|
||||
"""Test that the build reference is correctly assigned to the PK of the Build"""
|
||||
Build = self.new_state.apps.get_model('build', 'build')
|
||||
|
||||
self.assertEqual(Build.objects.count(), 3)
|
||||
|
@ -1,3 +1,5 @@
|
||||
"""Basic unit tests for the BuildOrder app"""
|
||||
|
||||
from django.urls import reverse
|
||||
|
||||
from datetime import datetime, timedelta
|
||||
@ -11,6 +13,7 @@ from InvenTree.status_codes import BuildStatus
|
||||
|
||||
|
||||
class BuildTestSimple(InvenTreeTestCase):
|
||||
"""Basic set of tests for the BuildOrder model functionality"""
|
||||
|
||||
fixtures = [
|
||||
'category',
|
||||
@ -26,7 +29,7 @@ class BuildTestSimple(InvenTreeTestCase):
|
||||
]
|
||||
|
||||
def test_build_objects(self):
|
||||
# Ensure the Build objects were correctly created
|
||||
"""Ensure the Build objects were correctly created"""
|
||||
self.assertEqual(Build.objects.count(), 5)
|
||||
b = Build.objects.get(pk=2)
|
||||
self.assertEqual(b.batch, 'B2')
|
||||
@ -35,10 +38,12 @@ class BuildTestSimple(InvenTreeTestCase):
|
||||
self.assertEqual(str(b), 'BO0002')
|
||||
|
||||
def test_url(self):
|
||||
"""Test URL lookup"""
|
||||
b1 = Build.objects.get(pk=1)
|
||||
self.assertEqual(b1.get_absolute_url(), '/build/1/')
|
||||
|
||||
def test_is_complete(self):
|
||||
"""Test build completion status"""
|
||||
b1 = Build.objects.get(pk=1)
|
||||
b2 = Build.objects.get(pk=2)
|
||||
|
||||
@ -63,6 +68,7 @@ class BuildTestSimple(InvenTreeTestCase):
|
||||
self.assertFalse(build.is_overdue)
|
||||
|
||||
def test_is_active(self):
|
||||
"""Test active / inactive build status"""
|
||||
b1 = Build.objects.get(pk=1)
|
||||
b2 = Build.objects.get(pk=2)
|
||||
|
||||
@ -70,8 +76,9 @@ class BuildTestSimple(InvenTreeTestCase):
|
||||
self.assertEqual(b2.is_active, False)
|
||||
|
||||
def test_required_parts(self):
|
||||
# TODO - Generate BOM for test part
|
||||
pass
|
||||
"""Test set of required BOM items for the build"""
|
||||
# TODO: Generate BOM for test part
|
||||
...
|
||||
|
||||
def test_cancel_build(self):
|
||||
"""Test build cancellation function."""
|
||||
@ -101,6 +108,7 @@ class TestBuildViews(InvenTreeTestCase):
|
||||
]
|
||||
|
||||
def setUp(self):
|
||||
"""Fixturing for this suite of unit tests"""
|
||||
super().setUp()
|
||||
|
||||
# Create a build output for build # 1
|
||||
|
@ -22,19 +22,6 @@ class BuildIndex(InvenTreeRoleMixin, ListView):
|
||||
"""Return all Build objects (order by date, newest first)"""
|
||||
return Build.objects.order_by('status', '-completion_date')
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
|
||||
context = super().get_context_data(**kwargs)
|
||||
|
||||
context['BuildStatus'] = BuildStatus
|
||||
|
||||
context['active'] = self.get_queryset().filter(status__in=BuildStatus.ACTIVE_CODES)
|
||||
|
||||
context['completed'] = self.get_queryset().filter(status=BuildStatus.COMPLETE)
|
||||
context['cancelled'] = self.get_queryset().filter(status=BuildStatus.CANCELLED)
|
||||
|
||||
return context
|
||||
|
||||
|
||||
class BuildDetail(InvenTreeRoleMixin, InvenTreePluginViewMixin, DetailView):
|
||||
"""Detail view of a single Build object."""
|
||||
@ -44,7 +31,7 @@ class BuildDetail(InvenTreeRoleMixin, InvenTreePluginViewMixin, DetailView):
|
||||
context_object_name = 'build'
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
|
||||
"""Return extra context information for the BuildDetail view"""
|
||||
ctx = super().get_context_data(**kwargs)
|
||||
|
||||
build = self.get_object()
|
||||
|
Loading…
Reference in New Issue
Block a user