mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-14 09:51:22 -06:00
#8037: Add role field to InventoryItem
This commit is contained in:
parent
04fb5e544d
commit
6e9afccfd7
@ -2,6 +2,6 @@
|
|||||||
|
|
||||||
Inventory items represent hardware components installed within a device, such as a power supply or CPU or line card. Inventory items are distinct from other device components in that they cannot be templatized on a device type, and cannot be connected by cables. They are intended to be used primarily for inventory purposes.
|
Inventory items represent hardware components installed within a device, such as a power supply or CPU or line card. Inventory items are distinct from other device components in that they cannot be templatized on a device type, and cannot be connected by cables. They are intended to be used primarily for inventory purposes.
|
||||||
|
|
||||||
Each inventory item can be assigned a manufacturer, part ID, serial number, and asset tag (all optional). A boolean toggle is also provided to indicate whether each item was entered manually or discovered automatically (by some process outside of NetBox).
|
Each inventory item can be assigned a functional role, manufacturer, part ID, serial number, and asset tag (all optional). A boolean toggle is also provided to indicate whether each item was entered manually or discovered automatically (by some process outside of NetBox).
|
||||||
|
|
||||||
Inventory items are hierarchical in nature, such that any individual item may be designated as the parent for other items. For example, an inventory item might be created to represent a line card which houses several SFP optics, each of which exists as a child item within the device.
|
Inventory items are hierarchical in nature, such that any individual item may be designated as the parent for other items. For example, an inventory item might be created to represent a line card which houses several SFP optics, each of which exists as a child item within the device.
|
||||||
|
@ -811,12 +811,13 @@ class InventoryItemSerializer(PrimaryModelSerializer):
|
|||||||
device = NestedDeviceSerializer()
|
device = NestedDeviceSerializer()
|
||||||
parent = serializers.PrimaryKeyRelatedField(queryset=InventoryItem.objects.all(), allow_null=True, default=None)
|
parent = serializers.PrimaryKeyRelatedField(queryset=InventoryItem.objects.all(), allow_null=True, default=None)
|
||||||
manufacturer = NestedManufacturerSerializer(required=False, allow_null=True, default=None)
|
manufacturer = NestedManufacturerSerializer(required=False, allow_null=True, default=None)
|
||||||
|
role = NestedInventoryItemRoleSerializer(required=False, allow_null=True)
|
||||||
_depth = serializers.IntegerField(source='level', read_only=True)
|
_depth = serializers.IntegerField(source='level', read_only=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = InventoryItem
|
model = InventoryItem
|
||||||
fields = [
|
fields = [
|
||||||
'id', 'url', 'display', 'device', 'parent', 'name', 'label', 'manufacturer', 'part_id', 'serial',
|
'id', 'url', 'display', 'device', 'parent', 'name', 'label', 'role', 'manufacturer', 'part_id', 'serial',
|
||||||
'asset_tag', 'discovered', 'description', 'tags', 'custom_fields', 'created', 'last_updated', '_depth',
|
'asset_tag', 'discovered', 'description', 'tags', 'custom_fields', 'created', 'last_updated', '_depth',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -1284,6 +1284,16 @@ class InventoryItemFilterSet(PrimaryModelFilterSet, DeviceComponentFilterSet):
|
|||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
label='Manufacturer (slug)',
|
label='Manufacturer (slug)',
|
||||||
)
|
)
|
||||||
|
role_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
|
queryset=InventoryItemRole.objects.all(),
|
||||||
|
label='Role (ID)',
|
||||||
|
)
|
||||||
|
role = django_filters.ModelMultipleChoiceFilter(
|
||||||
|
field_name='role__slug',
|
||||||
|
queryset=InventoryItemRole.objects.all(),
|
||||||
|
to_field_name='slug',
|
||||||
|
label='Role (slug)',
|
||||||
|
)
|
||||||
serial = django_filters.CharFilter(
|
serial = django_filters.CharFilter(
|
||||||
lookup_expr='iexact'
|
lookup_expr='iexact'
|
||||||
)
|
)
|
||||||
|
@ -107,11 +107,11 @@ class DeviceBayBulkCreateForm(DeviceBulkAddComponentForm):
|
|||||||
|
|
||||||
|
|
||||||
class InventoryItemBulkCreateForm(
|
class InventoryItemBulkCreateForm(
|
||||||
form_from_model(InventoryItem, ['manufacturer', 'part_id', 'serial', 'asset_tag', 'discovered']),
|
form_from_model(InventoryItem, ['role', 'manufacturer', 'part_id', 'serial', 'asset_tag', 'discovered']),
|
||||||
DeviceBulkAddComponentForm
|
DeviceBulkAddComponentForm
|
||||||
):
|
):
|
||||||
model = InventoryItem
|
model = InventoryItem
|
||||||
field_order = (
|
field_order = (
|
||||||
'name_pattern', 'label_pattern', 'manufacturer', 'part_id', 'serial', 'asset_tag', 'discovered', 'description',
|
'name_pattern', 'label_pattern', 'role', 'manufacturer', 'part_id', 'serial', 'asset_tag', 'discovered',
|
||||||
'tags',
|
'description', 'tags',
|
||||||
)
|
)
|
||||||
|
@ -1172,7 +1172,7 @@ class DeviceBayBulkEditForm(
|
|||||||
|
|
||||||
|
|
||||||
class InventoryItemBulkEditForm(
|
class InventoryItemBulkEditForm(
|
||||||
form_from_model(InventoryItem, ['label', 'manufacturer', 'part_id', 'description']),
|
form_from_model(InventoryItem, ['label', 'role', 'manufacturer', 'part_id', 'description']),
|
||||||
AddRemoveTagsForm,
|
AddRemoveTagsForm,
|
||||||
CustomFieldModelBulkEditForm
|
CustomFieldModelBulkEditForm
|
||||||
):
|
):
|
||||||
@ -1180,13 +1180,17 @@ class InventoryItemBulkEditForm(
|
|||||||
queryset=InventoryItem.objects.all(),
|
queryset=InventoryItem.objects.all(),
|
||||||
widget=forms.MultipleHiddenInput()
|
widget=forms.MultipleHiddenInput()
|
||||||
)
|
)
|
||||||
|
role = DynamicModelChoiceField(
|
||||||
|
queryset=InventoryItemRole.objects.all(),
|
||||||
|
required=False
|
||||||
|
)
|
||||||
manufacturer = DynamicModelChoiceField(
|
manufacturer = DynamicModelChoiceField(
|
||||||
queryset=Manufacturer.objects.all(),
|
queryset=Manufacturer.objects.all(),
|
||||||
required=False
|
required=False
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
nullable_fields = ['label', 'manufacturer', 'part_id', 'description']
|
nullable_fields = ['label', 'role', 'manufacturer', 'part_id', 'description']
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
|
@ -772,6 +772,11 @@ class InventoryItemCSVForm(CustomFieldModelCSVForm):
|
|||||||
queryset=Device.objects.all(),
|
queryset=Device.objects.all(),
|
||||||
to_field_name='name'
|
to_field_name='name'
|
||||||
)
|
)
|
||||||
|
role = CSVModelChoiceField(
|
||||||
|
queryset=InventoryItemRole.objects.all(),
|
||||||
|
to_field_name='name',
|
||||||
|
required=False
|
||||||
|
)
|
||||||
manufacturer = CSVModelChoiceField(
|
manufacturer = CSVModelChoiceField(
|
||||||
queryset=Manufacturer.objects.all(),
|
queryset=Manufacturer.objects.all(),
|
||||||
to_field_name='name',
|
to_field_name='name',
|
||||||
@ -787,7 +792,8 @@ class InventoryItemCSVForm(CustomFieldModelCSVForm):
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = InventoryItem
|
model = InventoryItem
|
||||||
fields = (
|
fields = (
|
||||||
'device', 'name', 'label', 'manufacturer', 'part_id', 'serial', 'asset_tag', 'discovered', 'description',
|
'device', 'name', 'label', 'role', 'manufacturer', 'part_id', 'serial', 'asset_tag', 'discovered',
|
||||||
|
'description',
|
||||||
)
|
)
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
|
@ -1100,6 +1100,12 @@ class InventoryItemFilterForm(DeviceComponentFilterForm):
|
|||||||
['name', 'label', 'manufacturer_id', 'serial', 'asset_tag', 'discovered'],
|
['name', 'label', 'manufacturer_id', 'serial', 'asset_tag', 'discovered'],
|
||||||
['region_id', 'site_group_id', 'site_id', 'location_id', 'virtual_chassis_id', 'device_id'],
|
['region_id', 'site_group_id', 'site_id', 'location_id', 'virtual_chassis_id', 'device_id'],
|
||||||
]
|
]
|
||||||
|
role_id = DynamicModelMultipleChoiceField(
|
||||||
|
queryset=InventoryItemRole.objects.all(),
|
||||||
|
required=False,
|
||||||
|
label=_('Role'),
|
||||||
|
fetch_trigger='open'
|
||||||
|
)
|
||||||
manufacturer_id = DynamicModelMultipleChoiceField(
|
manufacturer_id = DynamicModelMultipleChoiceField(
|
||||||
queryset=Manufacturer.objects.all(),
|
queryset=Manufacturer.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
|
@ -1368,6 +1368,10 @@ class InventoryItemForm(CustomFieldModelForm):
|
|||||||
'device_id': '$device'
|
'device_id': '$device'
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
role = DynamicModelChoiceField(
|
||||||
|
queryset=InventoryItemRole.objects.all(),
|
||||||
|
required=False
|
||||||
|
)
|
||||||
manufacturer = DynamicModelChoiceField(
|
manufacturer = DynamicModelChoiceField(
|
||||||
queryset=Manufacturer.objects.all(),
|
queryset=Manufacturer.objects.all(),
|
||||||
required=False
|
required=False
|
||||||
@ -1380,8 +1384,8 @@ class InventoryItemForm(CustomFieldModelForm):
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = InventoryItem
|
model = InventoryItem
|
||||||
fields = [
|
fields = [
|
||||||
'device', 'parent', 'name', 'label', 'manufacturer', 'part_id', 'serial', 'asset_tag', 'description',
|
'device', 'parent', 'name', 'label', 'role', 'manufacturer', 'part_id', 'serial', 'asset_tag',
|
||||||
'tags',
|
'description', 'tags',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@ -652,10 +652,6 @@ class DeviceBayCreateForm(ComponentCreateForm):
|
|||||||
|
|
||||||
class InventoryItemCreateForm(ComponentCreateForm):
|
class InventoryItemCreateForm(ComponentCreateForm):
|
||||||
model = InventoryItem
|
model = InventoryItem
|
||||||
manufacturer = DynamicModelChoiceField(
|
|
||||||
queryset=Manufacturer.objects.all(),
|
|
||||||
required=False
|
|
||||||
)
|
|
||||||
parent = DynamicModelChoiceField(
|
parent = DynamicModelChoiceField(
|
||||||
queryset=InventoryItem.objects.all(),
|
queryset=InventoryItem.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
@ -663,6 +659,14 @@ class InventoryItemCreateForm(ComponentCreateForm):
|
|||||||
'device_id': '$device'
|
'device_id': '$device'
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
role = DynamicModelChoiceField(
|
||||||
|
queryset=InventoryItemRole.objects.all(),
|
||||||
|
required=False
|
||||||
|
)
|
||||||
|
manufacturer = DynamicModelChoiceField(
|
||||||
|
queryset=Manufacturer.objects.all(),
|
||||||
|
required=False
|
||||||
|
)
|
||||||
part_id = forms.CharField(
|
part_id = forms.CharField(
|
||||||
max_length=50,
|
max_length=50,
|
||||||
required=False,
|
required=False,
|
||||||
@ -677,6 +681,6 @@ class InventoryItemCreateForm(ComponentCreateForm):
|
|||||||
required=False,
|
required=False,
|
||||||
)
|
)
|
||||||
field_order = (
|
field_order = (
|
||||||
'device', 'parent', 'name_pattern', 'label_pattern', 'manufacturer', 'part_id', 'serial', 'asset_tag',
|
'device', 'parent', 'name_pattern', 'label_pattern', 'role', 'manufacturer', 'part_id', 'serial', 'asset_tag',
|
||||||
'description', 'tags',
|
'description', 'tags',
|
||||||
)
|
)
|
||||||
|
@ -994,6 +994,13 @@ class InventoryItem(MPTTModel, ComponentModel):
|
|||||||
null=True,
|
null=True,
|
||||||
db_index=True
|
db_index=True
|
||||||
)
|
)
|
||||||
|
role = models.ForeignKey(
|
||||||
|
to='dcim.InventoryItemRole',
|
||||||
|
on_delete=models.PROTECT,
|
||||||
|
related_name='inventory_items',
|
||||||
|
blank=True,
|
||||||
|
null=True
|
||||||
|
)
|
||||||
manufacturer = models.ForeignKey(
|
manufacturer = models.ForeignKey(
|
||||||
to='dcim.Manufacturer',
|
to='dcim.Manufacturer',
|
||||||
on_delete=models.PROTECT,
|
on_delete=models.PROTECT,
|
||||||
@ -1007,13 +1014,6 @@ class InventoryItem(MPTTModel, ComponentModel):
|
|||||||
blank=True,
|
blank=True,
|
||||||
help_text='Manufacturer-assigned part identifier'
|
help_text='Manufacturer-assigned part identifier'
|
||||||
)
|
)
|
||||||
role = models.ForeignKey(
|
|
||||||
to='dcim.InventoryItemRole',
|
|
||||||
on_delete=models.PROTECT,
|
|
||||||
related_name='inventory_items',
|
|
||||||
blank=True,
|
|
||||||
null=True
|
|
||||||
)
|
|
||||||
serial = models.CharField(
|
serial = models.CharField(
|
||||||
max_length=50,
|
max_length=50,
|
||||||
verbose_name='Serial number',
|
verbose_name='Serial number',
|
||||||
@ -1034,7 +1034,7 @@ class InventoryItem(MPTTModel, ComponentModel):
|
|||||||
|
|
||||||
objects = TreeManager()
|
objects = TreeManager()
|
||||||
|
|
||||||
clone_fields = ['device', 'parent', 'manufacturer', 'part_id', 'role']
|
clone_fields = ['device', 'parent', 'role', 'manufacturer', 'part_id']
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ('device__id', 'parent__id', '_name')
|
ordering = ('device__id', 'parent__id', '_name')
|
||||||
|
@ -774,6 +774,9 @@ class InventoryItemTable(DeviceComponentTable):
|
|||||||
'args': [Accessor('device_id')],
|
'args': [Accessor('device_id')],
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
role = tables.Column(
|
||||||
|
linkify=True
|
||||||
|
)
|
||||||
manufacturer = tables.Column(
|
manufacturer = tables.Column(
|
||||||
linkify=True
|
linkify=True
|
||||||
)
|
)
|
||||||
@ -786,10 +789,33 @@ class InventoryItemTable(DeviceComponentTable):
|
|||||||
class Meta(BaseTable.Meta):
|
class Meta(BaseTable.Meta):
|
||||||
model = InventoryItem
|
model = InventoryItem
|
||||||
fields = (
|
fields = (
|
||||||
'pk', 'id', 'name', 'device', 'label', 'manufacturer', 'part_id', 'serial', 'asset_tag', 'description',
|
'pk', 'id', 'name', 'device', 'label', 'role', 'manufacturer', 'part_id', 'serial', 'asset_tag',
|
||||||
'discovered', 'tags',
|
'description', 'discovered', 'tags',
|
||||||
|
)
|
||||||
|
default_columns = ('pk', 'name', 'device', 'label', 'role', 'manufacturer', 'part_id', 'serial', 'asset_tag')
|
||||||
|
|
||||||
|
|
||||||
|
class DeviceInventoryItemTable(InventoryItemTable):
|
||||||
|
name = tables.TemplateColumn(
|
||||||
|
template_code='<a href="{{ record.get_absolute_url }}" style="padding-left: {{ record.level }}0px">'
|
||||||
|
'{{ value }}</a>',
|
||||||
|
order_by=Accessor('_name'),
|
||||||
|
attrs={'td': {'class': 'text-nowrap'}}
|
||||||
|
)
|
||||||
|
actions = ButtonsColumn(
|
||||||
|
model=InventoryItem,
|
||||||
|
buttons=('edit', 'delete')
|
||||||
|
)
|
||||||
|
|
||||||
|
class Meta(BaseTable.Meta):
|
||||||
|
model = InventoryItem
|
||||||
|
fields = (
|
||||||
|
'pk', 'id', 'name', 'label', 'role', 'manufacturer', 'part_id', 'serial', 'asset_tag', 'description',
|
||||||
|
'discovered', 'tags', 'actions',
|
||||||
|
)
|
||||||
|
default_columns = (
|
||||||
|
'pk', 'name', 'label', 'role', 'manufacturer', 'part_id', 'serial', 'asset_tag', 'description', 'actions',
|
||||||
)
|
)
|
||||||
default_columns = ('pk', 'name', 'device', 'label', 'manufacturer', 'part_id', 'serial', 'asset_tag')
|
|
||||||
|
|
||||||
|
|
||||||
class InventoryItemRoleTable(BaseTable):
|
class InventoryItemRoleTable(BaseTable):
|
||||||
@ -816,30 +842,6 @@ class InventoryItemRoleTable(BaseTable):
|
|||||||
default_columns = ('pk', 'name', 'inventoryitem_count', 'color', 'description', 'actions')
|
default_columns = ('pk', 'name', 'inventoryitem_count', 'color', 'description', 'actions')
|
||||||
|
|
||||||
|
|
||||||
class DeviceInventoryItemTable(InventoryItemTable):
|
|
||||||
name = tables.TemplateColumn(
|
|
||||||
template_code='<a href="{{ record.get_absolute_url }}" style="padding-left: {{ record.level }}0px">'
|
|
||||||
'{{ value }}</a>',
|
|
||||||
order_by=Accessor('_name'),
|
|
||||||
attrs={'td': {'class': 'text-nowrap'}}
|
|
||||||
)
|
|
||||||
actions = ButtonsColumn(
|
|
||||||
model=InventoryItem,
|
|
||||||
buttons=('edit', 'delete')
|
|
||||||
)
|
|
||||||
|
|
||||||
class Meta(BaseTable.Meta):
|
|
||||||
model = InventoryItem
|
|
||||||
fields = (
|
|
||||||
'pk', 'id', 'name', 'label', 'manufacturer', 'part_id', 'serial', 'asset_tag', 'description', 'discovered',
|
|
||||||
'tags', 'actions',
|
|
||||||
)
|
|
||||||
default_columns = (
|
|
||||||
'pk', 'name', 'label', 'manufacturer', 'part_id', 'serial', 'asset_tag', 'description', 'discovered',
|
|
||||||
'actions',
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Virtual chassis
|
# Virtual chassis
|
||||||
#
|
#
|
||||||
|
@ -1626,24 +1626,33 @@ class InventoryItemTest(APIViewTestCases.APIViewTestCase):
|
|||||||
devicerole = DeviceRole.objects.create(name='Test Device Role 1', slug='test-device-role-1', color='ff0000')
|
devicerole = DeviceRole.objects.create(name='Test Device Role 1', slug='test-device-role-1', color='ff0000')
|
||||||
device = Device.objects.create(device_type=devicetype, device_role=devicerole, name='Device 1', site=site)
|
device = Device.objects.create(device_type=devicetype, device_role=devicerole, name='Device 1', site=site)
|
||||||
|
|
||||||
InventoryItem.objects.create(device=device, name='Inventory Item 1', manufacturer=manufacturer)
|
roles = (
|
||||||
InventoryItem.objects.create(device=device, name='Inventory Item 2', manufacturer=manufacturer)
|
InventoryItemRole(name='Inventory Item Role 1', slug='inventory-item-role-1'),
|
||||||
InventoryItem.objects.create(device=device, name='Inventory Item 3', manufacturer=manufacturer)
|
InventoryItemRole(name='Inventory Item Role 2', slug='inventory-item-role-2'),
|
||||||
|
)
|
||||||
|
InventoryItemRole.objects.bulk_create(roles)
|
||||||
|
|
||||||
|
InventoryItem.objects.create(device=device, name='Inventory Item 1', role=roles[0], manufacturer=manufacturer)
|
||||||
|
InventoryItem.objects.create(device=device, name='Inventory Item 2', role=roles[0], manufacturer=manufacturer)
|
||||||
|
InventoryItem.objects.create(device=device, name='Inventory Item 3', role=roles[0], manufacturer=manufacturer)
|
||||||
|
|
||||||
cls.create_data = [
|
cls.create_data = [
|
||||||
{
|
{
|
||||||
'device': device.pk,
|
'device': device.pk,
|
||||||
'name': 'Inventory Item 4',
|
'name': 'Inventory Item 4',
|
||||||
|
'role': roles[1].pk,
|
||||||
'manufacturer': manufacturer.pk,
|
'manufacturer': manufacturer.pk,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'device': device.pk,
|
'device': device.pk,
|
||||||
'name': 'Inventory Item 5',
|
'name': 'Inventory Item 5',
|
||||||
|
'role': roles[1].pk,
|
||||||
'manufacturer': manufacturer.pk,
|
'manufacturer': manufacturer.pk,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'device': device.pk,
|
'device': device.pk,
|
||||||
'name': 'Inventory Item 6',
|
'name': 'Inventory Item 6',
|
||||||
|
'role': roles[1].pk,
|
||||||
'manufacturer': manufacturer.pk,
|
'manufacturer': manufacturer.pk,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
@ -2949,7 +2949,6 @@ class InventoryItemTestCase(TestCase, ChangeLoggedFilterSetTests):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def setUpTestData(cls):
|
def setUpTestData(cls):
|
||||||
|
|
||||||
manufacturers = (
|
manufacturers = (
|
||||||
Manufacturer(name='Manufacturer 1', slug='manufacturer-1'),
|
Manufacturer(name='Manufacturer 1', slug='manufacturer-1'),
|
||||||
Manufacturer(name='Manufacturer 2', slug='manufacturer-2'),
|
Manufacturer(name='Manufacturer 2', slug='manufacturer-2'),
|
||||||
@ -2998,10 +2997,17 @@ class InventoryItemTestCase(TestCase, ChangeLoggedFilterSetTests):
|
|||||||
)
|
)
|
||||||
Device.objects.bulk_create(devices)
|
Device.objects.bulk_create(devices)
|
||||||
|
|
||||||
|
roles = (
|
||||||
|
InventoryItemRole(name='Inventory Item Role 1', slug='inventory-item-role-1'),
|
||||||
|
InventoryItemRole(name='Inventory Item Role 2', slug='inventory-item-role-2'),
|
||||||
|
InventoryItemRole(name='Inventory Item Role 3', slug='inventory-item-role-3'),
|
||||||
|
)
|
||||||
|
InventoryItemRole.objects.bulk_create(roles)
|
||||||
|
|
||||||
inventory_items = (
|
inventory_items = (
|
||||||
InventoryItem(device=devices[0], manufacturer=manufacturers[0], name='Inventory Item 1', label='A', part_id='1001', serial='ABC', asset_tag='1001', discovered=True, description='First'),
|
InventoryItem(device=devices[0], role=roles[0], manufacturer=manufacturers[0], name='Inventory Item 1', label='A', part_id='1001', serial='ABC', asset_tag='1001', discovered=True, description='First'),
|
||||||
InventoryItem(device=devices[1], manufacturer=manufacturers[1], name='Inventory Item 2', label='B', part_id='1002', serial='DEF', asset_tag='1002', discovered=True, description='Second'),
|
InventoryItem(device=devices[1], role=roles[1], manufacturer=manufacturers[1], name='Inventory Item 2', label='B', part_id='1002', serial='DEF', asset_tag='1002', discovered=True, description='Second'),
|
||||||
InventoryItem(device=devices[2], manufacturer=manufacturers[2], name='Inventory Item 3', label='C', part_id='1003', serial='GHI', asset_tag='1003', discovered=False, description='Third'),
|
InventoryItem(device=devices[2], role=roles[2], manufacturer=manufacturers[2], name='Inventory Item 3', label='C', part_id='1003', serial='GHI', asset_tag='1003', discovered=False, description='Third'),
|
||||||
)
|
)
|
||||||
for i in inventory_items:
|
for i in inventory_items:
|
||||||
i.save()
|
i.save()
|
||||||
@ -3077,6 +3083,13 @@ class InventoryItemTestCase(TestCase, ChangeLoggedFilterSetTests):
|
|||||||
params = {'parent_id': [parent_items[0].pk, parent_items[1].pk]}
|
params = {'parent_id': [parent_items[0].pk, parent_items[1].pk]}
|
||||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||||
|
|
||||||
|
def test_role(self):
|
||||||
|
roles = InventoryItemRole.objects.all()[:2]
|
||||||
|
params = {'role_id': [roles[0].pk, roles[1].pk]}
|
||||||
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||||
|
params = {'role': [roles[0].slug, roles[1].slug]}
|
||||||
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||||
|
|
||||||
def test_manufacturer(self):
|
def test_manufacturer(self):
|
||||||
manufacturers = Manufacturer.objects.all()[:2]
|
manufacturers = Manufacturer.objects.all()[:2]
|
||||||
params = {'manufacturer_id': [manufacturers[0].pk, manufacturers[1].pk]}
|
params = {'manufacturer_id': [manufacturers[0].pk, manufacturers[1].pk]}
|
||||||
|
@ -2331,14 +2331,21 @@ class InventoryItemTestCase(ViewTestCases.DeviceComponentViewTestCase):
|
|||||||
device = create_test_device('Device 1')
|
device = create_test_device('Device 1')
|
||||||
manufacturer, _ = Manufacturer.objects.get_or_create(name='Manufacturer 1', slug='manufacturer-1')
|
manufacturer, _ = Manufacturer.objects.get_or_create(name='Manufacturer 1', slug='manufacturer-1')
|
||||||
|
|
||||||
InventoryItem.objects.create(device=device, name='Inventory Item 1')
|
roles = (
|
||||||
InventoryItem.objects.create(device=device, name='Inventory Item 2')
|
InventoryItemRole(name='Inventory Item Role 1', slug='inventory-item-role-1'),
|
||||||
InventoryItem.objects.create(device=device, name='Inventory Item 3')
|
InventoryItemRole(name='Inventory Item Role 2', slug='inventory-item-role-2'),
|
||||||
|
)
|
||||||
|
InventoryItemRole.objects.bulk_create(roles)
|
||||||
|
|
||||||
|
InventoryItem.objects.create(device=device, name='Inventory Item 1', role=roles[0], manufacturer=manufacturer)
|
||||||
|
InventoryItem.objects.create(device=device, name='Inventory Item 2', role=roles[0], manufacturer=manufacturer)
|
||||||
|
InventoryItem.objects.create(device=device, name='Inventory Item 3', role=roles[0], manufacturer=manufacturer)
|
||||||
|
|
||||||
tags = create_tags('Alpha', 'Bravo', 'Charlie')
|
tags = create_tags('Alpha', 'Bravo', 'Charlie')
|
||||||
|
|
||||||
cls.form_data = {
|
cls.form_data = {
|
||||||
'device': device.pk,
|
'device': device.pk,
|
||||||
|
'role': roles[1].pk,
|
||||||
'manufacturer': manufacturer.pk,
|
'manufacturer': manufacturer.pk,
|
||||||
'name': 'Inventory Item X',
|
'name': 'Inventory Item X',
|
||||||
'parent': None,
|
'parent': None,
|
||||||
@ -2353,6 +2360,7 @@ class InventoryItemTestCase(ViewTestCases.DeviceComponentViewTestCase):
|
|||||||
cls.bulk_create_data = {
|
cls.bulk_create_data = {
|
||||||
'device': device.pk,
|
'device': device.pk,
|
||||||
'name_pattern': 'Inventory Item [4-6]',
|
'name_pattern': 'Inventory Item [4-6]',
|
||||||
|
'role': roles[1].pk,
|
||||||
'manufacturer': manufacturer.pk,
|
'manufacturer': manufacturer.pk,
|
||||||
'parent': None,
|
'parent': None,
|
||||||
'discovered': False,
|
'discovered': False,
|
||||||
@ -2363,6 +2371,7 @@ class InventoryItemTestCase(ViewTestCases.DeviceComponentViewTestCase):
|
|||||||
}
|
}
|
||||||
|
|
||||||
cls.bulk_edit_data = {
|
cls.bulk_edit_data = {
|
||||||
|
'role': roles[1].pk,
|
||||||
'part_id': '123456',
|
'part_id': '123456',
|
||||||
'description': 'New description',
|
'description': 'New description',
|
||||||
}
|
}
|
||||||
|
@ -2412,7 +2412,7 @@ class InventoryItemBulkImportView(generic.BulkImportView):
|
|||||||
|
|
||||||
|
|
||||||
class InventoryItemBulkEditView(generic.BulkEditView):
|
class InventoryItemBulkEditView(generic.BulkEditView):
|
||||||
queryset = InventoryItem.objects.prefetch_related('device', 'manufacturer')
|
queryset = InventoryItem.objects.prefetch_related('device', 'manufacturer', 'role')
|
||||||
filterset = filtersets.InventoryItemFilterSet
|
filterset = filtersets.InventoryItemFilterSet
|
||||||
table = tables.InventoryItemTable
|
table = tables.InventoryItemTable
|
||||||
form = forms.InventoryItemBulkEditForm
|
form = forms.InventoryItemBulkEditForm
|
||||||
@ -2423,7 +2423,7 @@ class InventoryItemBulkRenameView(generic.BulkRenameView):
|
|||||||
|
|
||||||
|
|
||||||
class InventoryItemBulkDeleteView(generic.BulkDeleteView):
|
class InventoryItemBulkDeleteView(generic.BulkDeleteView):
|
||||||
queryset = InventoryItem.objects.prefetch_related('device', 'manufacturer')
|
queryset = InventoryItem.objects.prefetch_related('device', 'manufacturer', 'role')
|
||||||
table = tables.InventoryItemTable
|
table = tables.InventoryItemTable
|
||||||
template_name = 'dcim/inventoryitem_bulk_delete.html'
|
template_name = 'dcim/inventoryitem_bulk_delete.html'
|
||||||
|
|
||||||
|
@ -13,9 +13,7 @@
|
|||||||
<div class="row mb-3">
|
<div class="row mb-3">
|
||||||
<div class="col col-md-6">
|
<div class="col col-md-6">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<h5 class="card-header">
|
<h5 class="card-header">Inventory Item</h5>
|
||||||
Inventory Item
|
|
||||||
</h5>
|
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<table class="table table-hover attr-table">
|
<table class="table table-hover attr-table">
|
||||||
<tr>
|
<tr>
|
||||||
@ -42,6 +40,16 @@
|
|||||||
<th scope="row">Label</th>
|
<th scope="row">Label</th>
|
||||||
<td>{{ object.label|placeholder }}</td>
|
<td>{{ object.label|placeholder }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th scope="row">Role</th>
|
||||||
|
<td>
|
||||||
|
{% if object.role %}
|
||||||
|
<a href="{{ object.role.get_absolute_url }}">{{ object.role }}</a>
|
||||||
|
{% else %}
|
||||||
|
<span class="text-muted">—</span>
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row">Manufacturer</th>
|
<th scope="row">Manufacturer</th>
|
||||||
<td>
|
<td>
|
||||||
|
Loading…
Reference in New Issue
Block a user