diff --git a/InvenTree/InvenTree/validators.py b/InvenTree/InvenTree/validators.py index e85dc40810..70322df062 100644 --- a/InvenTree/InvenTree/validators.py +++ b/InvenTree/InvenTree/validators.py @@ -6,11 +6,22 @@ from django.conf import settings from django.core.exceptions import ValidationError from django.utils.translation import gettext_lazy as _ +from moneyed import CURRENCIES + import common.models import re +def validate_currency_code(code): + """ + Check that a given code is a valid currency code. + """ + + if code not in CURRENCIES: + raise ValidationError(_('Not a valid currency code')) + + def allowable_url_schemes(): """ Return the list of allowable URL schemes. In addition to the default schemes allowed by Django, diff --git a/InvenTree/common/settings.py b/InvenTree/common/settings.py new file mode 100644 index 0000000000..832a07f040 --- /dev/null +++ b/InvenTree/common/settings.py @@ -0,0 +1,23 @@ +""" +User-configurable settings for the common app +""" + +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from moneyed import CURRENCIES + +from common.models import InvenTreeSetting + + +def currency_code_default(): + """ + Returns the default currency code (or USD if not specified) + """ + + code = InvenTreeSetting.get_setting('INVENTREE_DEFAULT_CURRENCY') + + if code not in CURRENCIES: + code = 'USD' + + return code diff --git a/InvenTree/company/forms.py b/InvenTree/company/forms.py index 7a27f08251..27f2dce0dc 100644 --- a/InvenTree/company/forms.py +++ b/InvenTree/company/forms.py @@ -9,10 +9,13 @@ from InvenTree.forms import HelperForm from InvenTree.fields import RoundingDecimalFormField from django.utils.translation import ugettext as _ +import django.forms +import djmoney.settings from djmoney.forms.fields import MoneyField from common.models import InvenTreeSetting +import common.settings from .models import Company from .models import SupplierPart @@ -30,6 +33,13 @@ class EditCompanyForm(HelperForm): 'phone': 'fa-phone', } + currency = django.forms.ChoiceField( + required=False, + help_text=_('Default currency used for this company'), + choices=[('', '----------')] + djmoney.settings.CURRENCY_CHOICES, + initial=common.settings.currency_code_default, + ) + class Meta: model = Company fields = [ @@ -37,6 +47,7 @@ class EditCompanyForm(HelperForm): 'description', 'website', 'address', + 'currency', 'phone', 'email', 'contact', diff --git a/InvenTree/company/migrations/0029_company_currency.py b/InvenTree/company/migrations/0029_company_currency.py new file mode 100644 index 0000000000..fda3c15e96 --- /dev/null +++ b/InvenTree/company/migrations/0029_company_currency.py @@ -0,0 +1,19 @@ +# Generated by Django 3.0.7 on 2020-11-11 23:22 + +import InvenTree.validators +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('company', '0028_remove_supplierpricebreak_cost'), + ] + + operations = [ + migrations.AddField( + model_name='company', + name='currency', + field=models.CharField(blank=True, help_text='Default currency used for this company', max_length=3, validators=[InvenTree.validators.validate_currency_code], verbose_name='Currency'), + ), + ] diff --git a/InvenTree/company/models.py b/InvenTree/company/models.py index 75b765bb2e..711a1966ff 100644 --- a/InvenTree/company/models.py +++ b/InvenTree/company/models.py @@ -26,6 +26,8 @@ from InvenTree.helpers import normalize from InvenTree.fields import InvenTreeURLField from InvenTree.status_codes import PurchaseOrderStatus +import InvenTree.validators + import common.models @@ -77,6 +79,7 @@ class Company(models.Model): is_customer: boolean value, is this company a customer is_supplier: boolean value, is this company a supplier is_manufacturer: boolean value, is this company a manufacturer + currency_code: Specifies the default currency for the company """ class Meta: @@ -126,6 +129,14 @@ class Company(models.Model): is_manufacturer = models.BooleanField(default=False, help_text=_('Does this company manufacture parts?')) + currency = models.CharField( + max_length=3, + verbose_name=_('Currency'), + blank=True, + help_text=_('Default currency used for this company'), + validators=[InvenTree.validators.validate_currency_code], + ) + def __str__(self): """ Get string representation of a Company """ return "{n} - {d}".format(n=self.name, d=self.description) diff --git a/InvenTree/company/templates/company/detail.html b/InvenTree/company/templates/company/detail.html index a166de9048..7243cf4fbc 100644 --- a/InvenTree/company/templates/company/detail.html +++ b/InvenTree/company/templates/company/detail.html @@ -8,25 +8,64 @@

{% trans "Company Details" %}


- - - - - - - - - - - - - - - - - - -
{% trans "Manufacturer" %}{% include "yesnolabel.html" with value=company.is_manufacturer %}
{% trans "Supplier" %}{% include 'yesnolabel.html' with value=company.is_supplier %}
{% trans "Customer" %}{% include 'yesnolabel.html' with value=company.is_customer %}
+
+
+ + + + + + + + + + + + + + + + + + + + + + + +
{% trans "Company Name" %}{{ company.name }}
{% trans "Description" %}{{ company.description }}
{% trans "Website" %} + {% if company.website %}{{ company.website }} + {% else %}{% trans "No website specified" %} + {% endif %} +
{% trans "Currency" %} + {% if company.currency %}{{ company.currency }} + {% else %}{% trans "Uses default currency" %} + {% endif %} +
+
+
+ + + + + + + + + + + + + + + + + + +
{% trans "Manufacturer" %}{% include "yesnolabel.html" with value=company.is_manufacturer %}
{% trans "Supplier" %}{% include 'yesnolabel.html' with value=company.is_supplier %}
{% trans "Customer" %}{% include 'yesnolabel.html' with value=company.is_customer %}
+ +
+
{% endblock %} {% block js_ready %} diff --git a/InvenTree/company/tests.py b/InvenTree/company/tests.py index 24c7e3a20f..5229fdb045 100644 --- a/InvenTree/company/tests.py +++ b/InvenTree/company/tests.py @@ -1,4 +1,5 @@ from django.test import TestCase +from django.core.exceptions import ValidationError import os @@ -119,6 +120,30 @@ class CompanySimpleTest(TestCase): self.assertIsNone(m3x12.get_price_info(3)) self.assertIsNotNone(m3x12.get_price_info(50)) + def test_currency_validation(self): + """ + Test validation for currency selection + """ + + # Create a company with a valid currency code (should pass) + company = Company.objects.create( + name='Test', + description='Toast', + currency='AUD', + ) + + company.full_clean() + + # Create a company with an invalid currency code (should fail) + company = Company.objects.create( + name='test', + description='Toasty', + currency='XZY', + ) + + with self.assertRaises(ValidationError): + company.full_clean() + class ContactSimpleTest(TestCase):