mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
Merge remote-tracking branch 'inventree/master'
This commit is contained in:
@ -28,6 +28,7 @@ class HelperForm(forms.ModelForm):
|
|||||||
self.helper = FormHelper()
|
self.helper = FormHelper()
|
||||||
|
|
||||||
self.helper.form_tag = False
|
self.helper.form_tag = False
|
||||||
|
self.helper.form_show_errors = True
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Create a default 'layout' for this form.
|
Create a default 'layout' for this form.
|
||||||
@ -43,11 +44,6 @@ class HelperForm(forms.ModelForm):
|
|||||||
|
|
||||||
valid = super(HelperForm, self).is_valid()
|
valid = super(HelperForm, self).is_valid()
|
||||||
|
|
||||||
# Check for errors from model validation
|
|
||||||
# If none, disable crispy form errors
|
|
||||||
if not self.errors:
|
|
||||||
self.helper.form_show_errors = False
|
|
||||||
|
|
||||||
return valid
|
return valid
|
||||||
|
|
||||||
def rebuild_layout(self):
|
def rebuild_layout(self):
|
||||||
|
@ -0,0 +1,17 @@
|
|||||||
|
{% extends "modal_form.html" %}
|
||||||
|
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% block pre_form_content %}
|
||||||
|
{{ block.super }}
|
||||||
|
|
||||||
|
{% if part %}
|
||||||
|
<div class='alert alert-block alert-info'>
|
||||||
|
{% include "hover_image.html" with image=part.image %}
|
||||||
|
{{ part.full_name}}
|
||||||
|
<br>
|
||||||
|
<i>{{ part.description }}</i>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% endblock %}
|
@ -291,7 +291,7 @@ class SupplierPartCreate(AjaxCreateView):
|
|||||||
|
|
||||||
model = SupplierPart
|
model = SupplierPart
|
||||||
form_class = EditSupplierPartForm
|
form_class = EditSupplierPartForm
|
||||||
ajax_template_name = 'modal_form.html'
|
ajax_template_name = 'company/supplier_part_create.html'
|
||||||
ajax_form_title = _('Create new Supplier Part')
|
ajax_form_title = _('Create new Supplier Part')
|
||||||
context_object_name = 'part'
|
context_object_name = 'part'
|
||||||
role_required = 'purchase_order.add'
|
role_required = 'purchase_order.add'
|
||||||
@ -304,6 +304,27 @@ class SupplierPartCreate(AjaxCreateView):
|
|||||||
# TODO - What validation steps can be performed on the single_pricing field?
|
# TODO - What validation steps can be performed on the single_pricing field?
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def get_context_data(self):
|
||||||
|
"""
|
||||||
|
Supply context data to the form
|
||||||
|
"""
|
||||||
|
|
||||||
|
ctx = super().get_context_data()
|
||||||
|
|
||||||
|
# Add 'part' object
|
||||||
|
form = self.get_form()
|
||||||
|
|
||||||
|
part = form['part'].value()
|
||||||
|
|
||||||
|
try:
|
||||||
|
part = Part.objects.get(pk=part)
|
||||||
|
except (ValueError, Part.DoesNotExist):
|
||||||
|
part = None
|
||||||
|
|
||||||
|
ctx['part'] = part
|
||||||
|
|
||||||
|
return ctx
|
||||||
|
|
||||||
def save(self, form):
|
def save(self, form):
|
||||||
"""
|
"""
|
||||||
If single_pricing is defined, add a price break for quantity=1
|
If single_pricing is defined, add a price break for quantity=1
|
||||||
|
@ -11,11 +11,11 @@
|
|||||||
|
|
||||||
{% if parts|length > 0 %}
|
{% if parts|length > 0 %}
|
||||||
<div class='alert alert-info alert-block' role='alert'>
|
<div class='alert alert-info alert-block' role='alert'>
|
||||||
{% trans "Select suppliers." %}
|
{% trans "Select suppliers" %}
|
||||||
</div>
|
</div>
|
||||||
{% else %}
|
{% else %}
|
||||||
<div class='alert alert-warning alert-block' role='alert'>
|
<div class='alert alert-warning alert-block' role='alert'>
|
||||||
{% trans "No purchaseable parts selected." %}
|
{% trans "No purchaseable parts selected" %}
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
@ -39,8 +39,8 @@
|
|||||||
{{ part.full_name }} <small><i>{{ part.description }}</i></small>
|
{{ part.full_name }} <small><i>{{ part.description }}</i></small>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<button class='btn btn-default btn-create' onClick='newSupplierPartFromOrderWizard()' id='new_supplier_part_{{ part.id }}' title='Create new supplier part for {{ part }}' type='button'>
|
<button class='btn btn-default btn-create' onClick='newSupplierPartFromOrderWizard()' id='new_supplier_part_{{ part.id }}' part='{{ part.pk }}' title='{% trans "Create new supplier part" $}' type='button'>
|
||||||
<span part-id='{{ part.id }}' class='fas fa-plus-circle'></span>
|
<span part='{{ part.pk }}' class='fas fa-plus-circle'></span>
|
||||||
</button>
|
</button>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
|
@ -336,7 +336,7 @@ class PartStarSerializer(InvenTreeModelSerializer):
|
|||||||
class BomItemSerializer(InvenTreeModelSerializer):
|
class BomItemSerializer(InvenTreeModelSerializer):
|
||||||
""" Serializer for BomItem object """
|
""" Serializer for BomItem object """
|
||||||
|
|
||||||
price_range = serializers.CharField(read_only=True)
|
# price_range = serializers.CharField(read_only=True)
|
||||||
|
|
||||||
quantity = serializers.FloatField()
|
quantity = serializers.FloatField()
|
||||||
|
|
||||||
@ -387,7 +387,7 @@ class BomItemSerializer(InvenTreeModelSerializer):
|
|||||||
'sub_part_detail',
|
'sub_part_detail',
|
||||||
'quantity',
|
'quantity',
|
||||||
'reference',
|
'reference',
|
||||||
'price_range',
|
# 'price_range',
|
||||||
'optional',
|
'optional',
|
||||||
'overage',
|
'overage',
|
||||||
'note',
|
'note',
|
||||||
|
@ -59,7 +59,7 @@
|
|||||||
{% endfor %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
{% if col.duplicate %}
|
{% if col.duplicate %}
|
||||||
<p class='help-inline'>Duplicate column selection</p>
|
<p class='help-inline'>{% trans "Duplicate column selection" %}</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
@ -63,7 +63,7 @@
|
|||||||
<option value=''>--- {% trans "Select Part" %} ---</option>
|
<option value=''>--- {% trans "Select Part" %} ---</option>
|
||||||
{% for part in row.part_options %}
|
{% for part in row.part_options %}
|
||||||
<option value='{{ part.id }}' {% if part.id == row.part.id %} selected='selected' {% elif part.id == row.part_match.id %} selected='selected' {% endif %}>
|
<option value='{{ part.id }}' {% if part.id == row.part.id %} selected='selected' {% elif part.id == row.part_match.id %} selected='selected' {% endif %}>
|
||||||
{{ part }} - {{ part.available_stock }}
|
{{ part }}
|
||||||
</option>
|
</option>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
|
@ -140,6 +140,12 @@ function loadBomTable(table, options) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set the parent ID of the multi-level table.
|
||||||
|
// We prepend this with the literal string value 'top-level-',
|
||||||
|
// because otherwise the unfortunate situation where BomItem.pk == BomItem.part.pk
|
||||||
|
// AND THIS BREAKS EVERYTHING
|
||||||
|
var parent_id = `top-level-${options.parent_id}`;
|
||||||
|
|
||||||
// Part column
|
// Part column
|
||||||
cols.push(
|
cols.push(
|
||||||
{
|
{
|
||||||
@ -197,11 +203,11 @@ function loadBomTable(table, options) {
|
|||||||
text = parseFloat(text);
|
text = parseFloat(text);
|
||||||
|
|
||||||
if (row.optional) {
|
if (row.optional) {
|
||||||
text += " ({% trans "Optional" %})";
|
text += ' ({% trans "Optional" %})';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (row.overage) {
|
if (row.overage) {
|
||||||
text += "<small> (+" + row.overage + ") </small>";
|
text += `<small> (${row.overage}) </small>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
return text;
|
return text;
|
||||||
@ -228,6 +234,12 @@ function loadBomTable(table, options) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
// TODO - Re-introduce the pricing column at a later stage,
|
||||||
|
// once the pricing has been "fixed"
|
||||||
|
// O.W. 2020-11-24
|
||||||
|
|
||||||
cols.push(
|
cols.push(
|
||||||
{
|
{
|
||||||
field: 'price_range',
|
field: 'price_range',
|
||||||
@ -241,6 +253,7 @@ function loadBomTable(table, options) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
// Part notes
|
// Part notes
|
||||||
@ -327,9 +340,8 @@ function loadBomTable(table, options) {
|
|||||||
|
|
||||||
table.inventreeTable({
|
table.inventreeTable({
|
||||||
treeEnable: !options.editable,
|
treeEnable: !options.editable,
|
||||||
rootParentId: options.parent_id,
|
rootParentId: parent_id,
|
||||||
idField: 'pk',
|
idField: 'pk',
|
||||||
//uniqueId: 'pk',
|
|
||||||
parentIdField: 'parentId',
|
parentIdField: 'parentId',
|
||||||
treeShowField: 'sub_part',
|
treeShowField: 'sub_part',
|
||||||
showColumns: true,
|
showColumns: true,
|
||||||
@ -338,12 +350,18 @@ function loadBomTable(table, options) {
|
|||||||
search: true,
|
search: true,
|
||||||
rowStyle: function(row, index) {
|
rowStyle: function(row, index) {
|
||||||
if (row.validated) {
|
if (row.validated) {
|
||||||
return {classes: 'rowvalid'};
|
return {
|
||||||
|
classes: 'rowvalid'
|
||||||
|
};
|
||||||
} else {
|
} else {
|
||||||
return {classes: 'rowinvalid'};
|
return {
|
||||||
|
classes: 'rowinvalid'
|
||||||
|
};
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
formatNoMatches: function() { return '{% trans "No BOM items found" %}'; },
|
formatNoMatches: function() {
|
||||||
|
return '{% trans "No BOM items found" %}';
|
||||||
|
},
|
||||||
clickToSelect: true,
|
clickToSelect: true,
|
||||||
queryParams: filters,
|
queryParams: filters,
|
||||||
original: params,
|
original: params,
|
||||||
@ -376,7 +394,7 @@ function loadBomTable(table, options) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Set the parent ID of the top-level rows
|
// Set the parent ID of the top-level rows
|
||||||
row.parentId = options.parent_id;
|
row.parentId = parent_id;
|
||||||
|
|
||||||
table.bootstrapTable('updateRow', idx, row, true);
|
table.bootstrapTable('updateRow', idx, row, true);
|
||||||
|
|
||||||
|
@ -243,6 +243,22 @@ function loadBuildOutputAllocationTable(buildInfo, output, options={}) {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Callback for 'buy' button
|
||||||
|
$(table).find('.button-buy').click(function() {
|
||||||
|
var pk = $(this).attr('pk');
|
||||||
|
|
||||||
|
var idx = $(this).closest('tr').attr('data-index');
|
||||||
|
var row = $(table).bootstrapTable('getData')[idx];
|
||||||
|
|
||||||
|
launchModalForm('{% url "order-parts" %}', {
|
||||||
|
data: {
|
||||||
|
parts: [
|
||||||
|
pk,
|
||||||
|
]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// Callback for 'build' button
|
// Callback for 'build' button
|
||||||
$(table).find('.button-build').click(function() {
|
$(table).find('.button-build').click(function() {
|
||||||
var pk = $(this).attr('pk');
|
var pk = $(this).attr('pk');
|
||||||
@ -563,7 +579,7 @@ function loadBuildOutputAllocationTable(buildInfo, output, options={}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (row.sub_part_detail.purchaseable) {
|
if (row.sub_part_detail.purchaseable) {
|
||||||
html += makeIconButton('fa-shopping-cart icon-blue', 'button-buy', row.sub_part, '{% trans "Order stock" %}', {disabled: true});
|
html += makeIconButton('fa-shopping-cart icon-blue', 'button-buy', row.sub_part, '{% trans "Order stock" %}');
|
||||||
}
|
}
|
||||||
|
|
||||||
html += makeIconButton('fa-sign-in-alt icon-green', 'button-add', row.sub_part, '{% trans "Allocate stock" %}');
|
html += makeIconButton('fa-sign-in-alt icon-green', 'button-add', row.sub_part, '{% trans "Allocate stock" %}');
|
||||||
|
@ -21,9 +21,16 @@ function newSupplierPartFromOrderWizard(e) {
|
|||||||
|
|
||||||
e = e || window.event;
|
e = e || window.event;
|
||||||
|
|
||||||
var src = e.target || e.srcElement;
|
var src = e.srcElement || e.target;
|
||||||
|
|
||||||
var part = $(src).attr('part-id');
|
var part = $(src).attr('part');
|
||||||
|
|
||||||
|
console.log('part: ' + part);
|
||||||
|
|
||||||
|
if (!part) {
|
||||||
|
part = $(src).closest('button').attr('part');
|
||||||
|
console.log('parent: ' + part);
|
||||||
|
}
|
||||||
|
|
||||||
launchModalForm("/supplier-part/new/", {
|
launchModalForm("/supplier-part/new/", {
|
||||||
modal: '#modal-form-secondary',
|
modal: '#modal-form-secondary',
|
||||||
@ -125,7 +132,7 @@ function loadPurchaseOrderTable(table, options) {
|
|||||||
name: 'purchaseorder',
|
name: 'purchaseorder',
|
||||||
groupBy: false,
|
groupBy: false,
|
||||||
original: options.params,
|
original: options.params,
|
||||||
formatNoMatches: function() { return "{% trans "No purchase orders found" %}"; },
|
formatNoMatches: function() { return '{% trans "No purchase orders found" %}'; },
|
||||||
columns: [
|
columns: [
|
||||||
{
|
{
|
||||||
field: 'pk',
|
field: 'pk',
|
||||||
@ -208,7 +215,7 @@ function loadSalesOrderTable(table, options) {
|
|||||||
name: 'salesorder',
|
name: 'salesorder',
|
||||||
groupBy: false,
|
groupBy: false,
|
||||||
original: options.params,
|
original: options.params,
|
||||||
formatNoMatches: function() { return "{% trans "No sales orders found" %}"; },
|
formatNoMatches: function() { return '{% trans "No sales orders found" %}'; },
|
||||||
columns: [
|
columns: [
|
||||||
{
|
{
|
||||||
field: 'pk',
|
field: 'pk',
|
||||||
@ -265,7 +272,7 @@ function loadSalesOrderTable(table, options) {
|
|||||||
{
|
{
|
||||||
sortable: true,
|
sortable: true,
|
||||||
field: 'shipment_date',
|
field: 'shipment_date',
|
||||||
title: "{% trans "Shipment Date" %}",
|
title: '{% trans "Shipment Date" %}',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
sortable: true,
|
sortable: true,
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
{% load i18n %}
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
{% if form.pre_form_info %}
|
{% if form.pre_form_info %}
|
||||||
<div class='alert alert-info alert-block' role='alert'>
|
<div class='alert alert-info alert-block' role='alert'>
|
||||||
@ -9,18 +11,6 @@
|
|||||||
{{ form.pre_form_warning }}
|
{{ form.pre_form_warning }}
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% block non_field_error %}
|
|
||||||
{% if form.non_field_errors %}
|
|
||||||
<div class='alert alert-danger alert-block' role='alert'>
|
|
||||||
<b>Error Submitting Form:</b>
|
|
||||||
<ul>
|
|
||||||
{% for error in form.non_field_errors %}
|
|
||||||
{{ error }}
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
{% endblock %}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% block pre_form_content %}
|
{% block pre_form_content %}
|
||||||
|
10
tasks.py
10
tasks.py
@ -102,6 +102,16 @@ def install(c):
|
|||||||
print("Config file 'config.yaml' does not exist - copying from template.")
|
print("Config file 'config.yaml' does not exist - copying from template.")
|
||||||
copyfile(CONFIG_TEMPLATE_FILE, CONFIG_FILE)
|
copyfile(CONFIG_TEMPLATE_FILE, CONFIG_FILE)
|
||||||
|
|
||||||
|
|
||||||
|
@task
|
||||||
|
def shell(c):
|
||||||
|
"""
|
||||||
|
Open a python shell with access to the InvenTree database models.
|
||||||
|
"""
|
||||||
|
|
||||||
|
manage(c, 'shell', pty=True)
|
||||||
|
|
||||||
|
|
||||||
@task
|
@task
|
||||||
def superuser(c):
|
def superuser(c):
|
||||||
"""
|
"""
|
||||||
|
Reference in New Issue
Block a user