diff --git a/InvenTree/InvenTree/fields.py b/InvenTree/InvenTree/fields.py index 462d2b0e0e..71b31c55a8 100644 --- a/InvenTree/InvenTree/fields.py +++ b/InvenTree/InvenTree/fields.py @@ -20,7 +20,6 @@ from djmoney.forms.fields import MoneyField from djmoney.models.validators import MinMoneyValidator import InvenTree.helpers -import common.settings class InvenTreeURLFormField(FormURLField): @@ -42,9 +41,11 @@ class InvenTreeURLField(models.URLField): def money_kwargs(): """ returns the database settings for MoneyFields """ + from common.settings import currency_code_mappings, currency_code_default + kwargs = {} - kwargs['currency_choices'] = common.settings.currency_code_mappings() - kwargs['default_currency'] = common.settings.currency_code_default + kwargs['currency_choices'] = currency_code_mappings() + kwargs['default_currency'] = currency_code_default() return kwargs diff --git a/InvenTree/InvenTree/helpers.py b/InvenTree/InvenTree/helpers.py index 330bd2bb68..628fd2e646 100644 --- a/InvenTree/InvenTree/helpers.py +++ b/InvenTree/InvenTree/helpers.py @@ -631,13 +631,34 @@ def clean_decimal(number): """ Clean-up decimal value """ # Check if empty - if number is None or number == '': + if number is None or number == '' or number == 0: return Decimal(0) - # Check if decimal type + # Convert to string and remove spaces + number = str(number).replace(' ', '') + + # Guess what type of decimal and thousands separators are used + count_comma = number.count(',') + count_point = number.count('.') + + if count_comma == 1: + # Comma is used as decimal separator + if count_point > 0: + # Points are used as thousands separators: remove them + number = number.replace('.', '') + # Replace decimal separator with point + number = number.replace(',', '.') + elif count_point == 1: + # Point is used as decimal separator + if count_comma > 0: + # Commas are used as thousands separators: remove them + number = number.replace(',', '') + + # Convert to Decimal type try: clean_number = Decimal(number) except InvalidOperation: - clean_number = number + # Number cannot be converted to Decimal (eg. a string containing letters) + return Decimal(0) return clean_number.quantize(Decimal(1)) if clean_number == clean_number.to_integral() else clean_number.normalize() diff --git a/InvenTree/common/files.py b/InvenTree/common/files.py index 45b6c80050..39c97dca6c 100644 --- a/InvenTree/common/files.py +++ b/InvenTree/common/files.py @@ -53,17 +53,20 @@ class FileManager: ext = os.path.splitext(file.name)[-1].lower().replace('.', '') - if ext in ['csv', 'tsv', ]: - # These file formats need string decoding - raw_data = file.read().decode('utf-8') - # Reset stream position to beginning of file - file.seek(0) - elif ext in ['xls', 'xlsx', 'json', 'yaml', ]: - raw_data = file.read() - # Reset stream position to beginning of file - file.seek(0) - else: - raise ValidationError(_(f'Unsupported file format: {ext.upper()}')) + try: + if ext in ['csv', 'tsv', ]: + # These file formats need string decoding + raw_data = file.read().decode('utf-8') + # Reset stream position to beginning of file + file.seek(0) + elif ext in ['xls', 'xlsx', 'json', 'yaml', ]: + raw_data = file.read() + # Reset stream position to beginning of file + file.seek(0) + else: + raise ValidationError(_(f'Unsupported file format: {ext.upper()}')) + except UnicodeEncodeError: + raise ValidationError(_('Error reading file (invalid encoding)')) try: cleaned_data = tablib.Dataset().load(raw_data, format=ext) diff --git a/InvenTree/common/models.py b/InvenTree/common/models.py index 3d18d56880..dcdeea5292 100644 --- a/InvenTree/common/models.py +++ b/InvenTree/common/models.py @@ -18,8 +18,6 @@ from djmoney.settings import CURRENCY_CHOICES from djmoney.contrib.exchange.models import convert_money from djmoney.contrib.exchange.exceptions import MissingRate -import common.settings - from django.utils.translation import ugettext_lazy as _ from django.core.validators import MinValueValidator, URLValidator from django.core.exceptions import ValidationError @@ -781,6 +779,7 @@ def get_price(instance, quantity, moq=True, multiples=True, currency=None, break - If MOQ (minimum order quantity) is required, bump quantity - If order multiples are to be observed, then we need to calculate based on that, too """ + from common.settings import currency_code_default if hasattr(instance, break_name): price_breaks = getattr(instance, break_name).all() @@ -804,7 +803,7 @@ def get_price(instance, quantity, moq=True, multiples=True, currency=None, break if currency is None: # Default currency selection - currency = common.settings.currency_code_default() + currency = currency_code_default() pb_min = None for pb in price_breaks: diff --git a/InvenTree/common/settings.py b/InvenTree/common/settings.py index b34cf0b785..80f80b886f 100644 --- a/InvenTree/common/settings.py +++ b/InvenTree/common/settings.py @@ -8,15 +8,19 @@ from __future__ import unicode_literals from moneyed import CURRENCIES from django.conf import settings -import common.models - def currency_code_default(): """ Returns the default currency code (or USD if not specified) """ + from django.db.utils import ProgrammingError + from common.models import InvenTreeSetting - code = common.models.InvenTreeSetting.get_setting('INVENTREE_DEFAULT_CURRENCY') + try: + code = InvenTreeSetting.get_setting('INVENTREE_DEFAULT_CURRENCY') + except ProgrammingError: + # database is not initialized yet + code = '' if code not in CURRENCIES: code = 'USD' @@ -42,5 +46,6 @@ def stock_expiry_enabled(): """ Returns True if the stock expiry feature is enabled """ + from common.models import InvenTreeSetting - return common.models.InvenTreeSetting.get_setting('STOCK_ENABLE_EXPIRY') + return InvenTreeSetting.get_setting('STOCK_ENABLE_EXPIRY') diff --git a/InvenTree/order/templates/order/order_wizard/po_upload.html b/InvenTree/order/templates/order/order_wizard/po_upload.html index 4357bce2c6..7c2dea1af9 100644 --- a/InvenTree/order/templates/order/order_wizard/po_upload.html +++ b/InvenTree/order/templates/order/order_wizard/po_upload.html @@ -1,50 +1,74 @@ -{% extends "order/order_base.html" %} +{% extends "order/purchase_order_detail.html" %} {% load inventree_extras %} {% load i18n %} {% load static %} -{% block heading %} -{% trans "Upload File for Purchase Order" %} -{{ wizard.form.media }} +{% block menubar %} + {% endblock %} -{% block details %} -{% if order.status == PurchaseOrderStatus.PENDING and roles.purchase_order.change %} +{% block page_content %} -

{% blocktrans with step=wizard.steps.step1 count=wizard.steps.count %}Step {{step}} of {{count}}{% endblocktrans %} -{% if description %}- {{ description }}{% endif %}

+
+
+ {% block heading %} +

{% trans "Upload File for Purchase Order" %}

+ {{ wizard.form.media }} + {% endblock %} +
+
+ {% block details %} + {% if order.status == PurchaseOrderStatus.PENDING and roles.purchase_order.change %} -{% block form_alert %} -{% endblock form_alert %} +

{% blocktrans with step=wizard.steps.step1 count=wizard.steps.count %}Step {{step}} of {{count}}{% endblocktrans %} + {% if description %}- {{ description }}{% endif %}

-
-{% csrf_token %} -{% load crispy_forms_tags %} + {% block form_alert %} + {% endblock form_alert %} -{% block form_buttons_top %} -{% endblock form_buttons_top %} + + {% csrf_token %} + {% load crispy_forms_tags %} - -{{ wizard.management_form }} -{% block form_content %} -{% crispy wizard.form %} -{% endblock form_content %} -
+ {% block form_buttons_top %} + {% endblock form_buttons_top %} -{% block form_buttons_bottom %} -{% if wizard.steps.prev %} - -{% endif %} - -
-{% endblock form_buttons_bottom %} + + {{ wizard.management_form }} + {% block form_content %} + {% crispy wizard.form %} + {% endblock form_content %} +
-{% else %} - -{% endif %} -{% endblock details %} + {% block form_buttons_bottom %} + {% if wizard.steps.prev %} + + {% endif %} + + + {% endblock form_buttons_bottom %} + + {% else %} + + {% endif %} + {% endblock details %} +
+ +{% endblock %} {% block js_ready %} {{ block.super }} diff --git a/InvenTree/order/templates/order/po_navbar.html b/InvenTree/order/templates/order/po_navbar.html index 9f7967810d..bddcce4ba3 100644 --- a/InvenTree/order/templates/order/po_navbar.html +++ b/InvenTree/order/templates/order/po_navbar.html @@ -15,14 +15,6 @@ {% trans "Order Items" %} - {% if order.status == PurchaseOrderStatus.PENDING and roles.purchase_order.change %} -
  • - - - {% trans "Upload File" %} - -
  • - {% endif %}
  • diff --git a/InvenTree/order/templates/order/purchase_order_detail.html b/InvenTree/order/templates/order/purchase_order_detail.html index 193fd75daa..b05bfa7cc2 100644 --- a/InvenTree/order/templates/order/purchase_order_detail.html +++ b/InvenTree/order/templates/order/purchase_order_detail.html @@ -22,6 +22,9 @@ + + {% trans "Upload File" %} + {% endif %}
  • diff --git a/InvenTree/order/views.py b/InvenTree/order/views.py index be1107f17b..a109b036e0 100644 --- a/InvenTree/order/views.py +++ b/InvenTree/order/views.py @@ -393,16 +393,7 @@ class PurchaseOrderUpload(FileManagementFormView): p_val = row['data'][p_idx]['cell'] if p_val: - # Delete commas - p_val = p_val.replace(',', '') - - try: - # Attempt to extract a valid decimal value from the field - purchase_price = Decimal(p_val) - # Store the 'purchase_price' value - row['purchase_price'] = purchase_price - except (ValueError, InvalidOperation): - pass + row['purchase_price'] = p_val # Check if there is a column corresponding to "reference" if r_idx >= 0: