netbox-contract 2.4.4__tar.gz → 2.4.6__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. {netbox_contract-2.4.4/netbox_contract.egg-info → netbox_contract-2.4.6}/PKG-INFO +7 -1
  2. {netbox_contract-2.4.4 → netbox_contract-2.4.6}/README.md +6 -0
  3. {netbox_contract-2.4.4 → netbox_contract-2.4.6}/netbox_contract/__init__.py +3 -2
  4. {netbox_contract-2.4.4 → netbox_contract-2.4.6}/netbox_contract/api/serializers.py +15 -4
  5. {netbox_contract-2.4.4 → netbox_contract-2.4.6}/netbox_contract/forms.py +8 -4
  6. netbox_contract-2.4.6/netbox_contract/template_content.py +45 -0
  7. {netbox_contract-2.4.4 → netbox_contract-2.4.6}/netbox_contract/templates/netbox_contract/contract.html +1 -1
  8. netbox_contract-2.4.6/netbox_contract/templates/netbox_contract/object_contracts.html +7 -0
  9. {netbox_contract-2.4.4 → netbox_contract-2.4.6}/netbox_contract/views.py +65 -1
  10. {netbox_contract-2.4.4 → netbox_contract-2.4.6/netbox_contract.egg-info}/PKG-INFO +7 -1
  11. {netbox_contract-2.4.4 → netbox_contract-2.4.6}/netbox_contract.egg-info/SOURCES.txt +1 -0
  12. {netbox_contract-2.4.4 → netbox_contract-2.4.6}/pyproject.toml +1 -1
  13. netbox_contract-2.4.4/netbox_contract/template_content.py +0 -32
  14. {netbox_contract-2.4.4 → netbox_contract-2.4.6}/CONTRIBUTING.md +0 -0
  15. {netbox_contract-2.4.4 → netbox_contract-2.4.6}/LICENSE +0 -0
  16. {netbox_contract-2.4.4 → netbox_contract-2.4.6}/MANIFEST.in +0 -0
  17. {netbox_contract-2.4.4 → netbox_contract-2.4.6}/docs/img/accounting_dimensions.png +0 -0
  18. {netbox_contract-2.4.4 → netbox_contract-2.4.6}/docs/img/contract.png +0 -0
  19. {netbox_contract-2.4.4 → netbox_contract-2.4.6}/docs/img/contract_linked_objects.png +0 -0
  20. {netbox_contract-2.4.4 → netbox_contract-2.4.6}/docs/img/invoice.png +0 -0
  21. {netbox_contract-2.4.4 → netbox_contract-2.4.6}/docs/img/invoice_line.png +0 -0
  22. {netbox_contract-2.4.4 → netbox_contract-2.4.6}/docs/img/invoice_linked_objects.png +0 -0
  23. {netbox_contract-2.4.4 → netbox_contract-2.4.6}/netbox_contract/api/__init__.py +0 -0
  24. {netbox_contract-2.4.4 → netbox_contract-2.4.6}/netbox_contract/api/urls.py +0 -0
  25. {netbox_contract-2.4.4 → netbox_contract-2.4.6}/netbox_contract/api/views.py +0 -0
  26. {netbox_contract-2.4.4 → netbox_contract-2.4.6}/netbox_contract/constants.py +1 -1
  27. {netbox_contract-2.4.4 → netbox_contract-2.4.6}/netbox_contract/filtersets.py +3 -3
  28. {netbox_contract-2.4.4 → netbox_contract-2.4.6}/netbox_contract/locale/en/LC_MESSAGES/django.mo +0 -0
  29. {netbox_contract-2.4.4 → netbox_contract-2.4.6}/netbox_contract/locale/en/LC_MESSAGES/django.po +0 -0
  30. {netbox_contract-2.4.4 → netbox_contract-2.4.6}/netbox_contract/locale/fr/LC_MESSAGES/django.mo +0 -0
  31. {netbox_contract-2.4.4 → netbox_contract-2.4.6}/netbox_contract/locale/fr/LC_MESSAGES/django.po +0 -0
  32. {netbox_contract-2.4.4 → netbox_contract-2.4.6}/netbox_contract/migrations/0001_initial.py +0 -0
  33. {netbox_contract-2.4.4 → netbox_contract-2.4.6}/netbox_contract/migrations/0002_alter_contract_circuit_and_more.py +0 -0
  34. {netbox_contract-2.4.4 → netbox_contract-2.4.6}/netbox_contract/migrations/0003_alter_contract_custom_field_data_and_more.py +0 -0
  35. {netbox_contract-2.4.4 → netbox_contract-2.4.6}/netbox_contract/migrations/0004_contract_currency_invoice_currency.py +0 -0
  36. {netbox_contract-2.4.4 → netbox_contract-2.4.6}/netbox_contract/migrations/0005_contract_accounting_dimensions_and_more.py +0 -0
  37. {netbox_contract-2.4.4 → netbox_contract-2.4.6}/netbox_contract/migrations/0006_alter_contract_circuit.py +0 -0
  38. {netbox_contract-2.4.4 → netbox_contract-2.4.6}/netbox_contract/migrations/0007_invoice_date.py +0 -0
  39. {netbox_contract-2.4.4 → netbox_contract-2.4.6}/netbox_contract/migrations/0008_invoice_comments.py +0 -0
  40. {netbox_contract-2.4.4 → netbox_contract-2.4.6}/netbox_contract/migrations/0009_contract_external_reference.py +0 -0
  41. {netbox_contract-2.4.4 → netbox_contract-2.4.6}/netbox_contract/migrations/0010_invoice_contracts.py +0 -0
  42. {netbox_contract-2.4.4 → netbox_contract-2.4.6}/netbox_contract/migrations/0011_auto_20230122_2112.py +0 -0
  43. {netbox_contract-2.4.4 → netbox_contract-2.4.6}/netbox_contract/migrations/0012_remove_invoice_contract.py +0 -0
  44. {netbox_contract-2.4.4 → netbox_contract-2.4.6}/netbox_contract/migrations/0013_contract_documents_invoice_documents.py +0 -0
  45. {netbox_contract-2.4.4 → netbox_contract-2.4.6}/netbox_contract/migrations/0014_contract_end_date.py +0 -0
  46. {netbox_contract-2.4.4 → netbox_contract-2.4.6}/netbox_contract/migrations/0015_contractassignement.py +0 -0
  47. {netbox_contract-2.4.4 → netbox_contract-2.4.6}/netbox_contract/migrations/0016_contract_parent.py +0 -0
  48. {netbox_contract-2.4.4 → netbox_contract-2.4.6}/netbox_contract/migrations/0017_alter_contract_accounting_dimensions.py +0 -0
  49. {netbox_contract-2.4.4 → netbox_contract-2.4.6}/netbox_contract/migrations/0018_contract_external_partie_object_id_and_more.py +0 -0
  50. {netbox_contract-2.4.4 → netbox_contract-2.4.6}/netbox_contract/migrations/0019_auto_20230924_1813.py +0 -0
  51. {netbox_contract-2.4.4 → netbox_contract-2.4.6}/netbox_contract/migrations/0020_alter_contract_external_partie.py +0 -0
  52. {netbox_contract-2.4.4 → netbox_contract-2.4.6}/netbox_contract/migrations/0021_alter_contract_external_partie.py +0 -0
  53. {netbox_contract-2.4.4 → netbox_contract-2.4.6}/netbox_contract/migrations/0022_alter_contract_internal_partie_alter_contract_parent.py +0 -0
  54. {netbox_contract-2.4.4 → netbox_contract-2.4.6}/netbox_contract/migrations/0023_rename_contractassignement_contractassignment_and_more.py +0 -0
  55. {netbox_contract-2.4.4 → netbox_contract-2.4.6}/netbox_contract/migrations/0024_remove_contract_external_partie.py +0 -0
  56. {netbox_contract-2.4.4 → netbox_contract-2.4.6}/netbox_contract/migrations/0025_remove_contract_circuit_and_more.py +0 -0
  57. {netbox_contract-2.4.4 → netbox_contract-2.4.6}/netbox_contract/migrations/0026_auto_20240421_1550.py +0 -0
  58. {netbox_contract-2.4.4 → netbox_contract-2.4.6}/netbox_contract/migrations/0027_invoiceline.py +0 -0
  59. {netbox_contract-2.4.4 → netbox_contract-2.4.6}/netbox_contract/migrations/0028_invoiceline_accounting_dimensions.py +0 -0
  60. {netbox_contract-2.4.4 → netbox_contract-2.4.6}/netbox_contract/migrations/0029_remove_invoiceline_accounting_dimensions_and_more.py +0 -0
  61. {netbox_contract-2.4.4 → netbox_contract-2.4.6}/netbox_contract/migrations/0030_alter_invoiceline_accounting_dimensions.py +0 -0
  62. {netbox_contract-2.4.4 → netbox_contract-2.4.6}/netbox_contract/migrations/0031_contract_invoice_template_invoice_template.py +0 -0
  63. {netbox_contract-2.4.4 → netbox_contract-2.4.6}/netbox_contract/migrations/0032_alter_invoice_period_end_alter_invoice_period_start_and_more.py +0 -0
  64. {netbox_contract-2.4.4 → netbox_contract-2.4.6}/netbox_contract/migrations/0033_remove_contract_invoice_template.py +0 -0
  65. {netbox_contract-2.4.4 → netbox_contract-2.4.6}/netbox_contract/migrations/0034_contract_yrc_alter_contract_mrc.py +0 -0
  66. {netbox_contract-2.4.4 → netbox_contract-2.4.6}/netbox_contract/migrations/0035_accountingdimension_status_and_more.py +0 -0
  67. {netbox_contract-2.4.4 → netbox_contract-2.4.6}/netbox_contract/migrations/0036_alter_invoiceline_accounting_dimensions.py +0 -0
  68. {netbox_contract-2.4.4 → netbox_contract-2.4.6}/netbox_contract/migrations/0037_contract_notice_period_alter_contract_currency_and_more.py +0 -0
  69. {netbox_contract-2.4.4 → netbox_contract-2.4.6}/netbox_contract/migrations/0038_alter_accountingdimension_options_and_more.py +0 -0
  70. {netbox_contract-2.4.4 → netbox_contract-2.4.6}/netbox_contract/migrations/0039_alter_accountingdimension_status_and_more.py +0 -0
  71. {netbox_contract-2.4.4 → netbox_contract-2.4.6}/netbox_contract/migrations/0040_alter_contract_currency_alter_invoice_currency_and_more.py +0 -0
  72. {netbox_contract-2.4.4 → netbox_contract-2.4.6}/netbox_contract/migrations/0041_contracttype_contract_contract_type.py +0 -0
  73. {netbox_contract-2.4.4 → netbox_contract-2.4.6}/netbox_contract/migrations/0042_remove_contract_netbox_cont_externa_6343fb_idx_and_more.py +0 -0
  74. {netbox_contract-2.4.4 → netbox_contract-2.4.6}/netbox_contract/migrations/0043_invoice_status.py +0 -0
  75. {netbox_contract-2.4.4 → netbox_contract-2.4.6}/netbox_contract/migrations/__init__.py +0 -0
  76. {netbox_contract-2.4.4 → netbox_contract-2.4.6}/netbox_contract/models.py +0 -0
  77. {netbox_contract-2.4.4 → netbox_contract-2.4.6}/netbox_contract/navigation.py +0 -0
  78. {netbox_contract-2.4.4 → netbox_contract-2.4.6}/netbox_contract/search.py +0 -0
  79. {netbox_contract-2.4.4 → netbox_contract-2.4.6}/netbox_contract/tables.py +0 -0
  80. {netbox_contract-2.4.4 → netbox_contract-2.4.6}/netbox_contract/templates/contract_assignments_bottom.html +0 -0
  81. {netbox_contract-2.4.4 → netbox_contract-2.4.6}/netbox_contract/templates/contract_list_bottom.html +0 -0
  82. {netbox_contract-2.4.4 → netbox_contract-2.4.6}/netbox_contract/templates/netbox_contract/accountingdimension.html +0 -0
  83. {netbox_contract-2.4.4 → netbox_contract-2.4.6}/netbox_contract/templates/netbox_contract/contractassignment.html +0 -0
  84. {netbox_contract-2.4.4 → netbox_contract-2.4.6}/netbox_contract/templates/netbox_contract/contracttype.html +0 -0
  85. {netbox_contract-2.4.4 → netbox_contract-2.4.6}/netbox_contract/templates/netbox_contract/invoice.html +0 -0
  86. {netbox_contract-2.4.4 → netbox_contract-2.4.6}/netbox_contract/templates/netbox_contract/invoiceline.html +0 -0
  87. {netbox_contract-2.4.4 → netbox_contract-2.4.6}/netbox_contract/templates/netbox_contract/serviceprovider.html +0 -0
  88. {netbox_contract-2.4.4 → netbox_contract-2.4.6}/netbox_contract/urls.py +0 -0
  89. {netbox_contract-2.4.4 → netbox_contract-2.4.6}/netbox_contract.egg-info/dependency_links.txt +0 -0
  90. {netbox_contract-2.4.4 → netbox_contract-2.4.6}/netbox_contract.egg-info/requires.txt +0 -0
  91. {netbox_contract-2.4.4 → netbox_contract-2.4.6}/netbox_contract.egg-info/top_level.txt +0 -0
  92. {netbox_contract-2.4.4 → netbox_contract-2.4.6}/setup.cfg +0 -0
  93. {netbox_contract-2.4.4 → netbox_contract-2.4.6}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: netbox-contract
3
- Version: 2.4.4
3
+ Version: 2.4.6
4
4
  Summary: Contract management plugin for Netbox
5
5
  Author-email: Marc Lebreuil <marc@famillelebreuil.net>
6
6
  Project-URL: Homepage, https://github.com/mlebreuil/netbox-contract
@@ -49,6 +49,7 @@ Accounting dimensions can be linked with invoice lines.
49
49
  | 4.2 | 2.3 |
50
50
  | 4.3 | 2.4 |
51
51
  | 4.4 | 2.4 |
52
+ | 4.5 | 2.4 |
52
53
 
53
54
  ## Installing
54
55
 
@@ -101,12 +102,17 @@ PLUGINS_CONFIG = {
101
102
  'ipam.ipaddress',
102
103
  'ipam.prefix',
103
104
  ],
105
+ 'contract_assignments_display': 'both', # options: 'tab', 'inline', 'both'
104
106
  }
105
107
  }
106
108
 
107
109
  ```
108
110
 
109
111
  * top_level_menu : If "Contracts" appears under the "Plugins" menu item or on its own
112
+ * contract_assignments_display:
113
+ * `'tab'` - only the Contracts view tab is visible on the related object page.
114
+ * `'inline'` - only the inline contract assignments table is shown in object detail; the Contracts tab is hidden.
115
+ * `'both'` - both tab and inline table are shown (default behavior).
110
116
  * default_accounting_dimensions: The accounting dimensions which will appear in the field' background when empty. Note that accounting dimensions are now managed as individual objects. The use of this field is deprecated.
111
117
  * mandatory_contract_fields, mandatory_invoice_fields: Fields which are not required by default and can be set as such. The list of fields is at the bottom of the contract import form.
112
118
  * hidden_contract_fields, hidden_invoice_fields: List of fields to be hidden. Fields should not be required to be hidden.
@@ -22,6 +22,7 @@ Accounting dimensions can be linked with invoice lines.
22
22
  | 4.2 | 2.3 |
23
23
  | 4.3 | 2.4 |
24
24
  | 4.4 | 2.4 |
25
+ | 4.5 | 2.4 |
25
26
 
26
27
  ## Installing
27
28
 
@@ -74,12 +75,17 @@ PLUGINS_CONFIG = {
74
75
  'ipam.ipaddress',
75
76
  'ipam.prefix',
76
77
  ],
78
+ 'contract_assignments_display': 'both', # options: 'tab', 'inline', 'both'
77
79
  }
78
80
  }
79
81
 
80
82
  ```
81
83
 
82
84
  * top_level_menu : If "Contracts" appears under the "Plugins" menu item or on its own
85
+ * contract_assignments_display:
86
+ * `'tab'` - only the Contracts view tab is visible on the related object page.
87
+ * `'inline'` - only the inline contract assignments table is shown in object detail; the Contracts tab is hidden.
88
+ * `'both'` - both tab and inline table are shown (default behavior).
83
89
  * default_accounting_dimensions: The accounting dimensions which will appear in the field' background when empty. Note that accounting dimensions are now managed as individual objects. The use of this field is deprecated.
84
90
  * mandatory_contract_fields, mandatory_invoice_fields: Fields which are not required by default and can be set as such. The list of fields is at the bottom of the contract import form.
85
91
  * hidden_contract_fields, hidden_invoice_fields: List of fields to be hidden. Fields should not be required to be hidden.
@@ -5,11 +5,11 @@ class ContractsConfig(PluginConfig):
5
5
  name = 'netbox_contract'
6
6
  verbose_name = 'Netbox contract'
7
7
  description = 'Contract management plugin for Netbox'
8
- version = '2.4.4'
8
+ version = '2.4.6'
9
9
  author = 'Marc Lebreuil'
10
10
  author_email = 'marc@famillelebreuil.net'
11
11
  base_url = 'contracts'
12
- min_version = '4.3.0'
12
+ min_version = '4.5.0'
13
13
  required_settings = []
14
14
  default_settings = {
15
15
  'top_level_menu': False,
@@ -27,6 +27,7 @@ class ContractsConfig(PluginConfig):
27
27
  'virtualization.virtualmachine',
28
28
  'virtualization.cluster',
29
29
  ],
30
+ 'contract_assignments_display': 'both', # options: 'tab', 'inline', 'both'
30
31
  }
31
32
 
32
33
 
@@ -46,12 +46,14 @@ class NestedContractSerializer(WritableNestedSerializer):
46
46
  'end_date',
47
47
  'initial_term',
48
48
  'renewal_term',
49
+ 'notice_period',
49
50
  'currency',
50
51
  'mrc',
51
52
  'yrc',
52
53
  'nrc',
53
54
  'invoice_frequency',
54
55
  'comments',
56
+ 'documents',
55
57
  )
56
58
 
57
59
  @swagger_serializer_method(serializer_or_field=serializers.JSONField)
@@ -98,6 +100,7 @@ class ContractTypeSerializer(NetBoxModelSerializer):
98
100
  'display',
99
101
  'name',
100
102
  'description',
103
+ 'color',
101
104
  'tags',
102
105
  'custom_fields',
103
106
  'created',
@@ -136,12 +139,14 @@ class ContractSerializer(NetBoxModelSerializer):
136
139
  'end_date',
137
140
  'initial_term',
138
141
  'renewal_term',
142
+ 'notice_period',
139
143
  'currency',
140
144
  'mrc',
141
145
  'yrc',
142
146
  'nrc',
143
147
  'invoice_frequency',
144
148
  'comments',
149
+ 'documents',
145
150
  'parent',
146
151
  'tags',
147
152
  'custom_fields',
@@ -205,12 +210,14 @@ class InvoiceSerializer(NetBoxModelSerializer):
205
210
  'number',
206
211
  'date',
207
212
  'template',
213
+ 'status',
208
214
  'contracts',
209
215
  'period_start',
210
216
  'period_end',
211
217
  'currency',
212
218
  'amount',
213
219
  'comments',
220
+ 'documents',
214
221
  'tags',
215
222
  'custom_fields',
216
223
  'created',
@@ -310,13 +317,15 @@ class ServiceProviderSerializer(NetBoxModelSerializer):
310
317
  'url',
311
318
  'display',
312
319
  'name',
320
+ 'slug',
313
321
  'portal_url',
322
+ 'comments',
314
323
  'tags',
315
324
  'custom_fields',
316
325
  'created',
317
326
  'last_updated',
318
327
  )
319
- brief_fields = ('id', 'url', 'display', 'name')
328
+ brief_fields = ('id', 'url', 'display', 'name', 'slug')
320
329
 
321
330
 
322
331
  class ContractAssignmentSerializer(NetBoxModelSerializer):
@@ -337,10 +346,12 @@ class ContractAssignmentSerializer(NetBoxModelSerializer):
337
346
  'object_id',
338
347
  'content_object',
339
348
  'contract',
349
+ 'tags',
350
+ 'custom_fields',
340
351
  'created',
341
352
  'last_updated',
342
353
  )
343
- brief_fields = ('id', 'url', 'display', 'content_object', 'contract')
354
+ brief_fields = ('id', 'url', 'display', 'content_object', 'contract', 'tags', 'custom_fields')
344
355
 
345
356
  @swagger_serializer_method(serializer_or_field=serializers.JSONField)
346
357
  def get_content_object(self, instance):
@@ -394,8 +405,7 @@ class InvoiceLineSerializer(NetBoxModelSerializer):
394
405
  for dimension in accounting_dimensions:
395
406
  if dimension.name in dimensions_names:
396
407
  raise serializers.ValidationError('duplicate accounting dimension')
397
- else:
398
- dimensions_names.append(dimension.name)
408
+ dimensions_names.append(dimension.name)
399
409
  return data
400
410
 
401
411
 
@@ -412,6 +422,7 @@ class AccountingDimensionSerializer(NetBoxModelSerializer):
412
422
  'display',
413
423
  'name',
414
424
  'value',
425
+ 'status',
415
426
  'comments',
416
427
  'tags',
417
428
  'custom_fields',
@@ -1,3 +1,4 @@
1
+ from circuits.models import Provider
1
2
  from django import forms
2
3
  from django.conf import settings
3
4
  from django.contrib.contenttypes.models import ContentType
@@ -11,7 +12,6 @@ from netbox.forms import (
11
12
  )
12
13
  from tenancy.forms import ContactModelFilterForm, TenancyFilterForm
13
14
  from tenancy.models import Tenant
14
- from circuits.models import Provider
15
15
  from utilities.forms import BOOLEAN_WITH_BLANK_CHOICES, get_field_value
16
16
  from utilities.forms.fields import (
17
17
  ColorField,
@@ -40,9 +40,9 @@ from .models import (
40
40
  InternalEntityChoices,
41
41
  Invoice,
42
42
  InvoiceLine,
43
+ InvoiceStatusChoices,
43
44
  ServiceProvider,
44
45
  StatusChoices,
45
- InvoiceStatusChoices,
46
46
  )
47
47
 
48
48
  plugin_settings = settings.PLUGINS_CONFIG['netbox_contract']
@@ -61,6 +61,11 @@ class ContractForm(NetBoxModelForm):
61
61
  label=_('External party object type'),
62
62
  )
63
63
  external_party_object = forms.ModelChoiceField(queryset=None, label=_('External party object'))
64
+ internal_party = internal_party = forms.ChoiceField(
65
+ choices=InternalEntityChoices,
66
+ required=True,
67
+ label=_('Internal party')
68
+ )
64
69
  tenant = DynamicModelChoiceField(queryset=Tenant.objects.all(), required=False, selector=True, label=_('Tenant'))
65
70
  parent = DynamicModelChoiceField(
66
71
  queryset=Contract.objects.all(),
@@ -683,8 +688,7 @@ class InvoiceLineForm(NetBoxModelForm):
683
688
  for dimension in accounting_dimensions:
684
689
  if dimension.name in dimensions_names:
685
690
  raise ValidationError('duplicate accounting dimension')
686
- else:
687
- dimensions_names.append(dimension.name)
691
+ dimensions_names.append(dimension.name)
688
692
 
689
693
  # Make sure mandatory dimensions are present
690
694
  mandatory_dimensions = plugin_settings.get('mandatory_dimensions')
@@ -0,0 +1,45 @@
1
+ from django.conf import settings
2
+ from django.contrib.contenttypes.models import ContentType
3
+ from netbox.plugins import PluginTemplateExtension
4
+
5
+ from . import tables
6
+ from .constants import ASSIGNEMENT_TYPES
7
+ from .models import ContractAssignment
8
+
9
+
10
+ class ObjectContractAssignments(PluginTemplateExtension):
11
+ models = ASSIGNEMENT_TYPES
12
+
13
+ def full_width_page(self):
14
+ display = settings.PLUGINS_CONFIG.get('netbox_contract', {}).get(
15
+ 'contract_assignments_display', 'both'
16
+ )
17
+
18
+ # 'tab' means only the contract tab is shown; inline table stays hidden.
19
+ if display == 'tab':
20
+ return ''
21
+
22
+ # 'inline' or 'both' renders inline table; the tab is still built via ObjectChildrenView.
23
+ if display in ('inline', 'both'):
24
+ object = self.context['object']
25
+ object_type = ContentType.objects.get_for_model(object)
26
+
27
+ contract_assignments = ContractAssignment.objects.filter(
28
+ content_type__pk=object_type.id, object_id=object.id
29
+ )
30
+ assignments_table = tables.ContractAssignmentObjectTable(contract_assignments)
31
+ assignments_table.configure(self.context['request'])
32
+
33
+ return self.render(
34
+ 'contract_assignments_bottom.html',
35
+ extra_context={
36
+ 'assignments_table': assignments_table,
37
+ },
38
+ )
39
+
40
+ return ''
41
+
42
+
43
+ template_extensions = [
44
+ ObjectContractAssignments,
45
+ ]
@@ -42,7 +42,7 @@
42
42
  </tr>
43
43
  <tr>
44
44
  <th scope="row">{% trans "Internal party" %}</th>
45
- <td>{{ object.internal_party }}</td>
45
+ <td>{{ object.get_internal_party_display }}</td>
46
46
  </tr>
47
47
  {% if not 'tenant' in hidden_fields %}
48
48
  <tr>
@@ -0,0 +1,7 @@
1
+ {% extends 'generic/object_children.html' %}
2
+ {% load buttons %}
3
+
4
+ {% block table_controls %}
5
+ {# Only the page header action button is needed (avoid duplicate table button) #}
6
+ {{ block.super }}
7
+ {% endblock table_controls %}
@@ -1,19 +1,23 @@
1
1
  from datetime import date, timedelta
2
2
 
3
3
  from dateutil.relativedelta import relativedelta
4
+ from django.apps import apps
4
5
  from django.conf import settings
5
6
  from django.contrib.contenttypes.models import ContentType
6
7
  from django.core.exceptions import ObjectDoesNotExist
7
8
  from django.db.models import Case, F, When
8
9
  from django.db.models.functions import Round
9
10
  from django.shortcuts import get_object_or_404, render
11
+ from django.utils.translation import gettext_lazy as _
12
+ from netbox.object_actions import *
10
13
  from netbox.views import generic
11
14
  from netbox.views.generic.utils import get_prerequisite_model
12
15
  from utilities.forms import restrict_form_fields
13
16
  from utilities.querydict import normalize_querydict
14
- from utilities.views import register_model_view
17
+ from utilities.views import ViewTab, get_action_url, register_model_view
15
18
 
16
19
  from . import filtersets, forms, tables
20
+ from .constants import ASSIGNEMENT_TYPES
17
21
  from .models import (
18
22
  AccountingDimension,
19
23
  Contract,
@@ -182,6 +186,66 @@ class ContractAssignmentBulkDeleteView(generic.BulkDeleteView):
182
186
  filterset = filtersets.ContractAssignmentFilterSet
183
187
  table = tables.ContractAssignmentListTable
184
188
 
189
+
190
+ class AddContractAssignment(AddObject):
191
+ label = _('Add contract')
192
+
193
+ @classmethod
194
+ def get_url(cls, obj):
195
+ # obj will be the parent object in the custom template (ObjectChildren hook)
196
+ if hasattr(obj, 'pk') and hasattr(obj, '_meta') and obj.pk:
197
+ parent = obj
198
+ parent_ct = ContentType.objects.get_for_model(parent)
199
+ base_url = get_action_url(ContractAssignment, action='add')
200
+ return (
201
+ f"{base_url}?content_type={parent_ct.pk}"
202
+ f"&object_id={parent.pk}&return_url={parent.get_absolute_url()}"
203
+ )
204
+
205
+ # fallback for a class value (if called as model class)
206
+ return get_action_url(ContractAssignment, action='add')
207
+
208
+
209
+ class BaseObjectContractAssignmentView(generic.ObjectChildrenView):
210
+ child_model = ContractAssignment
211
+ table = tables.ContractAssignmentObjectTable
212
+ filterset = filtersets.ContractAssignmentFilterSet
213
+ template_name = 'netbox_contract/object_contracts.html'
214
+ actions = (AddContractAssignment, BulkEdit, BulkDelete)
215
+ tab = ViewTab(
216
+ label=_('Contracts'),
217
+ visible=lambda obj: plugin_settings.get('contract_assignments_display', 'both') != 'inline',
218
+ badge=lambda obj: ContractAssignment.objects.filter(
219
+ content_type=ContentType.objects.get_for_model(obj), object_id=obj.id
220
+ ).count(),
221
+ permission='contracts.view_contractassignment',
222
+ weight=550,
223
+ hide_if_empty=True,
224
+ )
225
+
226
+ def get_children(self, request, parent):
227
+ object_type = ContentType.objects.get_for_model(parent)
228
+ contract_assignments = ContractAssignment.objects.filter(
229
+ content_type__pk=object_type.id, object_id=parent.id
230
+ )
231
+ return contract_assignments
232
+
233
+
234
+ # Dynamically register the view for all supported models
235
+ for model_string in ASSIGNEMENT_TYPES:
236
+ app_label, model_name = model_string.split('.')
237
+ model = apps.get_model(app_label, model_name)
238
+
239
+ class_name = f"{model_name.title()}ContractAssignmentView"
240
+ attrs = {
241
+ 'queryset': model.objects.all(),
242
+ 'viewname': f'netbox_contract:{model_name}_contracts',
243
+ }
244
+ view_class = type(class_name, (BaseObjectContractAssignmentView,), attrs)
245
+
246
+ register_model_view(model, 'contracts', path='contracts')(view_class)
247
+
248
+
185
249
  # Contract views
186
250
 
187
251
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: netbox-contract
3
- Version: 2.4.4
3
+ Version: 2.4.6
4
4
  Summary: Contract management plugin for Netbox
5
5
  Author-email: Marc Lebreuil <marc@famillelebreuil.net>
6
6
  Project-URL: Homepage, https://github.com/mlebreuil/netbox-contract
@@ -49,6 +49,7 @@ Accounting dimensions can be linked with invoice lines.
49
49
  | 4.2 | 2.3 |
50
50
  | 4.3 | 2.4 |
51
51
  | 4.4 | 2.4 |
52
+ | 4.5 | 2.4 |
52
53
 
53
54
  ## Installing
54
55
 
@@ -101,12 +102,17 @@ PLUGINS_CONFIG = {
101
102
  'ipam.ipaddress',
102
103
  'ipam.prefix',
103
104
  ],
105
+ 'contract_assignments_display': 'both', # options: 'tab', 'inline', 'both'
104
106
  }
105
107
  }
106
108
 
107
109
  ```
108
110
 
109
111
  * top_level_menu : If "Contracts" appears under the "Plugins" menu item or on its own
112
+ * contract_assignments_display:
113
+ * `'tab'` - only the Contracts view tab is visible on the related object page.
114
+ * `'inline'` - only the inline contract assignments table is shown in object detail; the Contracts tab is hidden.
115
+ * `'both'` - both tab and inline table are shown (default behavior).
110
116
  * default_accounting_dimensions: The accounting dimensions which will appear in the field' background when empty. Note that accounting dimensions are now managed as individual objects. The use of this field is deprecated.
111
117
  * mandatory_contract_fields, mandatory_invoice_fields: Fields which are not required by default and can be set as such. The list of fields is at the bottom of the contract import form.
112
118
  * hidden_contract_fields, hidden_invoice_fields: List of fields to be hidden. Fields should not be required to be hidden.
@@ -86,4 +86,5 @@ netbox_contract/templates/netbox_contract/contractassignment.html
86
86
  netbox_contract/templates/netbox_contract/contracttype.html
87
87
  netbox_contract/templates/netbox_contract/invoice.html
88
88
  netbox_contract/templates/netbox_contract/invoiceline.html
89
+ netbox_contract/templates/netbox_contract/object_contracts.html
89
90
  netbox_contract/templates/netbox_contract/serviceprovider.html
@@ -7,7 +7,7 @@ build-backend = "setuptools.build_meta"
7
7
 
8
8
  [project]
9
9
  name = "netbox-contract"
10
- version = "2.4.4"
10
+ version = "2.4.6"
11
11
  authors = [
12
12
  { name="Marc Lebreuil", email="marc@famillelebreuil.net" },
13
13
  ]
@@ -1,32 +0,0 @@
1
- from django.contrib.contenttypes.models import ContentType
2
- from netbox.plugins import PluginTemplateExtension
3
-
4
- from . import tables
5
- from .models import ContractAssignment
6
- from .constants import ASSIGNEMENT_TYPES
7
-
8
-
9
- class ObjectContractAssignments(PluginTemplateExtension):
10
- models = ASSIGNEMENT_TYPES
11
-
12
- def full_width_page(self):
13
- object = self.context['object']
14
- object_type = ContentType.objects.get_for_model(object)
15
-
16
- contract_assignments = ContractAssignment.objects.filter(
17
- content_type__pk=object_type.id, object_id=object.id
18
- )
19
- assignments_table = tables.ContractAssignmentObjectTable(contract_assignments)
20
- assignments_table.configure(self.context['request'])
21
-
22
- return self.render(
23
- 'contract_assignments_bottom.html',
24
- extra_context={
25
- 'assignments_table': assignments_table,
26
- },
27
- )
28
-
29
-
30
- template_extensions = [
31
- ObjectContractAssignments,
32
- ]
File without changes
@@ -1,5 +1,5 @@
1
- from django.db.models import Q
2
1
  from django.conf import settings
2
+ from django.db.models import Q
3
3
 
4
4
  plugin_settings = settings.PLUGINS_CONFIG['netbox_contract']
5
5
 
@@ -1,9 +1,9 @@
1
1
  import django_filters
2
- from django.db.models import Q
2
+ from circuits.models import Provider
3
3
  from django.contrib.contenttypes.models import ContentType
4
+ from django.db.models import Q
4
5
  from netbox.filtersets import NetBoxModelFilterSet
5
6
  from tenancy.filtersets import ContactModelFilterSet, TenancyFilterSet
6
- from circuits.models import Provider
7
7
 
8
8
  from .models import (
9
9
  AccountingDimension,
@@ -15,9 +15,9 @@ from .models import (
15
15
  InternalEntityChoices,
16
16
  Invoice,
17
17
  InvoiceLine,
18
+ InvoiceStatusChoices,
18
19
  ServiceProvider,
19
20
  StatusChoices,
20
- InvoiceStatusChoices,
21
21
  )
22
22
 
23
23