Merge pull request #1844 from eeintech/po_import

Purchase Order File Upload Update
This commit is contained in:
Oliver 2021-07-20 07:58:48 +10:00 committed by GitHub
commit 17c8dc3441
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 115 additions and 76 deletions

View File

@ -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

View File

@ -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()

View File

@ -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)

View File

@ -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:

View File

@ -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')

View File

@ -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 %}
<ul class='list-group'>
<li class='list-group-item'>
<a href='#' id='po-menu-toggle'>
<span class='menu-tab-icon fas fa-expand-arrows-alt'></span>
</a>
</li>
<li class='list-group-item' title='{% trans "Return To Order" %}'>
<a href='{% url "po-detail" order.id %}' id='select-upload-file' class='nav-toggle'>
<span class='fas fa-undo side-icon'></span>
{% trans "Return To Order" %}
</a>
</li>
</ul>
{% endblock %}
{% block details %}
{% if order.status == PurchaseOrderStatus.PENDING and roles.purchase_order.change %}
{% block page_content %}
<p>{% blocktrans with step=wizard.steps.step1 count=wizard.steps.count %}Step {{step}} of {{count}}{% endblocktrans %}
{% if description %}- {{ description }}{% endif %}</p>
<div class='panel panel-default panel-inventree' id='panel-upload-file'>
<div class='panel-heading'>
{% block heading %}
<h4>{% trans "Upload File for Purchase Order" %}</h4>
{{ wizard.form.media }}
{% endblock %}
</div>
<div class='panel-content'>
{% block details %}
{% if order.status == PurchaseOrderStatus.PENDING and roles.purchase_order.change %}
{% block form_alert %}
{% endblock form_alert %}
<p>{% blocktrans with step=wizard.steps.step1 count=wizard.steps.count %}Step {{step}} of {{count}}{% endblocktrans %}
{% if description %}- {{ description }}{% endif %}</p>
<form action="" method="post" class='js-modal-form' enctype="multipart/form-data">
{% csrf_token %}
{% load crispy_forms_tags %}
{% block form_alert %}
{% endblock form_alert %}
{% block form_buttons_top %}
{% endblock form_buttons_top %}
<form action="" method="post" class='js-modal-form' enctype="multipart/form-data">
{% csrf_token %}
{% load crispy_forms_tags %}
<table class='table table-striped' style='margin-top: 12px; margin-bottom: 0px'>
{{ wizard.management_form }}
{% block form_content %}
{% crispy wizard.form %}
{% endblock form_content %}
</table>
{% block form_buttons_top %}
{% endblock form_buttons_top %}
{% block form_buttons_bottom %}
{% if wizard.steps.prev %}
<button name="wizard_goto_step" type="submit" value="{{ wizard.steps.prev }}" class="save btn btn-default">{% trans "Previous Step" %}</button>
{% endif %}
<button type="submit" class="save btn btn-default">{% trans "Upload File" %}</button>
</form>
{% endblock form_buttons_bottom %}
<table class='table table-striped' style='margin-top: 12px; margin-bottom: 0px'>
{{ wizard.management_form }}
{% block form_content %}
{% crispy wizard.form %}
{% endblock form_content %}
</table>
{% else %}
<div class='alert alert-danger alert-block' role='alert'>
{% trans "Order is already processed. Files cannot be uploaded." %}
</div>
{% endif %}
{% endblock details %}
{% block form_buttons_bottom %}
{% if wizard.steps.prev %}
<button name="wizard_goto_step" type="submit" value="{{ wizard.steps.prev }}" class="save btn btn-default">{% trans "Previous Step" %}</button>
{% endif %}
<button type="submit" class="save btn btn-default">{% trans "Upload File" %}</button>
</form>
{% endblock form_buttons_bottom %}
{% else %}
<div class='alert alert-danger alert-block' role='alert'>
{% trans "Order is already processed. Files cannot be uploaded." %}
</div>
{% endif %}
{% endblock details %}
</div>
{% endblock %}
{% block js_ready %}
{{ block.super }}

View File

@ -15,14 +15,6 @@
{% trans "Order Items" %}
</a>
</li>
{% if order.status == PurchaseOrderStatus.PENDING and roles.purchase_order.change %}
<li class='list-group-item' title='{% trans "Upload File" %}'>
<a href='{% url "po-upload" order.id %}'>
<span class='fas fa-file-upload side-icon'></span>
{% trans "Upload File" %}
</a>
</li>
{% endif %}
<li class='list-group-item' title='{% trans "Received Stock Items" %}'>
<a href='#' id='select-received-items' class='nav-toggle'>
<span class='fas fa-sign-in-alt side-icon'></span>

View File

@ -22,6 +22,9 @@
<button type='button' class='btn btn-primary' id='new-po-line'>
<span class='fas fa-plus-circle'></span> {% trans "Add Line Item" %}
</button>
<a class='btn btn-primary' href='{% url "po-upload" order.id %}' role='button'>
<span class='fas fa-file-upload side-icon'></span> {% trans "Upload File" %}
</a>
{% endif %}
</div>

View File

@ -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: