2
0
mirror of https://github.com/inventree/InvenTree.git synced 2025-08-13 07:10:53 +00:00

Merge branch 'inventree:master' into matmair/issue2279

This commit is contained in:
Matthias Mair
2022-02-16 02:02:02 +01:00
committed by GitHub
91 changed files with 848 additions and 724 deletions

View File

@@ -40,6 +40,6 @@ class PartConfig(AppConfig):
item.part.trackable = True
item.part.clean()
item.part.save()
except (OperationalError, ProgrammingError):
except (OperationalError, ProgrammingError): # pragma: no cover
# Exception if the database has not been migrated yet
pass

View File

@@ -11,7 +11,7 @@ import InvenTree.validators
import part.models
def attach_file(instance, filename):
def attach_file(instance, filename): # pragma: no cover
"""
Generate a filename for the uploaded attachment.

View File

@@ -10,7 +10,7 @@ def update_tree(apps, schema_editor):
Part.objects.rebuild()
def nupdate_tree(apps, schema_editor):
def nupdate_tree(apps, schema_editor): # pragma: no cover
pass

View File

@@ -33,7 +33,7 @@ def migrate_currencies(apps, schema_editor):
remap = {}
for index, row in enumerate(results):
for index, row in enumerate(results): # pragma: no cover
pk, suffix, description = row
suffix = suffix.strip().upper()
@@ -57,7 +57,7 @@ def migrate_currencies(apps, schema_editor):
count = 0
for index, row in enumerate(results):
for index, row in enumerate(results): # pragma: no cover
pk, cost, currency_id, price, price_currency = row
# Copy the 'cost' field across to the 'price' field
@@ -71,10 +71,10 @@ def migrate_currencies(apps, schema_editor):
count += 1
if count > 0:
if count > 0: # pragma: no cover
print(f"Updated {count} SupplierPriceBreak rows")
def reverse_currencies(apps, schema_editor):
def reverse_currencies(apps, schema_editor): # pragma: no cover
"""
Reverse the "update" process.

View File

@@ -1498,6 +1498,16 @@ class Part(MPTTModel):
def has_bom(self):
return self.get_bom_items().count() > 0
def get_trackable_parts(self):
"""
Return a queryset of all trackable parts in the BOM for this part
"""
queryset = self.get_bom_items()
queryset = queryset.filter(sub_part__trackable=True)
return queryset
@property
def has_trackable_parts(self):
"""
@@ -1505,11 +1515,7 @@ class Part(MPTTModel):
This is important when building the part.
"""
for bom_item in self.get_bom_items().all():
if bom_item.sub_part.trackable:
return True
return False
return self.get_trackable_parts().count() > 0
@property
def bom_count(self):

View File

@@ -960,6 +960,9 @@ class BomExtractSerializer(serializers.Serializer):
"""
quantity = self.find_matching_data(row, 'quantity', self.dataset.headers)
# Ensure quantity field is provided
row['quantity'] = quantity
if quantity is None:
row_error['quantity'] = _('Quantity not provided')
else:

View File

@@ -1,99 +1,2 @@
{% extends "part/import_wizard/part_upload.html" %}
{% load inventree_extras %}
{% load i18n %}
{% load static %}
{% block form_alert %}
{% if missing_columns and missing_columns|length > 0 %}
<div class='alert alert-danger alert-block' role='alert'>
{% trans "Missing selections for the following required columns" %}:
<br>
<ul>
{% for col in missing_columns %}
<li>{{ col }}</li>
{% endfor %}
</ul>
</div>
{% endif %}
{% if duplicates and duplicates|length > 0 %}
<div class='alert alert-danger alert-block' role='alert'>
{% trans "Duplicate selections found, see below. Fix them then retry submitting." %}
</div>
{% endif %}
{% endblock form_alert %}
{% block form_buttons_top %}
{% if wizard.steps.prev %}
<button name="wizard_goto_step" type="submit" value="{{ wizard.steps.prev }}" class="save btn btn-outline-secondary">{% trans "Previous Step" %}</button>
{% endif %}
<button type="submit" class="save btn btn-outline-secondary">{% trans "Submit Selections" %}</button>
{% endblock form_buttons_top %}
{% block form_content %}
<thead>
<tr>
<th>{% trans "File Fields" %}</th>
<th></th>
{% for col in form %}
<th>
<div>
<input type='hidden' name='col_name_{{ forloop.counter0 }}' value='{{ col.name }}'/>
{{ col.name }}
<button class='btn btn-outline-secondary btn-remove' onClick='removeColFromBomWizard()' id='del_col_{{ forloop.counter0 }}' style='display: inline; float: right;' title='{% trans "Remove column" %}'>
<span col_id='{{ forloop.counter0 }}' class='fas fa-trash-alt icon-red'></span>
</button>
</div>
</th>
{% endfor %}
</tr>
</thead>
<tbody>
<tr>
<td>{% trans "Match Fields" %}</td>
<td></td>
{% for col in form %}
<td>
{{ col }}
{% for duplicate in duplicates %}
{% if duplicate == col.value %}
<div class='alert alert-danger alert-block text-center' role='alert' style='padding:2px; margin-top:6px; margin-bottom:2px'>
<strong>{% trans "Duplicate selection" %}</strong>
</div>
{% endif %}
{% endfor %}
</td>
{% endfor %}
</tr>
{% for row in rows %}
{% with forloop.counter as row_index %}
<tr>
<td style='width: 32px;'>
<button class='btn btn-outline-secondary btn-remove' onClick='removeRowFromBomWizard()' id='del_row_{{ row_index }}' style='display: inline; float: left;' title='{% trans "Remove row" %}'>
<span row_id='{{ row_index }}' class='fas fa-trash-alt icon-red'></span>
</button>
</td>
<td style='text-align: left;'>{{ row_index }}</td>
{% for item in row.data %}
<td>
<input type='hidden' name='row_{{ row_index }}_col_{{ forloop.counter0 }}' value='{{ item }}'/>
{{ item }}
</td>
{% endfor %}
</tr>
{% endwith %}
{% endfor %}
</tbody>
{% endblock form_content %}
{% block form_buttons_bottom %}
{% endblock form_buttons_bottom %}
{% block js_ready %}
{{ block.super }}
$('.fieldselect').select2({
width: '100%',
matcher: partialMatcher,
});
{% endblock %}
{% include "patterns/wizard/match_fields.html" %}

View File

@@ -10,51 +10,10 @@
{% endblock %}
{% block content %}
<div class='panel'>
<div class='panel-heading'>
<h4>
{% trans "Import Parts from File" %}
{{ wizard.form.media }}
</h4>
</div>
<div class='panel-content'>
{% if roles.part.change %}
<p>{% blocktrans with step=wizard.steps.step1 count=wizard.steps.count %}Step {{step}} of {{count}}{% endblocktrans %}
{% if description %}- {{ description }}{% endif %}</p>
{% block form_alert %}
{% endblock form_alert %}
<form action="" method="post" class='js-modal-form' enctype="multipart/form-data">
{% csrf_token %}
{% load crispy_forms_tags %}
{% block form_buttons_top %}
{% endblock form_buttons_top %}
<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_bottom %}
{% if wizard.steps.prev %}
<button name="wizard_goto_step" type="submit" value="{{ wizard.steps.prev }}" class="save btn btn-outline-secondary">{% trans "Previous Step" %}</button>
{% endif %}
<button type="submit" class="save btn btn-outline-secondary">{% trans "Upload File" %}</button>
</form>
{% endblock form_buttons_bottom %}
{% else %}
<div class='alert alert-danger alert-block' role='alert'>
{% trans "Unsuffitient privileges." %}
</div>
{% endif %}
</div>
</div>
{% trans "Import Parts from File" as header_text %}
{% roles.part.change as upload_go_ahead %}
{% trans "Unsuffitient privileges." as error_text %}
{% include "patterns/wizard/upload.html" with header_text=header_text upload_go_ahead=upload_go_ahead error_text=error_text %}
{% endblock %}
{% block js_ready %}

View File

@@ -14,7 +14,7 @@
{% endblock %}
{% block breadcrumbs %}
<a href='#' id='breadcrumb-tree-toggle' class="breadcrumb-item"><i class="fas fa-bars"></i></a>
<a href='#' id='breadcrumb-tree-toggle' class="breadcrumb-item"><span class="fas fa-bars"></span></a>
{% if part %}
{% include "part/cat_link.html" with category=part.category part=part %}
{% else %}

View File

@@ -89,8 +89,11 @@ $('#bom-upload').click(function() {
},
title: '{% trans "Upload BOM File" %}',
onSuccess: function(response) {
$('#bom-upload').hide();
// Clear existing entries from the table
$('.bom-import-row').remove();
// Disable the "submit" button
$('#bom-submit').show();
constructBomUploadTable(response);

View File

@@ -855,7 +855,7 @@ class PartAPIAggregationTest(InvenTreeAPITestCase):
return part
# We should never get here!
self.assertTrue(False)
self.assertTrue(False) # pragma: no cover
def test_stock_quantity(self):
"""

View File

@@ -55,7 +55,7 @@ class BomItemTest(TestCase):
with self.assertRaises(django_exceptions.ValidationError):
# A validation error should be raised here
item = BomItem.objects.create(part=self.bob, sub_part=self.bob, quantity=7)
item.clean()
item.clean() # pragma: no cover
def test_integer_quantity(self):
"""

View File

@@ -127,7 +127,7 @@ class CategoryTest(TestCase):
with self.assertRaises(ValidationError) as err:
cat.full_clean()
cat.save()
cat.save() # pragma: no cover
self.assertIn('Illegal character in name', str(err.exception.error_dict.get('name')))
@@ -160,10 +160,6 @@ class CategoryTest(TestCase):
self.assertEqual(str(self.fasteners.default_location), 'Office/Drawer_1 - In my desk')
# Test that parts in this location return the same default location, too
for p in self.fasteners.children.all():
self.assert_equal(p.get_default_location().pathstring, 'Office/Drawer_1')
# Any part under electronics should default to 'Home'
r1 = Part.objects.get(name='R_2K2_0805')
self.assertIsNone(r1.default_location)

View File

@@ -44,7 +44,7 @@ class TestParams(TestCase):
with self.assertRaises(django_exceptions.ValidationError):
t3 = PartParameterTemplate(name='aBcde', units='dd')
t3.full_clean()
t3.save()
t3.save() # pragma: no cover
class TestCategoryTemplates(TransactionTestCase):

View File

@@ -111,7 +111,7 @@ class PartTest(TestCase):
try:
part.save()
self.assertTrue(False)
self.assertTrue(False) # pragma: no cover
except:
pass