diff --git a/README.md b/README.md index 79fb0db..b8ea722 100644 --- a/README.md +++ b/README.md @@ -88,4 +88,5 @@ Add support for Netbox 3.5 which become the minimum version supported to accomod * Add bulk update capability for contract assignement * [#63](https://github.com/mlebreuil/netbox-contract/issues/63) Correct an API issue on the invoice object. +* [#64](https://github.com/mlebreuil/netbox-contract/issues/64) Add hierarchy to contract; New parent field created. diff --git a/src/netbox_contract/api/serializers.py b/src/netbox_contract/api/serializers.py index dca290a..1a9b47c 100644 --- a/src/netbox_contract/api/serializers.py +++ b/src/netbox_contract/api/serializers.py @@ -50,11 +50,12 @@ class ContractSerializer(NetBoxModelSerializer): ) circuit= NestedCircuitSerializer(many=True, required=False) external_partie = NestedServiceProviderSerializer(many=False) + parent = NestedContracSerializer(many=False, required=False) class Meta: model = Contract fields = ( - 'id', 'url','display', 'name', 'status', 'external_partie','internal_partie','circuit','comments', + 'id', 'url','display', 'name', 'status', 'external_partie','internal_partie','parent','circuit','comments', 'tags', 'custom_fields', 'created', 'last_updated', ) diff --git a/src/netbox_contract/api/views.py b/src/netbox_contract/api/views.py index 24de632..9a5fd15 100644 --- a/src/netbox_contract/api/views.py +++ b/src/netbox_contract/api/views.py @@ -4,7 +4,9 @@ from .serializers import ContractSerializer, InvoiceSerializer, ServiceProviderSerializer, ContractAssignementSerializer class ContractViewSet(NetBoxModelViewSet): - queryset = models.Contract.objects.prefetch_related('circuit','tags') + queryset = models.Contract.objects.prefetch_related( + 'parent','circuit','tags' + ) serializer_class = ContractSerializer class InvoiceViewSet(NetBoxModelViewSet): diff --git a/src/netbox_contract/filtersets.py b/src/netbox_contract/filtersets.py index 2cfe177..dbfb748 100644 --- a/src/netbox_contract/filtersets.py +++ b/src/netbox_contract/filtersets.py @@ -7,7 +7,7 @@ class ContractFilterSet(NetBoxModelFilterSet): class Meta: model = Contract - fields = ('id', 'external_partie', 'internal_partie', 'status','circuit') + fields = ('id', 'external_partie', 'internal_partie', 'status','parent','circuit') def search(self, queryset, name, value): return queryset.filter( Q(name__icontains=value) diff --git a/src/netbox_contract/forms.py b/src/netbox_contract/forms.py index f3adc47..3d90a0b 100644 --- a/src/netbox_contract/forms.py +++ b/src/netbox_contract/forms.py @@ -17,12 +17,16 @@ class ContractForm(NetBoxModelForm): external_partie=DynamicModelChoiceField( queryset=ServiceProvider.objects.all() ) + parent=DynamicModelChoiceField( + queryset=Contract.objects.all(), + required=False + ) class Meta: model = Contract fields = ('name', 'external_partie', 'external_reference', 'internal_partie','tenant', 'status', 'start_date', 'end_date','initial_term', 'renewal_term', 'currency','accounting_dimensions', - 'mrc', 'nrc','invoice_frequency','circuit', 'documents', 'comments', 'tags') + 'mrc', 'nrc','invoice_frequency','circuit','parent','documents', 'comments', 'tags') widgets = { 'start_date': DatePicker(), @@ -47,7 +51,7 @@ class Meta: class ContractFilterSetForm(NetBoxModelFilterSetForm): model = Contract - external_partie=DynamicModelMultipleChoiceField( + external_partie=DynamicModelChoiceField( queryset=ServiceProvider.objects.all(), required=False ) @@ -65,6 +69,10 @@ class ContractFilterSetForm(NetBoxModelFilterSetForm): queryset=Circuit.objects.all(), required=False ) + parent=DynamicModelChoiceField( + queryset=Contract.objects.all(), + required=False + ) class InvoiceFilterSetForm(NetBoxModelFilterSetForm): model = Invoice @@ -77,7 +85,8 @@ class ContractCSVForm(NetBoxModelImportForm): circuit = CSVModelChoiceField( queryset=Circuit.objects.all(), to_field_name='name', - help_text='Related Circuit' + help_text='Related Circuit', + required=False ) class Meta: @@ -85,7 +94,7 @@ class Meta: fields = [ 'name', 'external_partie', 'internal_partie','tenant', 'status', 'start_date', 'initial_term', 'renewal_term', 'mrc', 'nrc', - 'invoice_frequency', 'circuit' + 'invoice_frequency', 'parent','circuit' ] class ContractBulkEditForm(NetBoxModelBulkEditForm): @@ -93,9 +102,9 @@ class ContractBulkEditForm(NetBoxModelBulkEditForm): max_length=100, required=True ) - external_partie = forms.CharField( - max_length=30, - required=True + external_partie = DynamicModelChoiceField( + queryset=ServiceProvider.objects.all(), + required=False ) external_reference=forms.CharField( max_length=100, @@ -107,7 +116,12 @@ class ContractBulkEditForm(NetBoxModelBulkEditForm): ) comments = CommentField() circuit=DynamicModelChoiceField( - queryset=Circuit.objects.all() + queryset=Circuit.objects.all(), + required=False + ) + parent = DynamicModelChoiceField( + queryset=Contract.objects.all(), + required=False ) nullable_fields = ( diff --git a/src/netbox_contract/migrations/0016_contract_parent.py b/src/netbox_contract/migrations/0016_contract_parent.py new file mode 100644 index 0000000..eead24f --- /dev/null +++ b/src/netbox_contract/migrations/0016_contract_parent.py @@ -0,0 +1,25 @@ +# Generated by Django 4.1.9 on 2023-06-18 14:45 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ("netbox_contract", "0015_contractassignement"), + ] + + operations = [ + migrations.AddField( + model_name="contract", + name="parent", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="child", + to="netbox_contract.contract", + ), + ), + ] diff --git a/src/netbox_contract/models.py b/src/netbox_contract/models.py index 6254d51..f10a189 100644 --- a/src/netbox_contract/models.py +++ b/src/netbox_contract/models.py @@ -172,6 +172,13 @@ class Contract(NetBoxModel): comments = models.TextField( blank=True ) + parent = models.ForeignKey( + 'self', + on_delete=models.CASCADE, + related_name='child', + null=True, + blank=True + ) def get_absolute_url(self): return reverse('plugins:netbox_contract:contract', args=[self.pk]) diff --git a/src/netbox_contract/tables.py b/src/netbox_contract/tables.py index ab9b899..397f1c9 100644 --- a/src/netbox_contract/tables.py +++ b/src/netbox_contract/tables.py @@ -63,13 +63,16 @@ class ContractListTable(NetBoxTable): linkify=True ) circuit = tables.ManyToManyColumn() + parent = tables.Column( + linkify=True + ) class Meta(NetBoxTable.Meta): model = Contract fields = ('pk', 'id', 'name', 'circuit', 'external_partie', 'external_reference','internal_partie', 'status', 'mrc', - 'comments', 'actions') - default_columns = ('name', 'status', 'circuit') + 'parent','comments', 'actions') + default_columns = ('name', 'status', 'parent','circuit') class ContractListBottomTable(NetBoxTable): name = tables.Column( diff --git a/src/netbox_contract/templates/netbox_contract/contract.html b/src/netbox_contract/templates/netbox_contract/contract.html index 708b560..19981bd 100644 --- a/src/netbox_contract/templates/netbox_contract/contract.html +++ b/src/netbox_contract/templates/netbox_contract/contract.html @@ -73,6 +73,12 @@