mirror of
https://github.com/inventree/InvenTree.git
synced 2025-04-30 04:26:44 +00:00
Add 'currency' option for company
- e.g. an external supplier might have a default currency - Adds a form input which only allows selection of allowed currency codes - Add unit testing for currency validation
This commit is contained in:
parent
ebac06ebee
commit
1532be9c1e
@ -6,11 +6,22 @@ from django.conf import settings
|
|||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
|
from moneyed import CURRENCIES
|
||||||
|
|
||||||
import common.models
|
import common.models
|
||||||
|
|
||||||
import re
|
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():
|
def allowable_url_schemes():
|
||||||
""" Return the list of allowable URL schemes.
|
""" Return the list of allowable URL schemes.
|
||||||
In addition to the default schemes allowed by Django,
|
In addition to the default schemes allowed by Django,
|
||||||
|
23
InvenTree/common/settings.py
Normal file
23
InvenTree/common/settings.py
Normal file
@ -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
|
@ -9,10 +9,13 @@ from InvenTree.forms import HelperForm
|
|||||||
from InvenTree.fields import RoundingDecimalFormField
|
from InvenTree.fields import RoundingDecimalFormField
|
||||||
|
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
|
import django.forms
|
||||||
|
|
||||||
|
import djmoney.settings
|
||||||
from djmoney.forms.fields import MoneyField
|
from djmoney.forms.fields import MoneyField
|
||||||
|
|
||||||
from common.models import InvenTreeSetting
|
from common.models import InvenTreeSetting
|
||||||
|
import common.settings
|
||||||
|
|
||||||
from .models import Company
|
from .models import Company
|
||||||
from .models import SupplierPart
|
from .models import SupplierPart
|
||||||
@ -30,6 +33,13 @@ class EditCompanyForm(HelperForm):
|
|||||||
'phone': 'fa-phone',
|
'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:
|
class Meta:
|
||||||
model = Company
|
model = Company
|
||||||
fields = [
|
fields = [
|
||||||
@ -37,6 +47,7 @@ class EditCompanyForm(HelperForm):
|
|||||||
'description',
|
'description',
|
||||||
'website',
|
'website',
|
||||||
'address',
|
'address',
|
||||||
|
'currency',
|
||||||
'phone',
|
'phone',
|
||||||
'email',
|
'email',
|
||||||
'contact',
|
'contact',
|
||||||
|
19
InvenTree/company/migrations/0029_company_currency.py
Normal file
19
InvenTree/company/migrations/0029_company_currency.py
Normal file
@ -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'),
|
||||||
|
),
|
||||||
|
]
|
@ -26,6 +26,8 @@ from InvenTree.helpers import normalize
|
|||||||
from InvenTree.fields import InvenTreeURLField
|
from InvenTree.fields import InvenTreeURLField
|
||||||
from InvenTree.status_codes import PurchaseOrderStatus
|
from InvenTree.status_codes import PurchaseOrderStatus
|
||||||
|
|
||||||
|
import InvenTree.validators
|
||||||
|
|
||||||
import common.models
|
import common.models
|
||||||
|
|
||||||
|
|
||||||
@ -77,6 +79,7 @@ class Company(models.Model):
|
|||||||
is_customer: boolean value, is this company a customer
|
is_customer: boolean value, is this company a customer
|
||||||
is_supplier: boolean value, is this company a supplier
|
is_supplier: boolean value, is this company a supplier
|
||||||
is_manufacturer: boolean value, is this company a manufacturer
|
is_manufacturer: boolean value, is this company a manufacturer
|
||||||
|
currency_code: Specifies the default currency for the company
|
||||||
"""
|
"""
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -126,6 +129,14 @@ class Company(models.Model):
|
|||||||
|
|
||||||
is_manufacturer = models.BooleanField(default=False, help_text=_('Does this company manufacture parts?'))
|
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):
|
def __str__(self):
|
||||||
""" Get string representation of a Company """
|
""" Get string representation of a Company """
|
||||||
return "{n} - {d}".format(n=self.name, d=self.description)
|
return "{n} - {d}".format(n=self.name, d=self.description)
|
||||||
|
@ -8,6 +8,42 @@
|
|||||||
<h4>{% trans "Company Details" %}</h4>
|
<h4>{% trans "Company Details" %}</h4>
|
||||||
<hr>
|
<hr>
|
||||||
|
|
||||||
|
<div class='row'>
|
||||||
|
<div class='col-sm-6'>
|
||||||
|
<table class='table table-striped'>
|
||||||
|
<col width='25'>
|
||||||
|
<col>
|
||||||
|
<tr>
|
||||||
|
<td><span class='fas fa-font'></span></td>
|
||||||
|
<td>{% trans "Company Name" %}</td>
|
||||||
|
<td>{{ company.name }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><span class='fas fa-info'></span></td>
|
||||||
|
<td>{% trans "Description" %}</td>
|
||||||
|
<td>{{ company.description }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><span class='fas fa-globe'></span></td>
|
||||||
|
<td>{% trans "Website" %}</td>
|
||||||
|
<td>
|
||||||
|
{% if company.website %}<a href='{{ company.website }}'>{{ company.website }}</a>
|
||||||
|
{% else %}<i>{% trans "No website specified" %}</i>
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><span class='fas fa-dollar-sign'></span></td>
|
||||||
|
<td>{% trans "Currency" %}</td>
|
||||||
|
<td>
|
||||||
|
{% if company.currency %}{{ company.currency }}
|
||||||
|
{% else %}<i>{% trans "Uses default currency" %}</i>
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<div class='col-sm-6'>
|
||||||
<table class='table table-striped'>
|
<table class='table table-striped'>
|
||||||
<col width='25'>
|
<col width='25'>
|
||||||
<col>
|
<col>
|
||||||
@ -28,6 +64,9 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block js_ready %}
|
{% block js_ready %}
|
||||||
{{ block.super }}
|
{{ block.super }}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
|
from django.core.exceptions import ValidationError
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
|
||||||
@ -119,6 +120,30 @@ class CompanySimpleTest(TestCase):
|
|||||||
self.assertIsNone(m3x12.get_price_info(3))
|
self.assertIsNone(m3x12.get_price_info(3))
|
||||||
self.assertIsNotNone(m3x12.get_price_info(50))
|
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):
|
class ContactSimpleTest(TestCase):
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user