mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
Merge pull request #2770 from SchrodingersGat/order-parts-wizard
Order parts wizard
This commit is contained in:
commit
e938870b32
@ -312,7 +312,7 @@ class SupplierPartList(generics.ListCreateAPIView):
|
|||||||
try:
|
try:
|
||||||
params = self.request.query_params
|
params = self.request.query_params
|
||||||
kwargs['part_detail'] = str2bool(params.get('part_detail', None))
|
kwargs['part_detail'] = str2bool(params.get('part_detail', None))
|
||||||
kwargs['supplier_detail'] = str2bool(params.get('supplier_detail', None))
|
kwargs['supplier_detail'] = str2bool(params.get('supplier_detail', True))
|
||||||
kwargs['manufacturer_detail'] = str2bool(params.get('manufacturer_detail', None))
|
kwargs['manufacturer_detail'] = str2bool(params.get('manufacturer_detail', None))
|
||||||
kwargs['pretty'] = str2bool(params.get('pretty', None))
|
kwargs['pretty'] = str2bool(params.get('pretty', None))
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
|
@ -223,14 +223,30 @@ class PurchaseOrderLineItemSerializer(InvenTreeModelSerializer):
|
|||||||
if order_detail is not True:
|
if order_detail is not True:
|
||||||
self.fields.pop('order_detail')
|
self.fields.pop('order_detail')
|
||||||
|
|
||||||
quantity = serializers.FloatField(default=1)
|
quantity = serializers.FloatField(min_value=0, required=True)
|
||||||
received = serializers.FloatField(default=0)
|
|
||||||
|
def validate_quantity(self, quantity):
|
||||||
|
|
||||||
|
if quantity <= 0:
|
||||||
|
raise ValidationError(_("Quantity must be greater than zero"))
|
||||||
|
|
||||||
|
return quantity
|
||||||
|
|
||||||
|
def validate_purchase_order(self, purchase_order):
|
||||||
|
|
||||||
|
if purchase_order.status not in PurchaseOrderStatus.OPEN:
|
||||||
|
raise ValidationError(_('Order is not open'))
|
||||||
|
|
||||||
|
return purchase_order
|
||||||
|
|
||||||
|
received = serializers.FloatField(default=0, read_only=True)
|
||||||
|
|
||||||
overdue = serializers.BooleanField(required=False, read_only=True)
|
overdue = serializers.BooleanField(required=False, read_only=True)
|
||||||
|
|
||||||
total_price = serializers.FloatField(read_only=True)
|
total_price = serializers.FloatField(read_only=True)
|
||||||
|
|
||||||
part_detail = PartBriefSerializer(source='get_base_part', many=False, read_only=True)
|
part_detail = PartBriefSerializer(source='get_base_part', many=False, read_only=True)
|
||||||
|
|
||||||
supplier_part_detail = SupplierPartSerializer(source='part', many=False, read_only=True)
|
supplier_part_detail = SupplierPartSerializer(source='part', many=False, read_only=True)
|
||||||
|
|
||||||
purchase_price = InvenTreeMoneySerializer(
|
purchase_price = InvenTreeMoneySerializer(
|
||||||
@ -248,6 +264,32 @@ class PurchaseOrderLineItemSerializer(InvenTreeModelSerializer):
|
|||||||
|
|
||||||
order_detail = PurchaseOrderSerializer(source='order', read_only=True, many=False)
|
order_detail = PurchaseOrderSerializer(source='order', read_only=True, many=False)
|
||||||
|
|
||||||
|
def validate(self, data):
|
||||||
|
|
||||||
|
data = super().validate(data)
|
||||||
|
|
||||||
|
supplier_part = data.get('part', None)
|
||||||
|
purchase_order = data.get('order', None)
|
||||||
|
|
||||||
|
if not supplier_part:
|
||||||
|
raise ValidationError({
|
||||||
|
'part': _('Supplier part must be specified'),
|
||||||
|
})
|
||||||
|
|
||||||
|
if not purchase_order:
|
||||||
|
raise ValidationError({
|
||||||
|
'order': _('Purchase order must be specified'),
|
||||||
|
})
|
||||||
|
|
||||||
|
# Check that the supplier part and purchase order match
|
||||||
|
if supplier_part is not None and supplier_part.supplier != purchase_order.supplier:
|
||||||
|
raise ValidationError({
|
||||||
|
'part': _('Supplier must match purchase order'),
|
||||||
|
'order': _('Purchase order must match supplier'),
|
||||||
|
})
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = order.models.PurchaseOrderLineItem
|
model = order.models.PurchaseOrderLineItem
|
||||||
|
|
||||||
|
@ -169,13 +169,18 @@
|
|||||||
</button>
|
</button>
|
||||||
<ul class='dropdown-menu'>
|
<ul class='dropdown-menu'>
|
||||||
{% if roles.part.change %}
|
{% if roles.part.change %}
|
||||||
<li><a class='dropdown-item' href='#' id='multi-part-category' title='{% trans "Set category" %}'>{% trans "Set Category" %}</a></li>
|
<li><a class='dropdown-item' href='#' id='multi-part-category' title='{% trans "Set category" %}'>
|
||||||
|
<span class='fas fa-sitemap'></span> {% trans "Set Category" %}
|
||||||
|
</a></li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<li><a class='dropdown-item' href='#' id='multi-part-order' title='{% trans "Order parts" %}'>{% trans "Order Parts" %}</a></li>
|
<li><a class='dropdown-item' href='#' id='multi-part-order' title='{% trans "Order parts" %}'>
|
||||||
|
<span class='fas fa-shopping-cart'></span> {% trans "Order Parts" %}
|
||||||
|
</a></li>
|
||||||
{% if report_enabled %}
|
{% if report_enabled %}
|
||||||
<li><a class='dropdown-item' href='#' id='multi-part-print-label' title='{% trans "Print Labels" %}'>{% trans "Print Labels" %}</a></li>
|
<li><a class='dropdown-item' href='#' id='multi-part-print-label' title='{% trans "Print Labels" %}'>
|
||||||
|
<span class='fas fa-tag'></span> {% trans "Print Labels" %}
|
||||||
|
</a></li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<li><a class='dropdown-item' href='#' id='multi-part-export' title='{% trans "Export" %}'>{% trans "Export Data" %}</a></li>
|
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
{% include "filter_list.html" with id="parts" %}
|
{% include "filter_list.html" with id="parts" %}
|
||||||
|
@ -536,6 +536,22 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
$("#part-order").click(function() {
|
$("#part-order").click(function() {
|
||||||
|
|
||||||
|
inventreeGet(
|
||||||
|
'{% url "api-part-detail" part.pk %}',
|
||||||
|
{},
|
||||||
|
{
|
||||||
|
success: function(part) {
|
||||||
|
orderParts(
|
||||||
|
[part],
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return;
|
||||||
|
|
||||||
launchModalForm("{% url 'order-parts' %}", {
|
launchModalForm("{% url 'order-parts' %}", {
|
||||||
data: {
|
data: {
|
||||||
part: {{ part.id }},
|
part: {{ part.id }},
|
||||||
|
@ -105,7 +105,7 @@ function inventreeFormDataUpload(url, data, options={}) {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
error: function(xhr, status, error) {
|
error: function(xhr, status, error) {
|
||||||
console.log('Form data upload failure: ' + status);
|
console.error('Form data upload failure: ' + status);
|
||||||
|
|
||||||
if (options.error) {
|
if (options.error) {
|
||||||
options.error(xhr, status, error);
|
options.error(xhr, status, error);
|
||||||
|
@ -86,7 +86,6 @@ function onCameraAvailable(hasCamera, options) {
|
|||||||
|
|
||||||
function onBarcodeScanCompleted(result, options) {
|
function onBarcodeScanCompleted(result, options) {
|
||||||
if (result.data == '') return;
|
if (result.data == '') return;
|
||||||
console.log('decoded qr code:', result.data);
|
|
||||||
stopQrScanner();
|
stopQrScanner();
|
||||||
postBarcodeData(result.data, options);
|
postBarcodeData(result.data, options);
|
||||||
}
|
}
|
||||||
|
@ -129,7 +129,7 @@ function constructBomUploadTable(data, options={}) {
|
|||||||
|
|
||||||
var modal = createNewModal({
|
var modal = createNewModal({
|
||||||
title: '{% trans "Row Data" %}',
|
title: '{% trans "Row Data" %}',
|
||||||
cancelText: '{% trans "Close" %}',
|
closeText: '{% trans "Close" %}',
|
||||||
hideSubmitButton: true
|
hideSubmitButton: true
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -617,7 +617,7 @@ function bomSubstitutesDialog(bom_item_id, substitutes, options={}) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
preFormContent: html,
|
preFormContent: html,
|
||||||
cancelText: '{% trans "Close" %}',
|
closeText: '{% trans "Close" %}',
|
||||||
submitText: '{% trans "Add Substitute" %}',
|
submitText: '{% trans "Add Substitute" %}',
|
||||||
title: '{% trans "Edit BOM Item Substitutes" %}',
|
title: '{% trans "Edit BOM Item Substitutes" %}',
|
||||||
afterRender: function(fields, opts) {
|
afterRender: function(fields, opts) {
|
||||||
@ -1061,7 +1061,7 @@ function loadBomTable(table, options={}) {
|
|||||||
table.bootstrapTable('append', response);
|
table.bootstrapTable('append', response);
|
||||||
},
|
},
|
||||||
error: function(xhr) {
|
error: function(xhr) {
|
||||||
console.log('Error requesting BOM for part=' + part_pk);
|
console.error('Error requesting BOM for part=' + part_pk);
|
||||||
showApiError(xhr);
|
showApiError(xhr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -115,10 +115,6 @@ function supplierPartFields() {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
part: {},
|
part: {},
|
||||||
supplier: {},
|
|
||||||
SKU: {
|
|
||||||
icon: 'fa-hashtag',
|
|
||||||
},
|
|
||||||
manufacturer_part: {
|
manufacturer_part: {
|
||||||
filters: {
|
filters: {
|
||||||
part_detail: true,
|
part_detail: true,
|
||||||
@ -126,6 +122,10 @@ function supplierPartFields() {
|
|||||||
},
|
},
|
||||||
auto_fill: true,
|
auto_fill: true,
|
||||||
},
|
},
|
||||||
|
supplier: {},
|
||||||
|
SKU: {
|
||||||
|
icon: 'fa-hashtag',
|
||||||
|
},
|
||||||
description: {},
|
description: {},
|
||||||
link: {
|
link: {
|
||||||
icon: 'fa-link',
|
icon: 'fa-link',
|
||||||
|
@ -62,7 +62,7 @@ function loadTableFilters(tableKey) {
|
|||||||
if (f.length == 2) {
|
if (f.length == 2) {
|
||||||
filters[f[0]] = f[1];
|
filters[f[0]] = f[1];
|
||||||
} else {
|
} else {
|
||||||
console.log(`Improperly formatted filter: ${item}`);
|
console.warn(`Improperly formatted filter: ${item}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -274,7 +274,7 @@ function setupFilterList(tableKey, table, target, options={}) {
|
|||||||
var element = $(target);
|
var element = $(target);
|
||||||
|
|
||||||
if (!element || !element.exists()) {
|
if (!element || !element.exists()) {
|
||||||
console.log(`WARNING: setupFilterList could not find target '${target}'`);
|
console.warn(`setupFilterList could not find target '${target}'`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -135,7 +135,7 @@ function getApiEndpointOptions(url, callback) {
|
|||||||
success: callback,
|
success: callback,
|
||||||
error: function(xhr) {
|
error: function(xhr) {
|
||||||
// TODO: Handle error
|
// TODO: Handle error
|
||||||
console.log(`ERROR in getApiEndpointOptions at '${url}'`);
|
console.error(`Error in getApiEndpointOptions at '${url}'`);
|
||||||
showApiError(xhr, url);
|
showApiError(xhr, url);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -227,7 +227,7 @@ function constructChangeForm(fields, options) {
|
|||||||
},
|
},
|
||||||
error: function(xhr) {
|
error: function(xhr) {
|
||||||
// TODO: Handle error here
|
// TODO: Handle error here
|
||||||
console.log(`ERROR in constructChangeForm at '${options.url}'`);
|
console.error(`Error in constructChangeForm at '${options.url}'`);
|
||||||
|
|
||||||
showApiError(xhr, options.url);
|
showApiError(xhr, options.url);
|
||||||
}
|
}
|
||||||
@ -268,7 +268,7 @@ function constructDeleteForm(fields, options) {
|
|||||||
},
|
},
|
||||||
error: function(xhr) {
|
error: function(xhr) {
|
||||||
// TODO: Handle error here
|
// TODO: Handle error here
|
||||||
console.log(`ERROR in constructDeleteForm at '${options.url}`);
|
console.error(`Error in constructDeleteForm at '${options.url}`);
|
||||||
|
|
||||||
showApiError(xhr, options.url);
|
showApiError(xhr, options.url);
|
||||||
}
|
}
|
||||||
@ -354,7 +354,7 @@ function constructForm(url, options) {
|
|||||||
icon: 'fas fa-user-times',
|
icon: 'fas fa-user-times',
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log(`'POST action unavailable at ${url}`);
|
console.warn(`'POST action unavailable at ${url}`);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'PUT':
|
case 'PUT':
|
||||||
@ -369,7 +369,7 @@ function constructForm(url, options) {
|
|||||||
icon: 'fas fa-user-times',
|
icon: 'fas fa-user-times',
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log(`${options.method} action unavailable at ${url}`);
|
console.warn(`${options.method} action unavailable at ${url}`);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'DELETE':
|
case 'DELETE':
|
||||||
@ -383,7 +383,7 @@ function constructForm(url, options) {
|
|||||||
icon: 'fas fa-user-times',
|
icon: 'fas fa-user-times',
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log(`DELETE action unavailable at ${url}`);
|
console.warn(`DELETE action unavailable at ${url}`);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'GET':
|
case 'GET':
|
||||||
@ -397,11 +397,11 @@ function constructForm(url, options) {
|
|||||||
icon: 'fas fa-user-times',
|
icon: 'fas fa-user-times',
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log(`GET action unavailable at ${url}`);
|
console.warn(`GET action unavailable at ${url}`);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
console.log(`constructForm() called with invalid method '${options.method}'`);
|
console.warn(`constructForm() called with invalid method '${options.method}'`);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -731,7 +731,7 @@ function submitFormData(fields, options) {
|
|||||||
data[name] = value;
|
data[name] = value;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
console.log(`WARNING: Could not find field matching '${name}'`);
|
console.warn(`Could not find field matching '${name}'`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -776,7 +776,7 @@ function submitFormData(fields, options) {
|
|||||||
default:
|
default:
|
||||||
$(options.modal).modal('hide');
|
$(options.modal).modal('hide');
|
||||||
|
|
||||||
console.log(`upload error at ${options.url}`);
|
console.error(`Upload error at ${options.url}`);
|
||||||
showApiError(xhr, options.url);
|
showApiError(xhr, options.url);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -827,7 +827,7 @@ function updateFieldValue(name, value, field, options) {
|
|||||||
var el = getFormFieldElement(name, options);
|
var el = getFormFieldElement(name, options);
|
||||||
|
|
||||||
if (!el) {
|
if (!el) {
|
||||||
console.log(`WARNING: updateFieldValue could not find field '${name}'`);
|
console.warn(`updateFieldValue could not find field '${name}'`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -870,7 +870,7 @@ function getFormFieldElement(name, options) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!el.exists) {
|
if (!el.exists) {
|
||||||
console.log(`ERROR: Could not find form element for field '${name}'`);
|
console.error(`Could not find form element for field '${name}'`);
|
||||||
}
|
}
|
||||||
|
|
||||||
return el;
|
return el;
|
||||||
@ -918,7 +918,7 @@ function getFormFieldValue(name, field={}, options={}) {
|
|||||||
var el = getFormFieldElement(name, options);
|
var el = getFormFieldElement(name, options);
|
||||||
|
|
||||||
if (!el.exists()) {
|
if (!el.exists()) {
|
||||||
console.log(`ERROR: getFormFieldValue could not locate field '${name}'`);
|
console.error(`getFormFieldValue could not locate field '${name}'`);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1104,7 +1104,7 @@ function handleNestedErrors(errors, field_name, options={}) {
|
|||||||
|
|
||||||
// Nest list must be provided!
|
// Nest list must be provided!
|
||||||
if (!nest_list) {
|
if (!nest_list) {
|
||||||
console.log(`WARNING: handleNestedErrors missing nesting options for field '${fieldName}'`);
|
console.warn(`handleNestedErrors missing nesting options for field '${fieldName}'`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1113,7 +1113,7 @@ function handleNestedErrors(errors, field_name, options={}) {
|
|||||||
var error_item = error_list[idx];
|
var error_item = error_list[idx];
|
||||||
|
|
||||||
if (idx >= nest_list.length) {
|
if (idx >= nest_list.length) {
|
||||||
console.log(`WARNING: handleNestedErrors returned greater number of errors (${error_list.length}) than could be handled (${nest_list.length})`);
|
console.warn(`handleNestedErrors returned greater number of errors (${error_list.length}) than could be handled (${nest_list.length})`);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1218,29 +1218,26 @@ function handleFormErrors(errors, fields={}, options={}) {
|
|||||||
|
|
||||||
for (var field_name in errors) {
|
for (var field_name in errors) {
|
||||||
|
|
||||||
if (field_name in fields) {
|
var field = fields[field_name] || {};
|
||||||
|
|
||||||
var field = fields[field_name];
|
if ((field.type == 'field') && ('child' in field)) {
|
||||||
|
// This is a "nested" field
|
||||||
|
handleNestedErrors(errors, field_name, options);
|
||||||
|
} else {
|
||||||
|
// This is a "simple" field
|
||||||
|
|
||||||
if ((field.type == 'field') && ('child' in field)) {
|
var field_errors = errors[field_name];
|
||||||
// This is a "nested" field
|
|
||||||
handleNestedErrors(errors, field_name, options);
|
|
||||||
} else {
|
|
||||||
// This is a "simple" field
|
|
||||||
|
|
||||||
var field_errors = errors[field_name];
|
if (field_errors && !first_error_field && isFieldVisible(field_name, options)) {
|
||||||
|
first_error_field = field_name;
|
||||||
|
}
|
||||||
|
|
||||||
if (field_errors && !first_error_field && isFieldVisible(field_name, options)) {
|
// Add an entry for each returned error message
|
||||||
first_error_field = field_name;
|
for (var ii = field_errors.length-1; ii >= 0; ii--) {
|
||||||
}
|
|
||||||
|
|
||||||
// Add an entry for each returned error message
|
var error_text = field_errors[ii];
|
||||||
for (var ii = field_errors.length-1; ii >= 0; ii--) {
|
|
||||||
|
|
||||||
var error_text = field_errors[ii];
|
addFieldErrorMessage(field_name, error_text, ii, options);
|
||||||
|
|
||||||
addFieldErrorMessage(field_name, error_text, ii, options);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1285,7 +1282,7 @@ function addFieldErrorMessage(name, error_text, error_idx=0, options={}) {
|
|||||||
|
|
||||||
field_dom.append(error_html);
|
field_dom.append(error_html);
|
||||||
} else {
|
} else {
|
||||||
console.log(`WARNING: addFieldErrorMessage could not locate field '${field_name}'`);
|
console.warn(`addFieldErrorMessage could not locate field '${field_name}'`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1358,7 +1355,7 @@ function addClearCallback(name, field, options={}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!el) {
|
if (!el) {
|
||||||
console.log(`WARNING: addClearCallback could not find field '${name}'`);
|
console.warn(`addClearCallback could not find field '${name}'`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1582,7 +1579,7 @@ function initializeRelatedField(field, fields, options={}) {
|
|||||||
var name = field.name;
|
var name = field.name;
|
||||||
|
|
||||||
if (!field.api_url) {
|
if (!field.api_url) {
|
||||||
console.log(`WARNING: Related field '${name}' missing 'api_url' parameter.`);
|
console.warn(`Related field '${name}' missing 'api_url' parameter.`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1712,7 +1709,7 @@ function initializeRelatedField(field, fields, options={}) {
|
|||||||
return $(html);
|
return $(html);
|
||||||
} else {
|
} else {
|
||||||
// Return a simple renderering
|
// Return a simple renderering
|
||||||
console.log(`WARNING: templateResult() missing 'field.model' for '${name}'`);
|
console.warn(`templateResult() missing 'field.model' for '${name}'`);
|
||||||
return `${name} - ${item.id}`;
|
return `${name} - ${item.id}`;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -1742,7 +1739,7 @@ function initializeRelatedField(field, fields, options={}) {
|
|||||||
return $(html);
|
return $(html);
|
||||||
} else {
|
} else {
|
||||||
// Return a simple renderering
|
// Return a simple renderering
|
||||||
console.log(`WARNING: templateSelection() missing 'field.model' for '${name}'`);
|
console.warn(`templateSelection() missing 'field.model' for '${name}'`);
|
||||||
return `${name} - ${item.id}`;
|
return `${name} - ${item.id}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1780,6 +1777,11 @@ function initializeRelatedField(field, fields, options={}) {
|
|||||||
// Only a single result is available, given the provided filters
|
// Only a single result is available, given the provided filters
|
||||||
if (data.count == 1) {
|
if (data.count == 1) {
|
||||||
setRelatedFieldData(name, data.results[0], options);
|
setRelatedFieldData(name, data.results[0], options);
|
||||||
|
|
||||||
|
// Run "callback" function (if supplied)
|
||||||
|
if (field.onEdit) {
|
||||||
|
field.onEdit(data.results[0], name, field, options);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -1911,7 +1913,7 @@ function renderModelData(name, model, data, parameters, options) {
|
|||||||
if (html != null) {
|
if (html != null) {
|
||||||
return html;
|
return html;
|
||||||
} else {
|
} else {
|
||||||
console.log(`ERROR: Rendering not implemented for model '${model}'`);
|
console.error(`Rendering not implemented for model '${model}'`);
|
||||||
// Simple text rendering
|
// Simple text rendering
|
||||||
return `${model} - ID ${data.id}`;
|
return `${model} - ID ${data.id}`;
|
||||||
}
|
}
|
||||||
@ -1924,6 +1926,10 @@ function renderModelData(name, model, data, parameters, options) {
|
|||||||
function getFieldName(name, options={}) {
|
function getFieldName(name, options={}) {
|
||||||
var field_name = name;
|
var field_name = name;
|
||||||
|
|
||||||
|
if (options.field_suffix) {
|
||||||
|
field_name += options.field_suffix;
|
||||||
|
}
|
||||||
|
|
||||||
if (options && options.depth) {
|
if (options && options.depth) {
|
||||||
field_name += `_${options.depth}`;
|
field_name += `_${options.depth}`;
|
||||||
}
|
}
|
||||||
@ -2196,7 +2202,7 @@ function constructInput(name, parameters, options={}) {
|
|||||||
if (func != null) {
|
if (func != null) {
|
||||||
html = func(name, parameters, options);
|
html = func(name, parameters, options);
|
||||||
} else {
|
} else {
|
||||||
console.log(`WARNING: Unhandled form field type: '${parameters.type}'`);
|
console.warn(`Unhandled form field type: '${parameters.type}'`);
|
||||||
}
|
}
|
||||||
|
|
||||||
return html;
|
return html;
|
||||||
@ -2499,12 +2505,12 @@ function constructHelpText(name, parameters) {
|
|||||||
function selectImportFields(url, data={}, options={}) {
|
function selectImportFields(url, data={}, options={}) {
|
||||||
|
|
||||||
if (!data.model_fields) {
|
if (!data.model_fields) {
|
||||||
console.log(`WARNING: selectImportFields is missing 'model_fields'`);
|
console.warn(`selectImportFields is missing 'model_fields'`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!data.file_fields) {
|
if (!data.file_fields) {
|
||||||
console.log(`WARNING: selectImportFields is missing 'file_fields'`);
|
console.warn(`selectImportFields is missing 'file_fields'`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2595,7 +2601,7 @@ function selectImportFields(url, data={}, options={}) {
|
|||||||
default:
|
default:
|
||||||
$(opts.modal).modal('hide');
|
$(opts.modal).modal('hide');
|
||||||
|
|
||||||
console.log(`upload error at ${opts.url}`);
|
console.error(`upload error at ${opts.url}`);
|
||||||
showApiError(xhr, opts.url);
|
showApiError(xhr, opts.url);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -85,12 +85,25 @@ function createNewModal(options={}) {
|
|||||||
|
|
||||||
var modal_name = `#modal-form-${id}`;
|
var modal_name = `#modal-form-${id}`;
|
||||||
|
|
||||||
|
// Callback *after* the modal has been rendered
|
||||||
$(modal_name).on('shown.bs.modal', function() {
|
$(modal_name).on('shown.bs.modal', function() {
|
||||||
$(modal_name + ' .modal-form-content').scrollTop(0);
|
$(modal_name + ' .modal-form-content').scrollTop(0);
|
||||||
|
|
||||||
if (options.focus) {
|
if (options.focus) {
|
||||||
getFieldByName(modal_name, options.focus).focus();
|
getFieldByName(modal_name, options.focus).focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Steal keyboard focus
|
||||||
|
$(modal_name).focus();
|
||||||
|
|
||||||
|
if (options.hideCloseButton) {
|
||||||
|
$(modal_name).find('#modal-form-cancel').hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.preventSubmit || options.hideSubmitButton) {
|
||||||
|
$(modal_name).find('#modal-form-submit').hide();
|
||||||
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Automatically remove the modal when it is deleted!
|
// Automatically remove the modal when it is deleted!
|
||||||
@ -102,8 +115,11 @@ function createNewModal(options={}) {
|
|||||||
$(modal_name).on('keydown', 'input', function(event) {
|
$(modal_name).on('keydown', 'input', function(event) {
|
||||||
if (event.keyCode == 13) {
|
if (event.keyCode == 13) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
// Simulate a click on the 'Submit' button
|
|
||||||
$(modal_name).find('#modal-form-submit').click();
|
if (!options.preventSubmit) {
|
||||||
|
// Simulate a click on the 'Submit' button
|
||||||
|
$(modal_name).find('#modal-form-submit').click();
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -117,18 +133,7 @@ function createNewModal(options={}) {
|
|||||||
// Set labels based on supplied options
|
// Set labels based on supplied options
|
||||||
modalSetTitle(modal_name, options.title || '{% trans "Form Title" %}');
|
modalSetTitle(modal_name, options.title || '{% trans "Form Title" %}');
|
||||||
modalSetSubmitText(modal_name, options.submitText || '{% trans "Submit" %}');
|
modalSetSubmitText(modal_name, options.submitText || '{% trans "Submit" %}');
|
||||||
modalSetCloseText(modal_name, options.cancelText || '{% trans "Cancel" %}');
|
modalSetCloseText(modal_name, options.closeText || '{% trans "Cancel" %}');
|
||||||
|
|
||||||
if (options.hideSubmitButton) {
|
|
||||||
$(modal_name).find('#modal-form-submit').hide();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options.hideCloseButton) {
|
|
||||||
$(modal_name).find('#modal-form-cancel').hide();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Steal keyboard focus
|
|
||||||
$(modal_name).focus();
|
|
||||||
|
|
||||||
// Return the "name" of the modal
|
// Return the "name" of the modal
|
||||||
return modal_name;
|
return modal_name;
|
||||||
@ -274,7 +279,7 @@ function reloadFieldOptions(fieldName, options) {
|
|||||||
setFieldOptions(fieldName, opts);
|
setFieldOptions(fieldName, opts);
|
||||||
},
|
},
|
||||||
error: function() {
|
error: function() {
|
||||||
console.log('Error GETting field options');
|
console.error('Error GETting field options');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -581,7 +586,7 @@ function showAlertDialog(title, content, options={}) {
|
|||||||
|
|
||||||
var modal = createNewModal({
|
var modal = createNewModal({
|
||||||
title: title,
|
title: title,
|
||||||
cancelText: '{% trans "Close" %}',
|
closeText: '{% trans "Close" %}',
|
||||||
hideSubmitButton: true,
|
hideSubmitButton: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -607,7 +612,7 @@ function showQuestionDialog(title, content, options={}) {
|
|||||||
var modal = createNewModal({
|
var modal = createNewModal({
|
||||||
title: title,
|
title: title,
|
||||||
submitText: options.accept_text || '{% trans "Accept" %}',
|
submitText: options.accept_text || '{% trans "Accept" %}',
|
||||||
cancelText: options.cancel_text || '{% trans "Cancel" %}',
|
closeText: options.cancel_text || '{% trans "Cancel" %}',
|
||||||
});
|
});
|
||||||
|
|
||||||
modalSetContent(modal, content);
|
modalSetContent(modal, content);
|
||||||
@ -842,7 +847,7 @@ function attachFieldCallback(modal, callback) {
|
|||||||
// Run the callback function with the new value of the field!
|
// Run the callback function with the new value of the field!
|
||||||
callback.action(field.val(), field);
|
callback.action(field.val(), field);
|
||||||
} else {
|
} else {
|
||||||
console.log(`Value changed for field ${callback.field} - ${field.val()}`);
|
console.info(`Value changed for field ${callback.field} - ${field.val()} (no callback attached)`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -1085,8 +1090,8 @@ function launchModalForm(url, options = {}) {
|
|||||||
showAlertDialog('{% trans "Error requesting form data" %}', renderErrorMessage(xhr));
|
showAlertDialog('{% trans "Error requesting form data" %}', renderErrorMessage(xhr));
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('Modal form error: ' + xhr.status);
|
console.error('Modal form error: ' + xhr.status);
|
||||||
console.log('Message: ' + xhr.responseText);
|
console.info('Message: ' + xhr.responseText);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -34,7 +34,7 @@
|
|||||||
// Should the ID be rendered for this string
|
// Should the ID be rendered for this string
|
||||||
function renderId(title, pk, parameters={}) {
|
function renderId(title, pk, parameters={}) {
|
||||||
|
|
||||||
// Default = do not display
|
// Default = do not render
|
||||||
var render = false;
|
var render = false;
|
||||||
|
|
||||||
if ('render_pk' in parameters) {
|
if ('render_pk' in parameters) {
|
||||||
@ -297,7 +297,12 @@ function renderSalesOrderShipment(name, data, parameters={}, options={}) {
|
|||||||
|
|
||||||
var so_prefix = global_settings.SALESORDER_REFERENCE_PREFIX;
|
var so_prefix = global_settings.SALESORDER_REFERENCE_PREFIX;
|
||||||
|
|
||||||
var html = `<span>${so_prefix}${data.order_detail.reference} - {% trans "Shipment" %} ${data.reference}</span>`;
|
var html = `
|
||||||
|
<span>${so_prefix}${data.order_detail.reference} - {% trans "Shipment" %} ${data.reference}</span>
|
||||||
|
<span class='float-right'>
|
||||||
|
<small>{% trans "Shipment ID" %}: ${data.pk}</small>
|
||||||
|
</span>
|
||||||
|
`;
|
||||||
|
|
||||||
html += renderId('{% trans "Shipment ID" %}', data.pk, parameters);
|
html += renderId('{% trans "Shipment ID" %}', data.pk, parameters);
|
||||||
|
|
||||||
@ -384,10 +389,18 @@ function renderSupplierPart(name, data, parameters={}, options={}) {
|
|||||||
var html = '';
|
var html = '';
|
||||||
|
|
||||||
html += select2Thumbnail(supplier_image);
|
html += select2Thumbnail(supplier_image);
|
||||||
html += select2Thumbnail(part_image);
|
|
||||||
|
|
||||||
html += ` <span><b>${data.supplier_detail.name}</b> - ${data.SKU}</span>`;
|
if (data.part_detail) {
|
||||||
html += ` - <i>${data.part_detail.full_name}</i>`;
|
html += select2Thumbnail(part_image);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.supplier_detail) {
|
||||||
|
html += ` <span><b>${data.supplier_detail.name}</b> - ${data.SKU}</span>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.part_detail) {
|
||||||
|
html += ` - <i>${data.part_detail.full_name}</i>`;
|
||||||
|
}
|
||||||
|
|
||||||
html += renderId('{% trans "Supplier Part ID" %}', data.pk, parameters);
|
html += renderId('{% trans "Supplier Part ID" %}', data.pk, parameters);
|
||||||
|
|
||||||
|
@ -35,6 +35,7 @@
|
|||||||
loadSalesOrderTable,
|
loadSalesOrderTable,
|
||||||
newPurchaseOrderFromOrderWizard,
|
newPurchaseOrderFromOrderWizard,
|
||||||
newSupplierPartFromOrderWizard,
|
newSupplierPartFromOrderWizard,
|
||||||
|
orderParts,
|
||||||
removeOrderRowFromOrderWizard,
|
removeOrderRowFromOrderWizard,
|
||||||
removePurchaseOrderLineItem,
|
removePurchaseOrderLineItem,
|
||||||
loadOrderTotal,
|
loadOrderTotal,
|
||||||
@ -259,8 +260,8 @@ function createPurchaseOrder(options={}) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
supplier_reference: {},
|
|
||||||
description: {},
|
description: {},
|
||||||
|
supplier_reference: {},
|
||||||
target_date: {
|
target_date: {
|
||||||
icon: 'fa-calendar-alt',
|
icon: 'fa-calendar-alt',
|
||||||
},
|
},
|
||||||
@ -476,6 +477,280 @@ function exportOrder(redirect_url, options={}) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Create a new form to order parts based on the list of provided parts.
|
||||||
|
*/
|
||||||
|
function orderParts(parts_list, options={}) {
|
||||||
|
|
||||||
|
var parts = [];
|
||||||
|
|
||||||
|
parts_list.forEach(function(part) {
|
||||||
|
if (part.purchaseable) {
|
||||||
|
parts.push(part);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (parts.length == 0) {
|
||||||
|
showAlertDialog(
|
||||||
|
'{% trans "Select Parts" %}',
|
||||||
|
'{% trans "At least one purchaseable part must be selected" %}',
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Render a single part within the dialog
|
||||||
|
function renderPart(part, opts={}) {
|
||||||
|
|
||||||
|
var pk = part.pk;
|
||||||
|
|
||||||
|
var thumb = thumbnailImage(part.thumbnail || part.image);
|
||||||
|
|
||||||
|
// The "quantity" field should have been provided for each part
|
||||||
|
var quantity = part.quantity || 1;
|
||||||
|
|
||||||
|
if (quantity < 0) {
|
||||||
|
quantity = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
var quantity_input = constructField(
|
||||||
|
`quantity_${pk}`,
|
||||||
|
{
|
||||||
|
type: 'decimal',
|
||||||
|
min_value: 0,
|
||||||
|
value: quantity,
|
||||||
|
title: '{% trans "Quantity to order" %}',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
hideLabels: true,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
var supplier_part_prefix = `
|
||||||
|
<button type='button' class='input-group-text button-row-new-sp' pk='${pk}' title='{% trans "New supplier part" %}'>
|
||||||
|
<span class='fas fa-plus-circle icon-green'></span>
|
||||||
|
</button>
|
||||||
|
`;
|
||||||
|
|
||||||
|
var supplier_part_input = constructField(
|
||||||
|
`part_${pk}`,
|
||||||
|
{
|
||||||
|
type: 'related field',
|
||||||
|
required: true,
|
||||||
|
prefixRaw: supplier_part_prefix,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
hideLabels: true,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
var purchase_order_prefix = `
|
||||||
|
<button type='button' class='input-group-text button-row-new-po' pk='${pk}' title='{% trans "New purchase order" %}'>
|
||||||
|
<span class='fas fa-plus-circle icon-green'></span>
|
||||||
|
</button>
|
||||||
|
`;
|
||||||
|
|
||||||
|
var purchase_order_input = constructField(
|
||||||
|
`order_${pk}`,
|
||||||
|
{
|
||||||
|
type: 'related field',
|
||||||
|
required: true,
|
||||||
|
prefixRaw: purchase_order_prefix,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
hideLabels: 'true',
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
var buttons = `<div class='btn-group float-right' role='group'>`;
|
||||||
|
|
||||||
|
if (parts.length > 1) {
|
||||||
|
buttons += makeIconButton(
|
||||||
|
'fa-times icon-red',
|
||||||
|
'button-row-remove',
|
||||||
|
pk,
|
||||||
|
'{% trans "Remove row" %}',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Button to add row to purchase order
|
||||||
|
buttons += makeIconButton(
|
||||||
|
'fa-shopping-cart icon-blue',
|
||||||
|
'button-row-add',
|
||||||
|
pk,
|
||||||
|
'{% trans "Add to purchase order" %}',
|
||||||
|
);
|
||||||
|
|
||||||
|
buttons += `</div>`;
|
||||||
|
|
||||||
|
var html = `
|
||||||
|
<tr id='order_row_${pk}' class='part-order-row'>
|
||||||
|
<td id='td_part_${pk}'>${thumb} ${part.full_name}</td>
|
||||||
|
<td id='td_supplier_part_${pk}'>${supplier_part_input}</td>
|
||||||
|
<td id='td_order_${pk}'>${purchase_order_input}</td>
|
||||||
|
<td id='td_quantity_${pk}'>${quantity_input}</td>
|
||||||
|
<td id='td_actions_${pk}'>${buttons}</td>
|
||||||
|
</tr>`;
|
||||||
|
|
||||||
|
return html;
|
||||||
|
}
|
||||||
|
|
||||||
|
var table_entries = '';
|
||||||
|
|
||||||
|
parts.forEach(function(part) {
|
||||||
|
table_entries += renderPart(part);
|
||||||
|
});
|
||||||
|
|
||||||
|
var html = '';
|
||||||
|
|
||||||
|
// Add table
|
||||||
|
html += `
|
||||||
|
<table class='table table-striped table-condensed' id='order-parts-table'>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>{% trans "Part" %}</th>
|
||||||
|
<th style='min-width: 300px;'>{% trans "Supplier Part" %}</th>
|
||||||
|
<th style='min-width: 300px;'>{% trans "Purchase Order" %}</th>
|
||||||
|
<th style='min-width: 50px;'>{% trans "Quantity" %}</th>
|
||||||
|
<th><!-- Actions --></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
${table_entries}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
`;
|
||||||
|
|
||||||
|
constructFormBody({}, {
|
||||||
|
preFormContent: html,
|
||||||
|
title: '{% trans "Order Parts" %}',
|
||||||
|
preventSubmit: true,
|
||||||
|
closeText: '{% trans "Close" %}',
|
||||||
|
afterRender: function(fields, opts) {
|
||||||
|
// TODO
|
||||||
|
parts.forEach(function(part) {
|
||||||
|
// Configure the "supplier part" field
|
||||||
|
initializeRelatedField({
|
||||||
|
name: `part_${part.pk}`,
|
||||||
|
model: 'supplierpart',
|
||||||
|
api_url: '{% url "api-supplier-part-list" %}',
|
||||||
|
required: true,
|
||||||
|
type: 'related field',
|
||||||
|
auto_fill: true,
|
||||||
|
filters: {
|
||||||
|
part: part.pk,
|
||||||
|
supplier_detail: true,
|
||||||
|
part_detail: false,
|
||||||
|
},
|
||||||
|
noResults: function(query) {
|
||||||
|
return '{% trans "No matching supplier parts" %}';
|
||||||
|
}
|
||||||
|
}, null, opts);
|
||||||
|
|
||||||
|
// Configure the "purchase order" field
|
||||||
|
initializeRelatedField({
|
||||||
|
name: `order_${part.pk}`,
|
||||||
|
model: 'purchaseorder',
|
||||||
|
api_url: '{% url "api-po-list" %}',
|
||||||
|
required: true,
|
||||||
|
type: 'related field',
|
||||||
|
auto_fill: false,
|
||||||
|
filters: {
|
||||||
|
status: {{ PurchaseOrderStatus.PENDING }},
|
||||||
|
supplier_detail: true,
|
||||||
|
},
|
||||||
|
noResults: function(query) {
|
||||||
|
return '{% trans "No matching purchase orders" %}';
|
||||||
|
}
|
||||||
|
}, null, opts);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add callback for "add to purchase order" button
|
||||||
|
$(opts.modal).find('.button-row-add').click(function() {
|
||||||
|
var pk = $(this).attr('pk');
|
||||||
|
|
||||||
|
opts.field_suffix = null;
|
||||||
|
|
||||||
|
// Extract information from the row
|
||||||
|
var data = {
|
||||||
|
quantity: getFormFieldValue(`quantity_${pk}`, {type: 'decimal'}, opts),
|
||||||
|
part: getFormFieldValue(`part_${pk}`, {}, opts),
|
||||||
|
order: getFormFieldValue(`order_${pk}`, {}, opts),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Duplicate the form options, to prevent 'field_suffix' override
|
||||||
|
var row_opts = Object.assign(opts);
|
||||||
|
row_opts.field_suffix = `_${pk}`;
|
||||||
|
|
||||||
|
inventreePut(
|
||||||
|
'{% url "api-po-line-list" %}',
|
||||||
|
data,
|
||||||
|
{
|
||||||
|
method: 'POST',
|
||||||
|
success: function(response) {
|
||||||
|
// Remove the row
|
||||||
|
$(opts.modal).find(`#order_row_${pk}`).remove();
|
||||||
|
},
|
||||||
|
error: function(xhr) {
|
||||||
|
switch (xhr.status) {
|
||||||
|
case 400:
|
||||||
|
handleFormErrors(xhr.responseJSON, fields, row_opts);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
console.error(`Error adding line to purchase order`);
|
||||||
|
showApiError(xhr, options.url);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add callback for "remove row" button
|
||||||
|
$(opts.modal).find('.button-row-remove').click(function() {
|
||||||
|
var pk = $(this).attr('pk');
|
||||||
|
|
||||||
|
$(opts.modal).find(`#order_row_${pk}`).remove();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add callback for "new supplier part" button
|
||||||
|
$(opts.modal).find('.button-row-new-sp').click(function() {
|
||||||
|
var pk = $(this).attr('pk');
|
||||||
|
|
||||||
|
// Launch dialog to create new supplier part
|
||||||
|
createSupplierPart({
|
||||||
|
part: pk,
|
||||||
|
onSuccess: function(response) {
|
||||||
|
setRelatedFieldData(
|
||||||
|
`part_${pk}`,
|
||||||
|
response,
|
||||||
|
opts
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add callback for "new purchase order" button
|
||||||
|
$(opts.modal).find('.button-row-new-po').click(function() {
|
||||||
|
var pk = $(this).attr('pk');
|
||||||
|
|
||||||
|
// Launch dialog to create new purchase order
|
||||||
|
createPurchaseOrder({
|
||||||
|
onSuccess: function(response) {
|
||||||
|
setRelatedFieldData(
|
||||||
|
`order_${pk}`,
|
||||||
|
response,
|
||||||
|
opts
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
function newPurchaseOrderFromOrderWizard(e) {
|
function newPurchaseOrderFromOrderWizard(e) {
|
||||||
/* Create a new purchase order directly from an order form.
|
/* Create a new purchase order directly from an order form.
|
||||||
* Launches a secondary modal and (if successful),
|
* Launches a secondary modal and (if successful),
|
||||||
@ -681,12 +956,14 @@ function receivePurchaseOrderItems(order_id, line_items, options={}) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
buttons += makeIconButton(
|
if (line_items.length > 1) {
|
||||||
'fa-times icon-red',
|
buttons += makeIconButton(
|
||||||
'button-row-remove',
|
'fa-times icon-red',
|
||||||
pk,
|
'button-row-remove',
|
||||||
'{% trans "Remove row" %}',
|
pk,
|
||||||
);
|
'{% trans "Remove row" %}',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
buttons += '</div>';
|
buttons += '</div>';
|
||||||
|
|
||||||
@ -1155,7 +1432,7 @@ function loadPurchaseOrderLineItemTable(table, options={}) {
|
|||||||
var line_item = $(table).bootstrapTable('getRowByUniqueId', pk);
|
var line_item = $(table).bootstrapTable('getRowByUniqueId', pk);
|
||||||
|
|
||||||
if (!line_item) {
|
if (!line_item) {
|
||||||
console.log('WARNING: getRowByUniqueId returned null');
|
console.warn('getRowByUniqueId returned null');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1414,12 +1691,12 @@ function loadPurchaseOrderExtraLineTable(table, options={}) {
|
|||||||
options.params = options.params || {};
|
options.params = options.params || {};
|
||||||
|
|
||||||
if (!options.order) {
|
if (!options.order) {
|
||||||
console.log('ERROR: function called without order ID');
|
console.error('function called without order ID');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!options.status) {
|
if (!options.status) {
|
||||||
console.log('ERROR: function called without order status');
|
console.error('function called without order status');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2541,12 +2818,12 @@ function loadSalesOrderLineItemTable(table, options={}) {
|
|||||||
options.params = options.params || {};
|
options.params = options.params || {};
|
||||||
|
|
||||||
if (!options.order) {
|
if (!options.order) {
|
||||||
console.log('ERROR: function called without order ID');
|
console.error('function called without order ID');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!options.status) {
|
if (!options.status) {
|
||||||
console.log('ERROR: function called without order status');
|
console.error('function called without order status');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3049,12 +3326,12 @@ function loadSalesOrderExtraLineTable(table, options={}) {
|
|||||||
options.params = options.params || {};
|
options.params = options.params || {};
|
||||||
|
|
||||||
if (!options.order) {
|
if (!options.order) {
|
||||||
console.log('ERROR: function called without order ID');
|
console.error('function called without order ID');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!options.status) {
|
if (!options.status) {
|
||||||
console.log('ERROR: function called without order status');
|
console.error('function called without order status');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -876,7 +876,7 @@ function loadPartPurchaseOrderTable(table, part_id, options={}) {
|
|||||||
var line_item = $(table).bootstrapTable('getRowByUniqueId', pk);
|
var line_item = $(table).bootstrapTable('getRowByUniqueId', pk);
|
||||||
|
|
||||||
if (!line_item) {
|
if (!line_item) {
|
||||||
console.log('WARNING: getRowByUniqueId returned null');
|
console.warn('getRowByUniqueId returned null');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1564,15 +1564,16 @@ function loadPartTable(table, url, options={}) {
|
|||||||
|
|
||||||
var parts = [];
|
var parts = [];
|
||||||
|
|
||||||
selections.forEach(function(item) {
|
selections.forEach(function(part) {
|
||||||
parts.push(item.pk);
|
parts.push(part);
|
||||||
});
|
});
|
||||||
|
|
||||||
launchModalForm('/order/purchase-order/order-parts/', {
|
orderParts(
|
||||||
data: {
|
parts,
|
||||||
parts: parts,
|
{
|
||||||
},
|
|
||||||
});
|
}
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
$('#multi-part-category').click(function() {
|
$('#multi-part-category').click(function() {
|
||||||
@ -1603,19 +1604,6 @@ function loadPartTable(table, url, options={}) {
|
|||||||
|
|
||||||
printPartLabels(items);
|
printPartLabels(items);
|
||||||
});
|
});
|
||||||
|
|
||||||
$('#multi-part-export').click(function() {
|
|
||||||
var selections = $(table).bootstrapTable('getSelections');
|
|
||||||
|
|
||||||
var parts = '';
|
|
||||||
|
|
||||||
selections.forEach(function(item) {
|
|
||||||
parts += item.pk;
|
|
||||||
parts += ',';
|
|
||||||
});
|
|
||||||
|
|
||||||
location.href = '/part/export/?parts=' + parts;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -39,7 +39,7 @@ function downloadTableData(table, opts={}) {
|
|||||||
var url = table_options.url;
|
var url = table_options.url;
|
||||||
|
|
||||||
if (!url) {
|
if (!url) {
|
||||||
console.log('Error: downloadTableData could not find "url" parameter.');
|
console.error('downloadTableData could not find "url" parameter.');
|
||||||
}
|
}
|
||||||
|
|
||||||
var query_params = table_options.query_params || {};
|
var query_params = table_options.query_params || {};
|
||||||
@ -343,7 +343,7 @@ $.fn.inventreeTable = function(options) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
console.log(`Could not get list of visible columns for table '${tableName}'`);
|
console.error(`Could not get list of visible columns for table '${tableName}'`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user