Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Consumables concept #137

Draft
wants to merge 16 commits into
base: master
Choose a base branch
from
Draft
14 changes: 13 additions & 1 deletion netbox_inventory/api/nested_serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from dcim.api.serializers import NestedManufacturerSerializer
from netbox.api.serializers import WritableNestedSerializer
from ..models import Asset, Delivery, InventoryItemType, InventoryItemGroup, Purchase, Supplier
from ..models import Asset, Delivery, InventoryItemType, InventoryItemGroup, Purchase, Supplier, ConsumableType

__all__ = (
'NestedAssetSerializer',
Expand All @@ -11,6 +11,7 @@
'NestedDeliverySerializer',
'NestedInventoryItemTypeSerializer',
'NestedInventoryItemGroupSerializer',
'NestedConsumableTypeSerializer',
)


Expand Down Expand Up @@ -75,3 +76,14 @@ class NestedInventoryItemGroupSerializer(WritableNestedSerializer):
class Meta:
model = InventoryItemGroup
fields = ('id', 'url', 'display', 'name', '_depth')

class NestedConsumableTypeSerializer(WritableNestedSerializer):
url = serializers.HyperlinkedIdentityField(
view_name='plugins-api:netbox_inventory-api:consumabletype-detail'
)

manufacturer = NestedManufacturerSerializer(read_only=True)

class Meta:
model = ConsumableType
fields = ('id', 'url', 'display', 'slug', 'name', 'manufacturer', 'description', 'part_number')
31 changes: 30 additions & 1 deletion netbox_inventory/api/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from tenancy.api.serializers import NestedContactSerializer, NestedTenantSerializer
from netbox.api.serializers import NetBoxModelSerializer
from .nested_serializers import *
from ..models import Asset, Delivery, InventoryItemType, InventoryItemGroup, Purchase, Supplier
from ..models import Asset, Delivery, InventoryItemType, InventoryItemGroup, Purchase, Supplier, ConsumableType, Consumable


class AssetSerializer(NetBoxModelSerializer):
Expand Down Expand Up @@ -137,3 +137,32 @@ class Meta:
'id', 'url', 'display', 'name', 'parent', 'comments', 'tags', 'custom_fields',
'created', 'last_updated', 'asset_count',
)


class ConsumableTypeSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(
view_name='plugins-api:netbox_inventory-api:consumabletype-detail'
)

manufacturer = NestedManufacturerSerializer()

class Meta:
model = ConsumableType
fields = (
'id', 'url', 'display', 'name', 'slug', 'manufacturer', 'description', 'part_number',
)


class ConsumableSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(
view_name='plugins-api:netbox_inventory-api:consumable-detail'
)

consumable_type = NestedConsumableTypeSerializer()

class Meta:
model = Consumable
fields = (
'id', 'url', 'display', 'consumable_type', 'quantity', 'alert_at_quantity',
'created', 'last_updated',
)
2 changes: 2 additions & 0 deletions netbox_inventory/api/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,7 @@
router.register('dcim/devices', views.DeviceAssetViewSet)
router.register('dcim/modules', views.ModuleAssetViewSet)
router.register('dcim/inventory-items', views.InventoryItemAssetViewSet)
router.register('consumable-type', views.ConsumableTypeViewSet)
router.register('consumable', views.ConsumableViewSet)

urlpatterns = router.urls
18 changes: 17 additions & 1 deletion netbox_inventory/api/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from .. import filtersets, models
from .serializers import (
AssetSerializer, InventoryItemTypeSerializer, InventoryItemGroupSerializer,
DeliverySerializer, PurchaseSerializer, SupplierSerializer
DeliverySerializer, PurchaseSerializer, SupplierSerializer, ConsumableTypeSerializer, ConsumableSerializer
)


Expand Down Expand Up @@ -77,3 +77,19 @@ class ModuleAssetViewSet(ModuleViewSet):
class InventoryItemAssetViewSet(InventoryItemViewSet):
"""Adds option to filter on asset assignemnet"""
filterset_class = filtersets.InventoryItemAssetFilterSet


class ConsumableTypeViewSet(NetBoxModelViewSet):
queryset = models.ConsumableType.objects.prefetch_related(
'manufacturer', 'storage_location', 'tags'
)
serializer_class = ConsumableTypeSerializer
filterset_class = filtersets.ConsumableTypeFilterSet


class ConsumableViewSet(NetBoxModelViewSet):
queryset = models.Consumable.objects.prefetch_related(
'consumable_type', 'tags'
)
serializer_class = ConsumableSerializer
filterset_class = filtersets.ConsumableFilterSet
8 changes: 8 additions & 0 deletions netbox_inventory/choices.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,11 @@ class HardwareKindChoices(ChoiceSet):
('module', 'Module'),
('inventoryitem', 'Inventory Item'),
]

class ConsumableQuantityStatusChoices(ChoiceSet):
key = 'Consumable.quantity_status'

CHOICES = [
('In-Stock', 'In-Stock', 'green'),
('Low-Stock', 'Low-Stock', 'red')
]
38 changes: 37 additions & 1 deletion netbox_inventory/filtersets.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from tenancy.filtersets import ContactModelFilterSet
from tenancy.models import Contact, Tenant
from .choices import HardwareKindChoices, AssetStatusChoices
from .models import Asset, Delivery, InventoryItemType, InventoryItemGroup, Purchase, Supplier
from .models import Asset, Delivery, InventoryItemType, InventoryItemGroup, Purchase, Supplier, Consumable, ConsumableType
from .utils import query_located, get_asset_custom_fields_search_filters


Expand Down Expand Up @@ -454,3 +454,39 @@ class ModuleAssetFilterSet(HasAssetFilterMixin, ModuleFilterSet):

class InventoryItemAssetFilterSet(HasAssetFilterMixin, InventoryItemFilterSet):
pass

class ConsumableTypeFilterSet(NetBoxModelFilterSet):
manufacturer_id = django_filters.ModelMultipleChoiceFilter(
field_name='manufacturer',
queryset=Manufacturer.objects.all(),
label='Manufacturer',
)

class Meta:
fields = (
'id', 'manufacturer_id', 'part_number',
)
model = ConsumableType


class ConsumableFilterSet(NetBoxModelFilterSet):
consumable_type_id = django_filters.ModelMultipleChoiceFilter(
field_name='consumable_type',
queryset=ConsumableType.objects.all(),
label='Consumable Type',
)
storage_location_id = django_filters.ModelMultipleChoiceFilter(
field_name='storage_location',
queryset=Location.objects.all(),
label='Storage Location',
)
manufacturer_id = django_filters.ModelMultipleChoiceFilter(
field_name='consumable_type__manufacturer',
queryset=Manufacturer.objects.all(),
label='Manufacturer',
)
class Meta:
fields = (
'storage_location_id', 'consumable_type_id', 'manufacturer_id'
)
model = Consumable
1 change: 1 addition & 0 deletions netbox_inventory/forms/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@
from .filters import *
from .models import *
from .reassign import *
from .consumable import *
62 changes: 60 additions & 2 deletions netbox_inventory/forms/bulk.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
)
from tenancy.models import Contact, Tenant
from ..choices import AssetStatusChoices, HardwareKindChoices
from ..models import Asset, Delivery, InventoryItemType, InventoryItemGroup, Purchase, Supplier
from ..models import Asset, Delivery, InventoryItemType, InventoryItemGroup, Purchase, Supplier, ConsumableType, Consumable
from ..utils import get_plugin_setting

__all__ = (
Expand All @@ -27,6 +27,10 @@
'InventoryItemTypeBulkEditForm',
'InventoryItemGroupImportForm',
'InventoryItemGroupBulkEditForm',
'ConsumableTypeImportForm',
'ConsumableTypeBulkEditForm',
'ConsumableImportForm',
'ConsumableBulkEditForm',
)


Expand Down Expand Up @@ -578,4 +582,58 @@ class InventoryItemGroupBulkEditForm(NetBoxModelBulkEditForm):
fieldsets = (
(None, ('parent',)),
)
nullable_fields = ('parent',)
nullable_fields = ('parent',)


class ConsumableTypeImportForm(NetBoxModelImportForm):
class Meta:
model = ConsumableType
fields = (
'name', 'slug', 'manufacturer', 'description', 'part_number'
)


class ConsumableTypeBulkEditForm(NetBoxModelBulkEditForm):
manufacturer = DynamicModelChoiceField(
queryset=Manufacturer.objects.all(),
required=False,
label='Manufacturer',
)
comments = CommentField(
required=False,
)

model = ConsumableType
fieldsets = (
('Consumable Type', ('manufacturer', 'part_number')),
)
nullable_fields = (
'description',
)


class ConsumableImportForm(NetBoxModelImportForm):
class Meta:
model = Consumable
fields = (
'consumable_type', 'storage_location', 'quantity', 'alert_at_quantity', 'comments'
)


class ConsumableBulkEditForm(NetBoxModelBulkEditForm):
manufacturer = DynamicModelChoiceField(
queryset=Manufacturer.objects.all(),
required=False,
label='Manufacturer',
)
comments = CommentField(
required=False,
)

model = Consumable
fieldsets = (
('Consumable', ('consumable_type', 'quantity')),
)
nullable_fields = (
'description',
)
56 changes: 56 additions & 0 deletions netbox_inventory/forms/consumable.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import logging

from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ValidationError
from django.forms import IntegerField, Form

from netbox.forms import NetBoxModelForm
from utilities.forms.fields import DynamicModelChoiceField
from ..utils import get_plugin_setting
from ..models import Consumable

__all__ = (
'ConsumableIncrementForm',
'ConsumableDecrementForm',
)


class ConsumableIncrementForm(NetBoxModelForm):
increment_qty = IntegerField(required=True)

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)

# remove tags field from form
self.fields.pop('tags')

class Meta:
model = Consumable
fields = ('increment_qty', )

def save(self, *args):
instance = super().save(*args)
instance.quantity += self.cleaned_data['increment_qty']
instance.save()
return instance


class ConsumableDecrementForm(NetBoxModelForm):
decrement_qty = IntegerField(required=True)

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)

# remove tags field from form
self.fields.pop('tags')

class Meta:
model = Consumable
fields = ('decrement_qty', )


def save(self, *args):
instance = super().save(*args)
instance.quantity -= self.cleaned_data['decrement_qty']
instance.save()
return instance
43 changes: 42 additions & 1 deletion netbox_inventory/forms/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from tenancy.forms import ContactModelFilterForm
from tenancy.models import Contact, Tenant
from ..choices import HardwareKindChoices, AssetStatusChoices
from ..models import Asset, Delivery, InventoryItemType, InventoryItemGroup, Purchase, Supplier
from ..models import Asset, Delivery, InventoryItemType, InventoryItemGroup, Purchase, Supplier, ConsumableType, Consumable


__all__ = (
Expand All @@ -18,6 +18,8 @@
'DeliveryFilterForm',
'InventoryItemTypeFilterForm',
'InventoryItemGroupFilterForm',
'ConsumableTypeFilterForm',
'ConsumableFilterForm',
)


Expand Down Expand Up @@ -347,3 +349,42 @@ class InventoryItemGroupFilterForm(NetBoxModelFilterSetForm):
label='Parent group'
)
tag = TagFilterField(model)


class ConsumableTypeFilterForm(NetBoxModelFilterSetForm):
model = ConsumableType
manufacturer_id = DynamicModelMultipleChoiceField(
queryset=Manufacturer.objects.all(),
required=False,
label='Manufacturer',
)
fieldsets = (
(None, ('q', 'filter_id', 'tag')),
('Consumable Type', ('manufacturer_id', ))
)
tag = TagFilterField(model)


class ConsumableFilterForm(NetBoxModelFilterSetForm):
model = Consumable
consumable_type_id = DynamicModelMultipleChoiceField(
queryset=ConsumableType.objects.all(),
required=False,
label='Consumable Type',
)
manufacturer_id = DynamicModelMultipleChoiceField(
queryset=Manufacturer.objects.all(),
required=False,
label='Manufacturer',
)
storage_location_id = DynamicModelMultipleChoiceField(
queryset=Location.objects.all(),
required=False,
label='Location',
)
fieldsets = (
(None, ('q', 'filter_id', 'tag')),
('Consumable Type', ('manufacturer_id', 'consumable_type_id')),
('Location', ('storage_location_id', )),
)
tag = TagFilterField(model)
Loading