diff --git a/README.md b/README.md index 5236b02..91485ac 100644 --- a/README.md +++ b/README.md @@ -92,3 +92,10 @@ Add support for Netbox 3.5 which become the minimum version supported to accomod * [#65](https://github.com/mlebreuil/netbox-contract/issues/65) Add end date to contact import form. * Removed the possibility of add or modify circuits to contracts. The field becomes read only and will be removed in next major release. * Make accounting dimensions optional. + +#### version 2.0.5 + +* [#75](https://github.com/mlebreuil/netbox-contract/issues/74) Fix contract assignement for service providers. +* [#73](https://github.com/mlebreuil/netbox-contract/issues/73) Add comment field to contract import form +* [#72](https://github.com/mlebreuil/netbox-contract/issues/72) Add fields to the contract assignement bottom tables +* Remove the 'add' actions from the contract assignment list view diff --git a/pyproject.toml b/pyproject.toml index 5618c1d..1002cdd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "netbox-contract" -version = "2.0.4" +version = "2.0.5" authors = [ { name="Marc Lebreuil", email="marc@famillelebreuil.net" }, ] diff --git a/src/netbox_contract/__init__.py b/src/netbox_contract/__init__.py index 66d3d74..7a48b9a 100644 --- a/src/netbox_contract/__init__.py +++ b/src/netbox_contract/__init__.py @@ -4,7 +4,7 @@ class ContractsConfig(PluginConfig): name = 'netbox_contract' verbose_name = 'Netbox contract' description = 'Contract management plugin for Netbox' - version = '2.0.4' + version = '2.0.5' author = 'Marc Lebreuil' author_email = 'marc@famillelebreuil.net' base_url = 'contracts' diff --git a/src/netbox_contract/forms.py b/src/netbox_contract/forms.py index 594f0e9..656ade0 100644 --- a/src/netbox_contract/forms.py +++ b/src/netbox_contract/forms.py @@ -2,7 +2,7 @@ from django.contrib.contenttypes.models import ContentType import django_filters from netbox.forms import NetBoxModelForm, NetBoxModelFilterSetForm, NetBoxModelBulkEditForm, NetBoxModelImportForm -from utilities.forms.fields import CommentField, DynamicModelChoiceField, DynamicModelMultipleChoiceField, MultipleChoiceField, CSVModelChoiceField, SlugField, CSVContentTypeField +from utilities.forms.fields import CommentField, CSVChoiceField, DynamicModelChoiceField, DynamicModelMultipleChoiceField, MultipleChoiceField, CSVModelChoiceField, SlugField, CSVContentTypeField from utilities.forms.widgets import DatePicker from extras.filters import TagFilter from circuits.models import Circuit @@ -98,6 +98,10 @@ class ContractCSVForm(NetBoxModelImportForm): help_text='Tenant name', required=False ) + status = CSVChoiceField( + choices=StatusChoices, + help_text='Contract status' + ) parent = CSVModelChoiceField( queryset=Contract.objects.all(), to_field_name='name', @@ -108,9 +112,9 @@ class ContractCSVForm(NetBoxModelImportForm): class Meta: model = Contract fields = [ - 'name', 'external_partie', 'internal_partie','tenant', 'status', + 'name', 'external_partie', 'internal_partie', 'external_reference','tenant', 'status', 'start_date', 'end_date','initial_term', 'renewal_term', 'mrc', 'nrc', - 'invoice_frequency', 'parent' + 'invoice_frequency', 'documents', 'comments', 'parent' ] class ContractBulkEditForm(NetBoxModelBulkEditForm): diff --git a/src/netbox_contract/tables.py b/src/netbox_contract/tables.py index 397f1c9..dd66b42 100644 --- a/src/netbox_contract/tables.py +++ b/src/netbox_contract/tables.py @@ -34,8 +34,10 @@ class ContractAssignementObjectTable(NetBoxTable): class Meta(NetBoxTable.Meta): model = ContractAssignement - fields = ('pk','contract','contract__external_partie','contract__status', 'actions') - default_columns = ('pk', 'contract','contract__external_partie','contract__status') + fields = ('pk','contract','contract__external_partie','contract__status','contract__start_date', + 'contract__end_date','contract__mrc','contract__nrc', 'actions') + default_columns = ('pk', 'contract','contract__external_partie','contract__status','contract__start_date', + 'contract__end_date','contract__mrc','contract__nrc',) class ContractAssignementContractTable(NetBoxTable): content_type = columns.ContentTypeColumn( @@ -43,16 +45,20 @@ class ContractAssignementContractTable(NetBoxTable): ) content_object = tables.Column( linkify=True, + verbose_name='Object', orderable=False ) + content_object__status = tables.Column( + verbose_name='Status' + ) actions = columns.ActionsColumn( actions=('edit', 'delete') ) class Meta(NetBoxTable.Meta): model = ContractAssignement - fields = ('pk', 'content_type', 'content_object','actions') - default_columns = ('pk', 'content_type', 'content_object') + fields = ('pk', 'content_type', 'content_object','content_object__status','actions') + default_columns = ('pk', 'content_type', 'content_object','content_object__status') class ContractListTable(NetBoxTable): diff --git a/src/netbox_contract/templates/contact_assignements_bottom.html b/src/netbox_contract/templates/contact_assignements_bottom.html new file mode 100644 index 0000000..6c3047b --- /dev/null +++ b/src/netbox_contract/templates/contact_assignements_bottom.html @@ -0,0 +1,63 @@ +{% load helpers %} + +
+
Contacts
+
+ {% with contacts=object.contacts.all %} + {% if contacts.exists %} + + + + + + + + + + {% for contact in contacts %} + + + + + + + + + {% endfor %} +
NameRolePriorityPhoneEmail
{{ contact.contact|linkify }}{{ contact.role|placeholder }}{{ contact.get_priority_display|placeholder }} + {% if contact.contact.phone %} + {{ contact.contact.phone }} + {% else %} + {{ ''|placeholder }} + {% endif %} + + {% if contact.contact.email %} + {{ contact.contact.email }} + {% else %} + {{ ''|placeholder }} + {% endif %} + + {% if perms.tenancy.change_contactassignment %} + + + + {% endif %} + {% if perms.tenancy.delete_contactassignment %} + + + + {% endif %} +
+ {% else %} +
None
+ {% endif %} + {% endwith %} +
+ {% if perms.tenancy.add_contactassignment %} + + {% endif %} +
\ No newline at end of file diff --git a/src/netbox_contract/templates/netbox_contract/serviceprovider.html b/src/netbox_contract/templates/netbox_contract/serviceprovider.html index 3e44489..bbab8dd 100644 --- a/src/netbox_contract/templates/netbox_contract/serviceprovider.html +++ b/src/netbox_contract/templates/netbox_contract/serviceprovider.html @@ -31,7 +31,7 @@
Service Provider
{% include 'inc/panels/custom_fields.html' %} {% include 'inc/panels/comments.html' %} {% include 'inc/panels/tags.html' %} - {% include 'inc/panels/contacts.html' %} + {% include 'contact_assignements_bottom.html' %} {% endblock content %} \ No newline at end of file diff --git a/src/netbox_contract/views.py b/src/netbox_contract/views.py index 05f6fc5..3c63017 100644 --- a/src/netbox_contract/views.py +++ b/src/netbox_contract/views.py @@ -4,59 +4,63 @@ from django.contrib.contenttypes.models import ContentType from django.shortcuts import render, get_object_or_404 from netbox.views import generic +from tenancy.views import ObjectContactsView from netbox.views.generic.utils import get_prerequisite_model from utilities.utils import count_related, normalize_querydict from utilities.forms import restrict_form_fields +from utilities.views import ViewTab, register_model_view from circuits.models import Circuit -from . import forms, models, tables, filtersets +from .models import ServiceProvider, ContractAssignement, Contract, Invoice +from . import forms, tables, filtersets # ServiceProvider views class ServiceProviderView(generic.ObjectView): - queryset = models.ServiceProvider.objects.all() + queryset = ServiceProvider.objects.all() class ServiceProviderListView(generic.ObjectListView): - queryset = models.ServiceProvider.objects.all() + queryset = ServiceProvider.objects.all() table = tables.ServiceProviderListTable filterset = filtersets.ServiceProviderFilterSet filterset_form = forms.ServiceProviderFilterSetForm class ServiceProviderEditView(generic.ObjectEditView): - queryset = models.ServiceProvider.objects.all() + queryset = ServiceProvider.objects.all() form = forms.ServiceProviderForm class ServiceProviderDeleteView(generic.ObjectDeleteView): - queryset = models.ServiceProvider.objects.all() + queryset = ServiceProvider.objects.all() class ServiceProviderBulkImportView(generic.BulkImportView): - queryset = models.ServiceProvider.objects.all() + queryset = ServiceProvider.objects.all() model_form = forms.ServiceProviderCSVForm table = tables.ServiceProviderListTable class ServiceProviderBulkEditView(generic.BulkEditView): - queryset = models.ServiceProvider.objects.annotate() + queryset = ServiceProvider.objects.annotate() filterset = filtersets.ServiceProviderFilterSet table = tables.ServiceProviderListTable form = forms.ServiceProviderBulkEditForm class ServiceProviderBulkDeleteView(generic.BulkDeleteView): - queryset = models.ServiceProvider.objects.annotate() + queryset = ServiceProvider.objects.annotate() filterset = filtersets.ServiceProviderFilterSet table = tables.ServiceProviderListTable # Contract assignement view class ContractAssignementView(generic.ObjectView): - queryset = models.ContractAssignement.objects.all() + queryset = ContractAssignement.objects.all() class ContractAssignementListView(generic.ObjectListView): - queryset = models.ContractAssignement.objects.all() + queryset = ContractAssignement.objects.all() table = tables.ContractAssignementListTable filterset = filtersets.ContractAssignementFilterSet filterset_form = forms.ContractAssignementFilterSetForm + actions = ['import', 'export', ] class ContractAssignementEditView(generic.ObjectEditView): - queryset = models.ContractAssignement.objects.all() + queryset = ContractAssignement.objects.all() form = forms.ContractAssignementForm def alter_object(self, instance, request, args, kwargs): @@ -73,17 +77,17 @@ def get_extra_addanother_params(self, request): } class ContractAssignementDeleteView(generic.ObjectDeleteView): - queryset = models.ContractAssignement.objects.all() + queryset = ContractAssignement.objects.all() class ContractAssignementBulkImportView(generic.BulkImportView): - queryset = models.ContractAssignement.objects.all() + queryset = ContractAssignement.objects.all() model_form = forms.ContractAssignementImportForm table = tables.ContractAssignementListTable # Contract views class ContractView(generic.ObjectView): - queryset = models.Contract.objects.all() + queryset = Contract.objects.all() def get_extra_context(self, request, instance): invoices_table = tables.InvoiceListTable(instance.invoices.all()) @@ -100,25 +104,25 @@ def get_extra_context(self, request, instance): } class ContractListView(generic.ObjectListView): - queryset = models.Contract.objects.all() + queryset = Contract.objects.all() table = tables.ContractListTable filterset = filtersets.ContractFilterSet filterset_form = forms.ContractFilterSetForm class ContractEditView(generic.ObjectEditView): - queryset = models.Contract.objects.all() + queryset = Contract.objects.all() form = forms.ContractForm class ContractDeleteView(generic.ObjectDeleteView): - queryset = models.Contract.objects.all() + queryset = Contract.objects.all() class ContractBulkImportView(generic.BulkImportView): - queryset = models.Contract.objects.all() + queryset = Contract.objects.all() model_form = forms.ContractCSVForm table = tables.ContractListTable class ContractBulkEditView(generic.BulkEditView): - queryset = models.Contract.objects.annotate( + queryset = Contract.objects.annotate( count_circuits=count_related(Circuit, 'contracts') ) filterset = filtersets.ContractFilterSet @@ -126,7 +130,7 @@ class ContractBulkEditView(generic.BulkEditView): form = forms.ContractBulkEditForm class ContractBulkDeleteView(generic.BulkDeleteView): - queryset = models.Contract.objects.annotate( + queryset = Contract.objects.annotate( count_circuits=count_related(Circuit, 'contracts') ) filterset = filtersets.ContractFilterSet @@ -135,7 +139,7 @@ class ContractBulkDeleteView(generic.BulkDeleteView): # Invoice views class InvoiceView(generic.ObjectView): - queryset = models.Invoice.objects.all() + queryset = Invoice.objects.all() def get_extra_context(self, request, instance): contracts_table = tables.ContractListTable(instance.contracts.all()) @@ -146,13 +150,13 @@ def get_extra_context(self, request, instance): } class InvoiceListView(generic.ObjectListView): - queryset = models.Invoice.objects.all() + queryset = Invoice.objects.all() table = tables.InvoiceListTable filterset = filtersets.InvoiceFilterSet filterset_form = forms.InvoiceFilterSetForm class InvoiceEditView(generic.ObjectEditView): - queryset = models.Invoice.objects.all() + queryset = Invoice.objects.all() form = forms.InvoiceForm def get(self, request, *args, **kwargs): @@ -171,7 +175,7 @@ def get(self, request, *args, **kwargs): initial_data = normalize_querydict(request.GET) initial_data['date'] = date.today() if 'contracts' in initial_data.keys(): - contract = models.Contract.objects.get(pk=initial_data['contracts']) + contract = Contract.objects.get(pk=initial_data['contracts']) try: last_invoice = contract.invoices.latest('period_end') @@ -206,20 +210,20 @@ def get(self, request, *args, **kwargs): }) class InvoiceDeleteView(generic.ObjectDeleteView): - queryset = models.Invoice.objects.all() + queryset = Invoice.objects.all() class InvoiceBulkImportView(generic.BulkImportView): - queryset = models.Invoice.objects.all() + queryset = Invoice.objects.all() model_form = forms.InvoiceCSVForm table = tables.InvoiceListTable class InvoiceBulkEditView(generic.BulkEditView): - queryset = models.Invoice.objects.all() + queryset = Invoice.objects.all() filterset = filtersets.InvoiceFilterSet table = tables.InvoiceListTable form = forms.InvoiceBulkEditForm class InvoiceBulkDeleteView(generic.BulkDeleteView): - queryset = models.Invoice.objects.all() + queryset = Invoice.objects.all() filterset = filtersets.InvoiceFilterSet table = tables.InvoiceListTable