netbox-contract 2.2.0__tar.gz → 2.2.2__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.
- {netbox_contract-2.2.0/src/netbox_contract.egg-info → netbox_contract-2.2.2}/PKG-INFO +1 -1
- {netbox_contract-2.2.0 → netbox_contract-2.2.2}/pyproject.toml +1 -1
- {netbox_contract-2.2.0 → netbox_contract-2.2.2}/src/netbox_contract/__init__.py +1 -1
- {netbox_contract-2.2.0 → netbox_contract-2.2.2}/src/netbox_contract/api/serializers.py +25 -6
- {netbox_contract-2.2.0 → netbox_contract-2.2.2}/src/netbox_contract/api/views.py +9 -3
- {netbox_contract-2.2.0 → netbox_contract-2.2.2}/src/netbox_contract/filtersets.py +2 -4
- {netbox_contract-2.2.0 → netbox_contract-2.2.2}/src/netbox_contract/forms.py +29 -6
- netbox_contract-2.2.2/src/netbox_contract/migrations/0034_contract_yrc_alter_contract_mrc.py +26 -0
- netbox_contract-2.2.2/src/netbox_contract/migrations/0035_accountingdimension_status_and_more.py +24 -0
- netbox_contract-2.2.2/src/netbox_contract/migrations/0036_alter_invoiceline_accounting_dimensions.py +19 -0
- {netbox_contract-2.2.0 → netbox_contract-2.2.2}/src/netbox_contract/models.py +37 -4
- {netbox_contract-2.2.0 → netbox_contract-2.2.2}/src/netbox_contract/tables.py +3 -2
- netbox_contract-2.2.2/src/netbox_contract/templates/contract_assignments_bottom.html +23 -0
- {netbox_contract-2.2.0 → netbox_contract-2.2.2}/src/netbox_contract/templates/netbox_contract/accountingdimension.html +4 -0
- {netbox_contract-2.2.0 → netbox_contract-2.2.2}/src/netbox_contract/templates/netbox_contract/contract.html +13 -7
- {netbox_contract-2.2.0 → netbox_contract-2.2.2}/src/netbox_contract/templates/netbox_contract/invoice.html +6 -0
- {netbox_contract-2.2.0 → netbox_contract-2.2.2}/src/netbox_contract/urls.py +20 -0
- {netbox_contract-2.2.0 → netbox_contract-2.2.2}/src/netbox_contract/views.py +27 -5
- {netbox_contract-2.2.0 → netbox_contract-2.2.2/src/netbox_contract.egg-info}/PKG-INFO +1 -1
- {netbox_contract-2.2.0 → netbox_contract-2.2.2}/src/netbox_contract.egg-info/SOURCES.txt +3 -1
- netbox_contract-2.2.0/src/netbox_contract/templates/contract_assignments_bottom.html +0 -21
- netbox_contract-2.2.0/src/netbox_contract/templates/contract_assignments_bottom_old.html +0 -23
- {netbox_contract-2.2.0 → netbox_contract-2.2.2}/LICENSE +0 -0
- {netbox_contract-2.2.0 → netbox_contract-2.2.2}/MANIFEST.in +0 -0
- {netbox_contract-2.2.0 → netbox_contract-2.2.2}/README.md +0 -0
- {netbox_contract-2.2.0 → netbox_contract-2.2.2}/setup.cfg +0 -0
- {netbox_contract-2.2.0 → netbox_contract-2.2.2}/setup.py +0 -0
- {netbox_contract-2.2.0 → netbox_contract-2.2.2}/src/netbox_contract/api/__init__.py +0 -0
- {netbox_contract-2.2.0 → netbox_contract-2.2.2}/src/netbox_contract/api/urls.py +0 -0
- {netbox_contract-2.2.0 → netbox_contract-2.2.2}/src/netbox_contract/constants.py +0 -0
- {netbox_contract-2.2.0 → netbox_contract-2.2.2}/src/netbox_contract/migrations/0001_initial.py +0 -0
- {netbox_contract-2.2.0 → netbox_contract-2.2.2}/src/netbox_contract/migrations/0002_alter_contract_circuit_and_more.py +0 -0
- {netbox_contract-2.2.0 → netbox_contract-2.2.2}/src/netbox_contract/migrations/0003_alter_contract_custom_field_data_and_more.py +0 -0
- {netbox_contract-2.2.0 → netbox_contract-2.2.2}/src/netbox_contract/migrations/0004_contract_currency_invoice_currency.py +0 -0
- {netbox_contract-2.2.0 → netbox_contract-2.2.2}/src/netbox_contract/migrations/0005_contract_accounting_dimensions_and_more.py +0 -0
- {netbox_contract-2.2.0 → netbox_contract-2.2.2}/src/netbox_contract/migrations/0006_alter_contract_circuit.py +0 -0
- {netbox_contract-2.2.0 → netbox_contract-2.2.2}/src/netbox_contract/migrations/0007_invoice_date.py +0 -0
- {netbox_contract-2.2.0 → netbox_contract-2.2.2}/src/netbox_contract/migrations/0008_invoice_comments.py +0 -0
- {netbox_contract-2.2.0 → netbox_contract-2.2.2}/src/netbox_contract/migrations/0009_contract_external_reference.py +0 -0
- {netbox_contract-2.2.0 → netbox_contract-2.2.2}/src/netbox_contract/migrations/0010_invoice_contracts.py +0 -0
- {netbox_contract-2.2.0 → netbox_contract-2.2.2}/src/netbox_contract/migrations/0011_auto_20230122_2112.py +0 -0
- {netbox_contract-2.2.0 → netbox_contract-2.2.2}/src/netbox_contract/migrations/0012_remove_invoice_contract.py +0 -0
- {netbox_contract-2.2.0 → netbox_contract-2.2.2}/src/netbox_contract/migrations/0013_contract_documents_invoice_documents.py +0 -0
- {netbox_contract-2.2.0 → netbox_contract-2.2.2}/src/netbox_contract/migrations/0014_contract_end_date.py +0 -0
- {netbox_contract-2.2.0 → netbox_contract-2.2.2}/src/netbox_contract/migrations/0015_contractassignement.py +0 -0
- {netbox_contract-2.2.0 → netbox_contract-2.2.2}/src/netbox_contract/migrations/0016_contract_parent.py +0 -0
- {netbox_contract-2.2.0 → netbox_contract-2.2.2}/src/netbox_contract/migrations/0017_alter_contract_accounting_dimensions.py +0 -0
- {netbox_contract-2.2.0 → netbox_contract-2.2.2}/src/netbox_contract/migrations/0018_contract_external_partie_object_id_and_more.py +0 -0
- {netbox_contract-2.2.0 → netbox_contract-2.2.2}/src/netbox_contract/migrations/0019_auto_20230924_1813.py +0 -0
- {netbox_contract-2.2.0 → netbox_contract-2.2.2}/src/netbox_contract/migrations/0020_alter_contract_external_partie.py +0 -0
- {netbox_contract-2.2.0 → netbox_contract-2.2.2}/src/netbox_contract/migrations/0021_alter_contract_external_partie.py +0 -0
- {netbox_contract-2.2.0 → netbox_contract-2.2.2}/src/netbox_contract/migrations/0022_alter_contract_internal_partie_alter_contract_parent.py +0 -0
- {netbox_contract-2.2.0 → netbox_contract-2.2.2}/src/netbox_contract/migrations/0023_rename_contractassignement_contractassignment_and_more.py +0 -0
- {netbox_contract-2.2.0 → netbox_contract-2.2.2}/src/netbox_contract/migrations/0024_remove_contract_external_partie.py +0 -0
- {netbox_contract-2.2.0 → netbox_contract-2.2.2}/src/netbox_contract/migrations/0025_remove_contract_circuit_and_more.py +0 -0
- {netbox_contract-2.2.0 → netbox_contract-2.2.2}/src/netbox_contract/migrations/0026_auto_20240421_1550.py +0 -0
- {netbox_contract-2.2.0 → netbox_contract-2.2.2}/src/netbox_contract/migrations/0027_invoiceline.py +0 -0
- {netbox_contract-2.2.0 → netbox_contract-2.2.2}/src/netbox_contract/migrations/0028_invoiceline_accounting_dimensions.py +0 -0
- {netbox_contract-2.2.0 → netbox_contract-2.2.2}/src/netbox_contract/migrations/0029_remove_invoiceline_accounting_dimensions_and_more.py +0 -0
- {netbox_contract-2.2.0 → netbox_contract-2.2.2}/src/netbox_contract/migrations/0030_alter_invoiceline_accounting_dimensions.py +0 -0
- {netbox_contract-2.2.0 → netbox_contract-2.2.2}/src/netbox_contract/migrations/0031_contract_invoice_template_invoice_template.py +0 -0
- {netbox_contract-2.2.0 → netbox_contract-2.2.2}/src/netbox_contract/migrations/0032_alter_invoice_period_end_alter_invoice_period_start_and_more.py +0 -0
- {netbox_contract-2.2.0 → netbox_contract-2.2.2}/src/netbox_contract/migrations/0033_remove_contract_invoice_template.py +0 -0
- {netbox_contract-2.2.0 → netbox_contract-2.2.2}/src/netbox_contract/migrations/__init__.py +0 -0
- {netbox_contract-2.2.0 → netbox_contract-2.2.2}/src/netbox_contract/navigation.py +0 -0
- {netbox_contract-2.2.0 → netbox_contract-2.2.2}/src/netbox_contract/search.py +0 -0
- {netbox_contract-2.2.0 → netbox_contract-2.2.2}/src/netbox_contract/template_content.py +0 -0
- {netbox_contract-2.2.0 → netbox_contract-2.2.2}/src/netbox_contract/templates/contract_list_bottom.html +0 -0
- {netbox_contract-2.2.0 → netbox_contract-2.2.2}/src/netbox_contract/templates/netbox_contract/contractassignment.html +0 -0
- {netbox_contract-2.2.0 → netbox_contract-2.2.2}/src/netbox_contract/templates/netbox_contract/invoiceline.html +0 -0
- {netbox_contract-2.2.0 → netbox_contract-2.2.2}/src/netbox_contract/templates/netbox_contract/serviceprovider.html +0 -0
- {netbox_contract-2.2.0 → netbox_contract-2.2.2}/src/netbox_contract.egg-info/dependency_links.txt +0 -0
- {netbox_contract-2.2.0 → netbox_contract-2.2.2}/src/netbox_contract.egg-info/not-zip-safe +0 -0
- {netbox_contract-2.2.0 → netbox_contract-2.2.2}/src/netbox_contract.egg-info/requires.txt +0 -0
- {netbox_contract-2.2.0 → netbox_contract-2.2.2}/src/netbox_contract.egg-info/top_level.txt +0 -0
|
@@ -5,7 +5,7 @@ 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.2.
|
|
8
|
+
version = '2.2.2'
|
|
9
9
|
author = 'Marc Lebreuil'
|
|
10
10
|
author_email = 'marc@famillelebreuil.net'
|
|
11
11
|
base_url = 'contracts'
|
|
@@ -50,7 +50,7 @@ class NestedInvoiceSerializer(WritableNestedSerializer):
|
|
|
50
50
|
|
|
51
51
|
class NestedContractAssignmentSerializer(WritableNestedSerializer):
|
|
52
52
|
url = serializers.HyperlinkedIdentityField(
|
|
53
|
-
view_name='plugins-api:netbox_contract-api:
|
|
53
|
+
view_name='plugins-api:netbox_contract-api:contractassignment-detail'
|
|
54
54
|
)
|
|
55
55
|
|
|
56
56
|
class Meta:
|
|
@@ -61,18 +61,25 @@ class NestedContractAssignmentSerializer(WritableNestedSerializer):
|
|
|
61
61
|
|
|
62
62
|
class NestedInvoicelineSerializer(WritableNestedSerializer):
|
|
63
63
|
url = serializers.HyperlinkedIdentityField(
|
|
64
|
-
view_name='plugins-api:netbox_contract-api:
|
|
64
|
+
view_name='plugins-api:netbox_contract-api:invoiceline-detail'
|
|
65
65
|
)
|
|
66
66
|
|
|
67
67
|
class Meta:
|
|
68
68
|
model = InvoiceLine
|
|
69
|
-
fields = ('id', 'url', 'display', 'invoice', 'amount')
|
|
70
|
-
brief_fields = (
|
|
69
|
+
fields = ('id', 'url', 'display', 'invoice', 'accounting_dimensions', 'amount')
|
|
70
|
+
brief_fields = (
|
|
71
|
+
'id',
|
|
72
|
+
'url',
|
|
73
|
+
'display',
|
|
74
|
+
'invoice',
|
|
75
|
+
'accounting_dimensions',
|
|
76
|
+
'amount',
|
|
77
|
+
)
|
|
71
78
|
|
|
72
79
|
|
|
73
80
|
class NestedAccountingDimensionSerializer(WritableNestedSerializer):
|
|
74
81
|
url = serializers.HyperlinkedIdentityField(
|
|
75
|
-
view_name='plugins-api:netbox_contract-api:
|
|
82
|
+
view_name='plugins-api:netbox_contract-api:accountingdimension-detail'
|
|
76
83
|
)
|
|
77
84
|
|
|
78
85
|
class Meta:
|
|
@@ -227,6 +234,10 @@ class InvoiceLineSerializer(NetBoxModelSerializer):
|
|
|
227
234
|
url = serializers.HyperlinkedIdentityField(
|
|
228
235
|
view_name='plugins-api:netbox_contract-api:invoiceline-detail'
|
|
229
236
|
)
|
|
237
|
+
invoice = NestedInvoiceSerializer(many=False, required=False)
|
|
238
|
+
accounting_dimensions = NestedAccountingDimensionSerializer(
|
|
239
|
+
many=True, required=False
|
|
240
|
+
)
|
|
230
241
|
|
|
231
242
|
class Meta:
|
|
232
243
|
model = InvoiceLine
|
|
@@ -237,13 +248,21 @@ class InvoiceLineSerializer(NetBoxModelSerializer):
|
|
|
237
248
|
'invoice',
|
|
238
249
|
'amount',
|
|
239
250
|
'currency',
|
|
251
|
+
'accounting_dimensions',
|
|
240
252
|
'comments',
|
|
241
253
|
'tags',
|
|
242
254
|
'custom_fields',
|
|
243
255
|
'created',
|
|
244
256
|
'last_updated',
|
|
245
257
|
)
|
|
246
|
-
brief_fields = (
|
|
258
|
+
brief_fields = (
|
|
259
|
+
'invoice',
|
|
260
|
+
'accounting_dimensions',
|
|
261
|
+
'amount',
|
|
262
|
+
'url',
|
|
263
|
+
'display',
|
|
264
|
+
'name',
|
|
265
|
+
)
|
|
247
266
|
|
|
248
267
|
|
|
249
268
|
class AccountingDimensionSerializer(NetBoxModelSerializer):
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
from django.db.models import F
|
|
1
|
+
from django.db.models import Case, F, When
|
|
2
|
+
from django.db.models.functions import Round
|
|
2
3
|
from netbox.api.viewsets import NetBoxModelViewSet
|
|
3
4
|
|
|
4
5
|
from .. import filtersets, models
|
|
@@ -14,7 +15,10 @@ from .serializers import (
|
|
|
14
15
|
|
|
15
16
|
class ContractViewSet(NetBoxModelViewSet):
|
|
16
17
|
queryset = models.Contract.objects.prefetch_related('parent', 'tags').annotate(
|
|
17
|
-
|
|
18
|
+
calculated_rc=Round(
|
|
19
|
+
Case(When(yrc__gt=0, then=F('yrc') / 12), default=F('mrc') * 12),
|
|
20
|
+
precision=2,
|
|
21
|
+
)
|
|
18
22
|
)
|
|
19
23
|
serializer_class = ContractSerializer
|
|
20
24
|
|
|
@@ -36,7 +40,9 @@ class ContractAssignmentViewSet(NetBoxModelViewSet):
|
|
|
36
40
|
|
|
37
41
|
|
|
38
42
|
class InvoiceLineViewSet(NetBoxModelViewSet):
|
|
39
|
-
queryset = models.InvoiceLine.objects.prefetch_related(
|
|
43
|
+
queryset = models.InvoiceLine.objects.prefetch_related(
|
|
44
|
+
'invoice', 'accounting_dimensions', 'tags'
|
|
45
|
+
)
|
|
40
46
|
serializer_class = InvoiceLineSerializer
|
|
41
47
|
|
|
42
48
|
|
|
@@ -61,7 +61,7 @@ class InvoiceLineFilterSet(NetBoxModelFilterSet):
|
|
|
61
61
|
|
|
62
62
|
def search(self, queryset, name, value):
|
|
63
63
|
return queryset.filter(
|
|
64
|
-
Q(comments__icontains=value) | Q(
|
|
64
|
+
Q(comments__icontains=value) | Q(invoice__number__icontains=value)
|
|
65
65
|
)
|
|
66
66
|
|
|
67
67
|
|
|
@@ -71,6 +71,4 @@ class AccountingDimensionFilterSet(NetBoxModelFilterSet):
|
|
|
71
71
|
fields = ('name', 'value')
|
|
72
72
|
|
|
73
73
|
def search(self, queryset, name, value):
|
|
74
|
-
return queryset.filter(
|
|
75
|
-
Q(comments__icontains=value) | Q(invoice__name__icontains=value)
|
|
76
|
-
)
|
|
74
|
+
return queryset.filter(Q(comments__icontains=value) | Q(name__icontains=value))
|
|
@@ -26,6 +26,7 @@ from utilities.forms.widgets import DatePicker, HTMXSelect
|
|
|
26
26
|
from .constants import SERVICE_PROVIDER_MODELS
|
|
27
27
|
from .models import (
|
|
28
28
|
AccountingDimension,
|
|
29
|
+
AccountingDimensionStatusChoices,
|
|
29
30
|
Contract,
|
|
30
31
|
ContractAssignment,
|
|
31
32
|
InternalEntityChoices,
|
|
@@ -129,6 +130,7 @@ class ContractForm(NetBoxModelForm):
|
|
|
129
130
|
'renewal_term',
|
|
130
131
|
'currency',
|
|
131
132
|
'accounting_dimensions',
|
|
133
|
+
'yrc',
|
|
132
134
|
'mrc',
|
|
133
135
|
'nrc',
|
|
134
136
|
'invoice_frequency',
|
|
@@ -143,6 +145,14 @@ class ContractForm(NetBoxModelForm):
|
|
|
143
145
|
'end_date': DatePicker(),
|
|
144
146
|
}
|
|
145
147
|
|
|
148
|
+
def clean(self):
|
|
149
|
+
super().clean()
|
|
150
|
+
|
|
151
|
+
if self.cleaned_data['mrc'] and self.cleaned_data['yrc']:
|
|
152
|
+
raise ValidationError(
|
|
153
|
+
'you should set monthly OR yearly recuring costs not both'
|
|
154
|
+
)
|
|
155
|
+
|
|
146
156
|
|
|
147
157
|
class ContractFilterSetForm(NetBoxModelFilterSetForm):
|
|
148
158
|
model = Contract
|
|
@@ -193,6 +203,7 @@ class ContractCSVForm(NetBoxModelImportForm):
|
|
|
193
203
|
'renewal_term',
|
|
194
204
|
'currency',
|
|
195
205
|
'accounting_dimensions',
|
|
206
|
+
'yrc',
|
|
196
207
|
'mrc',
|
|
197
208
|
'nrc',
|
|
198
209
|
'invoice_frequency',
|
|
@@ -261,7 +272,11 @@ class InvoiceForm(NetBoxModelForm):
|
|
|
261
272
|
)
|
|
262
273
|
|
|
263
274
|
# Prefix the invoice name with _template
|
|
264
|
-
self.cleaned_data['number'] = '
|
|
275
|
+
self.cleaned_data['number'] = '_invoice_template_' + contract.name
|
|
276
|
+
|
|
277
|
+
# set the periode start and end date to null
|
|
278
|
+
self.cleaned_data['period_start'] = None
|
|
279
|
+
self.cleaned_data['period_end'] = None
|
|
265
280
|
|
|
266
281
|
def save(self, *args, **kwargs):
|
|
267
282
|
is_new = not bool(self.instance.pk)
|
|
@@ -474,10 +489,7 @@ class InvoiceLineForm(NetBoxModelForm):
|
|
|
474
489
|
|
|
475
490
|
class InvoiceLineFilterSetForm(NetBoxModelFilterSetForm):
|
|
476
491
|
model = InvoiceLine
|
|
477
|
-
invoice = DynamicModelChoiceField(queryset=Invoice.objects.all())
|
|
478
|
-
accounting_dimensions = DynamicModelMultipleChoiceField(
|
|
479
|
-
queryset=AccountingDimension.objects.all()
|
|
480
|
-
)
|
|
492
|
+
invoice = DynamicModelChoiceField(queryset=Invoice.objects.all(), required=False)
|
|
481
493
|
|
|
482
494
|
|
|
483
495
|
class InvoiceLineImportForm(NetBoxModelImportForm):
|
|
@@ -488,7 +500,8 @@ class InvoiceLineImportForm(NetBoxModelImportForm):
|
|
|
488
500
|
)
|
|
489
501
|
accounting_dimensions = CSVModelChoiceField(
|
|
490
502
|
queryset=AccountingDimension.objects.all(),
|
|
491
|
-
|
|
503
|
+
to_field_name='id',
|
|
504
|
+
help_text='accounting dimension id',
|
|
492
505
|
)
|
|
493
506
|
|
|
494
507
|
class Meta:
|
|
@@ -520,6 +533,7 @@ class AccountingDimensionForm(NetBoxModelForm):
|
|
|
520
533
|
fields = [
|
|
521
534
|
'name',
|
|
522
535
|
'value',
|
|
536
|
+
'status',
|
|
523
537
|
'comments',
|
|
524
538
|
'tags',
|
|
525
539
|
]
|
|
@@ -528,17 +542,26 @@ class AccountingDimensionForm(NetBoxModelForm):
|
|
|
528
542
|
class AccountingDimensionFilterSetForm(NetBoxModelFilterSetForm):
|
|
529
543
|
model = AccountingDimension
|
|
530
544
|
|
|
545
|
+
name = forms.CharField(required=False)
|
|
546
|
+
value = forms.CharField(required=False)
|
|
547
|
+
status = forms.ChoiceField(choices=AccountingDimensionStatusChoices, required=False)
|
|
548
|
+
|
|
531
549
|
|
|
532
550
|
class AccountingDimensionImportForm(NetBoxModelImportForm):
|
|
551
|
+
status = CSVChoiceField(choices=StatusChoices, help_text='Contract status')
|
|
552
|
+
|
|
533
553
|
class Meta:
|
|
534
554
|
model = AccountingDimension
|
|
535
555
|
fields = [
|
|
536
556
|
'name',
|
|
537
557
|
'value',
|
|
558
|
+
'status',
|
|
538
559
|
'comments',
|
|
539
560
|
'tags',
|
|
540
561
|
]
|
|
541
562
|
|
|
542
563
|
|
|
543
564
|
class AccountingDimensionBulkEditForm(NetBoxModelBulkEditForm):
|
|
565
|
+
name = forms.CharField(max_length=20, required=False)
|
|
566
|
+
value = forms.CharField(max_length=20, required=False)
|
|
544
567
|
model = AccountingDimension
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Generated by Django 5.0.6 on 2024-08-01 15:43
|
|
2
|
+
|
|
3
|
+
from django.db import migrations, models
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Migration(migrations.Migration):
|
|
7
|
+
dependencies = [
|
|
8
|
+
('netbox_contract', '0033_remove_contract_invoice_template'),
|
|
9
|
+
]
|
|
10
|
+
|
|
11
|
+
operations = [
|
|
12
|
+
migrations.AddField(
|
|
13
|
+
model_name='contract',
|
|
14
|
+
name='yrc',
|
|
15
|
+
field=models.DecimalField(
|
|
16
|
+
blank=True, decimal_places=2, max_digits=10, null=True
|
|
17
|
+
),
|
|
18
|
+
),
|
|
19
|
+
migrations.AlterField(
|
|
20
|
+
model_name='contract',
|
|
21
|
+
name='mrc',
|
|
22
|
+
field=models.DecimalField(
|
|
23
|
+
blank=True, decimal_places=2, max_digits=10, null=True
|
|
24
|
+
),
|
|
25
|
+
),
|
|
26
|
+
]
|
netbox_contract-2.2.2/src/netbox_contract/migrations/0035_accountingdimension_status_and_more.py
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Generated by Django 5.0.6 on 2024-08-18 10:03
|
|
2
|
+
|
|
3
|
+
from django.db import migrations, models
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Migration(migrations.Migration):
|
|
7
|
+
dependencies = [
|
|
8
|
+
('extras', '0115_convert_dashboard_widgets'),
|
|
9
|
+
('netbox_contract', '0034_contract_yrc_alter_contract_mrc'),
|
|
10
|
+
]
|
|
11
|
+
|
|
12
|
+
operations = [
|
|
13
|
+
migrations.AddField(
|
|
14
|
+
model_name='accountingdimension',
|
|
15
|
+
name='status',
|
|
16
|
+
field=models.CharField(default='Active', max_length=50),
|
|
17
|
+
),
|
|
18
|
+
migrations.AddConstraint(
|
|
19
|
+
model_name='accountingdimension',
|
|
20
|
+
constraint=models.UniqueConstraint(
|
|
21
|
+
fields=('name', 'value'), name='unique_accounting_dimension'
|
|
22
|
+
),
|
|
23
|
+
),
|
|
24
|
+
]
|
netbox_contract-2.2.2/src/netbox_contract/migrations/0036_alter_invoiceline_accounting_dimensions.py
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Generated by Django 5.0.6 on 2024-08-18 19:37
|
|
2
|
+
|
|
3
|
+
from django.db import migrations, models
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Migration(migrations.Migration):
|
|
7
|
+
dependencies = [
|
|
8
|
+
('netbox_contract', '0035_accountingdimension_status_and_more'),
|
|
9
|
+
]
|
|
10
|
+
|
|
11
|
+
operations = [
|
|
12
|
+
migrations.AlterField(
|
|
13
|
+
model_name='invoiceline',
|
|
14
|
+
name='accounting_dimensions',
|
|
15
|
+
field=models.ManyToManyField(
|
|
16
|
+
blank=True, to='netbox_contract.accountingdimension'
|
|
17
|
+
),
|
|
18
|
+
),
|
|
19
|
+
]
|
|
@@ -20,6 +20,18 @@ class StatusChoices(ChoiceSet):
|
|
|
20
20
|
]
|
|
21
21
|
|
|
22
22
|
|
|
23
|
+
class AccountingDimensionStatusChoices(ChoiceSet):
|
|
24
|
+
key = 'AccountingDimension.status'
|
|
25
|
+
|
|
26
|
+
STATUS_ACTIVE = 'Active'
|
|
27
|
+
STATUS_INACTIVE = 'Inactive'
|
|
28
|
+
|
|
29
|
+
CHOICES = [
|
|
30
|
+
(STATUS_ACTIVE, 'Active', 'green'),
|
|
31
|
+
(STATUS_INACTIVE, 'Inactive', 'red'),
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
|
|
23
35
|
class InternalEntityChoices(ChoiceSet):
|
|
24
36
|
key = 'Contract.internal_partie'
|
|
25
37
|
|
|
@@ -44,6 +56,11 @@ class CurrencyChoices(ChoiceSet):
|
|
|
44
56
|
class AccountingDimension(NetBoxModel):
|
|
45
57
|
name = models.CharField(max_length=20)
|
|
46
58
|
value = models.CharField(max_length=20)
|
|
59
|
+
status = models.CharField(
|
|
60
|
+
max_length=50,
|
|
61
|
+
choices=AccountingDimensionStatusChoices,
|
|
62
|
+
default=StatusChoices.STATUS_ACTIVE,
|
|
63
|
+
)
|
|
47
64
|
comments = models.TextField(blank=True)
|
|
48
65
|
|
|
49
66
|
def get_absolute_url(self):
|
|
@@ -56,6 +73,13 @@ class AccountingDimension(NetBoxModel):
|
|
|
56
73
|
def __str__(self):
|
|
57
74
|
return self.dimension
|
|
58
75
|
|
|
76
|
+
class Meta:
|
|
77
|
+
constraints = [
|
|
78
|
+
models.UniqueConstraint(
|
|
79
|
+
fields=['name', 'value'], name='unique_accounting_dimension'
|
|
80
|
+
)
|
|
81
|
+
]
|
|
82
|
+
|
|
59
83
|
|
|
60
84
|
class ServiceProvider(ContactsMixin, NetBoxModel):
|
|
61
85
|
name = models.CharField(max_length=100)
|
|
@@ -130,8 +154,19 @@ class Contract(NetBoxModel):
|
|
|
130
154
|
max_length=3, choices=CurrencyChoices, default=CurrencyChoices.CURRENCY_USD
|
|
131
155
|
)
|
|
132
156
|
accounting_dimensions = models.JSONField(null=True, blank=True)
|
|
157
|
+
yrc = models.DecimalField(
|
|
158
|
+
verbose_name='yearly recuring cost',
|
|
159
|
+
max_digits=10,
|
|
160
|
+
decimal_places=2,
|
|
161
|
+
blank=True,
|
|
162
|
+
null=True,
|
|
163
|
+
)
|
|
133
164
|
mrc = models.DecimalField(
|
|
134
|
-
verbose_name='Monthly recuring cost',
|
|
165
|
+
verbose_name='Monthly recuring cost',
|
|
166
|
+
max_digits=10,
|
|
167
|
+
decimal_places=2,
|
|
168
|
+
blank=True,
|
|
169
|
+
null=True,
|
|
135
170
|
)
|
|
136
171
|
nrc = models.DecimalField(
|
|
137
172
|
verbose_name='None recuring cost', default=0, max_digits=10, decimal_places=2
|
|
@@ -204,9 +239,7 @@ class InvoiceLine(NetBoxModel):
|
|
|
204
239
|
max_length=3, choices=CurrencyChoices, default=CurrencyChoices.CURRENCY_USD
|
|
205
240
|
)
|
|
206
241
|
amount = models.DecimalField(max_digits=10, decimal_places=2)
|
|
207
|
-
accounting_dimensions = models.ManyToManyField(
|
|
208
|
-
AccountingDimension, related_name='invoicelines', blank=True
|
|
209
|
-
)
|
|
242
|
+
accounting_dimensions = models.ManyToManyField(AccountingDimension, blank=True)
|
|
210
243
|
comments = models.TextField(blank=True)
|
|
211
244
|
|
|
212
245
|
def get_absolute_url(self):
|
|
@@ -42,14 +42,15 @@ class ContractAssignmentListTable(NetBoxTable):
|
|
|
42
42
|
class ContractAssignmentObjectTable(NetBoxTable):
|
|
43
43
|
contract = tables.Column(linkify=True)
|
|
44
44
|
actions = columns.ActionsColumn(actions=('edit', 'delete'))
|
|
45
|
-
contract__external_partie_object = tables.Column(
|
|
45
|
+
contract__external_partie_object = tables.Column(
|
|
46
|
+
verbose_name='Partner', linkify=True
|
|
47
|
+
)
|
|
46
48
|
|
|
47
49
|
class Meta(NetBoxTable.Meta):
|
|
48
50
|
model = ContractAssignment
|
|
49
51
|
fields = (
|
|
50
52
|
'pk',
|
|
51
53
|
'contract',
|
|
52
|
-
'contract__external_partie_object_type',
|
|
53
54
|
'contract__external_partie_object',
|
|
54
55
|
'contract__status',
|
|
55
56
|
'contract__start_date',
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{% load render_table from django_tables2 %}
|
|
2
|
+
{% if perms.netbox_contract.view_contract %}
|
|
3
|
+
<div class="row">
|
|
4
|
+
<div class="col col-md-12">
|
|
5
|
+
<div class="card">
|
|
6
|
+
<h5 class="card-header">
|
|
7
|
+
Contracts Assignments
|
|
8
|
+
{% if perms.netbox_contract.add_contractassignment %}
|
|
9
|
+
<div class="card-actions">
|
|
10
|
+
<a href="{% url 'plugins:netbox_contract:contractassignment_add' %}?content_type={{ object|content_type_id }}&object_id={{ object.pk }}&return_url={{ object.get_absolute_url }}" class="btn btn-ghost-primary btn-sm">
|
|
11
|
+
<i class="mdi mdi-plus-thick" aria-hidden="true"></i>
|
|
12
|
+
Add assignment
|
|
13
|
+
</a>
|
|
14
|
+
</div>
|
|
15
|
+
{% endif %}
|
|
16
|
+
</h5>
|
|
17
|
+
{% if assignments_table %}
|
|
18
|
+
{% render_table assignments_table %}
|
|
19
|
+
{% endif %}
|
|
20
|
+
</div>
|
|
21
|
+
</div>
|
|
22
|
+
</div>
|
|
23
|
+
{% endif %}
|
|
@@ -85,6 +85,10 @@
|
|
|
85
85
|
<th scope="row">Yearly recuring costs</th>
|
|
86
86
|
<td>{{ object.yrc }}</td>
|
|
87
87
|
</tr>
|
|
88
|
+
<tr>
|
|
89
|
+
<th scope="row">calculated corresponding Yearly or Monthly value</th>
|
|
90
|
+
<td>{{ object.calculated_rc }}</td>
|
|
91
|
+
</tr>
|
|
88
92
|
<tr>
|
|
89
93
|
<th scope="row">Non recuring costs</th>
|
|
90
94
|
<td>{{ object.nrc }}</td>
|
|
@@ -138,16 +142,18 @@
|
|
|
138
142
|
<div class="row">
|
|
139
143
|
<div class="col col-md-12">
|
|
140
144
|
<div class="card">
|
|
141
|
-
<h5 class="card-header">
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
145
|
+
<h5 class="card-header">
|
|
146
|
+
Invoices
|
|
147
|
+
{% if perms.netbox_contract.add_invoice %}
|
|
148
|
+
<div class="card-actions">
|
|
149
|
+
<a href="{% url 'plugins:netbox_contract:invoice_add' %}?contracts={{ object.pk }}" class="btn btn-ghost-primary btn-sm">
|
|
146
150
|
<i class="mdi mdi-plus-thick" aria-hidden="true"></i>
|
|
147
151
|
Add an invoice
|
|
148
152
|
</a>
|
|
149
|
-
|
|
150
|
-
|
|
153
|
+
</div>
|
|
154
|
+
{% endif %}
|
|
155
|
+
</h5>
|
|
156
|
+
{% render_table invoices_table %}
|
|
151
157
|
</div>
|
|
152
158
|
</div>
|
|
153
159
|
</div>
|
|
@@ -19,10 +19,15 @@
|
|
|
19
19
|
<th scope="row">Number</th>
|
|
20
20
|
<td>{{ object.number }}</td>
|
|
21
21
|
</tr>
|
|
22
|
+
<tr>
|
|
23
|
+
<th scope="row">Template</th>
|
|
24
|
+
<td>{{ object.template }}</td>
|
|
25
|
+
</tr>
|
|
22
26
|
<tr>
|
|
23
27
|
<th scope="row">Date</th>
|
|
24
28
|
<td>{{ object.date }}</td>
|
|
25
29
|
</tr>
|
|
30
|
+
{% if not object.template %}
|
|
26
31
|
<tr>
|
|
27
32
|
<th scope="row">Period start</th>
|
|
28
33
|
<td>{{ object.period_start }}</td>
|
|
@@ -31,6 +36,7 @@
|
|
|
31
36
|
<th scope="row">Period end</th>
|
|
32
37
|
<td>{{ object.period_end }}</td>
|
|
33
38
|
</tr>
|
|
39
|
+
{% endif %}
|
|
34
40
|
<tr>
|
|
35
41
|
<th scope="row">Currency</th>
|
|
36
42
|
<td>{{ object.currency }}</td>
|
|
@@ -182,6 +182,16 @@ urlpatterns = (
|
|
|
182
182
|
views.InvoiceLineBulkImportView.as_view(),
|
|
183
183
|
name='invoiceline_import',
|
|
184
184
|
),
|
|
185
|
+
path(
|
|
186
|
+
'invoiceline/edit/',
|
|
187
|
+
views.InvoiceLineBulkEditView.as_view(),
|
|
188
|
+
name='invoiceline_bulk_edit',
|
|
189
|
+
),
|
|
190
|
+
path(
|
|
191
|
+
'invoiceline/delete/',
|
|
192
|
+
views.InvoiceLineBulkDeleteView.as_view(),
|
|
193
|
+
name='invoiceline_bulk_delete',
|
|
194
|
+
),
|
|
185
195
|
path(
|
|
186
196
|
'invoiceline/<int:pk>/',
|
|
187
197
|
views.InvoiceLineView.as_view(),
|
|
@@ -219,6 +229,16 @@ urlpatterns = (
|
|
|
219
229
|
views.AccountingDimensionBulkImportView.as_view(),
|
|
220
230
|
name='accountingdimension_import',
|
|
221
231
|
),
|
|
232
|
+
path(
|
|
233
|
+
'accountingdimension/edit/',
|
|
234
|
+
views.AccountingDimensionBulkEditView.as_view(),
|
|
235
|
+
name='accountingdimension_bulk_edit',
|
|
236
|
+
),
|
|
237
|
+
path(
|
|
238
|
+
'accountingdimension/delete/',
|
|
239
|
+
views.AccountingDimensionBulkDeleteView.as_view(),
|
|
240
|
+
name='accountingdimension_bulk_delete',
|
|
241
|
+
),
|
|
222
242
|
path(
|
|
223
243
|
'accountingdimension/<int:pk>/',
|
|
224
244
|
views.AccountingDimensionView.as_view(),
|
|
@@ -4,7 +4,8 @@ from dateutil.relativedelta import relativedelta
|
|
|
4
4
|
from django.conf import settings
|
|
5
5
|
from django.contrib.contenttypes.models import ContentType
|
|
6
6
|
from django.core.exceptions import ObjectDoesNotExist
|
|
7
|
-
from django.db.models import F
|
|
7
|
+
from django.db.models import Case, F, When
|
|
8
|
+
from django.db.models.functions import Round
|
|
8
9
|
from django.shortcuts import get_object_or_404, render
|
|
9
10
|
from netbox.views import generic
|
|
10
11
|
from netbox.views.generic.utils import get_prerequisite_model
|
|
@@ -129,7 +130,12 @@ class ContractAssignmentBulkImportView(generic.BulkImportView):
|
|
|
129
130
|
|
|
130
131
|
@register_model_view(Contract)
|
|
131
132
|
class ContractView(generic.ObjectView):
|
|
132
|
-
queryset = Contract.objects.annotate(
|
|
133
|
+
queryset = Contract.objects.annotate(
|
|
134
|
+
calculated_rc=Round(
|
|
135
|
+
Case(When(yrc__gt=0, then=F('yrc') / 12), default=F('mrc') * 12),
|
|
136
|
+
precision=2,
|
|
137
|
+
)
|
|
138
|
+
)
|
|
133
139
|
|
|
134
140
|
def get_extra_context(self, request, instance):
|
|
135
141
|
invoices_table = tables.InvoiceListTable(instance.invoices.all())
|
|
@@ -152,7 +158,12 @@ class ContractView(generic.ObjectView):
|
|
|
152
158
|
|
|
153
159
|
|
|
154
160
|
class ContractListView(generic.ObjectListView):
|
|
155
|
-
queryset = Contract.objects.annotate(
|
|
161
|
+
queryset = Contract.objects.annotate(
|
|
162
|
+
calculated_rc=Round(
|
|
163
|
+
Case(When(yrc__gt=0, then=F('yrc') / 12), default=F('mrc') * 12),
|
|
164
|
+
precision=2,
|
|
165
|
+
)
|
|
166
|
+
)
|
|
156
167
|
table = tables.ContractListTable
|
|
157
168
|
filterset = filtersets.ContractFilterSet
|
|
158
169
|
filterset_form = forms.ContractFilterSetForm
|
|
@@ -267,7 +278,9 @@ class InvoiceEditView(generic.ObjectEditView):
|
|
|
267
278
|
contract = Contract.objects.get(pk=initial_data['contracts'])
|
|
268
279
|
|
|
269
280
|
try:
|
|
270
|
-
last_invoice = contract.invoices.latest(
|
|
281
|
+
last_invoice = contract.invoices.filter(template=False).latest(
|
|
282
|
+
'period_end'
|
|
283
|
+
)
|
|
271
284
|
new_period_start = last_invoice.period_end + timedelta(days=1)
|
|
272
285
|
except ObjectDoesNotExist:
|
|
273
286
|
if contract.start_date:
|
|
@@ -281,7 +294,16 @@ class InvoiceEditView(generic.ObjectEditView):
|
|
|
281
294
|
new_period_end = new_period_start + delta - timedelta(days=1)
|
|
282
295
|
initial_data['period_end'] = new_period_end
|
|
283
296
|
|
|
284
|
-
|
|
297
|
+
if contract.yrc:
|
|
298
|
+
if contract.invoice_frequency == 12:
|
|
299
|
+
initial_data['amount'] = contract.yrc
|
|
300
|
+
else:
|
|
301
|
+
initial_data['amount'] = round(
|
|
302
|
+
contract.yrc / 12 * contract.invoice_frequency, 2
|
|
303
|
+
)
|
|
304
|
+
else:
|
|
305
|
+
initial_data['amount'] = contract.mrc * contract.invoice_frequency
|
|
306
|
+
|
|
285
307
|
initial_data['currency'] = contract.currency
|
|
286
308
|
if contract.accounting_dimensions:
|
|
287
309
|
initial_data['accounting_dimensions'] = contract.accounting_dimensions
|
|
@@ -57,9 +57,11 @@ src/netbox_contract/migrations/0030_alter_invoiceline_accounting_dimensions.py
|
|
|
57
57
|
src/netbox_contract/migrations/0031_contract_invoice_template_invoice_template.py
|
|
58
58
|
src/netbox_contract/migrations/0032_alter_invoice_period_end_alter_invoice_period_start_and_more.py
|
|
59
59
|
src/netbox_contract/migrations/0033_remove_contract_invoice_template.py
|
|
60
|
+
src/netbox_contract/migrations/0034_contract_yrc_alter_contract_mrc.py
|
|
61
|
+
src/netbox_contract/migrations/0035_accountingdimension_status_and_more.py
|
|
62
|
+
src/netbox_contract/migrations/0036_alter_invoiceline_accounting_dimensions.py
|
|
60
63
|
src/netbox_contract/migrations/__init__.py
|
|
61
64
|
src/netbox_contract/templates/contract_assignments_bottom.html
|
|
62
|
-
src/netbox_contract/templates/contract_assignments_bottom_old.html
|
|
63
65
|
src/netbox_contract/templates/contract_list_bottom.html
|
|
64
66
|
src/netbox_contract/templates/netbox_contract/accountingdimension.html
|
|
65
67
|
src/netbox_contract/templates/netbox_contract/contract.html
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
{% load render_table from django_tables2 %}
|
|
2
|
-
{% if perms.netbox_contract.view_contract %}
|
|
3
|
-
<div class="row">
|
|
4
|
-
<div class="col col-md-12">
|
|
5
|
-
<div class="card">
|
|
6
|
-
<h5 class="card-header">Contracts Assignments</h5>
|
|
7
|
-
{% if assignments_table %}
|
|
8
|
-
{% render_table assignments_table %}
|
|
9
|
-
{% endif %}
|
|
10
|
-
{% if perms.netbox_contract.add_contractassignment %}
|
|
11
|
-
<div class="card-footer text-end noprint">
|
|
12
|
-
<a href="{% url 'plugins:netbox_contract:contractassignment_add' %}?content_type={{ object|content_type_id }}&object_id={{ object.pk }}&return_url={{ object.get_absolute_url }}" class="btn btn-primary">
|
|
13
|
-
<i class="mdi mdi-plus-thick" aria-hidden="true"></i>
|
|
14
|
-
Add assignment
|
|
15
|
-
</a>
|
|
16
|
-
</div>
|
|
17
|
-
{% endif %}
|
|
18
|
-
</div>
|
|
19
|
-
</div>
|
|
20
|
-
</div>
|
|
21
|
-
{% endif %}
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
{% load render_table from django_tables2 %}
|
|
2
|
-
{% if perms.netbox_contract.view_contract %}
|
|
3
|
-
<div class="row">
|
|
4
|
-
<div class="col col-md-12">
|
|
5
|
-
<div class="card">
|
|
6
|
-
<h5 class="card-header">Contracts Assignments</h5>
|
|
7
|
-
{% if assignments_table %}
|
|
8
|
-
<div class="card-body table-responsive">
|
|
9
|
-
{% render_table assignments_table %}
|
|
10
|
-
</div>
|
|
11
|
-
{% endif %}
|
|
12
|
-
{% if perms.netbox_contract.add_contractassignment %}
|
|
13
|
-
<div class="card-footer text-end noprint">
|
|
14
|
-
<a href="{% url 'plugins:netbox_contract:contractassignment_add' %}?content_type={{ object|content_type_id }}&object_id={{ object.pk }}&return_url={{ object.get_absolute_url }}" class="btn btn-primary">
|
|
15
|
-
<i class="mdi mdi-plus-thick" aria-hidden="true"></i>
|
|
16
|
-
Add assignment
|
|
17
|
-
</a>
|
|
18
|
-
</div>
|
|
19
|
-
{% endif %}
|
|
20
|
-
</div>
|
|
21
|
-
</div>
|
|
22
|
-
</div>
|
|
23
|
-
{% endif %}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{netbox_contract-2.2.0 → netbox_contract-2.2.2}/src/netbox_contract/migrations/0001_initial.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{netbox_contract-2.2.0 → netbox_contract-2.2.2}/src/netbox_contract/migrations/0007_invoice_date.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{netbox_contract-2.2.0 → netbox_contract-2.2.2}/src/netbox_contract/migrations/0027_invoiceline.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{netbox_contract-2.2.0 → netbox_contract-2.2.2}/src/netbox_contract.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|