netbox-contract 2.3.0__tar.gz → 2.3.1__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.3.1/CONTRIBUTING.md +144 -0
- netbox_contract-2.3.1/MANIFEST.in +13 -0
- {netbox_contract-2.3.0/src/netbox_contract.egg-info → netbox_contract-2.3.1}/PKG-INFO +23 -14
- {netbox_contract-2.3.0 → netbox_contract-2.3.1}/README.md +22 -13
- netbox_contract-2.3.1/docs/img/accounting_dimensions.png +0 -0
- netbox_contract-2.3.1/docs/img/contract.png +0 -0
- netbox_contract-2.3.1/docs/img/contract_linked_objects.png +0 -0
- netbox_contract-2.3.1/docs/img/invoice.png +0 -0
- netbox_contract-2.3.1/docs/img/invoice_line.png +0 -0
- netbox_contract-2.3.1/docs/img/invoice_linked_objects.png +0 -0
- {netbox_contract-2.3.0 → netbox_contract-2.3.1}/pyproject.toml +1 -1
- {netbox_contract-2.3.0 → netbox_contract-2.3.1}/src/netbox_contract/__init__.py +2 -2
- {netbox_contract-2.3.0 → netbox_contract-2.3.1}/src/netbox_contract/forms.py +26 -80
- {netbox_contract-2.3.0 → netbox_contract-2.3.1}/src/netbox_contract/models.py +16 -42
- {netbox_contract-2.3.0 → netbox_contract-2.3.1}/src/netbox_contract/tables.py +11 -4
- {netbox_contract-2.3.0 → netbox_contract-2.3.1}/src/netbox_contract/templates/netbox_contract/contract.html +3 -3
- {netbox_contract-2.3.0 → netbox_contract-2.3.1}/src/netbox_contract/templates/netbox_contract/contractassignment.html +1 -1
- {netbox_contract-2.3.0 → netbox_contract-2.3.1}/src/netbox_contract/templates/netbox_contract/invoice.html +1 -1
- {netbox_contract-2.3.0 → netbox_contract-2.3.1}/src/netbox_contract/templates/netbox_contract/invoiceline.html +1 -1
- {netbox_contract-2.3.0 → netbox_contract-2.3.1/src/netbox_contract.egg-info}/PKG-INFO +23 -14
- {netbox_contract-2.3.0 → netbox_contract-2.3.1}/src/netbox_contract.egg-info/SOURCES.txt +10 -1
- netbox_contract-2.3.1/tests/__init__.py +1 -0
- netbox_contract-2.3.1/tests/test_netbox_contract.py +5 -0
- netbox_contract-2.3.0/MANIFEST.in +0 -2
- {netbox_contract-2.3.0 → netbox_contract-2.3.1}/LICENSE +0 -0
- {netbox_contract-2.3.0 → netbox_contract-2.3.1}/setup.cfg +0 -0
- {netbox_contract-2.3.0 → netbox_contract-2.3.1}/setup.py +0 -0
- {netbox_contract-2.3.0 → netbox_contract-2.3.1}/src/netbox_contract/api/__init__.py +0 -0
- {netbox_contract-2.3.0 → netbox_contract-2.3.1}/src/netbox_contract/api/serializers.py +0 -0
- {netbox_contract-2.3.0 → netbox_contract-2.3.1}/src/netbox_contract/api/urls.py +0 -0
- {netbox_contract-2.3.0 → netbox_contract-2.3.1}/src/netbox_contract/api/views.py +0 -0
- {netbox_contract-2.3.0 → netbox_contract-2.3.1}/src/netbox_contract/constants.py +0 -0
- {netbox_contract-2.3.0 → netbox_contract-2.3.1}/src/netbox_contract/filtersets.py +0 -0
- {netbox_contract-2.3.0 → netbox_contract-2.3.1}/src/netbox_contract/migrations/0001_initial.py +0 -0
- {netbox_contract-2.3.0 → netbox_contract-2.3.1}/src/netbox_contract/migrations/0002_alter_contract_circuit_and_more.py +0 -0
- {netbox_contract-2.3.0 → netbox_contract-2.3.1}/src/netbox_contract/migrations/0003_alter_contract_custom_field_data_and_more.py +0 -0
- {netbox_contract-2.3.0 → netbox_contract-2.3.1}/src/netbox_contract/migrations/0004_contract_currency_invoice_currency.py +0 -0
- {netbox_contract-2.3.0 → netbox_contract-2.3.1}/src/netbox_contract/migrations/0005_contract_accounting_dimensions_and_more.py +0 -0
- {netbox_contract-2.3.0 → netbox_contract-2.3.1}/src/netbox_contract/migrations/0006_alter_contract_circuit.py +0 -0
- {netbox_contract-2.3.0 → netbox_contract-2.3.1}/src/netbox_contract/migrations/0007_invoice_date.py +0 -0
- {netbox_contract-2.3.0 → netbox_contract-2.3.1}/src/netbox_contract/migrations/0008_invoice_comments.py +0 -0
- {netbox_contract-2.3.0 → netbox_contract-2.3.1}/src/netbox_contract/migrations/0009_contract_external_reference.py +0 -0
- {netbox_contract-2.3.0 → netbox_contract-2.3.1}/src/netbox_contract/migrations/0010_invoice_contracts.py +0 -0
- {netbox_contract-2.3.0 → netbox_contract-2.3.1}/src/netbox_contract/migrations/0011_auto_20230122_2112.py +0 -0
- {netbox_contract-2.3.0 → netbox_contract-2.3.1}/src/netbox_contract/migrations/0012_remove_invoice_contract.py +0 -0
- {netbox_contract-2.3.0 → netbox_contract-2.3.1}/src/netbox_contract/migrations/0013_contract_documents_invoice_documents.py +0 -0
- {netbox_contract-2.3.0 → netbox_contract-2.3.1}/src/netbox_contract/migrations/0014_contract_end_date.py +0 -0
- {netbox_contract-2.3.0 → netbox_contract-2.3.1}/src/netbox_contract/migrations/0015_contractassignement.py +0 -0
- {netbox_contract-2.3.0 → netbox_contract-2.3.1}/src/netbox_contract/migrations/0016_contract_parent.py +0 -0
- {netbox_contract-2.3.0 → netbox_contract-2.3.1}/src/netbox_contract/migrations/0017_alter_contract_accounting_dimensions.py +0 -0
- {netbox_contract-2.3.0 → netbox_contract-2.3.1}/src/netbox_contract/migrations/0018_contract_external_partie_object_id_and_more.py +0 -0
- {netbox_contract-2.3.0 → netbox_contract-2.3.1}/src/netbox_contract/migrations/0019_auto_20230924_1813.py +0 -0
- {netbox_contract-2.3.0 → netbox_contract-2.3.1}/src/netbox_contract/migrations/0020_alter_contract_external_partie.py +0 -0
- {netbox_contract-2.3.0 → netbox_contract-2.3.1}/src/netbox_contract/migrations/0021_alter_contract_external_partie.py +0 -0
- {netbox_contract-2.3.0 → netbox_contract-2.3.1}/src/netbox_contract/migrations/0022_alter_contract_internal_partie_alter_contract_parent.py +0 -0
- {netbox_contract-2.3.0 → netbox_contract-2.3.1}/src/netbox_contract/migrations/0023_rename_contractassignement_contractassignment_and_more.py +0 -0
- {netbox_contract-2.3.0 → netbox_contract-2.3.1}/src/netbox_contract/migrations/0024_remove_contract_external_partie.py +0 -0
- {netbox_contract-2.3.0 → netbox_contract-2.3.1}/src/netbox_contract/migrations/0025_remove_contract_circuit_and_more.py +0 -0
- {netbox_contract-2.3.0 → netbox_contract-2.3.1}/src/netbox_contract/migrations/0026_auto_20240421_1550.py +0 -0
- {netbox_contract-2.3.0 → netbox_contract-2.3.1}/src/netbox_contract/migrations/0027_invoiceline.py +0 -0
- {netbox_contract-2.3.0 → netbox_contract-2.3.1}/src/netbox_contract/migrations/0028_invoiceline_accounting_dimensions.py +0 -0
- {netbox_contract-2.3.0 → netbox_contract-2.3.1}/src/netbox_contract/migrations/0029_remove_invoiceline_accounting_dimensions_and_more.py +0 -0
- {netbox_contract-2.3.0 → netbox_contract-2.3.1}/src/netbox_contract/migrations/0030_alter_invoiceline_accounting_dimensions.py +0 -0
- {netbox_contract-2.3.0 → netbox_contract-2.3.1}/src/netbox_contract/migrations/0031_contract_invoice_template_invoice_template.py +0 -0
- {netbox_contract-2.3.0 → netbox_contract-2.3.1}/src/netbox_contract/migrations/0032_alter_invoice_period_end_alter_invoice_period_start_and_more.py +0 -0
- {netbox_contract-2.3.0 → netbox_contract-2.3.1}/src/netbox_contract/migrations/0033_remove_contract_invoice_template.py +0 -0
- {netbox_contract-2.3.0 → netbox_contract-2.3.1}/src/netbox_contract/migrations/0034_contract_yrc_alter_contract_mrc.py +0 -0
- {netbox_contract-2.3.0 → netbox_contract-2.3.1}/src/netbox_contract/migrations/0035_accountingdimension_status_and_more.py +0 -0
- {netbox_contract-2.3.0 → netbox_contract-2.3.1}/src/netbox_contract/migrations/0036_alter_invoiceline_accounting_dimensions.py +0 -0
- {netbox_contract-2.3.0 → netbox_contract-2.3.1}/src/netbox_contract/migrations/0037_contract_notice_period_alter_contract_currency_and_more.py +0 -0
- {netbox_contract-2.3.0 → netbox_contract-2.3.1}/src/netbox_contract/migrations/0038_alter_accountingdimension_options_and_more.py +0 -0
- {netbox_contract-2.3.0 → netbox_contract-2.3.1}/src/netbox_contract/migrations/0039_alter_accountingdimension_status_and_more.py +0 -0
- {netbox_contract-2.3.0 → netbox_contract-2.3.1}/src/netbox_contract/migrations/__init__.py +0 -0
- {netbox_contract-2.3.0 → netbox_contract-2.3.1}/src/netbox_contract/navigation.py +0 -0
- {netbox_contract-2.3.0 → netbox_contract-2.3.1}/src/netbox_contract/search.py +0 -0
- {netbox_contract-2.3.0 → netbox_contract-2.3.1}/src/netbox_contract/template_content.py +0 -0
- {netbox_contract-2.3.0 → netbox_contract-2.3.1}/src/netbox_contract/templates/contract_assignments_bottom.html +0 -0
- {netbox_contract-2.3.0 → netbox_contract-2.3.1}/src/netbox_contract/templates/contract_list_bottom.html +0 -0
- {netbox_contract-2.3.0 → netbox_contract-2.3.1}/src/netbox_contract/templates/netbox_contract/accountingdimension.html +0 -0
- {netbox_contract-2.3.0 → netbox_contract-2.3.1}/src/netbox_contract/templates/netbox_contract/serviceprovider.html +0 -0
- {netbox_contract-2.3.0 → netbox_contract-2.3.1}/src/netbox_contract/urls.py +0 -0
- {netbox_contract-2.3.0 → netbox_contract-2.3.1}/src/netbox_contract/views.py +0 -0
- {netbox_contract-2.3.0 → netbox_contract-2.3.1}/src/netbox_contract.egg-info/dependency_links.txt +0 -0
- {netbox_contract-2.3.0 → netbox_contract-2.3.1}/src/netbox_contract.egg-info/not-zip-safe +0 -0
- {netbox_contract-2.3.0 → netbox_contract-2.3.1}/src/netbox_contract.egg-info/requires.txt +0 -0
- {netbox_contract-2.3.0 → netbox_contract-2.3.1}/src/netbox_contract.egg-info/top_level.txt +0 -0
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
Contributions are welcome!
|
|
4
|
+
|
|
5
|
+
## Types of contributions
|
|
6
|
+
|
|
7
|
+
### Report bugs or submit feedback
|
|
8
|
+
|
|
9
|
+
Report bugs and submit feetback [here](https://github.com/mlebreuil/netbox-contract/issues).
|
|
10
|
+
|
|
11
|
+
### Fix bugs
|
|
12
|
+
|
|
13
|
+
Look through the GitHub issues for bugs. Anything tagged with "bug" and "help
|
|
14
|
+
wanted" is open to whoever wants to implement it.
|
|
15
|
+
|
|
16
|
+
### Implement features
|
|
17
|
+
|
|
18
|
+
Look through the GitHub issues for features. Anything tagged with "enhancement"
|
|
19
|
+
and "help wanted" is open to whoever wants to implement it.
|
|
20
|
+
|
|
21
|
+
## Coding conventions
|
|
22
|
+
|
|
23
|
+
Netbox [Style Guide](https://docs.netbox.dev/en/stable/development/style-guide/)
|
|
24
|
+
Django [Coding style](https://docs.djangoproject.com/en/4.2/internals/contributing/writing-code/coding-style/)
|
|
25
|
+
|
|
26
|
+
For this:
|
|
27
|
+
All files will be formated using the [black](https://black.readthedocs.io/en/stable/) auto-formatter.
|
|
28
|
+
Configuration is stored in pyproject.toml
|
|
29
|
+
|
|
30
|
+
[isort](https://github.com/PyCQA/isort#readme) is used to automate import sorting.
|
|
31
|
+
|
|
32
|
+
Linting and PEP8 style enforcement will be done with [Flake8](https://flake8.pycqa.org/en/latest/) which is a wrapper arround:
|
|
33
|
+
- PyFlakes
|
|
34
|
+
- pycodestyle
|
|
35
|
+
- Ned Batchelder’s McCabe script
|
|
36
|
+
Configuration is maintained in the .flake8 file (no support for pyproject.toml)
|
|
37
|
+
|
|
38
|
+
The pre-commit Python framework is used to simplify the managment of pre-commit hooks.
|
|
39
|
+
Config is stored in .pre-commit-config.yaml
|
|
40
|
+
|
|
41
|
+
## Repository structure
|
|
42
|
+
|
|
43
|
+
There are 2 permanent branch in the repository:
|
|
44
|
+
|
|
45
|
+
* master - The current stable release. Individual changes should never be pushed directly to this branch, but rather merged from develop.
|
|
46
|
+
* develop - Active development for the upcoming patch release. Pull requests will typically be based on this branch unless they introduce breaking changes that must be deferred until the next minor release.
|
|
47
|
+
|
|
48
|
+
For each new feature or bug fix a branch is created from the corresponding issue.
|
|
49
|
+
|
|
50
|
+
## Setup your development environment
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
1. [Install Netbox](https://github.com/netbox-community/netbox/blob/develop/docs/installation/).
|
|
54
|
+
Make sure taht at the Netbox installtion step you follow the "Option B: Clone the Git Repository"
|
|
55
|
+
|
|
56
|
+
2. From the Netbox directory you activate the NetBox virtual environment
|
|
57
|
+
|
|
58
|
+
```
|
|
59
|
+
$ cd netbox
|
|
60
|
+
$ source venv/netbox/bin/activate
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
3. Fork the [netbox-contract](https://github.com/mlebreuil/netbox-contract/) repo on GitHub.
|
|
64
|
+
4. Clone your fork locally
|
|
65
|
+
|
|
66
|
+
```
|
|
67
|
+
$ cd ..
|
|
68
|
+
$ git clone git@github.com:your_name_here/netbox-contract.git
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
5. Add the plugin to NetBox virtual environment:
|
|
72
|
+
|
|
73
|
+
```
|
|
74
|
+
$ cd netbox-contract
|
|
75
|
+
$ python3 -m pip install -e .
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
6. install pre-commit:
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
python -m pip install pre-commit
|
|
82
|
+
pre-commit install
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
6. Update the Netbox configuration and run the database migrations as mentionned in the plugin installation steps.
|
|
86
|
+
|
|
87
|
+
7. Create a branch for local development:
|
|
88
|
+
|
|
89
|
+
```
|
|
90
|
+
$ git checkout -b name-of-your-bugfix-or-feature
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Make your changes locally.
|
|
94
|
+
|
|
95
|
+
8. You can test your changes using the django development server:
|
|
96
|
+
|
|
97
|
+
```
|
|
98
|
+
$ python3 netbox/netbox/manage.py runserver 0.0.0.0:8000 --insecure
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Connect to the name or IP of the server (as defined in ALLOWED_HOSTS) on port 8000; for example, http://127.0.0.1:8000/.
|
|
102
|
+
|
|
103
|
+
9. Commit your changes and push your branch to GitHub:
|
|
104
|
+
|
|
105
|
+
```
|
|
106
|
+
$ git add .
|
|
107
|
+
$ git commit -m "Your detailed description of your changes."
|
|
108
|
+
$ git push origin name-of-your-bugfix-or-feature
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
10. Submit a pull request through the GitHub website.
|
|
112
|
+
|
|
113
|
+
## Model changes
|
|
114
|
+
|
|
115
|
+
Create the migrations:
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
source netbox/venv/bin/activate
|
|
119
|
+
python3 netbox/netbox/manage.py makemigrations netbox_contract --dry-run
|
|
120
|
+
python3 netbox/netbox/manage.py makemigrations netbox_contract
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Apply the migrations:
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
source netbox/venv/bin/activate
|
|
127
|
+
python3 netbox/netbox/manage.py migrate
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## Pull Request Guidelines
|
|
131
|
+
|
|
132
|
+
Before you submit a pull request, check that it meets these guidelines:
|
|
133
|
+
|
|
134
|
+
1. The pull request should include tests.
|
|
135
|
+
2. If the pull request adds functionality, the docs should be updated.
|
|
136
|
+
3. The pull request should work for Python 3.10 and 3.11. Check [Actions](https://github.com/mlebreuil/netbox-contract/actions)
|
|
137
|
+
and make sure that the tests pass for all supported Python versions.
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
## Deploying
|
|
141
|
+
|
|
142
|
+
A reminder for the maintainers on how to deploy.
|
|
143
|
+
Make sure all your changes are committed (including an entry in CHANGELOG.md) and that all tests pass.
|
|
144
|
+
Then in the github project go to `Releases` and create a new release with a new tag. This will automatically upload the release to pypi:
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
include CONTRIBUTING.md
|
|
2
|
+
include LICENSE
|
|
3
|
+
include README.md
|
|
4
|
+
|
|
5
|
+
recursive-include tests *
|
|
6
|
+
recursive-exclude * __pycache__
|
|
7
|
+
recursive-exclude * *.py[co]
|
|
8
|
+
|
|
9
|
+
recursive-include docs *.rst conf.py Makefile make.bat *.jpg *.png *.gif
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
graft src/netbox_contract/templates
|
|
13
|
+
graft src/netbox_contract/templates/netbox_contract
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: netbox-contract
|
|
3
|
-
Version: 2.3.
|
|
3
|
+
Version: 2.3.1
|
|
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
|
|
@@ -15,14 +15,29 @@ Requires-Dist: python-dateutil
|
|
|
15
15
|
Requires-Dist: drf_yasg
|
|
16
16
|
|
|
17
17
|
# Contract pluggin
|
|
18
|
-
|
|
18
|
+
|
|
19
|
+
NetBox plugin to manage contracts.
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
* Free software: MIT
|
|
23
|
+
* Documentation: https://mlebreuil.github.io/netbox-contract/
|
|
24
|
+
|
|
25
|
+
## Features
|
|
26
|
+
|
|
19
27
|
The pluggin adds contracts and invoices model to Netbox.
|
|
20
|
-
It allows to
|
|
21
|
-
|
|
28
|
+
It allows to link contract with objects.
|
|
29
|
+
And link invoice templates and invoices with contracts.
|
|
30
|
+
Invoice lines can be linked to each invoice / invoice template.
|
|
31
|
+
Accounitng dimentoins can be linked with invoice lines.
|
|
32
|
+
|
|
33
|
+
## Compatibility
|
|
22
34
|
|
|
23
|
-
|
|
35
|
+
| NetBox Version | Plugin Version |
|
|
36
|
+
|----------------|----------------|
|
|
37
|
+
| 4.1 | 2.2 |
|
|
38
|
+
| 4.2 | 2.3 |
|
|
24
39
|
|
|
25
|
-
##
|
|
40
|
+
## Installing
|
|
26
41
|
|
|
27
42
|
### Activate venv
|
|
28
43
|
To ensure our plugin is accessible to the NetBox installation, we first need to activate the Python [virtual environment](https://docs.python.org/3/library/venv.html) that was created when we installed NetBox. To do this, determine the virtual environment's path (this will be `/opt/netbox/venv/` if you use the documentation's defaults) and activate it:
|
|
@@ -57,11 +72,6 @@ The following configurationitems can be set:
|
|
|
57
72
|
PLUGINS_CONFIG = {
|
|
58
73
|
'netbox_contract': {
|
|
59
74
|
'top_level_menu': True,
|
|
60
|
-
'default_accounting_dimensions':{
|
|
61
|
-
"account": "",
|
|
62
|
-
"project": "",
|
|
63
|
-
"cost center": ""
|
|
64
|
-
},
|
|
65
75
|
'mandatory_contract_fields': [],
|
|
66
76
|
'hidden_contract_fields': [],
|
|
67
77
|
'mandatory_invoice_fields': [],
|
|
@@ -86,9 +96,8 @@ The first currency will also be the default currency for contracts.
|
|
|
86
96
|
# configuration.py
|
|
87
97
|
FIELD_CHOICES = {
|
|
88
98
|
'netbox_contract.Contract.internal_partie': (
|
|
89
|
-
('
|
|
90
|
-
('
|
|
91
|
-
('Nagra India', 'Nagra India', 'green'),
|
|
99
|
+
('entity1', 'Entity 1', 'green'),
|
|
100
|
+
('entity2', 'Entity 2', 'yellow'),
|
|
92
101
|
),
|
|
93
102
|
'netbox_contract.Contract.currency': (
|
|
94
103
|
('usd', 'USD'),
|
|
@@ -1,12 +1,27 @@
|
|
|
1
1
|
# Contract pluggin
|
|
2
|
-
|
|
2
|
+
|
|
3
|
+
NetBox plugin to manage contracts.
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
* Free software: MIT
|
|
7
|
+
* Documentation: https://mlebreuil.github.io/netbox-contract/
|
|
8
|
+
|
|
9
|
+
## Features
|
|
10
|
+
|
|
3
11
|
The pluggin adds contracts and invoices model to Netbox.
|
|
4
|
-
It allows to
|
|
5
|
-
|
|
12
|
+
It allows to link contract with objects.
|
|
13
|
+
And link invoice templates and invoices with contracts.
|
|
14
|
+
Invoice lines can be linked to each invoice / invoice template.
|
|
15
|
+
Accounitng dimentoins can be linked with invoice lines.
|
|
16
|
+
|
|
17
|
+
## Compatibility
|
|
6
18
|
|
|
7
|
-
|
|
19
|
+
| NetBox Version | Plugin Version |
|
|
20
|
+
|----------------|----------------|
|
|
21
|
+
| 4.1 | 2.2 |
|
|
22
|
+
| 4.2 | 2.3 |
|
|
8
23
|
|
|
9
|
-
##
|
|
24
|
+
## Installing
|
|
10
25
|
|
|
11
26
|
### Activate venv
|
|
12
27
|
To ensure our plugin is accessible to the NetBox installation, we first need to activate the Python [virtual environment](https://docs.python.org/3/library/venv.html) that was created when we installed NetBox. To do this, determine the virtual environment's path (this will be `/opt/netbox/venv/` if you use the documentation's defaults) and activate it:
|
|
@@ -41,11 +56,6 @@ The following configurationitems can be set:
|
|
|
41
56
|
PLUGINS_CONFIG = {
|
|
42
57
|
'netbox_contract': {
|
|
43
58
|
'top_level_menu': True,
|
|
44
|
-
'default_accounting_dimensions':{
|
|
45
|
-
"account": "",
|
|
46
|
-
"project": "",
|
|
47
|
-
"cost center": ""
|
|
48
|
-
},
|
|
49
59
|
'mandatory_contract_fields': [],
|
|
50
60
|
'hidden_contract_fields': [],
|
|
51
61
|
'mandatory_invoice_fields': [],
|
|
@@ -70,9 +80,8 @@ The first currency will also be the default currency for contracts.
|
|
|
70
80
|
# configuration.py
|
|
71
81
|
FIELD_CHOICES = {
|
|
72
82
|
'netbox_contract.Contract.internal_partie': (
|
|
73
|
-
('
|
|
74
|
-
('
|
|
75
|
-
('Nagra India', 'Nagra India', 'green'),
|
|
83
|
+
('entity1', 'Entity 1', 'green'),
|
|
84
|
+
('entity2', 'Entity 2', 'yellow'),
|
|
76
85
|
),
|
|
77
86
|
'netbox_contract.Contract.currency': (
|
|
78
87
|
('usd', 'USD'),
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -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.3.
|
|
8
|
+
version = '2.3.1'
|
|
9
9
|
author = 'Marc Lebreuil'
|
|
10
10
|
author_email = 'marc@famillelebreuil.net'
|
|
11
11
|
base_url = 'contracts'
|
|
12
|
-
min_version = '4.
|
|
12
|
+
min_version = '4.2.0'
|
|
13
13
|
required_settings = []
|
|
14
14
|
default_settings = {
|
|
15
15
|
'top_level_menu': False,
|
|
@@ -54,12 +54,8 @@ class ContractForm(NetBoxModelForm):
|
|
|
54
54
|
widget=HTMXSelect(),
|
|
55
55
|
label=_('External partie object type'),
|
|
56
56
|
)
|
|
57
|
-
external_partie_object = forms.ModelChoiceField(
|
|
58
|
-
|
|
59
|
-
)
|
|
60
|
-
tenant = DynamicModelChoiceField(
|
|
61
|
-
queryset=Tenant.objects.all(), required=False, selector=True, label=_('Tenant')
|
|
62
|
-
)
|
|
57
|
+
external_partie_object = forms.ModelChoiceField(queryset=None, label=_('External partie object'))
|
|
58
|
+
tenant = DynamicModelChoiceField(queryset=Tenant.objects.all(), required=False, selector=True, label=_('Tenant'))
|
|
63
59
|
parent = DynamicModelChoiceField(
|
|
64
60
|
queryset=Contract.objects.all(),
|
|
65
61
|
required=False,
|
|
@@ -73,37 +69,22 @@ class ContractForm(NetBoxModelForm):
|
|
|
73
69
|
|
|
74
70
|
# Initialize the external party object gfk
|
|
75
71
|
if initial and 'external_partie_object_type' in initial:
|
|
76
|
-
external_partie_object_type = ContentType.objects.get_for_id(
|
|
77
|
-
initial['external_partie_object_type']
|
|
78
|
-
)
|
|
72
|
+
external_partie_object_type = ContentType.objects.get_for_id(initial['external_partie_object_type'])
|
|
79
73
|
external_partie_class = external_partie_object_type.model_class()
|
|
80
|
-
self.fields[
|
|
81
|
-
'external_partie_object'
|
|
82
|
-
].queryset = external_partie_class.objects.all()
|
|
74
|
+
self.fields['external_partie_object'].queryset = external_partie_class.objects.all()
|
|
83
75
|
if (
|
|
84
76
|
self.instance.external_partie_object_type
|
|
85
|
-
and self.instance.external_partie_object_type.id
|
|
86
|
-
== external_partie_object_type.id
|
|
77
|
+
and self.instance.external_partie_object_type.id == external_partie_object_type.id
|
|
87
78
|
):
|
|
88
|
-
self.fields[
|
|
89
|
-
'external_partie_object'
|
|
90
|
-
].initial = self.instance.external_partie_object
|
|
79
|
+
self.fields['external_partie_object'].initial = self.instance.external_partie_object
|
|
91
80
|
else:
|
|
92
81
|
self.fields['external_partie_object'].initial = None
|
|
93
82
|
elif self.instance.external_partie_object_type:
|
|
94
|
-
external_partie_class = (
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
self.fields[
|
|
98
|
-
'external_partie_object'
|
|
99
|
-
].queryset = external_partie_class.objects.all()
|
|
100
|
-
self.fields[
|
|
101
|
-
'external_partie_object'
|
|
102
|
-
].initial = self.instance.external_partie_object
|
|
83
|
+
external_partie_class = self.instance.external_partie_object_type.model_class()
|
|
84
|
+
self.fields['external_partie_object'].queryset = external_partie_class.objects.all()
|
|
85
|
+
self.fields['external_partie_object'].initial = self.instance.external_partie_object
|
|
103
86
|
else:
|
|
104
|
-
self.fields[
|
|
105
|
-
'external_partie_object'
|
|
106
|
-
].queryset = ServiceProvider.objects.all()
|
|
87
|
+
self.fields['external_partie_object'].queryset = ServiceProvider.objects.all()
|
|
107
88
|
self.fields['external_partie_object'].initial = None
|
|
108
89
|
|
|
109
90
|
# Initialise fields settings
|
|
@@ -150,25 +131,17 @@ class ContractForm(NetBoxModelForm):
|
|
|
150
131
|
super().clean()
|
|
151
132
|
|
|
152
133
|
if self.cleaned_data['mrc'] and self.cleaned_data['yrc']:
|
|
153
|
-
raise ValidationError(
|
|
154
|
-
'you should set monthly OR yearly recuring costs not both'
|
|
155
|
-
)
|
|
134
|
+
raise ValidationError('you should set monthly OR yearly recuring costs not both')
|
|
156
135
|
|
|
157
136
|
|
|
158
137
|
class ContractFilterForm(NetBoxModelFilterSetForm):
|
|
159
138
|
model = Contract
|
|
160
139
|
|
|
161
|
-
tenant = DynamicModelChoiceField(
|
|
162
|
-
queryset=Tenant.objects.all(), required=False, selector=True, label=_('Tenant')
|
|
163
|
-
)
|
|
140
|
+
tenant = DynamicModelChoiceField(queryset=Tenant.objects.all(), required=False, selector=True, label=_('Tenant'))
|
|
164
141
|
external_reference = forms.CharField(required=False, label=_('External reference'))
|
|
165
|
-
internal_partie = forms.ChoiceField(
|
|
166
|
-
choices=InternalEntityChoices, required=False, label=_('Internal partie')
|
|
167
|
-
)
|
|
142
|
+
internal_partie = forms.ChoiceField(choices=InternalEntityChoices, required=False, label=_('Internal partie'))
|
|
168
143
|
status = forms.ChoiceField(choices=StatusChoices, required=False, label=_('Status'))
|
|
169
|
-
currency = forms.ChoiceField(
|
|
170
|
-
choices=CurrencyChoices, required=False, label=_('Currency')
|
|
171
|
-
)
|
|
144
|
+
currency = forms.ChoiceField(choices=CurrencyChoices, required=False, label=_('Currency'))
|
|
172
145
|
parent = DynamicModelChoiceField(
|
|
173
146
|
queryset=Contract.objects.all(),
|
|
174
147
|
required=False,
|
|
@@ -194,9 +167,7 @@ class ContractCSVForm(NetBoxModelImportForm):
|
|
|
194
167
|
required=False,
|
|
195
168
|
label=_('Tenant'),
|
|
196
169
|
)
|
|
197
|
-
status = CSVChoiceField(
|
|
198
|
-
choices=StatusChoices, help_text='Contract status', label=_('Status')
|
|
199
|
-
)
|
|
170
|
+
status = CSVChoiceField(choices=StatusChoices, help_text='Contract status', label=_('Status'))
|
|
200
171
|
parent = CSVModelChoiceField(
|
|
201
172
|
queryset=Contract.objects.all(),
|
|
202
173
|
to_field_name='name',
|
|
@@ -231,27 +202,17 @@ class ContractCSVForm(NetBoxModelImportForm):
|
|
|
231
202
|
|
|
232
203
|
def clean_external_partie_object_id(self):
|
|
233
204
|
name = self.cleaned_data.get('external_partie_object_id')
|
|
234
|
-
external_partie_object_type = self.cleaned_data.get(
|
|
235
|
-
|
|
236
|
-
)
|
|
237
|
-
external_partie_object = external_partie_object_type.get_object_for_this_type(
|
|
238
|
-
name=name
|
|
239
|
-
)
|
|
205
|
+
external_partie_object_type = self.cleaned_data.get('external_partie_object_type')
|
|
206
|
+
external_partie_object = external_partie_object_type.get_object_for_this_type(name=name)
|
|
240
207
|
|
|
241
208
|
return external_partie_object.id
|
|
242
209
|
|
|
243
210
|
|
|
244
211
|
class ContractBulkEditForm(NetBoxModelBulkEditForm):
|
|
245
212
|
name = forms.CharField(max_length=100, required=False, label=_('Name'))
|
|
246
|
-
external_reference = forms.CharField(
|
|
247
|
-
|
|
248
|
-
)
|
|
249
|
-
internal_partie = forms.ChoiceField(
|
|
250
|
-
choices=InternalEntityChoices, required=False, label=_('Internal partie')
|
|
251
|
-
)
|
|
252
|
-
tenant = DynamicModelChoiceField(
|
|
253
|
-
queryset=Tenant.objects.all(), required=False, selector=True, label=_('Tenant')
|
|
254
|
-
)
|
|
213
|
+
external_reference = forms.CharField(max_length=100, required=False, label=_('External reference'))
|
|
214
|
+
internal_partie = forms.ChoiceField(choices=InternalEntityChoices, required=False, label=_('Internal partie'))
|
|
215
|
+
tenant = DynamicModelChoiceField(queryset=Tenant.objects.all(), required=False, selector=True, label=_('Tenant'))
|
|
255
216
|
comments = CommentField(required=False, label=_('Comments'))
|
|
256
217
|
parent = DynamicModelChoiceField(
|
|
257
218
|
queryset=Contract.objects.all(),
|
|
@@ -302,9 +263,7 @@ class InvoiceForm(NetBoxModelForm):
|
|
|
302
263
|
for contract in contracts:
|
|
303
264
|
for invoice in contract.invoices.all():
|
|
304
265
|
if invoice.template and invoice.pk != self.instance.pk:
|
|
305
|
-
raise ValidationError(
|
|
306
|
-
'Only one invoice template allowed per contract'
|
|
307
|
-
)
|
|
266
|
+
raise ValidationError('Only one invoice template allowed per contract')
|
|
308
267
|
|
|
309
268
|
# Prefix the invoice name with _template
|
|
310
269
|
self.cleaned_data['number'] = '_invoice_template_' + contract.name
|
|
@@ -324,9 +283,7 @@ class InvoiceForm(NetBoxModelForm):
|
|
|
324
283
|
for contract in contracts:
|
|
325
284
|
try:
|
|
326
285
|
template_exists = True
|
|
327
|
-
invoice_template = Invoice.objects.get(
|
|
328
|
-
template=True, contracts=contract
|
|
329
|
-
)
|
|
286
|
+
invoice_template = Invoice.objects.get(template=True, contracts=contract)
|
|
330
287
|
except ObjectDoesNotExist:
|
|
331
288
|
template_exists = False
|
|
332
289
|
|
|
@@ -341,15 +298,8 @@ class InvoiceForm(NetBoxModelForm):
|
|
|
341
298
|
|
|
342
299
|
# adjust the first invoice line amount
|
|
343
300
|
amount = self.cleaned_data['amount']
|
|
344
|
-
if
|
|
345
|
-
|
|
346
|
-
and amount != invoice_template.total_invoicelines_amount
|
|
347
|
-
):
|
|
348
|
-
line.amount = (
|
|
349
|
-
line.amount
|
|
350
|
-
+ amount
|
|
351
|
-
- invoice_template.total_invoicelines_amount
|
|
352
|
-
)
|
|
301
|
+
if first and amount != invoice_template.total_invoicelines_amount:
|
|
302
|
+
line.amount = line.amount + amount - invoice_template.total_invoicelines_amount
|
|
353
303
|
|
|
354
304
|
line.save()
|
|
355
305
|
|
|
@@ -392,9 +342,7 @@ class InvoiceFilterForm(NetBoxModelFilterSetForm):
|
|
|
392
342
|
widget=forms.Select(choices=BOOLEAN_WITH_BLANK_CHOICES),
|
|
393
343
|
label=_('Template'),
|
|
394
344
|
)
|
|
395
|
-
currency = forms.ChoiceField(
|
|
396
|
-
choices=CurrencyChoices, required=False, label=_('Currency')
|
|
397
|
-
)
|
|
345
|
+
currency = forms.ChoiceField(choices=CurrencyChoices, required=False, label=_('Currency'))
|
|
398
346
|
contracts = DynamicModelMultipleChoiceField(
|
|
399
347
|
queryset=Contract.objects.all(),
|
|
400
348
|
required=False,
|
|
@@ -515,9 +463,7 @@ class ServiceProviderBulkEditForm(NetBoxModelBulkEditForm):
|
|
|
515
463
|
|
|
516
464
|
|
|
517
465
|
class ContractAssignmentForm(NetBoxModelForm):
|
|
518
|
-
contract = DynamicModelChoiceField(
|
|
519
|
-
queryset=Contract.objects.all(), selector=True, label=_('Contract')
|
|
520
|
-
)
|
|
466
|
+
contract = DynamicModelChoiceField(queryset=Contract.objects.all(), selector=True, label=_('Contract'))
|
|
521
467
|
|
|
522
468
|
class Meta:
|
|
523
469
|
model = ContractAssignment
|
|
@@ -86,13 +86,9 @@ class AccountingDimension(NetBoxModel):
|
|
|
86
86
|
|
|
87
87
|
def get_status_color(self):
|
|
88
88
|
return AccountingDimensionStatusChoices.colors.get(self.status)
|
|
89
|
-
|
|
89
|
+
|
|
90
90
|
class Meta:
|
|
91
|
-
constraints = [
|
|
92
|
-
models.UniqueConstraint(
|
|
93
|
-
fields=['name', 'value'], name='unique_accounting_dimension'
|
|
94
|
-
)
|
|
95
|
-
]
|
|
91
|
+
constraints = [models.UniqueConstraint(fields=['name', 'value'], name='unique_accounting_dimension')]
|
|
96
92
|
ordering = ('name', 'value')
|
|
97
93
|
verbose_name = _('accounting dimension')
|
|
98
94
|
verbose_name_plural = _('accounting dimensions')
|
|
@@ -117,9 +113,7 @@ class ServiceProvider(ContactsMixin, NetBoxModel):
|
|
|
117
113
|
|
|
118
114
|
|
|
119
115
|
class ContractAssignment(NetBoxModel):
|
|
120
|
-
content_type = models.ForeignKey(
|
|
121
|
-
to=ContentType, on_delete=models.CASCADE, verbose_name=_('content type')
|
|
122
|
-
)
|
|
116
|
+
content_type = models.ForeignKey(to=ContentType, on_delete=models.CASCADE, verbose_name=_('content type'))
|
|
123
117
|
object_id = models.PositiveBigIntegerField(verbose_name=_('object ID'))
|
|
124
118
|
content_object = GenericForeignKey(ct_field='content_type', fk_field='object_id')
|
|
125
119
|
contract = models.ForeignKey(
|
|
@@ -157,12 +151,9 @@ class Contract(NetBoxModel):
|
|
|
157
151
|
external_partie_object = GenericForeignKey(
|
|
158
152
|
ct_field='external_partie_object_type', fk_field='external_partie_object_id'
|
|
159
153
|
)
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
)
|
|
163
|
-
internal_partie = models.CharField(
|
|
164
|
-
max_length=50, choices=InternalEntityChoices, verbose_name=_('internal partie')
|
|
165
|
-
)
|
|
154
|
+
external_partie_object.editable = True
|
|
155
|
+
external_reference = models.CharField(max_length=100, blank=True, null=True, verbose_name=_('external reference'))
|
|
156
|
+
internal_partie = models.CharField(max_length=50, choices=InternalEntityChoices, verbose_name=_('internal partie'))
|
|
166
157
|
tenant = models.ForeignKey(
|
|
167
158
|
to='tenancy.Tenant',
|
|
168
159
|
on_delete=models.PROTECT,
|
|
@@ -220,9 +211,7 @@ class Contract(NetBoxModel):
|
|
|
220
211
|
null=True,
|
|
221
212
|
help_text=_('Use either this field of the yearly recuring cost field'),
|
|
222
213
|
)
|
|
223
|
-
nrc = models.DecimalField(
|
|
224
|
-
verbose_name=_('none recuring cost'), default=0, max_digits=10, decimal_places=2
|
|
225
|
-
)
|
|
214
|
+
nrc = models.DecimalField(verbose_name=_('none recuring cost'), default=0, max_digits=10, decimal_places=2)
|
|
226
215
|
invoice_frequency = models.IntegerField(
|
|
227
216
|
help_text=_('The frequency of invoices in month'),
|
|
228
217
|
default=1,
|
|
@@ -248,12 +237,11 @@ class Contract(NetBoxModel):
|
|
|
248
237
|
|
|
249
238
|
def get_status_color(self):
|
|
250
239
|
return StatusChoices.colors.get(self.status)
|
|
240
|
+
|
|
251
241
|
class Meta:
|
|
252
242
|
ordering = ('name',)
|
|
253
243
|
indexes = [
|
|
254
|
-
models.Index(
|
|
255
|
-
fields=['external_partie_object_type', 'external_partie_object_id']
|
|
256
|
-
),
|
|
244
|
+
models.Index(fields=['external_partie_object_type', 'external_partie_object_id']),
|
|
257
245
|
]
|
|
258
246
|
verbose_name = _('contract')
|
|
259
247
|
verbose_name_plural = _('contracts')
|
|
@@ -276,12 +264,8 @@ class Invoice(NetBoxModel):
|
|
|
276
264
|
help_text=_('Wether this invoice is a template or not'),
|
|
277
265
|
)
|
|
278
266
|
date = models.DateField(blank=True, null=True, verbose_name=_('date'))
|
|
279
|
-
contracts = models.ManyToManyField(
|
|
280
|
-
|
|
281
|
-
)
|
|
282
|
-
period_start = models.DateField(
|
|
283
|
-
blank=True, null=True, verbose_name=_('period start')
|
|
284
|
-
)
|
|
267
|
+
contracts = models.ManyToManyField(Contract, related_name='invoices', blank=True, verbose_name=_('contracts'))
|
|
268
|
+
period_start = models.DateField(blank=True, null=True, verbose_name=_('period start'))
|
|
285
269
|
period_end = models.DateField(blank=True, null=True, verbose_name=_('period end'))
|
|
286
270
|
currency = models.CharField(
|
|
287
271
|
max_length=3,
|
|
@@ -289,9 +273,7 @@ class Invoice(NetBoxModel):
|
|
|
289
273
|
default=CURRENCY_DEFAULT,
|
|
290
274
|
verbose_name=_('currency'),
|
|
291
275
|
)
|
|
292
|
-
amount = models.DecimalField(
|
|
293
|
-
max_digits=10, decimal_places=2, verbose_name=_('amount')
|
|
294
|
-
)
|
|
276
|
+
amount = models.DecimalField(max_digits=10, decimal_places=2, verbose_name=_('amount'))
|
|
295
277
|
documents = models.URLField(
|
|
296
278
|
blank=True,
|
|
297
279
|
verbose_name=_('documents'),
|
|
@@ -331,9 +313,7 @@ class InvoiceLine(NetBoxModel):
|
|
|
331
313
|
default=CURRENCY_DEFAULT,
|
|
332
314
|
verbose_name=_('currency'),
|
|
333
315
|
)
|
|
334
|
-
amount = models.DecimalField(
|
|
335
|
-
max_digits=10, decimal_places=2, verbose_name=_('amount')
|
|
336
|
-
)
|
|
316
|
+
amount = models.DecimalField(max_digits=10, decimal_places=2, verbose_name=_('amount'))
|
|
337
317
|
accounting_dimensions = models.ManyToManyField(
|
|
338
318
|
AccountingDimension, blank=True, verbose_name=_('accounting dimensions')
|
|
339
319
|
)
|
|
@@ -355,14 +335,8 @@ class InvoiceLine(NetBoxModel):
|
|
|
355
335
|
is_new = not bool(self.pk)
|
|
356
336
|
if is_new:
|
|
357
337
|
if amount > (invoice.amount - invoice.total_invoicelines_amount):
|
|
358
|
-
raise ValidationError(
|
|
359
|
-
'Sum of invoice line amount greater than invoice amount'
|
|
360
|
-
)
|
|
338
|
+
raise ValidationError('Sum of invoice line amount greater than invoice amount')
|
|
361
339
|
else:
|
|
362
340
|
previous_amount = self.__class__.objects.get(pk=self.pk).amount
|
|
363
|
-
if amount > (
|
|
364
|
-
invoice
|
|
365
|
-
):
|
|
366
|
-
raise ValidationError(
|
|
367
|
-
'Sum of invoice line amount greater than invoice amount'
|
|
368
|
-
)
|
|
341
|
+
if amount > (invoice.amount - invoice.total_invoicelines_amount + previous_amount):
|
|
342
|
+
raise ValidationError('Sum of invoice line amount greater than invoice amount')
|
|
@@ -45,6 +45,9 @@ class ContractAssignmentObjectTable(NetBoxTable):
|
|
|
45
45
|
contract__external_partie_object = tables.Column(
|
|
46
46
|
verbose_name='Partner', linkify=True
|
|
47
47
|
)
|
|
48
|
+
contract__status = columns.ChoiceFieldColumn(
|
|
49
|
+
verbose_name=('Status'),
|
|
50
|
+
)
|
|
48
51
|
|
|
49
52
|
class Meta(NetBoxTable.Meta):
|
|
50
53
|
model = ContractAssignment
|
|
@@ -75,7 +78,9 @@ class ContractAssignmentObjectTable(NetBoxTable):
|
|
|
75
78
|
class ContractAssignmentContractTable(NetBoxTable):
|
|
76
79
|
content_type = columns.ContentTypeColumn(verbose_name='Object Type')
|
|
77
80
|
content_object = tables.Column(linkify=True, verbose_name='Object', orderable=False)
|
|
78
|
-
content_object__status =
|
|
81
|
+
content_object__status = columns.ChoiceFieldColumn(
|
|
82
|
+
verbose_name=('Status'),
|
|
83
|
+
)
|
|
79
84
|
actions = columns.ActionsColumn(actions=('edit', 'delete'))
|
|
80
85
|
|
|
81
86
|
class Meta(NetBoxTable.Meta):
|
|
@@ -136,6 +141,9 @@ class ContractListTable(NetBoxTable):
|
|
|
136
141
|
class ContractListBottomTable(NetBoxTable):
|
|
137
142
|
name = tables.Column(linkify=True)
|
|
138
143
|
external_partie_object = tables.Column(linkify=True)
|
|
144
|
+
status = columns.ChoiceFieldColumn(
|
|
145
|
+
verbose_name=('Status'),
|
|
146
|
+
)
|
|
139
147
|
|
|
140
148
|
class Meta(NetBoxTable.Meta):
|
|
141
149
|
model = Contract
|
|
@@ -201,9 +209,7 @@ class ServiceProviderListTable(NetBoxTable):
|
|
|
201
209
|
|
|
202
210
|
class InvoiceLineListTable(NetBoxTable):
|
|
203
211
|
invoice = tables.Column(linkify=True)
|
|
204
|
-
accounting_dimensions = tables.ManyToManyColumn(
|
|
205
|
-
linkify=True, filter=lambda qs: qs.order_by('name')
|
|
206
|
-
)
|
|
212
|
+
accounting_dimensions = tables.ManyToManyColumn(linkify=True, filter=lambda qs: qs.order_by('name'))
|
|
207
213
|
|
|
208
214
|
class Meta(NetBoxTable.Meta):
|
|
209
215
|
model = InvoiceLine
|
|
@@ -229,6 +235,7 @@ class AccountingDimensionListTable(NetBoxTable):
|
|
|
229
235
|
status = columns.ChoiceFieldColumn(
|
|
230
236
|
verbose_name=('Status'),
|
|
231
237
|
)
|
|
238
|
+
|
|
232
239
|
class Meta(NetBoxTable.Meta):
|
|
233
240
|
model = AccountingDimension
|
|
234
241
|
fields = (
|
|
@@ -76,7 +76,7 @@
|
|
|
76
76
|
{% endif %}
|
|
77
77
|
<tr>
|
|
78
78
|
<th scope="row">{% trans "Currency" %}</th>
|
|
79
|
-
<td>{{ object.
|
|
79
|
+
<td>{{ object.get_currency_display }}</td>
|
|
80
80
|
</tr>
|
|
81
81
|
<tr>
|
|
82
82
|
<th scope="row">{% trans "Monthly recuring costs" %}</th>
|
|
@@ -87,7 +87,7 @@
|
|
|
87
87
|
<td>{{ object.yrc }}</td>
|
|
88
88
|
</tr>
|
|
89
89
|
<tr>
|
|
90
|
-
<th scope="row">{% trans "
|
|
90
|
+
<th scope="row">{% trans "Calculated corresponding Yearly or Monthly value" %}</th>
|
|
91
91
|
<td>{{ object.calculated_rc }}</td>
|
|
92
92
|
</tr>
|
|
93
93
|
<tr>
|
|
@@ -135,7 +135,7 @@
|
|
|
135
135
|
</tr>
|
|
136
136
|
<tr>
|
|
137
137
|
<th scope="row">{% trans "Currency" %}</th>
|
|
138
|
-
<td>{{ invoice_template.
|
|
138
|
+
<td>{{ invoice_template.get_currency_display }}</td>
|
|
139
139
|
</tr>
|
|
140
140
|
<tr>
|
|
141
141
|
<th scope="row">{% trans "Total amount" %}</th>
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
</tr>
|
|
13
13
|
<tr>
|
|
14
14
|
<th scope="row">{% trans "Object" %}</th>
|
|
15
|
-
<td><a href="{{ object.content_object.get_absolute_url }}">{{ object.content_object
|
|
15
|
+
<td><a href="{{ object.content_object.get_absolute_url }}">{{ object.content_object }}</a></td>
|
|
16
16
|
</tr>
|
|
17
17
|
<tr>
|
|
18
18
|
<th scope="row">{% trans "Contract" %}</th>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: netbox-contract
|
|
3
|
-
Version: 2.3.
|
|
3
|
+
Version: 2.3.1
|
|
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
|
|
@@ -15,14 +15,29 @@ Requires-Dist: python-dateutil
|
|
|
15
15
|
Requires-Dist: drf_yasg
|
|
16
16
|
|
|
17
17
|
# Contract pluggin
|
|
18
|
-
|
|
18
|
+
|
|
19
|
+
NetBox plugin to manage contracts.
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
* Free software: MIT
|
|
23
|
+
* Documentation: https://mlebreuil.github.io/netbox-contract/
|
|
24
|
+
|
|
25
|
+
## Features
|
|
26
|
+
|
|
19
27
|
The pluggin adds contracts and invoices model to Netbox.
|
|
20
|
-
It allows to
|
|
21
|
-
|
|
28
|
+
It allows to link contract with objects.
|
|
29
|
+
And link invoice templates and invoices with contracts.
|
|
30
|
+
Invoice lines can be linked to each invoice / invoice template.
|
|
31
|
+
Accounitng dimentoins can be linked with invoice lines.
|
|
32
|
+
|
|
33
|
+
## Compatibility
|
|
22
34
|
|
|
23
|
-
|
|
35
|
+
| NetBox Version | Plugin Version |
|
|
36
|
+
|----------------|----------------|
|
|
37
|
+
| 4.1 | 2.2 |
|
|
38
|
+
| 4.2 | 2.3 |
|
|
24
39
|
|
|
25
|
-
##
|
|
40
|
+
## Installing
|
|
26
41
|
|
|
27
42
|
### Activate venv
|
|
28
43
|
To ensure our plugin is accessible to the NetBox installation, we first need to activate the Python [virtual environment](https://docs.python.org/3/library/venv.html) that was created when we installed NetBox. To do this, determine the virtual environment's path (this will be `/opt/netbox/venv/` if you use the documentation's defaults) and activate it:
|
|
@@ -57,11 +72,6 @@ The following configurationitems can be set:
|
|
|
57
72
|
PLUGINS_CONFIG = {
|
|
58
73
|
'netbox_contract': {
|
|
59
74
|
'top_level_menu': True,
|
|
60
|
-
'default_accounting_dimensions':{
|
|
61
|
-
"account": "",
|
|
62
|
-
"project": "",
|
|
63
|
-
"cost center": ""
|
|
64
|
-
},
|
|
65
75
|
'mandatory_contract_fields': [],
|
|
66
76
|
'hidden_contract_fields': [],
|
|
67
77
|
'mandatory_invoice_fields': [],
|
|
@@ -86,9 +96,8 @@ The first currency will also be the default currency for contracts.
|
|
|
86
96
|
# configuration.py
|
|
87
97
|
FIELD_CHOICES = {
|
|
88
98
|
'netbox_contract.Contract.internal_partie': (
|
|
89
|
-
('
|
|
90
|
-
('
|
|
91
|
-
('Nagra India', 'Nagra India', 'green'),
|
|
99
|
+
('entity1', 'Entity 1', 'green'),
|
|
100
|
+
('entity2', 'Entity 2', 'yellow'),
|
|
92
101
|
),
|
|
93
102
|
'netbox_contract.Contract.currency': (
|
|
94
103
|
('usd', 'USD'),
|
|
@@ -1,8 +1,15 @@
|
|
|
1
|
+
CONTRIBUTING.md
|
|
1
2
|
LICENSE
|
|
2
3
|
MANIFEST.in
|
|
3
4
|
README.md
|
|
4
5
|
pyproject.toml
|
|
5
6
|
setup.py
|
|
7
|
+
docs/img/accounting_dimensions.png
|
|
8
|
+
docs/img/contract.png
|
|
9
|
+
docs/img/contract_linked_objects.png
|
|
10
|
+
docs/img/invoice.png
|
|
11
|
+
docs/img/invoice_line.png
|
|
12
|
+
docs/img/invoice_linked_objects.png
|
|
6
13
|
src/netbox_contract/__init__.py
|
|
7
14
|
src/netbox_contract/constants.py
|
|
8
15
|
src/netbox_contract/filtersets.py
|
|
@@ -71,4 +78,6 @@ src/netbox_contract/templates/netbox_contract/contract.html
|
|
|
71
78
|
src/netbox_contract/templates/netbox_contract/contractassignment.html
|
|
72
79
|
src/netbox_contract/templates/netbox_contract/invoice.html
|
|
73
80
|
src/netbox_contract/templates/netbox_contract/invoiceline.html
|
|
74
|
-
src/netbox_contract/templates/netbox_contract/serviceprovider.html
|
|
81
|
+
src/netbox_contract/templates/netbox_contract/serviceprovider.html
|
|
82
|
+
tests/__init__.py
|
|
83
|
+
tests/test_netbox_contract.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Unit test package for test_plugin."""
|
|
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.3.0 → netbox_contract-2.3.1}/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.3.0 → netbox_contract-2.3.1}/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.3.0 → netbox_contract-2.3.1}/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
|
|
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.3.0 → netbox_contract-2.3.1}/src/netbox_contract.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|