Merge remote-tracking branch 'inventree/master' into drf-api-forms

# Conflicts:
#	InvenTree/company/forms.py
This commit is contained in:
Oliver 2021-07-02 11:26:09 +10:00
commit 51ebe30754
20 changed files with 240 additions and 53 deletions

View File

@ -4,7 +4,6 @@ import logging
from django.apps import AppConfig
from django.core.exceptions import AppRegistryNotReady
from django.conf import settings
from InvenTree.ready import isInTestMode, canAppAccessDatabase
import InvenTree.tasks
@ -66,10 +65,11 @@ class InvenTreeConfig(AppConfig):
from djmoney.contrib.exchange.models import ExchangeBackend
from datetime import datetime, timedelta
from InvenTree.tasks import update_exchange_rates
from common.settings import currency_code_default
except AppRegistryNotReady:
pass
base_currency = settings.BASE_CURRENCY
base_currency = currency_code_default()
update = False

View File

@ -1,4 +1,4 @@
from django.conf import settings as inventree_settings
from common.settings import currency_code_default, currency_codes
from djmoney.contrib.exchange.backends.base import SimpleExchangeBackend
@ -22,8 +22,8 @@ class InvenTreeExchange(SimpleExchangeBackend):
return {
}
def update_rates(self, base_currency=inventree_settings.BASE_CURRENCY):
def update_rates(self, base_currency=currency_code_default()):
symbols = ','.join(inventree_settings.CURRENCIES)
symbols = ','.join(currency_codes())
super().update_rates(base=base_currency, symbols=symbols)

View File

@ -2,6 +2,7 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import sys
from .validators import allowable_url_schemes
@ -13,8 +14,11 @@ from django.core import validators
from django import forms
from decimal import Decimal
from djmoney.models.fields import MoneyField as ModelMoneyField
from djmoney.forms.fields import MoneyField
import InvenTree.helpers
import common.settings
class InvenTreeURLFormField(FormURLField):
@ -34,6 +38,42 @@ class InvenTreeURLField(models.URLField):
})
def money_kwargs():
""" returns the database settings for MoneyFields """
kwargs = {}
kwargs['currency_choices'] = common.settings.currency_code_mappings()
kwargs['default_currency'] = common.settings.currency_code_default
return kwargs
class InvenTreeModelMoneyField(ModelMoneyField):
""" custom MoneyField for clean migrations while using dynamic currency settings """
def __init__(self, **kwargs):
# detect if creating migration
if 'makemigrations' in sys.argv:
# remove currency information for a clean migration
kwargs['default_currency'] = ''
kwargs['currency_choices'] = []
else:
# set defaults
kwargs.update(money_kwargs())
super().__init__(**kwargs)
def formfield(self, **kwargs):
""" override form class to use own function """
kwargs['form_class'] = InvenTreeMoneyField
return super().formfield(**kwargs)
class InvenTreeMoneyField(MoneyField):
""" custom MoneyField for clean migrations while using dynamic currency settings """
def __init__(self, *args, **kwargs):
# override initial values with the real info from database
kwargs.update(money_kwargs())
super().__init__(*args, **kwargs)
class DatePickerFormField(forms.DateField):
"""
Custom date-picker field

View File

@ -523,10 +523,6 @@ for currency in CURRENCIES:
print(f"Currency code '{currency}' is not supported")
sys.exit(1)
BASE_CURRENCY = get_setting(
'INVENTREE_BASE_CURRENCY',
CONFIG.get('base_currency', 'USD')
)
# Custom currency exchange backend
EXCHANGE_BACKEND = 'InvenTree.exchange.InvenTreeExchange'

View File

@ -170,7 +170,7 @@ def update_exchange_rates():
try:
from InvenTree.exchange import InvenTreeExchange
from djmoney.contrib.exchange.models import ExchangeBackend, Rate
from django.conf import settings
from common.settings import currency_code_default, currency_codes
except AppRegistryNotReady:
# Apps not yet loaded!
logger.info("Could not perform 'update_exchange_rates' - App registry not ready")
@ -192,14 +192,14 @@ def update_exchange_rates():
backend = InvenTreeExchange()
print(f"Updating exchange rates from {backend.url}")
base = settings.BASE_CURRENCY
base = currency_code_default()
print(f"Using base currency '{base}'")
backend.update_rates(base_currency=base)
# Remove any exchange rates which are not in the provided currencies
Rate.objects.filter(backend="InvenTreeExchange").exclude(currency__in=settings.CURRENCIES).delete()
Rate.objects.filter(backend="InvenTreeExchange").exclude(currency__in=currency_codes()).delete()
def send_email(subject, body, recipients, from_email=None):

View File

@ -5,8 +5,6 @@ from django.test import TestCase
import django.core.exceptions as django_exceptions
from django.core.exceptions import ValidationError
from django.conf import settings
from djmoney.money import Money
from djmoney.contrib.exchange.models import Rate, convert_money
from djmoney.contrib.exchange.exceptions import MissingRate
@ -20,6 +18,7 @@ from decimal import Decimal
import InvenTree.tasks
from stock.models import StockLocation
from common.settings import currency_codes
class ValidatorTest(TestCase):
@ -335,13 +334,11 @@ class CurrencyTests(TestCase):
with self.assertRaises(MissingRate):
convert_money(Money(100, 'AUD'), 'USD')
currencies = settings.CURRENCIES
InvenTree.tasks.update_exchange_rates()
rates = Rate.objects.all()
self.assertEqual(rates.count(), len(currencies))
self.assertEqual(rates.count(), len(currency_codes()))
# Now that we have some exchange rate information, we can perform conversions

View File

@ -12,7 +12,6 @@ from django.utils.translation import gettext_lazy as _
from django.template.loader import render_to_string
from django.http import HttpResponse, JsonResponse, HttpResponseRedirect
from django.urls import reverse_lazy
from django.conf import settings
from django.contrib.auth.mixins import PermissionRequiredMixin
@ -21,6 +20,7 @@ from django.views.generic import ListView, DetailView, CreateView, FormView, Del
from django.views.generic.base import RedirectView, TemplateView
from djmoney.contrib.exchange.models import ExchangeBackend, Rate
from common.settings import currency_code_default, currency_codes
from part.models import Part, PartCategory
from stock.models import StockLocation, StockItem
@ -820,8 +820,8 @@ class CurrencySettingsView(TemplateView):
ctx = super().get_context_data(**kwargs).copy()
ctx['settings'] = InvenTreeSetting.objects.all().order_by('key')
ctx["base_currency"] = settings.BASE_CURRENCY
ctx["currencies"] = settings.CURRENCIES
ctx["base_currency"] = currency_code_default()
ctx["currencies"] = currency_codes
ctx["rates"] = Rate.objects.filter(backend="InvenTreeExchange")

View File

@ -0,0 +1,23 @@
# Generated by Django 3.2.4 on 2021-07-01 15:39
from django.db import migrations
from common.models import InvenTreeSetting
from InvenTree.settings import get_setting, CONFIG
def set_default_currency(apps, schema_editor):
""" migrate the currency setting from config.yml to db """
# get value from settings-file
base_currency = get_setting('INVENTREE_BASE_CURRENCY', CONFIG.get('base_currency', 'USD'))
# write to database
InvenTreeSetting.set_setting('INVENTREE_DEFAULT_CURRENCY', base_currency, None, create=True)
class Migration(migrations.Migration):
dependencies = [
('common', '0009_delete_currency'),
]
operations = [
migrations.RunPython(set_default_currency),
]

View File

@ -14,11 +14,11 @@ from django.db import models, transaction
from django.db.utils import IntegrityError, OperationalError
from django.conf import settings
from djmoney.models.fields import MoneyField
from djmoney.settings import CURRENCY_CHOICES
from djmoney.contrib.exchange.models import convert_money
from djmoney.contrib.exchange.exceptions import MissingRate
from common.settings import currency_code_default
import common.settings
from django.utils.translation import ugettext_lazy as _
from django.core.validators import MinValueValidator, URLValidator
@ -81,6 +81,13 @@ class InvenTreeSetting(models.Model):
'default': '',
},
'INVENTREE_DEFAULT_CURRENCY': {
'name': _('Default Currency'),
'description': _('Default currency'),
'default': 'USD',
'choices': CURRENCY_CHOICES,
},
'INVENTREE_DOWNLOAD_FROM_URL': {
'name': _('Download from URL'),
'description': _('Allow download of remote images and files from external URL'),
@ -735,10 +742,9 @@ class PriceBreak(models.Model):
help_text=_('Price break quantity'),
)
price = MoneyField(
price = InvenTree.fields.InvenTreeModelMoneyField(
max_digits=19,
decimal_places=4,
default_currency=currency_code_default(),
null=True,
verbose_name=_('Price'),
help_text=_('Unit price at specified quantity'),
@ -791,7 +797,7 @@ def get_price(instance, quantity, moq=True, multiples=True, currency=None, break
if currency is None:
# Default currency selection
currency = currency_code_default()
currency = common.settings.currency_code_default()
pb_min = None
for pb in price_breaks:

View File

@ -6,9 +6,9 @@ User-configurable settings for the common app
from __future__ import unicode_literals
from moneyed import CURRENCIES
from django.conf import settings
import common.models
from django.conf import settings
def currency_code_default():
@ -16,7 +16,7 @@ def currency_code_default():
Returns the default currency code (or USD if not specified)
"""
code = settings.BASE_CURRENCY
code = common.models.InvenTreeSetting.get_setting('INVENTREE_DEFAULT_CURRENCY')
if code not in CURRENCIES:
code = 'USD'
@ -24,6 +24,20 @@ def currency_code_default():
return code
def currency_code_mappings():
"""
Returns the current currency choices
"""
return [(a, a) for a in settings.CURRENCIES]
def currency_codes():
"""
Returns the current currency codes
"""
return [a for a in settings.CURRENCIES]
def stock_expiry_enabled():
"""
Returns True if the stock expiry feature is enabled

View File

@ -6,7 +6,7 @@ Django Forms for interacting with Company app
from __future__ import unicode_literals
from InvenTree.forms import HelperForm
from InvenTree.fields import RoundingDecimalFormField
from InvenTree.fields import InvenTreeMoneyField, RoundingDecimalFormField
from django.utils.translation import ugettext_lazy as _
import django.forms
@ -67,9 +67,8 @@ class EditSupplierPartForm(HelperForm):
'note': 'fa-pencil-alt',
}
single_pricing = MoneyField(
single_pricing = InvenTreeMoneyField(
label=_('Single Price'),
default_currency=currency_code_default(),
help_text=_('Single quantity price'),
decimal_places=4,
max_digits=19,

View File

@ -0,0 +1,25 @@
# Generated by Django 3.2.4 on 2021-07-01 05:09
import InvenTree.fields
from django.db import migrations
import djmoney.models.fields
class Migration(migrations.Migration):
dependencies = [
('company', '0038_manufacturerpartparameter'),
]
operations = [
migrations.AlterField(
model_name='supplierpricebreak',
name='price',
field=InvenTree.fields.InvenTreeModelMoneyField(currency_choices=[], decimal_places=4, default_currency='', help_text='Unit price at specified quantity', max_digits=19, null=True, verbose_name='Price'),
),
migrations.AlterField(
model_name='supplierpricebreak',
name='price_currency',
field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3),
),
]

View File

@ -10,15 +10,12 @@ from django.utils.translation import ugettext_lazy as _
from mptt.fields import TreeNodeChoiceField
from djmoney.forms.fields import MoneyField
from InvenTree.forms import HelperForm
from InvenTree.fields import RoundingDecimalFormField
from InvenTree.fields import InvenTreeMoneyField, RoundingDecimalFormField
from InvenTree.fields import DatePickerFormField
from InvenTree.helpers import clean_decimal
from common.models import InvenTreeSetting
from common.forms import MatchItemForm
import part.models
@ -297,9 +294,8 @@ class OrderMatchItemForm(MatchItemForm):
)
# set price field
elif 'price' in col_guess.lower():
return MoneyField(
return InvenTreeMoneyField(
label=_(col_guess),
default_currency=InvenTreeSetting.get_setting('INVENTREE_DEFAULT_CURRENCY'),
decimal_places=5,
max_digits=19,
required=False,

View File

@ -0,0 +1,35 @@
# Generated by Django 3.2.4 on 2021-07-01 05:09
import InvenTree.fields
from django.db import migrations
import djmoney.models.fields
class Migration(migrations.Migration):
dependencies = [
('order', '0046_purchaseorderlineitem_destination'),
]
operations = [
migrations.AlterField(
model_name='purchaseorderlineitem',
name='purchase_price',
field=InvenTree.fields.InvenTreeModelMoneyField(blank=True, currency_choices=[], decimal_places=4, default_currency='', help_text='Unit purchase price', max_digits=19, null=True, verbose_name='Purchase Price'),
),
migrations.AlterField(
model_name='purchaseorderlineitem',
name='purchase_price_currency',
field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3),
),
migrations.AlterField(
model_name='salesorderlineitem',
name='sale_price',
field=InvenTree.fields.InvenTreeModelMoneyField(blank=True, currency_choices=[], decimal_places=4, default_currency='', help_text='Unit sale price', max_digits=19, null=True, verbose_name='Sale Price'),
),
migrations.AlterField(
model_name='salesorderlineitem',
name='sale_price_currency',
field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3),
),
]

View File

@ -17,19 +17,15 @@ from django.contrib.auth.models import User
from django.urls import reverse
from django.utils.translation import ugettext_lazy as _
from common.settings import currency_code_default
from markdownx.models import MarkdownxField
from mptt.models import TreeForeignKey
from djmoney.models.fields import MoneyField
from users import models as UserModels
from part import models as PartModels
from stock import models as stock_models
from company.models import Company, SupplierPart
from InvenTree.fields import RoundingDecimalField
from InvenTree.fields import InvenTreeModelMoneyField, RoundingDecimalField
from InvenTree.helpers import decimal2string, increment, getSetting
from InvenTree.status_codes import PurchaseOrderStatus, SalesOrderStatus, StockStatus, StockHistoryCode
from InvenTree.models import InvenTreeAttachment
@ -684,10 +680,9 @@ class PurchaseOrderLineItem(OrderLineItem):
received = models.DecimalField(decimal_places=5, max_digits=15, default=0, verbose_name=_('Received'), help_text=_('Number of items received'))
purchase_price = MoneyField(
purchase_price = InvenTreeModelMoneyField(
max_digits=19,
decimal_places=4,
default_currency=currency_code_default(),
null=True, blank=True,
verbose_name=_('Purchase Price'),
help_text=_('Unit purchase price'),
@ -740,10 +735,9 @@ class SalesOrderLineItem(OrderLineItem):
part = models.ForeignKey('part.Part', on_delete=models.SET_NULL, related_name='sales_order_line_items', null=True, verbose_name=_('Part'), help_text=_('Part'), limit_choices_to={'salable': True})
sale_price = MoneyField(
sale_price = InvenTreeModelMoneyField(
max_digits=19,
decimal_places=4,
default_currency=currency_code_default(),
null=True, blank=True,
verbose_name=_('Sale Price'),
help_text=_('Unit sale price'),

View File

@ -0,0 +1,35 @@
# Generated by Django 3.2.4 on 2021-07-01 05:09
import InvenTree.fields
from django.db import migrations
import djmoney.models.fields
class Migration(migrations.Migration):
dependencies = [
('part', '0068_part_unique_part'),
]
operations = [
migrations.AlterField(
model_name='partinternalpricebreak',
name='price',
field=InvenTree.fields.InvenTreeModelMoneyField(currency_choices=[], decimal_places=4, default_currency='', help_text='Unit price at specified quantity', max_digits=19, null=True, verbose_name='Price'),
),
migrations.AlterField(
model_name='partinternalpricebreak',
name='price_currency',
field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3),
),
migrations.AlterField(
model_name='partsellpricebreak',
name='price',
field=InvenTree.fields.InvenTreeModelMoneyField(currency_choices=[], decimal_places=4, default_currency='', help_text='Unit price at specified quantity', max_digits=19, null=True, verbose_name='Price'),
),
migrations.AlterField(
model_name='partsellpricebreak',
name='price_currency',
field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3),
),
]

View File

@ -2793,7 +2793,7 @@ class PartSalePriceBreakCreate(AjaxCreateView):
initials['part'] = self.get_part()
default_currency = settings.BASE_CURRENCY
default_currency = inventree_settings.currency_code_default()
currency = CURRENCIES.get(default_currency, None)
if currency is not None:

View File

@ -0,0 +1,25 @@
# Generated by Django 3.2.4 on 2021-07-01 05:09
import InvenTree.fields
from django.db import migrations
import djmoney.models.fields
class Migration(migrations.Migration):
dependencies = [
('stock', '0064_auto_20210621_1724'),
]
operations = [
migrations.AlterField(
model_name='stockitem',
name='purchase_price',
field=InvenTree.fields.InvenTreeModelMoneyField(blank=True, currency_choices=[], decimal_places=4, default_currency='', help_text='Single unit purchase price at time of purchase', max_digits=19, null=True, verbose_name='Purchase Price'),
),
migrations.AlterField(
model_name='stockitem',
name='purchase_price_currency',
field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3),
),
]

View File

@ -20,14 +20,10 @@ from django.contrib.auth.models import User
from django.db.models.signals import pre_delete
from django.dispatch import receiver
from common.settings import currency_code_default
from markdownx.models import MarkdownxField
from mptt.models import MPTTModel, TreeForeignKey
from djmoney.models.fields import MoneyField
from decimal import Decimal, InvalidOperation
from datetime import datetime, timedelta
from InvenTree import helpers
@ -38,7 +34,7 @@ import label.models
from InvenTree.status_codes import StockStatus, StockHistoryCode
from InvenTree.models import InvenTreeTree, InvenTreeAttachment
from InvenTree.fields import InvenTreeURLField
from InvenTree.fields import InvenTreeModelMoneyField, InvenTreeURLField
from users.models import Owner
@ -541,10 +537,9 @@ class StockItem(MPTTModel):
help_text=_('Stock Item Notes')
)
purchase_price = MoneyField(
purchase_price = InvenTreeModelMoneyField(
max_digits=19,
decimal_places=4,
default_currency=currency_code_default(),
blank=True,
null=True,
verbose_name=_('Purchase Price'),

View File

@ -12,6 +12,13 @@
{% block settings %}
<table class='table table-striped table-condensed'>
{% include "InvenTree/settings/header.html" %}
<tbody>
{% include "InvenTree/settings/setting.html" with key="INVENTREE_DEFAULT_CURRENCY" icon="fa-globe" %}
</tbody>
</table>
<table class='table table-striped table-condensed'>
<tbody>
<tr>