diff --git a/.flake8 b/.flake8 index df74e48..5719290 100644 --- a/.flake8 +++ b/.flake8 @@ -1,6 +1,6 @@ [flake8] max-line-length = 120 -extend-ignore = W504,E501 +extend-ignore = W504,E501,C901 exclude = .git, __pycache__, diff --git a/.gitignore b/.gitignore index 68bc17f..329004e 100644 --- a/.gitignore +++ b/.gitignore @@ -52,7 +52,6 @@ coverage.xml cover/ # Translations -*.mo *.pot # Django stuff: diff --git a/CHANGELOG.md b/CHANGELOG.md index 4c84f69..c5557b1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,17 @@ ## Version 2 +### Version 2.2.8 + +* [198](https://github.com/mlebreuil/netbox-contract/issues/198) Add internationalization support and french translation. +* [196](https://github.com/mlebreuil/netbox-contract/issues/196) Add notice field to contract. Add an example custom script to report contract nearing cancelation notice. +* minor fix and cleanup + +### Version 2.2.8 + +* [167](https://github.com/mlebreuil/netbox-contract/issues/167) Add selector to object dynamic selection box. +* [193](https://github.com/mlebreuil/netbox-contract/pull/193) Set the first currency in the choiceset as default currency. + ### Version 2.2.7 * fix migration dependency diff --git a/README.md b/README.md index 011d8ab..c558721 100644 --- a/README.md +++ b/README.md @@ -57,13 +57,14 @@ PLUGINS_CONFIG = { ``` * top_level_menu : If "Contracts" appears under the "Plugins" menu item or on its own -* default_accounting_dimensions: The accounting dimensions which will appear in the field' background when empty. +* 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. * 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. * hidden_contract_fields, hidden_invoice_fields: List of fields to be hidden. Fields should not be required to be hidden. ### Customize the plugin fields choices Internal partie reference the legal entity of your organization that is a partie to the contract. +The first currency will also be the default currency for contracts. ```python # configuration.py diff --git a/pyproject.toml b/pyproject.toml index aa68945..ccb87ca 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "netbox-contract" -version = "2.2.7" +version = "2.2.8" authors = [ { name="Marc Lebreuil", email="marc@famillelebreuil.net" }, ] diff --git a/scripts/netbox-contract.py b/scripts/netbox-contract.py index e80ebaa..f13f51e 100644 --- a/scripts/netbox-contract.py +++ b/scripts/netbox-contract.py @@ -1,63 +1,71 @@ -from datetime import date -from decimal import * -from django.core.exceptions import ObjectDoesNotExist +from datetime import date, timedelta +from decimal import Decimal, InvalidOperation + from django.db.models import Count -from extras.scripts import * -from netbox_contract.models import Contract, Invoice, InvoiceLine, AccountingDimension, StatusChoices +from extras.scripts import ChoiceVar, IntegerVar, ObjectVar, Script, StringVar + +from netbox_contract.models import ( + AccountingDimension, + Contract, + Invoice, + InvoiceLine, + StatusChoices, +) -name = "Contracts related scripts" +name = 'Contracts related scripts' -AMOUNT_PRECEDENCE = ( - ('invoice', 'Invoice'), - ('dimensions', 'dimensions') -) +AMOUNT_PRECEDENCE = (('invoice', 'Invoice'), ('dimensions', 'dimensions')) -class update_expired_contract_status(Script): +class update_expired_contract_status(Script): class Meta: - name = "Update expired contracts status" - description = "Update the status of contract with end date prior to today's date" + name = 'Update expired contracts status' + description = ( + "Update the status of contract with end date prior to today's date" + ) commit_default = False def run(self, data, commit): - username = self.request.user.username - self.log_info(f"Running as user {username}") + self.log_info(f'Running as user {username}') output = [] - expired_contracts = Contract.objects.filter(end_date__lte = date.today()).filter(status = StatusChoices.STATUS_ACTIVE ) + expired_contracts = Contract.objects.filter(end_date__lte=date.today()).filter( + status=StatusChoices.STATUS_ACTIVE + ) expired_contracts.update(status=StatusChoices.STATUS_CANCELED) return '\n'.join(output) -class create_invoice_template(Script): +class create_invoice_template(Script): class Meta: - name = "Create invoice templates" - description = "Convert the Accounting dimensions json field in Contracts to invoice template" + name = 'Create invoice templates' + description = 'Convert the Accounting dimensions json field in Contracts to invoice template' commit_default = False def run(self, data, commit): - username = self.request.user.username - self.log_info(f"Running as user {username}") + self.log_info(f'Running as user {username}') output = [] - self.log_info(f"Creating invoice templates from contract dimensions") + self.log_info('Creating invoice templates from contract dimensions') # Create invoice templates for each active template - for contract in Contract.objects.filter(status = StatusChoices.STATUS_ACTIVE ): - self.log_info(f"Processing contract {contract.name}") + for contract in Contract.objects.filter(status=StatusChoices.STATUS_ACTIVE): + self.log_info(f'Processing contract {contract.name}') # check if invoice template exist - invoice_template = Invoice.objects.filter(template=True, contracts=contract).first() + invoice_template = Invoice.objects.filter( + template=True, contracts=contract + ).first() - if invoice_template : - self.log_info(f"Template already exists for {contract.name}") + if invoice_template: + self.log_info(f'Template already exists for {contract.name}') continue - + # if the invoice template does not exists create it if contract.accounting_dimensions: if contract.mrc is not None: @@ -65,50 +73,53 @@ def run(self, data, commit): else: amount = contract.yrc / 12 * contract.invoice_frequency invoice_template = Invoice( - template = True, - number = f"_invoice_template_{contract.name}", - period_start = None, - period_end = None, - amount = amount, - accounting_dimensions = contract.accounting_dimensions + template=True, + number=f'_invoice_template_{contract.name}', + period_start=None, + period_end=None, + amount=amount, + accounting_dimensions=contract.accounting_dimensions, ) invoice_template.save() invoice_template.contracts.add(contract) - self.log_info(f"Template {invoice_template.number} created for {contract.name}") + self.log_info( + f'Template {invoice_template.number} created for {contract.name}' + ) return '\n'.join(output) -class create_invoice_lines(Script): +class create_invoice_lines(Script): class Meta: - name = "Create invoice lines" - description = "Convert the Accounting dimensions json field in invoices to invoice lines" + name = 'Create invoice lines' + description = ( + 'Convert the Accounting dimensions json field in invoices to invoice lines' + ) commit_default = False ignore = StringVar( - label="Ignore", - description="Accounting dimensions to be ignored. List of string separated by comma.", + label='Ignore', + description='Accounting dimensions to be ignored. List of string separated by comma.', required=False, - regex=r"^\w+(,\w+)*$" + regex=r'^\w+(,\w+)*$', ) amount_precedence = ChoiceVar( - label="Amount precedence", - description="Select if the dimension amount or the invoice amount take precedence,", - choices = AMOUNT_PRECEDENCE, - required=False + label='Amount precedence', + description='Select if the dimension amount or the invoice amount take precedence,', + choices=AMOUNT_PRECEDENCE, + required=False, ) line_amount_key = StringVar( - label="Line amount key", - description="Key name for line amount in the accounting dimension json with multiple lines", + label='Line amount key', + description='Key name for line amount in the accounting dimension json with multiple lines', required=True, ) def run(self, data, commit): - username = self.request.user.username - self.log_info(f"Running as user {username}") + self.log_info(f'Running as user {username}') output = [] @@ -118,34 +129,34 @@ def run(self, data, commit): if data['ignore']: exclude.extend(data['ignore'].split(',')) - self.log_info(f"Creating invoice lines from invoices dimensions") - self.log_info(f"Ignoring dimensions {exclude}") - self.log_info(f"Line amount key {line_amount_key}") + self.log_info('Creating invoice lines from invoices dimensions') + self.log_info(f'Ignoring dimensions {exclude}') + self.log_info(f'Line amount key {line_amount_key}') # import existing dimensions - dimensions={} + dimensions = {} dims = AccountingDimension.objects.all() if dims.exists(): for dim in dims: - dimensions[f"{dim.name}_{dim.value}"] = dim + dimensions[f'{dim.name}_{dim.value}'] = dim # Get all invoices without invoice lines - invoices = Invoice.objects.annotate(numberoflines=Count("invoicelines")) + invoices = Invoice.objects.annotate(numberoflines=Count('invoicelines')) # Create invoice lines for each invoice for invoice in invoices: if invoice.numberoflines > 0: - self.log_info(f"Invoice skipped {invoice.number}. Exiting lines") + self.log_info(f'Invoice skipped {invoice.number}. Exiting lines') continue - self.log_info(f"Processing Invoice {invoice.number}") - + self.log_info(f'Processing Invoice {invoice.number}') + total_invoice_lines_amount = 0 # Create invoice template lines # Check if several lines have to be created if isinstance(invoice.accounting_dimensions, list): - # if the accounting dimensions is a list we assume that we have an "amount" + # if the accounting dimensions is a list we assume that we have an "amount" lines = invoice.accounting_dimensions else: lines = [invoice.accounting_dimensions] @@ -153,88 +164,129 @@ def run(self, data, commit): single_line_invoice = len(lines) == 1 for line in lines: - if single_line_invoice and data['amount_precedence']=='invoice': + if single_line_invoice and data['amount_precedence'] == 'invoice': amount = invoice.amount - else: + else: # Retrieving with get reduce the repetition of code amount = line.get(line_amount_key) # Checking first the case "not exist" allow us to remove one indent level # NOTE: This works fine because None is not a valid value in this case. if not amount: - self.log_warning(f"Multiple lines or dimensions precedence and no amount for line") + self.log_warning( + 'Multiple lines or dimensions precedence and no amount for line' + ) continue # The try-except part is the same and can be extracted - if isinstance(amount , str): - amount = amount.replace(",",".").replace(" ","") + if isinstance(amount, str): + amount = amount.replace(',', '.').replace(' ', '') try: amount = Decimal(line[line_amount_key]) - except: - self.log_warning(f"Wrong number format {line[line_amount_key]}") - output.append(f"{invoice.number}: dimensions amount format to be updated") + except InvalidOperation: + self.log_warning(f'Wrong number format {line[line_amount_key]}') + output.append( + f'{invoice.number}: dimensions amount format to be updated' + ) invoice_line = InvoiceLine( - invoice = invoice, - currency = invoice.currency, - amount = amount, + invoice=invoice, + currency=invoice.currency, + amount=amount, ) invoice_line.save() - self.log_info(f"Invoice line {invoice_line.id} created for {invoice.number}") + self.log_info( + f'Invoice line {invoice_line.id} created for {invoice.number}' + ) total_invoice_lines_amount = total_invoice_lines_amount + amount # create and add dimensions for key, value in line.items(): if key not in exclude and value is not None: - dimkey = f"{key}_{value}" + dimkey = f'{key}_{value}' if dimkey not in dimensions.keys(): - dimension = AccountingDimension( - name = key, - value = str(value) - ) + dimension = AccountingDimension(name=key, value=str(value)) dimension.save() dimensions[dimkey] = dimension invoice_line.accounting_dimensions.add(dimensions[dimkey]) - self.log_info(f"Accounting dimensions added to Invoice line {invoice_line.id}") - + self.log_info( + f'Accounting dimensions added to Invoice line {invoice_line.id}' + ) if total_invoice_lines_amount != invoice.amount: - self.log_warning(f"The total of invoice lines and invoice amount do not match.") - output.append(f"{invoice.number}: Sum of invoice lines amount to be checked") + self.log_warning( + 'The total of invoice lines and invoice amount do not match.' + ) + output.append( + f'{invoice.number}: Sum of invoice lines amount to be checked' + ) return '\n'.join(output) - -class bulk_replace_accounting_dimension(Script): + +class bulk_replace_accounting_dimension(Script): class Meta: - name = "Replace accounting dimension" - description = "Replace one accounting dimension by another one for all lines" + name = 'Replace accounting dimension' + description = 'Replace one accounting dimension by another one for all lines' commit_default = False current = ObjectVar( - label="Current dimension", - description="The accounting dimension to be replaced.", - model=AccountingDimension + label='Current dimension', + description='The accounting dimension to be replaced.', + model=AccountingDimension, ) new = ObjectVar( - label="New accounting dimension", - description="The new accounting dimension", - model=AccountingDimension + label='New accounting dimension', + description='The new accounting dimension', + model=AccountingDimension, ) def run(self, data, commit): - username = self.request.user.username - self.log_info(f"Running as user {username}") + self.log_info(f'Running as user {username}') output = [] - current_dimension = data["current"] - new_dimension = data["new"] + current_dimension = data['current'] + new_dimension = data['new'] lines = InvoiceLine.objects.filter(accounting_dimensions=current_dimension) for line in lines: line.accounting_dimensions.remove(current_dimension) line.accounting_dimensions.add(new_dimension) - self.log_info(f"invoice {line.invoice.number} updated") + self.log_info(f'invoice {line.invoice.number} updated') + + return '\n'.join(output) + + +class Check_contract_end(Script): + class Meta: + name = 'Check contract end' + description = 'Check which contract will end ' + commit_default = False + + days_before_notice = IntegerVar( + label='Days before notice', + description='Report on contract with notice periode approaching', + ) + + def run(self, data, commit): + username = self.request.user.username + self.log_info(f'Running as user {username}') + + output = [] + + days_before_notice = data['days_before_notice'] + + contracts = Contract.objects.filter(status=StatusChoices.STATUS_ACTIVE) + for contract in contracts: + if contract.notice_date <= date.today() + timedelta( + days=days_before_notice + ): + self.log_info( + f'Contract {contract} end date: {contract.end_date} - notice : {contract.notice_period} days' + ) + output.append( + f'{contract.name} - end date: {contract.end_date} - notice : {contract.notice_period} days' + ) return '\n'.join(output) diff --git a/src/netbox_contract/__init__.py b/src/netbox_contract/__init__.py index c3ee87c..484a7b8 100644 --- a/src/netbox_contract/__init__.py +++ b/src/netbox_contract/__init__.py @@ -5,7 +5,7 @@ class ContractsConfig(PluginConfig): name = 'netbox_contract' verbose_name = 'Netbox contract' description = 'Contract management plugin for Netbox' - version = '2.2.7' + version = '2.2.8' author = 'Marc Lebreuil' author_email = 'marc@famillelebreuil.net' base_url = 'contracts' diff --git a/src/netbox_contract/forms.py b/src/netbox_contract/forms.py index e7682b3..3707240 100644 --- a/src/netbox_contract/forms.py +++ b/src/netbox_contract/forms.py @@ -2,6 +2,7 @@ from django.conf import settings from django.contrib.contenttypes.models import ContentType from django.core.exceptions import ObjectDoesNotExist, ValidationError +from django.utils.translation import gettext_lazy as _ from netbox.forms import ( NetBoxModelBulkEditForm, NetBoxModelFilterSetForm, @@ -42,6 +43,9 @@ plugin_settings = settings.PLUGINS_CONFIG['netbox_contract'] default_dimensions = plugin_settings.get('default_accounting_dimensions') +# Dimensions +# Deprecated. To be removed in version 3.0 + class Dimensions(JSONField): """ @@ -57,17 +61,27 @@ def __init__(self, *args, **kwargs): class ContractForm(NetBoxModelForm): - comments = CommentField() + comments = CommentField(label=_('Comments')) external_partie_object_type = ContentTypeChoiceField( queryset=ContentType.objects.all(), limit_choices_to=SERVICE_PROVIDER_MODELS, widget=HTMXSelect(), + label=_('External partie object type'), + ) + external_partie_object = forms.ModelChoiceField( + queryset=None, label=_('External partie object') + ) + tenant = DynamicModelChoiceField( + queryset=Tenant.objects.all(), required=False, selector=True, label=_('Tenant') ) - external_partie_object = forms.ModelChoiceField(queryset=None) - tenant = DynamicModelChoiceField(queryset=Tenant.objects.all(), required=False) - parent = DynamicModelChoiceField(queryset=Contract.objects.all(), required=False) - accounting_dimensions = Dimensions(required=False) + parent = DynamicModelChoiceField( + queryset=Contract.objects.all(), + required=False, + selector=True, + label=_('Parent'), + ) + accounting_dimensions = Dimensions(required=False, label=_('Accounting dimensions')) def __init__(self, *args, **kwargs): initial = kwargs.get('initial', None) @@ -131,6 +145,7 @@ class Meta: 'end_date', 'initial_term', 'renewal_term', + 'notice_period', 'currency', 'accounting_dimensions', 'yrc', @@ -157,15 +172,26 @@ def clean(self): ) -class ContractFilterSetForm(NetBoxModelFilterSetForm): +class ContractFilterForm(NetBoxModelFilterSetForm): model = Contract - tenant = DynamicModelChoiceField(queryset=Tenant.objects.all(), required=False) - external_reference = forms.CharField(required=False) - internal_partie = forms.ChoiceField(choices=InternalEntityChoices, required=False) - status = forms.ChoiceField(choices=StatusChoices, required=False) - currency = forms.ChoiceField(choices=CurrencyChoices, required=False) - parent = DynamicModelChoiceField(queryset=Contract.objects.all(), required=False) + tenant = DynamicModelChoiceField( + queryset=Tenant.objects.all(), required=False, selector=True, label=_('Tenant') + ) + external_reference = forms.CharField(required=False, label=_('External reference')) + internal_partie = forms.ChoiceField( + choices=InternalEntityChoices, required=False, label=_('Internal partie') + ) + status = forms.ChoiceField(choices=StatusChoices, required=False, label=_('Status')) + currency = forms.ChoiceField( + choices=CurrencyChoices, required=False, label=_('Currency') + ) + parent = DynamicModelChoiceField( + queryset=Contract.objects.all(), + required=False, + selector=True, + label=_('Parent'), + ) tag = TagFilterField(model) @@ -176,20 +202,24 @@ class ContractCSVForm(NetBoxModelImportForm): help_text='service provider object type in the form .', ) external_partie_object_id = forms.CharField( - help_text='service provider object name', label='external_partie_name' + help_text='service provider object name', label=_('External partie name') ) tenant = CSVModelChoiceField( queryset=Tenant.objects.all(), to_field_name='name', help_text='Tenant name', required=False, + label=_('Tenant'), + ) + status = CSVChoiceField( + choices=StatusChoices, help_text='Contract status', label=_('Status') ) - status = CSVChoiceField(choices=StatusChoices, help_text='Contract status') parent = CSVModelChoiceField( queryset=Contract.objects.all(), to_field_name='name', help_text='Contract name', required=False, + label=_('Parent'), ) class Meta: @@ -230,13 +260,24 @@ def clean_external_partie_object_id(self): class ContractBulkEditForm(NetBoxModelBulkEditForm): - name = forms.CharField(max_length=100, required=False) - external_reference = forms.CharField(max_length=100, required=False) - internal_partie = forms.ChoiceField(choices=InternalEntityChoices, required=False) - tenant = DynamicModelChoiceField(queryset=Tenant.objects.all(), required=False) - accounting_dimensions = Dimensions(required=False) - comments = CommentField(required=False) - parent = DynamicModelChoiceField(queryset=Contract.objects.all(), required=False) + name = forms.CharField(max_length=100, required=False, label=_('Name')) + external_reference = forms.CharField( + max_length=100, required=False, label=_('External reference') + ) + internal_partie = forms.ChoiceField( + choices=InternalEntityChoices, required=False, label=_('Internal partie') + ) + tenant = DynamicModelChoiceField( + queryset=Tenant.objects.all(), required=False, selector=True, label=_('Tenant') + ) + accounting_dimensions = Dimensions(required=False, label=_('Accounting dimensions')) + comments = CommentField(required=False, label=_('Comments')) + parent = DynamicModelChoiceField( + queryset=Contract.objects.all(), + required=False, + selector=True, + label=_('Parent'), + ) nullable_fields = ( 'accounting_dimensions', @@ -252,11 +293,15 @@ class InvoiceForm(NetBoxModelForm): number = forms.CharField( max_length=100, help_text='Invoice template name will be overriden to _invoice_template_contract name', + label=_('Number'), ) contracts = DynamicModelMultipleChoiceField( - queryset=Contract.objects.all(), required=False + queryset=Contract.objects.all(), + required=False, + selector=True, + label=_('Contracts'), ) - accounting_dimensions = Dimensions(required=False) + accounting_dimensions = Dimensions(required=False, label=_('Accounting dimensions')) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) @@ -360,15 +405,25 @@ class Meta: } -class InvoiceFilterSetForm(NetBoxModelFilterSetForm): +class InvoiceFilterForm(NetBoxModelFilterSetForm): model = Invoice - number = forms.CharField(required=False) + number = forms.CharField( + required=False, + label=_('Number'), + ) template = forms.NullBooleanField( - required=False, widget=forms.Select(choices=BOOLEAN_WITH_BLANK_CHOICES) + required=False, + widget=forms.Select(choices=BOOLEAN_WITH_BLANK_CHOICES), + label=_('Template'), + ) + currency = forms.ChoiceField( + choices=CurrencyChoices, required=False, label=_('Currency') ) - currency = forms.ChoiceField(choices=CurrencyChoices, required=False) contracts = DynamicModelMultipleChoiceField( - queryset=Contract.objects.all(), required=False + queryset=Contract.objects.all(), + required=False, + selector=True, + label=_('Contracts'), ) tag = TagFilterField(model) @@ -378,6 +433,7 @@ class InvoiceCSVForm(NetBoxModelImportForm): queryset=Contract.objects.all(), to_field_name='name', help_text='Related Contracts', + label=_('Contracts'), ) class Meta: @@ -399,19 +455,52 @@ class Meta: class InvoiceBulkEditForm(NetBoxModelBulkEditForm): - number = forms.CharField(max_length=100, required=True) - template = forms.BooleanField(required=False) - date = forms.DateField(required=False) + number = forms.CharField(max_length=100, required=True, label=_('Number')) + template = forms.BooleanField( + required=False, + label=_('Template'), + help_text=_('Wether this invoice is a template or not'), + ) + date = forms.DateField( + required=False, + label=_('Date'), + ) contracts = DynamicModelMultipleChoiceField( - queryset=Contract.objects.all(), required=False - ) - period_start = forms.DateField(required=False) - period_end = forms.DateField(required=False) - currency = forms.ChoiceField(choices=CurrencyChoices, required=False) - accounting_dimensions = Dimensions(required=False) - amount = forms.DecimalField(max_digits=10, decimal_places=2, required=False) - documents = forms.URLField(required=False) - comments = CommentField() + queryset=Contract.objects.all(), + required=False, + selector=True, + ) + period_start = forms.DateField( + required=False, + label=_('Period start'), + ) + period_end = forms.DateField( + required=False, + label=_('Period end'), + ) + currency = forms.ChoiceField( + choices=CurrencyChoices, + required=False, + label=_('Currency'), + ) + accounting_dimensions = Dimensions( + required=False, + label=_('Accounting dimensions'), + ) + amount = forms.DecimalField( + max_digits=10, + decimal_places=2, + required=False, + label=_('Amount'), + ) + documents = forms.URLField( + required=False, + label=_('Documents'), + help_text=_('URL to the contract documents'), + ) + comments = CommentField( + label=_('Comments'), + ) nullable_fields = ( 'accounting_dimensions', 'comments', @@ -424,23 +513,23 @@ class InvoiceBulkEditForm(NetBoxModelBulkEditForm): class ServiceProviderForm(NetBoxModelForm): - slug = SlugField() - comments = CommentField() + slug = SlugField(label=_('Slug')) + comments = CommentField(label=_('Comments')) class Meta: model = ServiceProvider fields = ('name', 'slug', 'portal_url', 'comments', 'tags') -class ServiceProviderFilterSetForm(NetBoxModelFilterSetForm): +class ServiceProviderFilterForm(NetBoxModelFilterSetForm): model = ServiceProvider - name = forms.CharField(required=False) + name = forms.CharField(required=False, label=_('Name')) tag = TagFilterField(model) class ServiceProviderCSVForm(NetBoxModelImportForm): - slug = SlugField() - comments = CommentField() + slug = SlugField(label=_('Slug')) + comments = CommentField(label=_('Comments')) class Meta: model = ServiceProvider @@ -448,8 +537,8 @@ class Meta: class ServiceProviderBulkEditForm(NetBoxModelBulkEditForm): - name = forms.CharField(max_length=100, required=True) - comments = CommentField() + name = forms.CharField(max_length=100, required=True, label=_('Name')) + comments = CommentField(label=_('Comments')) nullable_fields = ('comments',) model = ServiceProvider @@ -458,7 +547,9 @@ class ServiceProviderBulkEditForm(NetBoxModelBulkEditForm): class ContractAssignmentForm(NetBoxModelForm): - contract = DynamicModelChoiceField(queryset=Contract.objects.all()) + contract = DynamicModelChoiceField( + queryset=Contract.objects.all(), selector=True, label=_('Contract') + ) class Meta: model = ContractAssignment @@ -469,18 +560,25 @@ class Meta: } -class ContractAssignmentFilterSetForm(NetBoxModelFilterSetForm): +class ContractAssignmentFilterForm(NetBoxModelFilterSetForm): model = ContractAssignment - contract = DynamicModelChoiceField(queryset=Contract.objects.all()) + contract = DynamicModelChoiceField( + queryset=Contract.objects.all(), + selector=True, + label=_('Contract'), + ) class ContractAssignmentImportForm(NetBoxModelImportForm): content_type = CSVContentTypeField( queryset=ContentType.objects.all(), help_text='Content Type in the form .', + label=_('Content type'), ) contract = CSVModelChoiceField( - queryset=Contract.objects.all(), help_text='Contract id' + queryset=Contract.objects.all(), + help_text='Contract id', + label=_('Contract'), ) class Meta: @@ -492,9 +590,16 @@ class Meta: class InvoiceLineForm(NetBoxModelForm): - invoice = DynamicModelChoiceField(queryset=Invoice.objects.all()) + invoice = DynamicModelChoiceField( + queryset=Invoice.objects.all(), + selector=True, + label=_('Invoice'), + ) accounting_dimensions = DynamicModelMultipleChoiceField( - queryset=AccountingDimension.objects.all(), required=False + queryset=AccountingDimension.objects.all(), + required=False, + selector=True, + label=_('Accounting dimensions'), ) def clean(self): @@ -527,13 +632,25 @@ class Meta: ] -class InvoiceLineFilterSetForm(NetBoxModelFilterSetForm): +class InvoiceLineFilterForm(NetBoxModelFilterSetForm): model = InvoiceLine - invoice = DynamicModelChoiceField(queryset=Invoice.objects.all(), required=False) + invoice = DynamicModelChoiceField( + queryset=Invoice.objects.all(), + required=False, + selector=True, + label=_('Invoice'), + ) accounting_dimensions = DynamicModelMultipleChoiceField( - queryset=AccountingDimension.objects.all(), required=False + queryset=AccountingDimension.objects.all(), + required=False, + selector=True, + label=_('Accounting dimensions'), + ) + currency = forms.ChoiceField( + choices=CurrencyChoices, + required=False, + label=_('Currency'), ) - currency = forms.ChoiceField(choices=CurrencyChoices, required=False) tag = TagFilterField(model) @@ -542,11 +659,13 @@ class InvoiceLineImportForm(NetBoxModelImportForm): queryset=Invoice.objects.all(), to_field_name='number', help_text='Invoice number', + label=_('Invoice'), ) accounting_dimensions = CSVModelMultipleChoiceField( queryset=AccountingDimension.objects.all(), to_field_name='id', help_text='accounting dimension id', + label=_('Accounting dimensions'), ) class Meta: @@ -564,7 +683,10 @@ class Meta: class InvoiceLineBulkEditForm(NetBoxModelBulkEditForm): invoice = DynamicModelChoiceField(queryset=Invoice.objects.all(), required=False) accounting_dimensions = DynamicModelMultipleChoiceField( - queryset=AccountingDimension.objects.all(), required=False + queryset=AccountingDimension.objects.all(), + required=False, + selector=True, + label=_('Accounting dimensions'), ) model = InvoiceLine @@ -584,12 +706,16 @@ class Meta: ] -class AccountingDimensionFilterSetForm(NetBoxModelFilterSetForm): +class AccountingDimensionFilterForm(NetBoxModelFilterSetForm): model = AccountingDimension - name = forms.CharField(required=False) - value = forms.CharField(required=False) - status = forms.ChoiceField(choices=AccountingDimensionStatusChoices, required=False) + name = forms.CharField(required=False, label=_('Name')) + value = forms.CharField(required=False, label=_('Value')) + status = forms.ChoiceField( + choices=AccountingDimensionStatusChoices, + required=False, + label=_('Status'), + ) class AccountingDimensionImportForm(NetBoxModelImportForm): @@ -607,6 +733,6 @@ class Meta: class AccountingDimensionBulkEditForm(NetBoxModelBulkEditForm): - name = forms.CharField(max_length=20, required=False) - value = forms.CharField(max_length=20, required=False) + name = forms.CharField(max_length=20, required=False, label=_('Name')) + value = forms.CharField(max_length=20, required=False, label=_('Value')) model = AccountingDimension diff --git a/src/netbox_contract/locale/en/LC_MESSAGES/django.mo b/src/netbox_contract/locale/en/LC_MESSAGES/django.mo new file mode 100644 index 0000000..71cbdf3 Binary files /dev/null and b/src/netbox_contract/locale/en/LC_MESSAGES/django.mo differ diff --git a/src/netbox_contract/locale/en/LC_MESSAGES/django.po b/src/netbox_contract/locale/en/LC_MESSAGES/django.po new file mode 100644 index 0000000..c1b4a1a --- /dev/null +++ b/src/netbox_contract/locale/en/LC_MESSAGES/django.po @@ -0,0 +1,499 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2025-01-04 16:10+0000\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#: forms.py:63 forms.py:275 forms.py:499 forms.py:514 forms.py:529 forms.py:542 +msgid "Comments" +msgstr "" + +#: forms.py:69 +msgid "External partie object type" +msgstr "" + +#: forms.py:71 +msgid "External partie object" +msgstr "" + +#: forms.py:73 forms.py:173 forms.py:199 forms.py:267 +#: templates/netbox_contract/contract.html:43 +msgid "Tenant" +msgstr "" + +#: forms.py:76 forms.py:180 forms.py:211 forms.py:281 +#: templates/netbox_contract/contract.html:109 +msgid "Parent" +msgstr "" + +#: forms.py:78 forms.py:271 forms.py:303 forms.py:485 forms.py:601 forms.py:646 +#: forms.py:667 forms.py:688 navigation.py:75 +#: templates/netbox_contract/contract.html:83 +#: templates/netbox_contract/invoice.html:47 +#: templates/netbox_contract/invoiceline.html:32 +msgid "Accounting dimensions" +msgstr "" + +#: forms.py:175 forms.py:256 templates/netbox_contract/contract.html:34 +msgid "External reference" +msgstr "" + +#: forms.py:176 forms.py:261 templates/netbox_contract/contract.html:38 +msgid "Internal partie" +msgstr "" + +#: forms.py:177 forms.py:204 forms.py:716 +#: templates/netbox_contract/accountingdimension.html:21 +#: templates/netbox_contract/contract.html:30 +msgid "Status" +msgstr "" + +#: forms.py:178 forms.py:418 forms.py:481 forms.py:651 +#: templates/netbox_contract/contract.html:78 +#: templates/netbox_contract/contract.html:143 +#: templates/netbox_contract/invoice.html:42 +#: templates/netbox_contract/invoiceline.html:28 +msgid "Currency" +msgstr "" + +#: forms.py:192 +msgid "External partie name" +msgstr "" + +#: forms.py:252 forms.py:523 forms.py:540 forms.py:711 forms.py:735 +#: templates/netbox_contract/accountingdimension.html:13 +#: templates/netbox_contract/contract.html:16 +#: templates/netbox_contract/serviceprovider.html:12 +msgid "Name" +msgstr "" + +#: forms.py:298 forms.py:411 forms.py:455 +#: templates/netbox_contract/contract.html:137 +#: templates/netbox_contract/invoice.html:24 +msgid "Number" +msgstr "" + +#: forms.py:301 forms.py:423 forms.py:433 navigation.py:54 navigation.py:103 +#: templates/contract_list_bottom.html:6 +#: templates/netbox_contract/invoice.html:91 +msgid "Contracts" +msgstr "" + +#: forms.py:416 forms.py:458 +msgid "Template" +msgstr "" + +#: forms.py:459 models.py:331 +msgid "Wether this invoice is a template or not" +msgstr "" + +#: forms.py:463 templates/netbox_contract/invoice.html:28 +msgid "Date" +msgstr "" + +#: forms.py:472 templates/netbox_contract/invoice.html:33 +msgid "Period start" +msgstr "" + +#: forms.py:476 templates/netbox_contract/invoice.html:37 +msgid "Period end" +msgstr "" + +#: forms.py:491 templates/netbox_contract/invoice.html:52 +#: templates/netbox_contract/invoiceline.html:24 +msgid "Amount" +msgstr "" + +#: forms.py:495 templates/netbox_contract/contract.html:117 +#: templates/netbox_contract/contract.html:119 +#: templates/netbox_contract/invoice.html:57 +#: templates/netbox_contract/invoice.html:59 +msgid "Documents" +msgstr "" + +#: forms.py:496 models.py:285 models.py:372 +msgid "URL to the contract documents" +msgstr "" + +#: forms.py:513 forms.py:528 +msgid "Slug" +msgstr "" + +#: forms.py:551 forms.py:567 forms.py:580 +#: templates/netbox_contract/contract.html:13 +#: templates/netbox_contract/contractassignment.html:18 +msgid "Contract" +msgstr "" + +#: forms.py:575 +msgid "Content type" +msgstr "" + +#: forms.py:595 forms.py:640 forms.py:661 +#: templates/netbox_contract/invoice.html:20 +#: templates/netbox_contract/invoiceline.html:18 +msgid "Invoice" +msgstr "" + +#: forms.py:712 forms.py:736 +#: templates/netbox_contract/accountingdimension.html:17 +msgid "Value" +msgstr "" + +#: models.py:65 +msgid "dimension name" +msgstr "" + +#: models.py:66 +msgid "Accounting dimension name. Ex: Department, Location, etc." +msgstr "" + +#: models.py:70 +msgid "value" +msgstr "" + +#: models.py:76 models.py:212 +msgid "status" +msgstr "" + +#: models.py:80 models.py:120 models.py:289 models.py:376 models.py:423 +msgid "comments" +msgstr "" + +#: models.py:100 +msgid "accounting dimension" +msgstr "" + +#: models.py:101 models.py:252 models.py:362 models.py:419 +msgid "accounting dimensions" +msgstr "" + +#: models.py:107 models.py:171 +msgid "name" +msgstr "" + +#: models.py:112 templates/netbox_contract/serviceprovider.html:16 +msgid "slug" +msgstr "" + +#: models.py:116 +msgid "portal URL" +msgstr "" + +#: models.py:125 +msgid "service provider" +msgstr "" + +#: models.py:126 +msgid "service providers" +msgstr "" + +#: models.py:139 +msgid "content type" +msgstr "" + +#: models.py:142 +msgid "object ID" +msgstr "" + +#: models.py:152 models.py:310 +msgid "contract" +msgstr "" + +#: models.py:161 +msgid "contract assignment" +msgstr "" + +#: models.py:162 +msgid "contract assignments" +msgstr "" + +#: models.py:178 +msgid "external partie object type" +msgstr "" + +#: models.py:183 +msgid "external partie object ID" +msgstr "" + +#: models.py:193 +msgid "external reference" +msgstr "" + +#: models.py:198 +msgid "internal partie" +msgstr "" + +#: models.py:206 +msgid "tenant" +msgstr "" + +#: models.py:217 +msgid "start date" +msgstr "" + +#: models.py:222 +msgid "end date" +msgstr "" + +#: models.py:225 models.py:232 +msgid "In month" +msgstr "" + +#: models.py:229 +msgid "initial term" +msgstr "" + +#: models.py:236 +msgid "renewal term" +msgstr "" + +#: models.py:239 +msgid "Contract notice period. Default to 90 days" +msgstr "" + +#: models.py:241 +msgid "notice period" +msgstr "" + +#: models.py:247 models.py:358 models.py:409 +msgid "currency" +msgstr "" + +#: models.py:253 +msgid "This field is deprecated and will be removed in version 3" +msgstr "" + +#: models.py:256 +msgid "yearly recuring cost" +msgstr "" + +#: models.py:261 +msgid "Use either this field of the monthly recuring cost field" +msgstr "" + +#: models.py:264 +msgid "monthly recuring cost" +msgstr "" + +#: models.py:269 +msgid "Use either this field of the yearly recuring cost field" +msgstr "" + +#: models.py:272 +msgid "none recuring cost" +msgstr "" + +#: models.py:278 +msgid "The frequency of invoices in month" +msgstr "" + +#: models.py:280 +msgid "invoice frequency" +msgstr "" + +#: models.py:284 models.py:371 +msgid "documents" +msgstr "" + +#: models.py:297 +msgid "parent" +msgstr "" + +#: models.py:311 models.py:342 +msgid "contracts" +msgstr "" + +#: models.py:324 +msgid "number" +msgstr "" + +#: models.py:330 +msgid "template" +msgstr "" + +#: models.py:336 +msgid "date" +msgstr "" + +#: models.py:347 +msgid "period start" +msgstr "" + +#: models.py:352 +msgid "period end" +msgstr "" + +#: models.py:367 models.py:414 +msgid "amount" +msgstr "" + +#: models.py:381 models.py:403 +msgid "invoice" +msgstr "" + +#: models.py:382 +msgid "invoices" +msgstr "" + +#: models.py:428 +msgid "invoice line" +msgstr "" + +#: models.py:429 +msgid "invoice lines" +msgstr "" + +#: navigation.py:10 navigation.py:19 navigation.py:28 navigation.py:37 +#: navigation.py:46 +msgid "Add" +msgstr "" + +#: navigation.py:61 templates/netbox_contract/contract.html:182 +msgid "Invoices" +msgstr "" + +#: navigation.py:68 templates/netbox_contract/invoice.html:77 +#: templates/netbox_contract/invoiceline.html:15 +msgid "Invoice lines" +msgstr "" + +#: navigation.py:82 +msgid "Service providers" +msgstr "" + +#: navigation.py:88 templates/contract_assignments_bottom.html:7 +msgid "Contracts assignments" +msgstr "" + +#: templates/contract_assignments_bottom.html:12 +msgid "Add assignment" +msgstr "" + +#: templates/netbox_contract/accountingdimension.html:10 +msgid "Accounting dimension" +msgstr "" + +#: templates/netbox_contract/contract.html:20 +msgid "External partie type" +msgstr "" + +#: templates/netbox_contract/contract.html:24 +msgid "External partie" +msgstr "" + +#: templates/netbox_contract/contract.html:49 +msgid "Start date" +msgstr "" + +#: templates/netbox_contract/contract.html:55 +msgid "End date" +msgstr "" + +#: templates/netbox_contract/contract.html:61 +msgid "Initial term" +msgstr "" + +#: templates/netbox_contract/contract.html:62 +#: templates/netbox_contract/contract.html:68 +msgid "month" +msgstr "" + +#: templates/netbox_contract/contract.html:67 +msgid "Renewal term" +msgstr "" + +#: templates/netbox_contract/contract.html:73 +msgid "Notice period" +msgstr "" + +#: templates/netbox_contract/contract.html:74 +msgid "days" +msgstr "" + +#: templates/netbox_contract/contract.html:88 +msgid "Monthly recuring costs" +msgstr "" + +#: templates/netbox_contract/contract.html:92 +msgid "Yearly recuring costs" +msgstr "" + +#: templates/netbox_contract/contract.html:96 +msgid "calculated corresponding Yearly or Monthly value" +msgstr "" + +#: templates/netbox_contract/contract.html:100 +msgid "Non recuring costs" +msgstr "" + +#: templates/netbox_contract/contract.html:104 +msgid "Invoice frequency" +msgstr "" + +#: templates/netbox_contract/contract.html:133 +#: templates/netbox_contract/invoice.html:18 +msgid "Invoice template" +msgstr "" + +#: templates/netbox_contract/contract.html:147 +msgid "Total amount" +msgstr "" + +#: templates/netbox_contract/contract.html:152 +msgid "Invoice template lines" +msgstr "" + +#: templates/netbox_contract/contract.html:162 +msgid "Assignments" +msgstr "" + +#: templates/netbox_contract/contract.html:171 +msgid "childs" +msgstr "" + +#: templates/netbox_contract/contract.html:187 +msgid "Add an invoice" +msgstr "" + +#: templates/netbox_contract/contractassignment.html:7 +msgid "Contract assignment" +msgstr "" + +#: templates/netbox_contract/contractassignment.html:10 +msgid "Model" +msgstr "" + +#: templates/netbox_contract/contractassignment.html:14 +msgid "Object" +msgstr "" + +#: templates/netbox_contract/invoice.html:64 +msgid "Invoice lines total" +msgstr "" + +#: templates/netbox_contract/invoice.html:80 +msgid "Add a line" +msgstr "" + +#: templates/netbox_contract/serviceprovider.html:9 +msgid "Service provider" +msgstr "" + +#: templates/netbox_contract/serviceprovider.html:20 +msgid "Portal url" +msgstr "" diff --git a/src/netbox_contract/locale/fr/LC_MESSAGES/django.mo b/src/netbox_contract/locale/fr/LC_MESSAGES/django.mo new file mode 100644 index 0000000..6197aad Binary files /dev/null and b/src/netbox_contract/locale/fr/LC_MESSAGES/django.mo differ diff --git a/src/netbox_contract/locale/fr/LC_MESSAGES/django.po b/src/netbox_contract/locale/fr/LC_MESSAGES/django.po new file mode 100644 index 0000000..bfe476d --- /dev/null +++ b/src/netbox_contract/locale/fr/LC_MESSAGES/django.po @@ -0,0 +1,503 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +# Translators: +# marc lebreuil, 2025 +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2025-01-04 16:10+0000\n" +"PO-Revision-Date: 2025-01-04 12:18+0000\n" +"Last-Translator: marc lebreuil, 2025\n" +"Language-Team: French (https://app.transifex.com/lebreuil/teams/203600/fr/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: fr\n" +"Plural-Forms: nplurals=3; plural=(n == 0 || n == 1) ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;\n" + +#: forms.py:63 forms.py:275 forms.py:499 forms.py:514 forms.py:529 +#: forms.py:542 +msgid "Comments" +msgstr "Commentaires" + +#: forms.py:69 +msgid "External partie object type" +msgstr "Type de prestataire " + +#: forms.py:71 +msgid "External partie object" +msgstr "Prestataire" + +#: forms.py:73 forms.py:173 forms.py:199 forms.py:267 +#: templates/netbox_contract/contract.html:43 +msgid "Tenant" +msgstr "Entité" + +#: forms.py:76 forms.py:180 forms.py:211 forms.py:281 +#: templates/netbox_contract/contract.html:109 +msgid "Parent" +msgstr "Parent" + +#: forms.py:78 forms.py:271 forms.py:303 forms.py:485 forms.py:601 +#: forms.py:646 forms.py:667 forms.py:688 navigation.py:75 +#: templates/netbox_contract/contract.html:83 +#: templates/netbox_contract/invoice.html:47 +#: templates/netbox_contract/invoiceline.html:32 +msgid "Accounting dimensions" +msgstr "Dimensions comptables" + +#: forms.py:175 forms.py:256 templates/netbox_contract/contract.html:34 +msgid "External reference" +msgstr "Reference externe" + +#: forms.py:176 forms.py:261 templates/netbox_contract/contract.html:38 +msgid "Internal partie" +msgstr "Entité interne" + +#: forms.py:177 forms.py:204 forms.py:716 +#: templates/netbox_contract/accountingdimension.html:21 +#: templates/netbox_contract/contract.html:30 +msgid "Status" +msgstr "Statut" + +#: forms.py:178 forms.py:418 forms.py:481 forms.py:651 +#: templates/netbox_contract/contract.html:78 +#: templates/netbox_contract/contract.html:143 +#: templates/netbox_contract/invoice.html:42 +#: templates/netbox_contract/invoiceline.html:28 +msgid "Currency" +msgstr "Devise" + +#: forms.py:192 +msgid "External partie name" +msgstr "Nom du prestataire" + +#: forms.py:252 forms.py:523 forms.py:540 forms.py:711 forms.py:735 +#: templates/netbox_contract/accountingdimension.html:13 +#: templates/netbox_contract/contract.html:16 +#: templates/netbox_contract/serviceprovider.html:12 +msgid "Name" +msgstr "Nom" + +#: forms.py:298 forms.py:411 forms.py:455 +#: templates/netbox_contract/contract.html:137 +#: templates/netbox_contract/invoice.html:24 +msgid "Number" +msgstr "Numéro" + +#: forms.py:301 forms.py:423 forms.py:433 navigation.py:54 navigation.py:103 +#: templates/contract_list_bottom.html:6 +#: templates/netbox_contract/invoice.html:91 +msgid "Contracts" +msgstr "Contrats" + +#: forms.py:416 forms.py:458 +msgid "Template" +msgstr "Modèle" + +#: forms.py:459 models.py:331 +msgid "Wether this invoice is a template or not" +msgstr "Cette facture est elle ou non un modèle" + +#: forms.py:463 templates/netbox_contract/invoice.html:28 +msgid "Date" +msgstr "Date" + +#: forms.py:472 templates/netbox_contract/invoice.html:33 +msgid "Period start" +msgstr "Début de période" + +#: forms.py:476 templates/netbox_contract/invoice.html:37 +msgid "Period end" +msgstr "Fin de période" + +#: forms.py:491 templates/netbox_contract/invoice.html:52 +#: templates/netbox_contract/invoiceline.html:24 +msgid "Amount" +msgstr "Montant" + +#: forms.py:495 templates/netbox_contract/contract.html:117 +#: templates/netbox_contract/contract.html:119 +#: templates/netbox_contract/invoice.html:57 +#: templates/netbox_contract/invoice.html:59 +msgid "Documents" +msgstr "Documents" + +#: forms.py:496 models.py:285 models.py:372 +msgid "URL to the contract documents" +msgstr "URL des documents contractuels" + +#: forms.py:513 forms.py:528 +msgid "Slug" +msgstr "Slug" + +#: forms.py:551 forms.py:567 forms.py:580 +#: templates/netbox_contract/contract.html:13 +#: templates/netbox_contract/contractassignment.html:18 +msgid "Contract" +msgstr "Contrat" + +#: forms.py:575 +msgid "Content type" +msgstr "Type de contenu" + +#: forms.py:595 forms.py:640 forms.py:661 +#: templates/netbox_contract/invoice.html:20 +#: templates/netbox_contract/invoiceline.html:18 +msgid "Invoice" +msgstr "Facture" + +#: forms.py:712 forms.py:736 +#: templates/netbox_contract/accountingdimension.html:17 +msgid "Value" +msgstr "Valeur" + +#: models.py:65 +msgid "dimension name" +msgstr "Nom de la dimension" + +#: models.py:66 +msgid "Accounting dimension name. Ex: Department, Location, etc." +msgstr "Dimmension comptable. Ex: département, centre de coût, etc." + +#: models.py:70 +msgid "value" +msgstr "valeur" + +#: models.py:76 models.py:212 +msgid "status" +msgstr "statut" + +#: models.py:80 models.py:120 models.py:289 models.py:376 models.py:423 +msgid "comments" +msgstr "commentaires" + +#: models.py:100 +msgid "accounting dimension" +msgstr "Dimension comptable" + +#: models.py:101 models.py:252 models.py:362 models.py:419 +msgid "accounting dimensions" +msgstr "dimensions comptables" + +#: models.py:107 models.py:171 +msgid "name" +msgstr "nom" + +#: models.py:112 templates/netbox_contract/serviceprovider.html:16 +msgid "slug" +msgstr "slug" + +#: models.py:116 +msgid "portal URL" +msgstr "URL du portail" + +#: models.py:125 +msgid "service provider" +msgstr "Prestataire de services" + +#: models.py:126 +msgid "service providers" +msgstr "Prestataires de services" + +#: models.py:139 +msgid "content type" +msgstr "type de contenu" + +#: models.py:142 +msgid "object ID" +msgstr "identifiant" + +#: models.py:152 models.py:310 +msgid "contract" +msgstr "contrat" + +#: models.py:161 +msgid "contract assignment" +msgstr "objet du contrat" + +#: models.py:162 +msgid "contract assignments" +msgstr "objets du contrat" + +#: models.py:178 +msgid "external partie object type" +msgstr "type de prestataire " + +#: models.py:183 +msgid "external partie object ID" +msgstr "identifiant du prestataire " + +#: models.py:193 +msgid "external reference" +msgstr "réference externe" + +#: models.py:198 +msgid "internal partie" +msgstr "entité interne" + +#: models.py:206 +msgid "tenant" +msgstr "entité" + +#: models.py:217 +msgid "start date" +msgstr "date de début" + +#: models.py:222 +msgid "end date" +msgstr "date de fin" + +#: models.py:225 models.py:232 +msgid "In month" +msgstr "en mois" + +#: models.py:229 +msgid "initial term" +msgstr "Durée initiale" + +#: models.py:236 +msgid "renewal term" +msgstr "durée après renouvellement" + +#: models.py:239 +msgid "Contract notice period. Default to 90 days" +msgstr "Préavis du contrat. 90 jours par defaut." + +#: models.py:241 +msgid "notice period" +msgstr "Préavis" + +#: models.py:247 models.py:358 models.py:409 +msgid "currency" +msgstr "devise" + +#: models.py:253 +msgid "This field is deprecated and will be removed in version 3" +msgstr "Ce champ est sera supprimé en version 3." + +#: models.py:256 +msgid "yearly recuring cost" +msgstr "coût annuel" + +#: models.py:261 +msgid "Use either this field of the monthly recuring cost field" +msgstr "Utiliser soit ce champ soit le champ \"coût mensuel\"." + +#: models.py:264 +msgid "monthly recuring cost" +msgstr "coût mensuel" + +#: models.py:269 +msgid "Use either this field of the yearly recuring cost field" +msgstr "Utiliser soit ce champ soit le champ \"cout annuel\"." + +#: models.py:272 +msgid "none recuring cost" +msgstr "coûts uniques" + +#: models.py:278 +msgid "The frequency of invoices in month" +msgstr "Fréquence des factures en mois" + +#: models.py:280 +msgid "invoice frequency" +msgstr "fréquemce des factures" + +#: models.py:284 models.py:371 +msgid "documents" +msgstr "documents" + +#: models.py:297 +msgid "parent" +msgstr "parent" + +#: models.py:311 models.py:342 +msgid "contracts" +msgstr "contrat" + +#: models.py:324 +msgid "number" +msgstr "numéro" + +#: models.py:330 +msgid "template" +msgstr "modèle" + +#: models.py:336 +msgid "date" +msgstr "date" + +#: models.py:347 +msgid "period start" +msgstr "début de période" + +#: models.py:352 +msgid "period end" +msgstr "fin de période" + +#: models.py:367 models.py:414 +msgid "amount" +msgstr "montant" + +#: models.py:381 models.py:403 +msgid "invoice" +msgstr "facture" + +#: models.py:382 +msgid "invoices" +msgstr "factures" + +#: models.py:428 +msgid "invoice line" +msgstr "ligne de facture" + +#: models.py:429 +msgid "invoice lines" +msgstr "lignes de facture" + +#: navigation.py:10 navigation.py:19 navigation.py:28 navigation.py:37 +#: navigation.py:46 +msgid "Add" +msgstr "Ajouter" + +#: navigation.py:61 templates/netbox_contract/contract.html:182 +msgid "Invoices" +msgstr "Factures" + +#: navigation.py:68 templates/netbox_contract/invoice.html:77 +#: templates/netbox_contract/invoiceline.html:15 +msgid "Invoice lines" +msgstr "Lignes de facture" + +#: navigation.py:82 +msgid "Service providers" +msgstr "Prestataires de services" + +#: navigation.py:88 templates/contract_assignments_bottom.html:7 +msgid "Contracts assignments" +msgstr "Liens contrats objets" + +#: templates/contract_assignments_bottom.html:12 +msgid "Add assignment" +msgstr "Lier un objet" + +#: templates/netbox_contract/accountingdimension.html:10 +msgid "Accounting dimension" +msgstr "Dimension comptable" + +#: templates/netbox_contract/contract.html:20 +msgid "External partie type" +msgstr "Type de prestataire " + +#: templates/netbox_contract/contract.html:24 +msgid "External partie" +msgstr "Prestataire" + +#: templates/netbox_contract/contract.html:49 +msgid "Start date" +msgstr "Date de début" + +#: templates/netbox_contract/contract.html:55 +msgid "End date" +msgstr "Date de fin" + +#: templates/netbox_contract/contract.html:61 +msgid "Initial term" +msgstr "Durée initiale" + +#: templates/netbox_contract/contract.html:62 +#: templates/netbox_contract/contract.html:68 +msgid "month" +msgstr "mois" + +#: templates/netbox_contract/contract.html:67 +msgid "Renewal term" +msgstr "Durée après renouvellement" + +#: templates/netbox_contract/contract.html:73 +msgid "Notice period" +msgstr "Préavis" + +#: templates/netbox_contract/contract.html:74 +msgid "days" +msgstr "jours" + +#: templates/netbox_contract/contract.html:88 +msgid "Monthly recuring costs" +msgstr "Coûts mensuels récurrents" + +#: templates/netbox_contract/contract.html:92 +msgid "Yearly recuring costs" +msgstr "Coûts annuels récurrents" + +#: templates/netbox_contract/contract.html:96 +msgid "calculated corresponding Yearly or Monthly value" +msgstr "valeur annuelle ou mensuelle correspondante calculée" + +#: templates/netbox_contract/contract.html:100 +msgid "Non recuring costs" +msgstr "Coûts uniques" + +#: templates/netbox_contract/contract.html:104 +msgid "Invoice frequency" +msgstr "Fréquence des factures" + +#: templates/netbox_contract/contract.html:133 +#: templates/netbox_contract/invoice.html:18 +msgid "Invoice template" +msgstr "Modèle de facture" + +#: templates/netbox_contract/contract.html:147 +msgid "Total amount" +msgstr "Montant total" + +#: templates/netbox_contract/contract.html:152 +msgid "Invoice template lines" +msgstr "Lignes du modèle de facture" + +#: templates/netbox_contract/contract.html:162 +msgid "Assignments" +msgstr "Objets" + +#: templates/netbox_contract/contract.html:171 +msgid "childs" +msgstr "enfants" + +#: templates/netbox_contract/contract.html:187 +msgid "Add an invoice" +msgstr "Ajouter une facture" + +#: templates/netbox_contract/contractassignment.html:7 +msgid "Contract assignment" +msgstr "Ajouter un objet au contrat" + +#: templates/netbox_contract/contractassignment.html:10 +msgid "Model" +msgstr "Modèle" + +#: templates/netbox_contract/contractassignment.html:14 +msgid "Object" +msgstr "Objet" + +#: templates/netbox_contract/invoice.html:64 +msgid "Invoice lines total" +msgstr "Total des lignes de facture" + +#: templates/netbox_contract/invoice.html:80 +msgid "Add a line" +msgstr "Ajouter une ligne" + +#: templates/netbox_contract/serviceprovider.html:9 +msgid "Service provider" +msgstr "Prestataire" + +#: templates/netbox_contract/serviceprovider.html:20 +msgid "Portal url" +msgstr "URL du portail" diff --git a/src/netbox_contract/migrations/0037_contract_notice_period_alter_contract_currency_and_more.py b/src/netbox_contract/migrations/0037_contract_notice_period_alter_contract_currency_and_more.py new file mode 100644 index 0000000..c23ee4b --- /dev/null +++ b/src/netbox_contract/migrations/0037_contract_notice_period_alter_contract_currency_and_more.py @@ -0,0 +1,32 @@ +# Generated by Django 5.0.10 on 2025-01-03 19:52 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ('netbox_contract', '0036_alter_invoiceline_accounting_dimensions'), + ] + + operations = [ + migrations.AddField( + model_name='contract', + name='notice_period', + field=models.IntegerField(default=90), + ), + migrations.AlterField( + model_name='contract', + name='currency', + field=models.CharField(default='eur', max_length=3), + ), + migrations.AlterField( + model_name='invoice', + name='currency', + field=models.CharField(default='eur', max_length=3), + ), + migrations.AlterField( + model_name='invoiceline', + name='currency', + field=models.CharField(default='eur', max_length=3), + ), + ] diff --git a/src/netbox_contract/models.py b/src/netbox_contract/models.py index a15e6f9..cd44300 100644 --- a/src/netbox_contract/models.py +++ b/src/netbox_contract/models.py @@ -1,8 +1,11 @@ +from datetime import timedelta + from django.contrib.contenttypes.fields import GenericForeignKey from django.contrib.contenttypes.models import ContentType from django.core.exceptions import ValidationError from django.db import models from django.urls import reverse +from django.utils.translation import gettext_lazy as _ from netbox.models import NetBoxModel from netbox.models.features import ContactsMixin from utilities.choices import ChoiceSet @@ -53,15 +56,23 @@ class CurrencyChoices(ChoiceSet): ] +CURRENCY_DEFAULT = CurrencyChoices.CHOICES[0][0] + + class AccountingDimension(NetBoxModel): - name = models.CharField(max_length=20) - value = models.CharField(max_length=20) + name = models.CharField( + max_length=20, + verbose_name=_('dimension name'), + help_text=_('Accounting dimension name. Ex: Department, Location, etc.'), + ) + value = models.CharField(max_length=20, verbose_name=_('value')) status = models.CharField( max_length=50, choices=AccountingDimensionStatusChoices, default=StatusChoices.STATUS_ACTIVE, + verbose_name=_('status'), ) - comments = models.TextField(blank=True) + comments = models.TextField(blank=True, verbose_name=_('comments')) def get_absolute_url(self): return reverse('plugins:netbox_contract:accountingdimension', args=[self.pk]) @@ -79,16 +90,21 @@ class Meta: fields=['name', 'value'], name='unique_accounting_dimension' ) ] + ordering = ('name', 'value') + verbose_name = _('accounting dimension') + verbose_name_plural = _('accounting dimensions') class ServiceProvider(ContactsMixin, NetBoxModel): - name = models.CharField(max_length=100) - slug = models.SlugField(max_length=100, unique=True) - portal_url = models.URLField(blank=True, verbose_name='Portal URL') - comments = models.TextField(blank=True) + name = models.CharField(max_length=100, verbose_name=_('name')) + slug = models.SlugField(max_length=100, unique=True, verbose_name=_('slug')) + portal_url = models.URLField(blank=True, verbose_name=_('portal URL')) + comments = models.TextField(blank=True, verbose_name=_('comments')) class Meta: ordering = ('name',) + verbose_name = _('service provider') + verbose_name_plural = _('service providers') def __str__(self): return self.name @@ -98,11 +114,16 @@ def get_absolute_url(self): class ContractAssignment(NetBoxModel): - content_type = models.ForeignKey(to=ContentType, on_delete=models.CASCADE) - object_id = models.PositiveBigIntegerField() + content_type = models.ForeignKey( + to=ContentType, on_delete=models.CASCADE, verbose_name=_('content type') + ) + object_id = models.PositiveBigIntegerField(verbose_name=_('object ID')) content_object = GenericForeignKey(ct_field='content_type', fk_field='object_id') contract = models.ForeignKey( - to='Contract', on_delete=models.CASCADE, related_name='assignments' + to='Contract', + on_delete=models.CASCADE, + related_name='assignments', + verbose_name=_('contract'), ) clone_fields = ('content_type', 'object_id', 'contract') @@ -111,26 +132,33 @@ class Meta: indexes = [ models.Index(fields=['content_type', 'object_id']), ] + verbose_name = _('contract assignment') + verbose_name_plural = _('contract assignments') def get_absolute_url(self): return reverse('plugins:netbox_contract:contractassignment', args=[self.pk]) class Contract(NetBoxModel): - name = models.CharField(max_length=100) - + name = models.CharField(max_length=100, verbose_name=_('name')) external_partie_object_type = models.ForeignKey( - to=ContentType, on_delete=models.CASCADE, blank=True, null=True + to=ContentType, + on_delete=models.CASCADE, + blank=True, + null=True, + verbose_name=_('external partie object type'), + ) + external_partie_object_id = models.PositiveBigIntegerField( + blank=True, null=True, verbose_name=_('external partie object ID') ) - external_partie_object_id = models.PositiveBigIntegerField(blank=True, null=True) external_partie_object = GenericForeignKey( ct_field='external_partie_object_type', fk_field='external_partie_object_id' ) - - external_reference = models.CharField(max_length=100, blank=True, null=True) + external_reference = models.CharField( + max_length=100, blank=True, null=True, verbose_name=_('external reference') + ) internal_partie = models.CharField( - max_length=50, - choices=InternalEntityChoices, + max_length=50, choices=InternalEntityChoices, verbose_name=_('internal partie') ) tenant = models.ForeignKey( to='tenancy.Tenant', @@ -138,46 +166,84 @@ class Contract(NetBoxModel): related_name='contracts', blank=True, null=True, + verbose_name=_('tenant'), ) status = models.CharField( - max_length=50, choices=StatusChoices, default=StatusChoices.STATUS_ACTIVE + max_length=50, + choices=StatusChoices, + default=StatusChoices.STATUS_ACTIVE, + verbose_name=_('status'), ) - start_date = models.DateField(blank=True, null=True) - end_date = models.DateField(blank=True, null=True) + start_date = models.DateField(blank=True, null=True, verbose_name=_('start date')) + end_date = models.DateField(blank=True, null=True, verbose_name=_('end date')) initial_term = models.IntegerField( - help_text='In month', default=12, blank=True, null=True + help_text=_('In month'), + default=12, + blank=True, + null=True, + verbose_name=_('initial term'), ) renewal_term = models.IntegerField( - help_text='In month', default=12, blank=True, null=True + help_text=_('In month'), + default=12, + blank=True, + null=True, + verbose_name=_('renewal term'), + ) + notice_period = models.IntegerField( + help_text=_('Contract notice period. Default to 90 days'), + default=90, + verbose_name=_('notice period'), ) currency = models.CharField( - max_length=3, choices=CurrencyChoices, default=CurrencyChoices.CURRENCY_USD + max_length=3, + choices=CurrencyChoices, + default=CURRENCY_DEFAULT, + verbose_name=_('currency'), + ) + accounting_dimensions = models.JSONField( + null=True, + blank=True, + verbose_name=_('accounting dimensions'), + help_text=_('This field is deprecated and will be removed in version 3'), ) - accounting_dimensions = models.JSONField(null=True, blank=True) yrc = models.DecimalField( - verbose_name='yearly recuring cost', + verbose_name=_('yearly recuring cost'), max_digits=10, decimal_places=2, blank=True, null=True, + help_text=_('Use either this field of the monthly recuring cost field'), ) mrc = models.DecimalField( - verbose_name='Monthly recuring cost', + verbose_name=_('monthly recuring cost'), max_digits=10, decimal_places=2, blank=True, null=True, + help_text=_('Use either this field of the yearly recuring cost field'), ) nrc = models.DecimalField( - verbose_name='None recuring cost', default=0, max_digits=10, decimal_places=2 + verbose_name=_('none recuring cost'), default=0, max_digits=10, decimal_places=2 ) invoice_frequency = models.IntegerField( - help_text='The frequency of invoices in month', default=1 + help_text=_('The frequency of invoices in month'), + default=1, + verbose_name=_('invoice frequency'), + ) + documents = models.URLField( + blank=True, + verbose_name=_('documents'), + help_text=_('URL to the contract documents'), ) - documents = models.URLField(blank=True) - comments = models.TextField(blank=True) + comments = models.TextField(blank=True, verbose_name=_('comments')) parent = models.ForeignKey( - 'self', on_delete=models.CASCADE, related_name='childs', null=True, blank=True + 'self', + on_delete=models.CASCADE, + related_name='childs', + null=True, + blank=True, + verbose_name=_('parent'), ) def get_absolute_url(self): @@ -190,32 +256,57 @@ class Meta: fields=['external_partie_object_type', 'external_partie_object_id'] ), ] + verbose_name = _('contract') + verbose_name_plural = _('contracts') + + @property + def notice_date(self): + return self.end_date - timedelta(days=self.notice_period) def __str__(self): return self.name class Invoice(NetBoxModel): - number = models.CharField(max_length=100) - template = models.BooleanField(blank=True, null=True, default=False) - date = models.DateField(blank=True, null=True) - contracts = models.ManyToManyField( - Contract, - related_name='invoices', + number = models.CharField(max_length=100, verbose_name=_('number')) + template = models.BooleanField( blank=True, + null=True, + default=False, + verbose_name=_('template'), + help_text=_('Wether this invoice is a template or not'), + ) + date = models.DateField(blank=True, null=True, verbose_name=_('date')) + contracts = models.ManyToManyField( + Contract, related_name='invoices', blank=True, verbose_name=_('contracts') + ) + period_start = models.DateField( + blank=True, null=True, verbose_name=_('period start') ) - period_start = models.DateField(blank=True, null=True) - period_end = models.DateField(blank=True, null=True) + period_end = models.DateField(blank=True, null=True, verbose_name=_('period end')) currency = models.CharField( - max_length=3, choices=CurrencyChoices, default=CurrencyChoices.CURRENCY_USD + max_length=3, + choices=CurrencyChoices, + default=CURRENCY_DEFAULT, + verbose_name=_('currency'), + ) + accounting_dimensions = models.JSONField( + null=True, verbose_name=_('accounting dimensions') + ) + amount = models.DecimalField( + max_digits=10, decimal_places=2, verbose_name=_('amount') + ) + documents = models.URLField( + blank=True, + verbose_name=_('documents'), + help_text=_('URL to the contract documents'), ) - accounting_dimensions = models.JSONField(null=True) - amount = models.DecimalField(max_digits=10, decimal_places=2) - documents = models.URLField(blank=True) - comments = models.TextField(blank=True) + comments = models.TextField(blank=True, verbose_name=_('comments')) class Meta: ordering = ('-period_start',) + verbose_name = _('invoice') + verbose_name_plural = _('invoices') def __str__(self): return self.number @@ -233,14 +324,29 @@ def total_invoicelines_amount(self): class InvoiceLine(NetBoxModel): invoice = models.ForeignKey( - to='Invoice', on_delete=models.CASCADE, related_name='invoicelines' + to='Invoice', + on_delete=models.CASCADE, + related_name='invoicelines', + verbose_name=_('invoice'), ) currency = models.CharField( - max_length=3, choices=CurrencyChoices, default=CurrencyChoices.CURRENCY_USD + max_length=3, + choices=CurrencyChoices, + default=CURRENCY_DEFAULT, + verbose_name=_('currency'), + ) + amount = models.DecimalField( + max_digits=10, decimal_places=2, verbose_name=_('amount') + ) + accounting_dimensions = models.ManyToManyField( + AccountingDimension, blank=True, verbose_name=_('accounting dimensions') ) - amount = models.DecimalField(max_digits=10, decimal_places=2) - accounting_dimensions = models.ManyToManyField(AccountingDimension, blank=True) - comments = models.TextField(blank=True) + comments = models.TextField(blank=True, verbose_name=_('comments')) + + class Meta: + ordering = ('invoice',) + verbose_name = _('invoice line') + verbose_name_plural = _('invoice lines') def get_absolute_url(self): return reverse('plugins:netbox_contract:invoiceline', args=[self.pk]) diff --git a/src/netbox_contract/navigation.py b/src/netbox_contract/navigation.py index 7f21d98..c54c6ec 100644 --- a/src/netbox_contract/navigation.py +++ b/src/netbox_contract/navigation.py @@ -1,4 +1,5 @@ from django.conf import settings +from django.utils.translation import gettext_lazy as _ from netbox.plugins import PluginMenu, PluginMenuButton, PluginMenuItem plugin_settings = settings.PLUGINS_CONFIG['netbox_contract'] @@ -6,7 +7,7 @@ contract_buttons = [ PluginMenuButton( link='plugins:netbox_contract:contract_add', - title='Add', + title=_('Add'), icon_class='mdi mdi-plus-thick', permissions=['netbox_contract.add_contract'], ) @@ -15,7 +16,7 @@ invoice_buttons = [ PluginMenuButton( link='plugins:netbox_contract:invoice_add', - title='Add', + title=_('Add'), icon_class='mdi mdi-plus-thick', permissions=['netbox_contract.add_invoice'], ) @@ -24,7 +25,7 @@ invoiceline_buttons = [ PluginMenuButton( link='plugins:netbox_contract:invoiceline_add', - title='Add', + title=_('Add'), icon_class='mdi mdi-plus-thick', permissions=['netbox_contract.add_invoice'], ) @@ -33,7 +34,7 @@ accountingdimension_buttons = [ PluginMenuButton( link='plugins:netbox_contract:accountingdimension_add', - title='Add', + title=_('Add'), icon_class='mdi mdi-plus-thick', permissions=['netbox_contract.add_invoice'], ) @@ -42,7 +43,7 @@ serviceprovider_buttons = [ PluginMenuButton( link='plugins:netbox_contract:serviceprovider_add', - title='Add', + title=_('Add'), icon_class='mdi mdi-plus-thick', permissions=['netbox_contract.add_serviceprovider'], ) @@ -50,41 +51,41 @@ contract_menu_item = PluginMenuItem( link='plugins:netbox_contract:contract_list', - link_text='Contracts', + link_text=_('Contracts'), buttons=contract_buttons, permissions=['netbox_contract.view_contract'], ) invoices_menu_item = PluginMenuItem( link='plugins:netbox_contract:invoice_list', - link_text='Invoices', + link_text=_('Invoices'), buttons=invoice_buttons, permissions=['netbox_contract.view_invoice'], ) invoicelines_menu_item = PluginMenuItem( link='plugins:netbox_contract:invoiceline_list', - link_text='Invoice lines', + link_text=_('Invoice lines'), buttons=invoiceline_buttons, permissions=['netbox_contract.view_invoice'], ) accounting_dimensions_menu_item = PluginMenuItem( link='plugins:netbox_contract:accountingdimension_list', - link_text='Accounting dimensions', + link_text=_('Accounting dimensions'), buttons=accountingdimension_buttons, permissions=['netbox_contract.view_invoice'], ) service_provider_menu_item = PluginMenuItem( link='plugins:netbox_contract:serviceprovider_list', - link_text='Service Providers', + link_text=_('Service providers'), buttons=serviceprovider_buttons, permissions=['netbox_contract.view_serviceprovider'], ) contract_assignemnt_menu_item = PluginMenuItem( link='plugins:netbox_contract:contractassignment_list', - link_text='Contract assignments', + link_text=_('Contracts assignments'), permissions=['netbox_contract.view_contractassignment'], ) @@ -99,7 +100,7 @@ if plugin_settings.get('top_level_menu'): menu = PluginMenu( - label='Contracts', + label=_('Contracts'), groups=(('Contracts', items),), icon_class='mdi mdi-file-sign', ) diff --git a/src/netbox_contract/templates/contract_assignments_bottom.html b/src/netbox_contract/templates/contract_assignments_bottom.html index b919c19..1cb8435 100644 --- a/src/netbox_contract/templates/contract_assignments_bottom.html +++ b/src/netbox_contract/templates/contract_assignments_bottom.html @@ -4,12 +4,12 @@
- Contracts Assignments + {% trans "Contracts assignments" %} {% if perms.netbox_contract.add_contractassignment %} {% endif %} diff --git a/src/netbox_contract/templates/contract_list_bottom.html b/src/netbox_contract/templates/contract_list_bottom.html index 0b36958..4845efe 100644 --- a/src/netbox_contract/templates/contract_list_bottom.html +++ b/src/netbox_contract/templates/contract_list_bottom.html @@ -3,7 +3,7 @@
-
Contracts
+
{% trans "Contracts" %}
{% render_table contracts_table %}
diff --git a/src/netbox_contract/templates/netbox_contract/accountingdimension.html b/src/netbox_contract/templates/netbox_contract/accountingdimension.html index 4ee8086..d36e154 100644 --- a/src/netbox_contract/templates/netbox_contract/accountingdimension.html +++ b/src/netbox_contract/templates/netbox_contract/accountingdimension.html @@ -1,23 +1,24 @@ {% extends 'generic/object.html' %} {% load plugins %} {% load render_table from django_tables2 %} +{% load i18n %} {% block content %}
-
Accounting dimension
+
{% trans "Accounting dimension" %}
- + - + - +
name{% trans "Name" %} {{ object.name }}
value{% trans "Value" %} {{ object.value }}
status{% trans "Status" %} {{ object.status }}
diff --git a/src/netbox_contract/templates/netbox_contract/contract.html b/src/netbox_contract/templates/netbox_contract/contract.html index 3fc75e2..e7dd6ae 100644 --- a/src/netbox_contract/templates/netbox_contract/contract.html +++ b/src/netbox_contract/templates/netbox_contract/contract.html @@ -1,6 +1,7 @@ {% extends 'generic/object.html' %} {% load plugins %} {% load render_table from django_tables2 %} +{% load i18n %} {% block breadcrumbs %} {{ block.super }} @@ -9,97 +10,103 @@
-
Contract
+
{% trans "Contract" %}
- + - - + + - + - + - + - + {% if not 'tenant' in hidden_fields %} - + {% endif %} {% if not 'start_date' in hidden_fields %} - + {% endif %} {% if not 'end_date' in hidden_fields %} - + {% endif %} {% if not 'initial_term' in hidden_fields %} - - + + {% endif %} {% if not 'renewal_term' in hidden_fields %} - - + + + + {% endif %} + {% if not 'notice_period' in hidden_fields %} + + + {% endif %} - + {% if not 'accounting_dimensions' in hidden_fields %} - + {% endif %} - + - + - + - + - + {% if not 'parent' in hidden_fields %} - + @@ -107,9 +114,9 @@
Contract
{% endif %} {% if object.documents %} - + {% endif %} @@ -123,26 +130,26 @@
Contract
{% if invoice_template %}
- Invoice template + {% trans "Invoice template" %}
Name{% trans "Name" %} {{ object.name }}
External partie type
{% trans "External partie type" %} {{ object.external_partie_object_type }}
External partie{% trans "External partie" %} {{ object.external_partie_object.name }}
Status{% trans "Status" %} {{ object.status }}
External reference{% trans "External reference" %} {{ object.external_reference }}
Internal partie{% trans "Internal partie" %} {{ object.internal_partie }}
Tenant{% trans "Tenant" %} {{ object.tenant }}
Start date{% trans "Start date" %} {{ object.start_date }}
End date{% trans "End date" %} {{ object.end_date }}
Initial term{{ object.initial_term }}{% trans "Initial term" %}{{ object.initial_term }} {% trans "month" %}
Renewal term{{ object.renewal_term }}{% trans "Renewal term" %}{{ object.renewal_term }} {% trans "month" %}
{% trans "Notice period" %}{{ object.notice_period }} {% trans "days" %}
Currency{% trans "Currency" %} {{ object.currency }}
Accounting dimensions{% trans "Accounting dimensions" %} {{ object.accounting_dimensions }}
Monthly recuring costs{% trans "Monthly recuring costs" %} {{ object.mrc }}
Yearly recuring costs{% trans "Yearly recuring costs" %} {{ object.yrc }}
calculated corresponding Yearly or Monthly value{% trans "calculated corresponding Yearly or Monthly value" %} {{ object.calculated_rc }}
Non recuring costs{% trans "Non recuring costs" %} {{ object.nrc }}
Invoice frequency{% trans "Invoice frequency" %} {{ object.invoice_frequency }}
Parent{% trans "Parent" %} {{ object.parent.name }}
Documents{% trans "Documents" %} - Documents + {% trans "Documents" %}
- + - + - +
Number{% trans "Number" %} {{ invoice_template.number }}
Currency{% trans "Currency" %} {{ invoice_template.currency }}
Total amount{% trans "Total amount" %} {{ invoice_template.total_invoicelines_amount }}
- Invoice template lines + {% trans "Invoice template lines" %}
{% render_table invoicelines_table %}
@@ -152,7 +159,7 @@
-
Assignments
+
{% trans "Assignments" %}
{% render_table assignments_table %}
@@ -161,7 +168,7 @@
Assignments
-
childs
+
{% trans "childs" %}
{% render_table childs_table %}
@@ -172,12 +179,12 @@
childs
- Invoices + {% trans "Invoices" %} {% if perms.netbox_contract.add_invoice %} {% endif %} diff --git a/src/netbox_contract/templates/netbox_contract/contractassignment.html b/src/netbox_contract/templates/netbox_contract/contractassignment.html index 7985267..dd5606b 100644 --- a/src/netbox_contract/templates/netbox_contract/contractassignment.html +++ b/src/netbox_contract/templates/netbox_contract/contractassignment.html @@ -1,20 +1,21 @@ {% extends 'generic/object.html' %} +{% load i18n %} {% block content %}
-
Contract assignment
+
{% trans "Contract assignment" %}
- + - + - +
Model{% trans "Model" %} {{ object.content_type }}
Object{% trans "Object" %} {{ object.content_object.name }}
Contract{% trans "Contract" %} {{ object.contract.name }}
diff --git a/src/netbox_contract/templates/netbox_contract/invoice.html b/src/netbox_contract/templates/netbox_contract/invoice.html index 54bdd5f..cf81a77 100644 --- a/src/netbox_contract/templates/netbox_contract/invoice.html +++ b/src/netbox_contract/templates/netbox_contract/invoice.html @@ -1,6 +1,7 @@ {% extends 'generic/object.html' %} {% load plugins %} {% load render_table from django_tables2 %} +{% load i18n %} {% block breadcrumbs %} {{ block.super }}