mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
commit
a921b3fcee
@ -11,6 +11,8 @@ from django.core import validators
|
||||
from django import forms
|
||||
from decimal import Decimal
|
||||
|
||||
from InvenTree.helpers import normalize
|
||||
|
||||
|
||||
class InvenTreeURLFormField(FormURLField):
|
||||
""" Custom URL form field with custom scheme validators """
|
||||
@ -53,7 +55,7 @@ class RoundingDecimalFormField(forms.DecimalField):
|
||||
"""
|
||||
|
||||
if type(value) == Decimal:
|
||||
return value.normalize()
|
||||
return normalize(value)
|
||||
else:
|
||||
return value
|
||||
|
||||
|
@ -8,6 +8,8 @@ import json
|
||||
import os.path
|
||||
from PIL import Image
|
||||
|
||||
from decimal import Decimal
|
||||
|
||||
from wsgiref.util import FileWrapper
|
||||
from django.http import StreamingHttpResponse
|
||||
from django.core.exceptions import ValidationError
|
||||
@ -104,6 +106,20 @@ def isNull(text):
|
||||
return str(text).strip().lower() in ['top', 'null', 'none', 'empty', 'false', '-1', '']
|
||||
|
||||
|
||||
def normalize(d):
|
||||
"""
|
||||
Normalize a decimal number, and remove exponential formatting.
|
||||
"""
|
||||
|
||||
if type(d) is not Decimal:
|
||||
d = Decimal(d)
|
||||
|
||||
d = d.normalize()
|
||||
|
||||
# Ref: https://docs.python.org/3/library/decimal.html
|
||||
return d.quantize(Decimal(1)) if d == d.to_integral() else d.normalize()
|
||||
|
||||
|
||||
def decimal2string(d):
|
||||
"""
|
||||
Format a Decimal number as a string,
|
||||
@ -117,6 +133,9 @@ def decimal2string(d):
|
||||
A string representation of the input number
|
||||
"""
|
||||
|
||||
if type(d) is Decimal:
|
||||
d = normalize(d)
|
||||
|
||||
try:
|
||||
# Ensure that the provided string can actually be converted to a float
|
||||
float(d)
|
||||
|
@ -125,6 +125,8 @@
|
||||
|
||||
.label-right {
|
||||
float: right;
|
||||
margin-left: 3px;
|
||||
margin-right: 3px;
|
||||
}
|
||||
|
||||
/* Bootstrap table overrides */
|
||||
@ -157,6 +159,66 @@
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.dropdown {
|
||||
padding-left: 1px;
|
||||
margin-left: 1px;
|
||||
}
|
||||
|
||||
/* Styles for table buttons and filtering */
|
||||
.button-toolbar .btn {
|
||||
margin-left: 1px;
|
||||
margin-right: 1px;
|
||||
}
|
||||
|
||||
.filter-list {
|
||||
display: inline-block;
|
||||
*display: inline;
|
||||
margin-bottom: 1px;
|
||||
margin-top: 1px;
|
||||
vertical-align: middle;
|
||||
margin: 1px;
|
||||
padding: 2px;
|
||||
background: #eee;
|
||||
border: 1px solid #eee;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.filter-list .close {
|
||||
cursor: pointer;
|
||||
right: 0%;
|
||||
padding-right: 2px;
|
||||
padding-left: 2px;
|
||||
transform: translate(0%, -25%);
|
||||
}
|
||||
|
||||
.filter-list .close:hover {background: #bbb;}
|
||||
|
||||
.filter-tag {
|
||||
display: inline-block;
|
||||
*display: inline;
|
||||
zoom: 1;
|
||||
padding-left: 3px;
|
||||
padding-right: 3px;
|
||||
padding-top: 2px;
|
||||
padding-bottom: 2px;
|
||||
border: 1px solid #aaa;
|
||||
border-radius: 3px;
|
||||
background: #eee;
|
||||
margin: 1px;
|
||||
margin-left: 5px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.filter-input {
|
||||
display: inline-block;
|
||||
*display: inline;
|
||||
zoom: 1;
|
||||
}
|
||||
|
||||
.filter-tag:hover {
|
||||
background: #ddd;
|
||||
}
|
||||
|
||||
/* Part image icons with full-display on mouse hover */
|
||||
|
||||
.hover-img-thumb {
|
||||
|
@ -3272,10 +3272,7 @@
|
||||
}, {
|
||||
key: 'getOptions',
|
||||
value: function getOptions() {
|
||||
// deep copy and remove data
|
||||
var options = JSON.parse(JSON.stringify(this.options));
|
||||
delete options.data;
|
||||
return options;
|
||||
return this.options;
|
||||
}
|
||||
}, {
|
||||
key: 'getSelections',
|
||||
|
@ -133,11 +133,11 @@ function loadBomTable(table, options) {
|
||||
title: 'Part',
|
||||
sortable: true,
|
||||
formatter: function(value, row, index, field) {
|
||||
var html = imageHoverIcon(row.sub_part_detail.image_url) + renderLink(row.sub_part_detail.full_name, row.sub_part_detail.url);
|
||||
var html = imageHoverIcon(row.sub_part_detail.thumbnail) + renderLink(row.sub_part_detail.full_name, row.sub_part_detail.url);
|
||||
|
||||
// Display an extra icon if this part is an assembly
|
||||
if (row.sub_part_detail.assembly) {
|
||||
html += "<a href='" + row.sub_part_detail.url + "bom'><span class='glyphicon-right glyphicon glyphicon-th-list'></span></a>";
|
||||
html += "<a href='" + row.sub_part_detail.url + "bom'><span title='Open subassembly' class='fas fa-stream label-right'></span></a>";
|
||||
}
|
||||
|
||||
return html;
|
||||
|
@ -1,3 +1,77 @@
|
||||
function loadBuildTable(table, options) {
|
||||
|
||||
var params = options.params || {};
|
||||
|
||||
var filters = loadTableFilters("build");
|
||||
|
||||
for (var key in params) {
|
||||
filters[key] = params[key];
|
||||
}
|
||||
|
||||
setupFilterList("build", table);
|
||||
|
||||
table.inventreeTable({
|
||||
method: 'get',
|
||||
formatNoMatches: function() {
|
||||
return "No builds matching query";
|
||||
},
|
||||
url: options.url,
|
||||
queryParams: filters,
|
||||
groupBy: false,
|
||||
original: params,
|
||||
columns: [
|
||||
{
|
||||
field: 'pk',
|
||||
title: 'ID',
|
||||
visible: false,
|
||||
},
|
||||
{
|
||||
field: 'title',
|
||||
title: 'Build',
|
||||
sortable: true,
|
||||
formatter: function(value, row, index, field) {
|
||||
return renderLink(value, '/build/' + row.pk + '/');
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'part',
|
||||
title: 'Part',
|
||||
sortable: true,
|
||||
formatter: function(value, row, index, field) {
|
||||
|
||||
var name = row.part_detail.full_name;
|
||||
|
||||
return imageHoverIcon(row.part_detail.thumbnail) + renderLink(name, '/part/' + row.part + '/');
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'quantity',
|
||||
title: 'Quantity',
|
||||
sortable: true,
|
||||
},
|
||||
{
|
||||
field: 'status',
|
||||
title: 'Status',
|
||||
sortable: true,
|
||||
formatter: function(value, row, index, field) {
|
||||
return buildStatusDisplay(value);
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'creation_date',
|
||||
title: 'Created',
|
||||
sortable: true,
|
||||
},
|
||||
{
|
||||
field: 'completion_date',
|
||||
title: 'Completed',
|
||||
sortable: true,
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function updateAllocationTotal(id, count, required) {
|
||||
|
||||
count = parseFloat(count);
|
||||
|
410
InvenTree/InvenTree/static/script/inventree/filters.js
Normal file
410
InvenTree/InvenTree/static/script/inventree/filters.js
Normal file
@ -0,0 +1,410 @@
|
||||
/**
|
||||
* Code for managing query filters / table options.
|
||||
*
|
||||
* Optional query filters are available to the user for various
|
||||
* tables display in the web interface.
|
||||
* These filters are saved to the web session, and should be
|
||||
* persistent for a given table type.
|
||||
*
|
||||
* This makes use of the 'inventreeSave' and 'inventreeLoad' functions
|
||||
* for writing to and reading from session storage.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
function defaultFilters() {
|
||||
return {
|
||||
stock: "cascade=1",
|
||||
build: "",
|
||||
parts: "cascade=1",
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Load table filters for the given table from session storage
|
||||
*
|
||||
* @param tableKey - String key for the particular table
|
||||
* @param defaults - Default filters for this table e.g. 'cascade=1&location=5'
|
||||
*/
|
||||
function loadTableFilters(tableKey) {
|
||||
|
||||
var lookup = "table-filters-" + tableKey.toLowerCase();
|
||||
|
||||
var defaults = defaultFilters()[tableKey] || '';
|
||||
|
||||
var filterstring = inventreeLoad(lookup, defaults);
|
||||
|
||||
var filters = {};
|
||||
|
||||
filterstring.split("&").forEach(function(item, index) {
|
||||
item = item.trim();
|
||||
|
||||
if (item.length > 0) {
|
||||
var f = item.split('=');
|
||||
|
||||
if (f.length == 2) {
|
||||
filters[f[0]] = f[1];
|
||||
} else {
|
||||
console.log(`Improperly formatted filter: ${item}`);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return filters;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Save table filters to session storage
|
||||
*
|
||||
* @param {*} tableKey - string key for the given table
|
||||
* @param {*} filters - object of string:string pairs
|
||||
*/
|
||||
function saveTableFilters(tableKey, filters) {
|
||||
var lookup = "table-filters-" + tableKey.toLowerCase();
|
||||
|
||||
var strings = [];
|
||||
|
||||
for (var key in filters) {
|
||||
strings.push(`${key.trim()}=${String(filters[key]).trim()}`);
|
||||
}
|
||||
|
||||
var filterstring = strings.join('&');
|
||||
|
||||
console.log(`Saving filters for table '${tableKey}' - ${filterstring}`);
|
||||
|
||||
inventreeSave(lookup, filterstring);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Remove a named filter parameter
|
||||
*/
|
||||
function removeTableFilter(tableKey, filterKey) {
|
||||
|
||||
var filters = loadTableFilters(tableKey);
|
||||
|
||||
delete filters[filterKey];
|
||||
|
||||
saveTableFilters(tableKey, filters);
|
||||
|
||||
// Return a copy of the updated filters
|
||||
return filters;
|
||||
}
|
||||
|
||||
|
||||
function addTableFilter(tableKey, filterKey, filterValue) {
|
||||
|
||||
var filters = loadTableFilters(tableKey);
|
||||
|
||||
filters[filterKey] = filterValue;
|
||||
|
||||
saveTableFilters(tableKey, filters);
|
||||
|
||||
// Return a copy of the updated filters
|
||||
return filters;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Clear all the custom filters for a given table
|
||||
*/
|
||||
function clearTableFilters(tableKey) {
|
||||
saveTableFilters(tableKey, {});
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Return a list of the "available" filters for a given table key.
|
||||
* A filter is "available" if it is not already being used to filter the table.
|
||||
* Once a filter is selected, it will not be returned here.
|
||||
*/
|
||||
function getRemainingTableFilters(tableKey) {
|
||||
|
||||
var filters = loadTableFilters(tableKey);
|
||||
|
||||
var remaining = getAvailableTableFilters(tableKey);
|
||||
|
||||
for (var key in filters) {
|
||||
// Delete the filter if it is already in use
|
||||
delete remaining[key];
|
||||
}
|
||||
|
||||
return remaining;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Return the filter settings for a given table and key combination.
|
||||
* Return empty object if the combination does not exist.
|
||||
*/
|
||||
function getFilterSettings(tableKey, filterKey) {
|
||||
|
||||
return getAvailableTableFilters(tableKey)[filterKey] || {};
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Return a set of key:value options for the given filter.
|
||||
* If no options are specified (e.g. for a number field),
|
||||
* then a null object is returned.
|
||||
*/
|
||||
function getFilterOptionList(tableKey, filterKey) {
|
||||
|
||||
var settings = getFilterSettings(tableKey, filterKey);
|
||||
|
||||
if (settings.type == 'bool') {
|
||||
return {
|
||||
'1': {
|
||||
key: '1',
|
||||
value: 'true',
|
||||
},
|
||||
'0': {
|
||||
key: '0',
|
||||
value: 'false',
|
||||
},
|
||||
};
|
||||
} else if ('options' in settings) {
|
||||
return settings.options;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Generate a list of <option> tags for the given table.
|
||||
*/
|
||||
function generateAvailableFilterList(tableKey) {
|
||||
|
||||
var remaining = getRemainingTableFilters(tableKey);
|
||||
|
||||
var id = 'filter-tag-' + tableKey.toLowerCase();
|
||||
|
||||
var html = `<select class='form-control filter-input' id='${id}' name='tag'>`;
|
||||
|
||||
html += `<option value=''>Select filter</option>`;
|
||||
|
||||
for (var opt in remaining) {
|
||||
var title = getFilterTitle(tableKey, opt);
|
||||
html += `<option value='${opt}'>${title}</option>`;
|
||||
}
|
||||
|
||||
html += `</select>`;
|
||||
|
||||
return html;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Generate an input for setting the value of a given filter.
|
||||
*/
|
||||
function generateFilterInput(tableKey, filterKey) {
|
||||
|
||||
var id = 'filter-value-' + tableKey.toLowerCase();
|
||||
|
||||
if (filterKey == null || filterKey.length == 0) {
|
||||
// Return an 'empty' element
|
||||
return `<div class='filter-input' id='${id}'></div>`;
|
||||
}
|
||||
|
||||
var options = getFilterOptionList(tableKey, filterKey);
|
||||
|
||||
var html = '';
|
||||
|
||||
// A 'null' options list means that a simple text-input dialog should be used
|
||||
if (options == null) {
|
||||
html = `<input class='form-control filter-input' id='${id}' name='value'></input>`;
|
||||
} else {
|
||||
// Return a 'select' input with the available values
|
||||
html = `<select class='form-control filter-input' id='${id}' name='value'>`;
|
||||
|
||||
for (var key in options) {
|
||||
option = options[key];
|
||||
html += `<option value='${key}'>${option.value}</option>`;
|
||||
}
|
||||
|
||||
html += `</select>`;
|
||||
}
|
||||
|
||||
return html;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Configure a filter list for a given table
|
||||
*
|
||||
* @param {*} tableKey - string lookup key for filter settings
|
||||
* @param {*} table - bootstrapTable element to update
|
||||
* @param {*} target - name of target element on page
|
||||
*/
|
||||
function setupFilterList(tableKey, table, target) {
|
||||
|
||||
var addClicked = false;
|
||||
|
||||
if (target == null || target.length == 0) {
|
||||
target = `#filter-list-${tableKey}`;
|
||||
}
|
||||
|
||||
var tag = `filter-tag-${tableKey}`;
|
||||
var add = `filter-add-${tableKey}`;
|
||||
var clear = `filter-clear-${tableKey}`;
|
||||
var make = `filter-make-${tableKey}`;
|
||||
|
||||
console.log(`Generating filter list: ${tableKey}`);
|
||||
|
||||
var filters = loadTableFilters(tableKey);
|
||||
|
||||
console.log("Filters: " + filters.count);
|
||||
|
||||
var element = $(target);
|
||||
|
||||
// One blank slate, please
|
||||
element.empty();
|
||||
|
||||
element.append(`<button id='${add}' title='Add new filter' class='btn btn-default filter-tag'><span class='fas fa-filter'></span></button>`);
|
||||
|
||||
if (Object.keys(filters).length > 0) {
|
||||
element.append(`<button id='${clear}' title='Clear all filters' class='btn btn-default filter-tag'><span class='fas fa-trash-alt'></span></button>`);
|
||||
}
|
||||
|
||||
for (var key in filters) {
|
||||
var value = getFilterOptionValue(tableKey, key, filters[key]);
|
||||
var title = getFilterTitle(tableKey, key);
|
||||
|
||||
element.append(`<div class='filter-tag'>${title} = ${value}<span ${tag}='${key}' class='close'>x</span></div>`);
|
||||
}
|
||||
|
||||
// Add a callback for adding a new filter
|
||||
element.find(`#${add}`).click(function clicked() {
|
||||
|
||||
if (!addClicked) {
|
||||
|
||||
addClicked = true;
|
||||
|
||||
var html = '';
|
||||
|
||||
//`<div class='filter-input'>`;
|
||||
|
||||
html += generateAvailableFilterList(tableKey);
|
||||
html += generateFilterInput(tableKey);
|
||||
|
||||
html += `<button title='Create filter' class='btn btn-default filter-tag' id='${make}'><span class='fas fa-plus'></span></button>`;
|
||||
|
||||
//html += '</div>';
|
||||
|
||||
element.append(html);
|
||||
|
||||
// Add a callback for when the filter tag selection is changed
|
||||
element.find(`#filter-tag-${tableKey}`).on('change', function() {
|
||||
var list = element.find(`#filter-value-${tableKey}`);
|
||||
|
||||
list.replaceWith(generateFilterInput(tableKey, this.value));
|
||||
});
|
||||
|
||||
// Add a callback for when the new filter is created
|
||||
element.find(`#filter-make-${tableKey}`).click(function() {
|
||||
var tag = element.find(`#filter-tag-${tableKey}`).val();
|
||||
var val = element.find(`#filter-value-${tableKey}`).val();
|
||||
|
||||
// Only add the new filter if it is not empty!
|
||||
if (tag && tag.length > 0) {
|
||||
var filters = addTableFilter(tableKey, tag, val);
|
||||
reloadTable(table, filters);
|
||||
|
||||
// Run this function again
|
||||
setupFilterList(tableKey, table, target);
|
||||
}
|
||||
|
||||
});
|
||||
} else {
|
||||
addClicked = false;
|
||||
|
||||
setupFilterList(tableKey, table, target);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
// Add a callback for clearing all the filters
|
||||
element.find(`#${clear}`).click(function() {
|
||||
var filters = clearTableFilters(tableKey);
|
||||
|
||||
reloadTable(table, filters);
|
||||
|
||||
setupFilterList(tableKey, table, target);
|
||||
});
|
||||
|
||||
// Add callback for deleting each filter
|
||||
element.find(".close").click(function(event) {
|
||||
var me = $(this);
|
||||
|
||||
var filter = me.attr(`filter-tag-${tableKey}`);
|
||||
|
||||
var filters = removeTableFilter(tableKey, filter);
|
||||
|
||||
reloadTable(table, filters);
|
||||
|
||||
// Run this function again!
|
||||
setupFilterList(tableKey, table, target);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the pretty title for the given table and filter selection.
|
||||
* If no title is provided, default to the key value.
|
||||
*
|
||||
*/
|
||||
function getFilterTitle(tableKey, filterKey) {
|
||||
var settings = getFilterSettings(tableKey, filterKey);
|
||||
return settings.title || filterKey;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Return a description for the given table and filter selection.
|
||||
*/
|
||||
function getFilterDescription(tableKey, filterKey) {
|
||||
var settings = getFilterSettings(tableKey, filterKey);
|
||||
return settings.description || filterKey;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Return the display value for a particular option
|
||||
*/
|
||||
function getFilterOptionValue(tableKey, filterKey, valueKey) {
|
||||
|
||||
var filter = getFilterSettings(tableKey, filterKey);
|
||||
|
||||
var value = String(valueKey);
|
||||
|
||||
// Lookup for boolean options
|
||||
if (filter.type == 'bool') {
|
||||
if (value == '1') return 'true';
|
||||
if (value == '0') return 'false';
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
// Iterate through a list of options
|
||||
if ('options' in filter) {
|
||||
for (var key in filter.options) {
|
||||
|
||||
if (key == valueKey) {
|
||||
return filter.options[key].value;
|
||||
}
|
||||
}
|
||||
|
||||
// Could not find a match
|
||||
return value;
|
||||
}
|
||||
|
||||
// Cannot map to a display string - return the original text
|
||||
return value;
|
||||
}
|
||||
|
@ -104,8 +104,21 @@ function removePurchaseOrderLineItem(e) {
|
||||
function loadPurchaseOrderTable(table, options) {
|
||||
/* Create a purchase-order table */
|
||||
|
||||
var params = options.params || {};
|
||||
|
||||
var filters = loadTableFilters("order");
|
||||
|
||||
for (var key in params) {
|
||||
filters[key] = params[key];
|
||||
}
|
||||
|
||||
setupFilterList("order", table);
|
||||
|
||||
table.inventreeTable({
|
||||
url: options.url,
|
||||
queryParams: filters,
|
||||
groupBy: false,
|
||||
original: params,
|
||||
formatNoMatches: function() { return "No purchase orders found"; },
|
||||
columns: [
|
||||
{
|
||||
@ -144,7 +157,7 @@ function loadPurchaseOrderTable(table, options) {
|
||||
field: 'status',
|
||||
title: 'Status',
|
||||
formatter: function(value, row, index, field) {
|
||||
return orderStatusLabel(row.status, row.status_text);
|
||||
return orderStatusDisplay(row.status, row.status_text);
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -155,39 +168,3 @@ function loadPurchaseOrderTable(table, options) {
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function orderStatusLabel(code, label) {
|
||||
/* Render a purchase-order status label. */
|
||||
|
||||
var html = "<span class='label";
|
||||
|
||||
switch (code) {
|
||||
case 10: // pending
|
||||
html += " label-info";
|
||||
break;
|
||||
case 20: // placed
|
||||
html += " label-primary";
|
||||
break;
|
||||
case 30: // complete
|
||||
html += " label-success";
|
||||
break;
|
||||
case 40: // cancelled
|
||||
case 50: // lost
|
||||
html += " label-warning";
|
||||
break;
|
||||
case 60: // returned
|
||||
html += " label-danger";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
html += "'>";
|
||||
html += label;
|
||||
html += "</span>";
|
||||
|
||||
console.log(html);
|
||||
|
||||
return html;
|
||||
}
|
@ -87,17 +87,15 @@ function loadPartTable(table, url, options={}) {
|
||||
* buttons: If provided, link buttons to selection status of this table
|
||||
*/
|
||||
|
||||
// Default query params
|
||||
query = options.query;
|
||||
var params = options.parms || {};
|
||||
|
||||
if (!options.allowInactive) {
|
||||
// Only display active parts
|
||||
query.active = true;
|
||||
var filters = loadTableFilters("parts");
|
||||
|
||||
for (var key in params) {
|
||||
filters[key] = params[key];
|
||||
}
|
||||
|
||||
// Include sub-category search
|
||||
// TODO - Make this user-configurable!
|
||||
query.cascade = true;
|
||||
setupFilterList("parts", $(table));
|
||||
|
||||
var columns = [
|
||||
{
|
||||
@ -142,11 +140,21 @@ function loadPartTable(table, url, options={}) {
|
||||
var display = imageHoverIcon(row.thumbnail) + renderLink(name, '/part/' + row.pk + '/');
|
||||
|
||||
if (row.is_template) {
|
||||
display = display + "<span class='label label-info' style='float: right;'>TEMPLATE</span>";
|
||||
display += `<span class='fas fa-clone label-right' title='Template part'></span>`;
|
||||
}
|
||||
|
||||
if (row.assembly) {
|
||||
display += `<span class='fas fa-tools label-right' title='Assembled part'></span>`;
|
||||
}
|
||||
|
||||
/*
|
||||
if (row.component) {
|
||||
display = display + `<span class='fas fa-cogs label-right' title='Component part'></span>`;
|
||||
}
|
||||
*/
|
||||
|
||||
if (!row.active) {
|
||||
display = display + "<span class='label label-warning' style='float: right;'>INACTIVE</span>";
|
||||
display += `<span class='label label-warning label-right'>INACTIVE</span>`;
|
||||
}
|
||||
return display;
|
||||
}
|
||||
@ -175,7 +183,7 @@ function loadPartTable(table, url, options={}) {
|
||||
return renderLink(row.category__name, "/part/category/" + row.category + "/");
|
||||
}
|
||||
else {
|
||||
return '';
|
||||
return 'No category';
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -217,10 +225,10 @@ function loadPartTable(table, url, options={}) {
|
||||
url: url,
|
||||
sortName: 'name',
|
||||
method: 'get',
|
||||
queryParams: filters,
|
||||
groupBy: false,
|
||||
original: params,
|
||||
formatNoMatches: function() { return "No parts found"; },
|
||||
queryParams: function(p) {
|
||||
return query;
|
||||
},
|
||||
columns: columns,
|
||||
});
|
||||
|
||||
|
@ -14,6 +14,7 @@ function getStockLocations(filters={}, options={}) {
|
||||
return inventreeGet('/api/stock/location/', filters, options)
|
||||
}
|
||||
|
||||
|
||||
/* Functions for interacting with stock management forms
|
||||
*/
|
||||
|
||||
@ -28,6 +29,7 @@ function removeStockRow(e) {
|
||||
$('#' + row).remove();
|
||||
}
|
||||
|
||||
|
||||
function loadStockTable(table, options) {
|
||||
/* Load data into a stock table with adjustable options.
|
||||
* Fetches data (via AJAX) and loads into a bootstrap table.
|
||||
@ -38,23 +40,39 @@ function loadStockTable(table, options) {
|
||||
* params - query params for augmenting stock data request
|
||||
* groupByField - Column for grouping stock items
|
||||
* buttons - Which buttons to link to stock selection callbacks
|
||||
* filterList - <ul> element where filters are displayed
|
||||
*/
|
||||
|
||||
// List of user-params which override the default filters
|
||||
var params = options.params || {};
|
||||
|
||||
// Enforce 'cascade' option
|
||||
// TODO - Make this user-configurable?
|
||||
params.cascade = true;
|
||||
var filterListElement = options.filterList || "#filter-list-stock";
|
||||
|
||||
console.log('load stock table');
|
||||
var filters = loadTableFilters("stock");
|
||||
|
||||
var original = {};
|
||||
|
||||
for (var key in params) {
|
||||
original[key] = params[key];
|
||||
}
|
||||
|
||||
setupFilterList("stock", table, filterListElement);
|
||||
|
||||
// Override the default values, or add new ones
|
||||
for (var key in params) {
|
||||
filters[key] = params[key];
|
||||
}
|
||||
|
||||
table.inventreeTable({
|
||||
method: 'get',
|
||||
formatNoMatches: function() {
|
||||
return 'No stock items matching query';
|
||||
},
|
||||
url: options.url,
|
||||
queryParams: filters,
|
||||
customSort: customGroupSorter,
|
||||
groupBy: true,
|
||||
original: original,
|
||||
groupByField: options.groupByField || 'part',
|
||||
groupByFormatter: function(field, id, data) {
|
||||
|
||||
@ -87,6 +105,29 @@ function loadStockTable(table, options) {
|
||||
stock = +stock.toFixed(5);
|
||||
|
||||
return stock + " (" + items + " items)";
|
||||
} else if (field == 'status') {
|
||||
var statii = [];
|
||||
|
||||
data.forEach(function(item) {
|
||||
var status = String(item.status);
|
||||
|
||||
if (!status || status == '') {
|
||||
status = '-';
|
||||
}
|
||||
|
||||
if (!statii.includes(status)) {
|
||||
statii.push(status);
|
||||
}
|
||||
});
|
||||
|
||||
// Multiple status codes
|
||||
if (statii.length > 1) {
|
||||
return "-";
|
||||
} else if (statii.length == 1) {
|
||||
return stockStatusDisplay(statii[0]);
|
||||
} else {
|
||||
return "-";
|
||||
}
|
||||
} else if (field == 'batch') {
|
||||
var batches = [];
|
||||
|
||||
@ -211,13 +252,17 @@ function loadStockTable(table, options) {
|
||||
|
||||
var text = renderLink(val, '/stock/item/' + row.pk + '/');
|
||||
|
||||
if (row.status_text != 'OK') {
|
||||
text = text + "<span class='badge'>" + row.status_text + "</span>";
|
||||
}
|
||||
|
||||
return text;
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'status',
|
||||
title: 'Status',
|
||||
sortable: 'true',
|
||||
formatter: function(value, row, index, field) {
|
||||
return stockStatusDisplay(value);
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'batch',
|
||||
title: 'Batch',
|
||||
@ -241,8 +286,6 @@ function loadStockTable(table, options) {
|
||||
title: 'Notes',
|
||||
}
|
||||
],
|
||||
url: options.url,
|
||||
queryParams: params,
|
||||
});
|
||||
|
||||
if (options.buttons) {
|
||||
|
@ -44,6 +44,42 @@ function isNumeric(n) {
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Reload a table which has already been made into a bootstrap table.
|
||||
* New filters can be optionally provided, to change the query params.
|
||||
*/
|
||||
function reloadTable(table, filters) {
|
||||
|
||||
// Simply perform a refresh
|
||||
if (filters == null) {
|
||||
table.bootstrapTable('refresh');
|
||||
return;
|
||||
}
|
||||
|
||||
// More complex refresh with new filters supplied
|
||||
var options = table.bootstrapTable('getOptions');
|
||||
|
||||
// Construct a new list of filters to use for the query
|
||||
var params = {};
|
||||
|
||||
for (var key in filters) {
|
||||
params[key] = filters[key];
|
||||
}
|
||||
|
||||
// Original query params will override
|
||||
if (options.original != null) {
|
||||
for (var key in options.original) {
|
||||
params[key] = options.original[key];
|
||||
}
|
||||
}
|
||||
|
||||
options.queryParams = params;
|
||||
|
||||
table.bootstrapTable('refreshOptions', options);
|
||||
table.bootstrapTable('refresh');
|
||||
}
|
||||
|
||||
|
||||
/* Wrapper function for bootstrapTable.
|
||||
* Sets some useful defaults, and manage persistent settings.
|
||||
*/
|
||||
|
@ -2,6 +2,56 @@ from django.utils.translation import ugettext as _
|
||||
|
||||
|
||||
class StatusCode:
|
||||
"""
|
||||
Base class for representing a set of StatusCodes.
|
||||
This is used to map a set of integer values to text.
|
||||
"""
|
||||
|
||||
labels = {}
|
||||
|
||||
@classmethod
|
||||
def render(cls, key):
|
||||
"""
|
||||
Render the value as a label.
|
||||
"""
|
||||
|
||||
print("Rendering:", key, cls.options)
|
||||
|
||||
# If the key cannot be found, pass it back
|
||||
if key not in cls.options.keys():
|
||||
return key
|
||||
|
||||
value = cls.options.get(key, key)
|
||||
label = cls.labels.get(key, None)
|
||||
|
||||
if label:
|
||||
return "<span class='label label-{label}'>{value}</span>".format(label=label, value=value)
|
||||
else:
|
||||
return value
|
||||
|
||||
@classmethod
|
||||
def list(cls):
|
||||
"""
|
||||
Return the StatusCode options as a list of mapped key / value items
|
||||
"""
|
||||
|
||||
codes = []
|
||||
|
||||
for key in cls.options.keys():
|
||||
|
||||
opt = {
|
||||
'key': key,
|
||||
'value': cls.options[key]
|
||||
}
|
||||
|
||||
label = cls.labels.get(key)
|
||||
|
||||
if label:
|
||||
opt['label'] = label
|
||||
|
||||
codes.append(opt)
|
||||
|
||||
return codes
|
||||
|
||||
@classmethod
|
||||
def items(cls):
|
||||
@ -41,6 +91,15 @@ class OrderStatus(StatusCode):
|
||||
RETURNED: _("Returned"),
|
||||
}
|
||||
|
||||
labels = {
|
||||
PENDING: "primary",
|
||||
PLACED: "primary",
|
||||
COMPLETE: "success",
|
||||
CANCELLED: "danger",
|
||||
LOST: "warning",
|
||||
RETURNED: "warning",
|
||||
}
|
||||
|
||||
# Open orders
|
||||
OPEN = [
|
||||
PENDING,
|
||||
@ -71,6 +130,12 @@ class StockStatus(StatusCode):
|
||||
LOST: _("Lost"),
|
||||
}
|
||||
|
||||
labels = {
|
||||
OK: 'success',
|
||||
ATTENTION: 'warning',
|
||||
DAMAGED: 'danger',
|
||||
}
|
||||
|
||||
# The following codes correspond to parts that are 'available' or 'in stock'
|
||||
AVAILABLE_CODES = [
|
||||
OK,
|
||||
@ -100,6 +165,13 @@ class BuildStatus(StatusCode):
|
||||
COMPLETE: _("Complete"),
|
||||
}
|
||||
|
||||
labels = {
|
||||
PENDING: 'primary',
|
||||
ALLOCATED: 'info',
|
||||
COMPLETE: 'success',
|
||||
CANCELLED: 'danger',
|
||||
}
|
||||
|
||||
ACTIVE_CODES = [
|
||||
PENDING,
|
||||
ALLOCATED
|
||||
|
@ -11,6 +11,8 @@ from rest_framework import generics, permissions
|
||||
|
||||
from django.conf.urls import url, include
|
||||
|
||||
from InvenTree.helpers import str2bool
|
||||
|
||||
from .models import Build, BuildItem
|
||||
from .serializers import BuildSerializer, BuildItemSerializer
|
||||
|
||||
@ -36,9 +38,41 @@ class BuildList(generics.ListCreateAPIView):
|
||||
]
|
||||
|
||||
filter_fields = [
|
||||
'part',
|
||||
]
|
||||
|
||||
def get_queryset(self):
|
||||
"""
|
||||
Override the queryset filtering,
|
||||
as some of the fields don't natively play nicely with DRF
|
||||
"""
|
||||
|
||||
build_list = super().get_queryset()
|
||||
|
||||
# Filter by part
|
||||
part = self.request.query_params.get('part', None)
|
||||
|
||||
if part is not None:
|
||||
build_list = build_list.filter(part=part)
|
||||
|
||||
# Filter by build status?
|
||||
status = self.request.query_params.get('status', None)
|
||||
|
||||
if status is not None:
|
||||
build_list = build_list.filter(status=status)
|
||||
|
||||
return build_list
|
||||
|
||||
def get_serializer(self, *args, **kwargs):
|
||||
|
||||
try:
|
||||
part_detail = str2bool(self.request.GET.get('part_detail', None))
|
||||
except AttributeError:
|
||||
part_detail = None
|
||||
|
||||
kwargs['part_detail'] = part_detail
|
||||
|
||||
return self.serializer_class(*args, **kwargs)
|
||||
|
||||
|
||||
class BuildDetail(generics.RetrieveUpdateAPIView):
|
||||
""" API endpoint for detail view of a Build object """
|
||||
|
@ -10,6 +10,7 @@ from InvenTree.serializers import InvenTreeModelSerializer
|
||||
from stock.serializers import StockItemSerializerBrief
|
||||
|
||||
from .models import Build, BuildItem
|
||||
from part.serializers import PartBriefSerializer
|
||||
|
||||
|
||||
class BuildSerializer(InvenTreeModelSerializer):
|
||||
@ -18,6 +19,16 @@ class BuildSerializer(InvenTreeModelSerializer):
|
||||
url = serializers.CharField(source='get_absolute_url', read_only=True)
|
||||
status_text = serializers.CharField(source='get_status_display', read_only=True)
|
||||
|
||||
part_detail = PartBriefSerializer(source='part', many=False, read_only=True)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
part_detail = kwargs.pop('part_detail', False)
|
||||
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
if part_detail is not True:
|
||||
self.fields.pop('part_detail')
|
||||
|
||||
class Meta:
|
||||
model = Build
|
||||
fields = [
|
||||
@ -27,6 +38,7 @@ class BuildSerializer(InvenTreeModelSerializer):
|
||||
'creation_date',
|
||||
'completion_date',
|
||||
'part',
|
||||
'part_detail',
|
||||
'quantity',
|
||||
'status',
|
||||
'status_text',
|
||||
|
@ -1,6 +1,8 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% load static %}
|
||||
{% load i18n %}
|
||||
{% load status_codes %}
|
||||
|
||||
{% block page_title %}
|
||||
InvenTree | Build - {{ build }}
|
||||
@ -22,9 +24,7 @@ InvenTree | Build - {{ build }}
|
||||
</div>
|
||||
</div>
|
||||
<div class='media-body'>
|
||||
<h4>Build Details</h4>
|
||||
|
||||
<p>
|
||||
<h4>{% trans "Build" %}</h4>
|
||||
<div class='btn-row'>
|
||||
<div class='btn-group'>
|
||||
<button type='button' class='btn btn-default btn-glyph' id='build-edit' title='Edit Build'>
|
||||
@ -45,38 +45,49 @@ InvenTree | Build - {{ build }}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</p>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class='col-sm-6'>
|
||||
<h4>{% trans "Build Details" %}</h4>
|
||||
<table class='table table-striped table-condensed'>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td>{% trans "Build Title" %}</td>
|
||||
<td>{{ build.title }}</td>
|
||||
<td>{% include "build_status.html" with build=build %}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><span class='fas fa-shapes'></span></td>
|
||||
<td>Part</td>
|
||||
<td><a href="{% url 'part-detail' build.part.id %}">{{ build.part.full_name }}</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Quantity</td>
|
||||
<td></td>
|
||||
<td>{% trans "Quantity" %}</td>
|
||||
<td>{{ build.quantity }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>BOM Price</td>
|
||||
<td><span class='fas fa-info'></span></td>
|
||||
<td>{% trans "Status" %}</td>
|
||||
<td>{% build_status build.status %}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><span class='fas fa-dollar-sign'></span></td>
|
||||
<td>{% trans "BOM Price" %}</td>
|
||||
<td>
|
||||
{% if bom_price %}
|
||||
{{ bom_price }}
|
||||
{% if build.part.has_complete_bom_pricing == False %}
|
||||
<br><span class='warning-msg'><i>BOM pricing is incomplete</i></span>
|
||||
<br><span class='warning-msg'><i>{% trans "BOM pricing is incomplete" %}</i></span>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<span class='warning-msg'><i>No pricing information</i></span>
|
||||
<span class='warning-msg'><i>{% trans "No pricing information" %}</i></span>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
@ -1,38 +0,0 @@
|
||||
{% extends "collapse.html" %}
|
||||
|
||||
{% block collapse_title %}
|
||||
<b>{{ title }}</b> - {{ builds | length }}
|
||||
{% endblock %}
|
||||
|
||||
{% block collapse_content %}
|
||||
<table class='table table-striped table-condensed build-table' id='build-table-{{collapse_id}}' data-toolbar='#button-toolbar'>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Build</th>
|
||||
<th>Part</th>
|
||||
<th>Quantity</th>
|
||||
<th>Status</th>
|
||||
{% if completed %}
|
||||
<th>Completed</th>
|
||||
{% else %}
|
||||
<th>Created</th>
|
||||
{% endif %}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for build in builds %}
|
||||
<tr>
|
||||
<td><a href="{% url 'build-detail' build.id %}">{{ build.title }}</a></td>
|
||||
<td><a href="{% url 'part-build' build.part.id %}">{{ build.part.full_name }}</a></td>
|
||||
<td>{{ build.quantity }}</td>
|
||||
<td>{% include "build_status.html" with build=build %}
|
||||
{% if completed %}
|
||||
<td>{{ build.completion_date }}<span class='badge'>{{ build.completed_by.username }}</span></td>
|
||||
{% else %}
|
||||
<td>{{ build.creation_date }}</td>
|
||||
{% endif %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% endblock %}
|
@ -2,6 +2,7 @@
|
||||
{% load static %}
|
||||
{% load i18n %}
|
||||
{% block details %}
|
||||
{% load status_codes %}
|
||||
|
||||
{% include "build/tabs.html" with tab='details' %}
|
||||
|
||||
@ -39,7 +40,7 @@
|
||||
<tr>
|
||||
<td><span class='fas fa-info'></span></td>
|
||||
<td>{% trans "Status" %}</td>
|
||||
<td>{% include "build_status.html" with build=build %}</td>
|
||||
<td>{% build_status build.status %}</td>
|
||||
</tr>
|
||||
{% if build.batch %}
|
||||
<tr>
|
||||
|
@ -13,21 +13,23 @@ InvenTree | Build List
|
||||
<h3>Part Builds</h3>
|
||||
</div>
|
||||
<div class='col-sm-6'>
|
||||
<div class='container' id='active-build-toolbar' style='float: right;'>
|
||||
<div class='btn-group' style='float: right;'>
|
||||
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<div id='button-toolbar'>
|
||||
<div class='button-toolbar container-fluid' style='float: right;'>
|
||||
<button type='button' class="btn btn-success" id='new-build'>Start New Build</button>
|
||||
<div class='filter-list' id='filter-list-build'>
|
||||
<!-- An empty div in which the filter list will be constructed -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
{% include "build/build_list.html" with builds=active title="Active Builds" completed=False collapse_id='active' %}
|
||||
|
||||
{% include "build/build_list.html" with builds=completed completed=True title="Completed Builds" collapse_id="complete" %}
|
||||
|
||||
{% include "build/build_list.html" with builds=cancelled title="Cancelled Builds" completed=False collapse_id="cancelled" %}
|
||||
<table class='table table-striped table-condensed' id='build-table' data-toolbar='#button-toolbar'>
|
||||
</table>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
@ -41,35 +43,15 @@ InvenTree | Build List
|
||||
"{% url 'build-create' %}",
|
||||
{
|
||||
follow: true
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
$(".build-table").inventreeTable({
|
||||
formatNoMatches: function() { return 'No builds found'; },
|
||||
columns: [
|
||||
{
|
||||
field: 'name',
|
||||
title: 'Build',
|
||||
sortable: true,
|
||||
loadBuildTable($("#build-table"), {
|
||||
url: "{% url 'api-build-list' %}",
|
||||
params: {
|
||||
part_detail: "true",
|
||||
},
|
||||
{
|
||||
field: 'part',
|
||||
title: 'Part',
|
||||
sortable: true,
|
||||
},
|
||||
{
|
||||
title: 'Quantity',
|
||||
sortable: true,
|
||||
searchable: false
|
||||
},
|
||||
{
|
||||
title: 'Status',
|
||||
sortable: true,
|
||||
},
|
||||
{
|
||||
sortable: true,
|
||||
},
|
||||
]
|
||||
});
|
||||
|
||||
{% endblock %}
|
@ -143,9 +143,7 @@ class TestBuildViews(TestCase):
|
||||
|
||||
content = str(response.content)
|
||||
|
||||
# Content should contain build titles
|
||||
for build in Build.objects.all():
|
||||
self.assertIn(build.title, content)
|
||||
self.assertIn("Part Builds", content)
|
||||
|
||||
def test_build_detail(self):
|
||||
""" Test the detail view for a Build object """
|
||||
|
@ -64,7 +64,22 @@
|
||||
field: 'part_detail.full_name',
|
||||
title: '{% trans "Part" %}',
|
||||
formatter: function(value, row, index, field) {
|
||||
return imageHoverIcon(row.part_detail.image_url) + renderLink(value, '/part/' + row.part + '/suppliers/');
|
||||
|
||||
var html = imageHoverIcon(row.part_detail.thumbnail) + renderLink(value, '/part/' + row.part + '/suppliers/');
|
||||
|
||||
if (row.part_detail.is_template) {
|
||||
html += `<span class='fas fa-clone label-right' title='Template part'></span>`;
|
||||
}
|
||||
|
||||
if (row.part_detail.assembly) {
|
||||
html += `<span class='fas fa-tools label-right' title='Assembled part'></span>`;
|
||||
}
|
||||
|
||||
if (!row.part_detail.active) {
|
||||
html += `<span class='label label-warning label-right'>INACTIVE</span>`;
|
||||
}
|
||||
|
||||
return html;
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -9,8 +9,11 @@
|
||||
<hr>
|
||||
|
||||
<div id='button-bar'>
|
||||
<div class='btn-group'>
|
||||
<div class='button-toolbar container-fluid' style='float: right;'>
|
||||
<button class='btn btn-primary' type='button' id='company-order2' title='Create new purchase order'>{% trans "New Purchase Order" %}</button>
|
||||
<div class='filter-list' id='filter-list-order'>
|
||||
<!-- Empty div -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -1,7 +0,0 @@
|
||||
{% for order in orders %}
|
||||
<tr>
|
||||
<td>{{ order }}</td>
|
||||
<td>{{ order.description }}</td>
|
||||
<td>{% include "order/order_status.html" with order=order %}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
@ -96,8 +96,8 @@ class CompanySimpleTest(TestCase):
|
||||
def test_part_pricing(self):
|
||||
m2x4 = Part.objects.get(name='M2x4 LPHS')
|
||||
|
||||
self.assertEqual(m2x4.get_price_info(10), "70.00000 - 75.00000")
|
||||
self.assertEqual(m2x4.get_price_info(100), "125.00000 - 350.00000")
|
||||
self.assertEqual(m2x4.get_price_info(10), "70 - 75")
|
||||
self.assertEqual(m2x4.get_price_info(100), "125 - 350")
|
||||
|
||||
pmin, pmax = m2x4.get_price_range(5)
|
||||
self.assertEqual(pmin, 35)
|
||||
|
@ -6,7 +6,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2020-04-09 15:04+0000\n"
|
||||
"POT-Creation-Date: 2020-04-11 15:00+0000\n"
|
||||
"PO-Revision-Date: 2020-02-02 08:07+0100\n"
|
||||
"Last-Translator: Christian Schlüter <chschlue@gmail.com>\n"
|
||||
"Language-Team: C <kde-i18n-doc@kde.org>\n"
|
||||
@ -17,30 +17,30 @@ msgstr ""
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
"X-Generator: Lokalize 19.12.0\n"
|
||||
|
||||
#: InvenTree/helpers.py:240 order/models.py:164 order/models.py:215
|
||||
#: InvenTree/helpers.py:259 order/models.py:164 order/models.py:215
|
||||
msgid "Invalid quantity provided"
|
||||
msgstr "Keine gültige Menge"
|
||||
|
||||
#: InvenTree/helpers.py:243
|
||||
#: InvenTree/helpers.py:262
|
||||
msgid "Empty serial number string"
|
||||
msgstr "Keine Seriennummer angegeben"
|
||||
|
||||
#: InvenTree/helpers.py:264 InvenTree/helpers.py:281
|
||||
#: InvenTree/helpers.py:283 InvenTree/helpers.py:300
|
||||
#, python-brace-format
|
||||
msgid "Duplicate serial: {n}"
|
||||
msgstr "Doppelte Seriennummer: {n}"
|
||||
|
||||
#: InvenTree/helpers.py:268 InvenTree/helpers.py:271 InvenTree/helpers.py:274
|
||||
#: InvenTree/helpers.py:285
|
||||
#: InvenTree/helpers.py:287 InvenTree/helpers.py:290 InvenTree/helpers.py:293
|
||||
#: InvenTree/helpers.py:304
|
||||
#, python-brace-format
|
||||
msgid "Invalid group: {g}"
|
||||
msgstr "Ungültige Gruppe: {g}"
|
||||
|
||||
#: InvenTree/helpers.py:291
|
||||
#: InvenTree/helpers.py:310
|
||||
msgid "No serial numbers found"
|
||||
msgstr "Keine Seriennummern gefunden"
|
||||
|
||||
#: InvenTree/helpers.py:295
|
||||
#: InvenTree/helpers.py:314
|
||||
#, python-brace-format
|
||||
msgid "Number of unique serial number ({s}) must match quantity ({q})"
|
||||
msgstr ""
|
||||
@ -71,49 +71,49 @@ msgstr "Französisch"
|
||||
msgid "Polish"
|
||||
msgstr "Polnisch"
|
||||
|
||||
#: InvenTree/status_codes.py:36 InvenTree/status_codes.py:97
|
||||
#: InvenTree/status_codes.py:86 InvenTree/status_codes.py:162
|
||||
msgid "Pending"
|
||||
msgstr "Ausstehend"
|
||||
|
||||
#: InvenTree/status_codes.py:37
|
||||
#: InvenTree/status_codes.py:87
|
||||
msgid "Placed"
|
||||
msgstr "Platziert"
|
||||
|
||||
#: InvenTree/status_codes.py:38 InvenTree/status_codes.py:100
|
||||
#: InvenTree/status_codes.py:88 InvenTree/status_codes.py:165
|
||||
msgid "Complete"
|
||||
msgstr "Fertig"
|
||||
|
||||
#: InvenTree/status_codes.py:39 InvenTree/status_codes.py:99
|
||||
#: InvenTree/status_codes.py:89 InvenTree/status_codes.py:164
|
||||
msgid "Cancelled"
|
||||
msgstr "Storniert"
|
||||
|
||||
#: InvenTree/status_codes.py:40 InvenTree/status_codes.py:71
|
||||
#: InvenTree/status_codes.py:90 InvenTree/status_codes.py:130
|
||||
msgid "Lost"
|
||||
msgstr "Verloren"
|
||||
|
||||
#: InvenTree/status_codes.py:41
|
||||
#: InvenTree/status_codes.py:91
|
||||
msgid "Returned"
|
||||
msgstr "Zurückgegeben"
|
||||
|
||||
#: InvenTree/status_codes.py:67
|
||||
#: InvenTree/status_codes.py:126
|
||||
msgid "OK"
|
||||
msgstr "OK"
|
||||
|
||||
#: InvenTree/status_codes.py:68
|
||||
#: InvenTree/status_codes.py:127
|
||||
msgid "Attention needed"
|
||||
msgstr "erfordert Eingriff"
|
||||
|
||||
#: InvenTree/status_codes.py:69
|
||||
#: InvenTree/status_codes.py:128
|
||||
msgid "Damaged"
|
||||
msgstr "Beschädigt"
|
||||
|
||||
#: InvenTree/status_codes.py:70
|
||||
#: InvenTree/status_codes.py:129
|
||||
msgid "Destroyed"
|
||||
msgstr "Zerstört"
|
||||
|
||||
#: InvenTree/status_codes.py:98 build/templates/build/allocate_edit.html:28
|
||||
#: InvenTree/status_codes.py:163 build/templates/build/allocate_edit.html:28
|
||||
#: build/templates/build/allocate_view.html:21
|
||||
#: part/templates/part/part_base.html:109 part/templates/part/tabs.html:21
|
||||
#: part/templates/part/part_base.html:114 part/templates/part/tabs.html:21
|
||||
msgid "Allocated"
|
||||
msgstr "Zugeordnet"
|
||||
|
||||
@ -178,7 +178,7 @@ msgstr ""
|
||||
msgid "Number of parts to build"
|
||||
msgstr "Anzahl der zu bauenden Teile"
|
||||
|
||||
#: build/models.py:82
|
||||
#: build/models.py:82 templates/table_filters.html:42
|
||||
msgid "Build status"
|
||||
msgstr "Bau-Status"
|
||||
|
||||
@ -231,10 +231,10 @@ msgstr "Zuweisung aufheben"
|
||||
|
||||
#: build/templates/build/allocate_edit.html:19
|
||||
#: build/templates/build/allocate_view.html:17
|
||||
#: build/templates/build/detail.html:21
|
||||
#: build/templates/build/detail.html:22
|
||||
#: company/templates/company/detail_part.html:65
|
||||
#: order/templates/order/order_wizard/select_parts.html:30
|
||||
#: order/templates/order/purchase_order_detail.html:25
|
||||
#: order/templates/order/purchase_order_detail.html:26
|
||||
#: part/templates/part/part_app_base.html:7
|
||||
msgid "Part"
|
||||
msgstr "Teil"
|
||||
@ -266,16 +266,62 @@ msgstr "Teile bestellen"
|
||||
#: company/templates/company/index.html:54
|
||||
#: company/templates/company/supplier_part_base.html:50
|
||||
#: company/templates/company/supplier_part_detail.html:27
|
||||
#: order/templates/order/purchase_order_detail.html:26
|
||||
#: order/templates/order/purchase_order_detail.html:27
|
||||
#: part/templates/part/detail.html:38
|
||||
msgid "Description"
|
||||
msgstr "Beschreibung"
|
||||
|
||||
#: build/templates/build/allocate_view.html:22
|
||||
#: part/templates/part/part_base.html:115
|
||||
#: part/templates/part/part_base.html:121
|
||||
msgid "On Order"
|
||||
msgstr "bestellt"
|
||||
|
||||
#: build/templates/build/build_base.html:27 part/templates/part/tabs.html:28
|
||||
#: stock/templates/stock/item_base.html:122 templates/navbar.html:12
|
||||
msgid "Build"
|
||||
msgstr "Bau"
|
||||
|
||||
#: build/templates/build/build_base.html:52 build/templates/build/detail.html:9
|
||||
msgid "Build Details"
|
||||
msgstr "Bau-Status"
|
||||
|
||||
#: build/templates/build/build_base.html:56
|
||||
#, fuzzy
|
||||
#| msgid "Build Notes"
|
||||
msgid "Build Title"
|
||||
msgstr "Bau-Bemerkungen"
|
||||
|
||||
#: build/templates/build/build_base.html:66
|
||||
#: build/templates/build/detail.html:27
|
||||
#: company/templates/company/supplier_part_pricing.html:27
|
||||
#: order/templates/order/order_wizard/select_parts.html:32
|
||||
#: order/templates/order/purchase_order_detail.html:30
|
||||
#: stock/templates/stock/item_base.html:108
|
||||
#: stock/templates/stock/stock_adjust.html:18
|
||||
msgid "Quantity"
|
||||
msgstr "Anzahl"
|
||||
|
||||
#: build/templates/build/build_base.html:71
|
||||
#: build/templates/build/detail.html:42
|
||||
#: order/templates/order/order_base.html:72
|
||||
#: stock/templates/stock/item_base.html:175
|
||||
msgid "Status"
|
||||
msgstr "Status"
|
||||
|
||||
#: build/templates/build/build_base.html:76
|
||||
msgid "BOM Price"
|
||||
msgstr ""
|
||||
|
||||
#: build/templates/build/build_base.html:81
|
||||
msgid "BOM pricing is incomplete"
|
||||
msgstr ""
|
||||
|
||||
#: build/templates/build/build_base.html:84
|
||||
#, fuzzy
|
||||
#| msgid "Show pricing information"
|
||||
msgid "No pricing information"
|
||||
msgstr "Kosteninformationen ansehen"
|
||||
|
||||
#: build/templates/build/build_output.html:9
|
||||
#, fuzzy
|
||||
#| msgid "Build status"
|
||||
@ -289,68 +335,49 @@ msgid "Are you sure you want to unallocate these parts?"
|
||||
msgstr ""
|
||||
"Sind Sie sicher, dass sie die folgenden Zulieferer-Teile löschen möchten?"
|
||||
|
||||
#: build/templates/build/detail.html:8
|
||||
msgid "Build Details"
|
||||
msgstr "Bau-Status"
|
||||
|
||||
#: build/templates/build/detail.html:16
|
||||
#: build/templates/build/detail.html:17
|
||||
msgid "Title"
|
||||
msgstr "Titel"
|
||||
|
||||
#: build/templates/build/detail.html:26
|
||||
#: company/templates/company/supplier_part_pricing.html:27
|
||||
#: order/templates/order/order_wizard/select_parts.html:32
|
||||
#: order/templates/order/purchase_order_detail.html:29
|
||||
#: stock/templates/stock/item_base.html:107
|
||||
#: stock/templates/stock/stock_adjust.html:18
|
||||
msgid "Quantity"
|
||||
msgstr "Anzahl"
|
||||
|
||||
#: build/templates/build/detail.html:30
|
||||
#: build/templates/build/detail.html:31
|
||||
msgid "Stock Source"
|
||||
msgstr "Lagerobjekt"
|
||||
|
||||
#: build/templates/build/detail.html:35
|
||||
#: build/templates/build/detail.html:36
|
||||
msgid "Stock can be taken from any available location."
|
||||
msgstr "Bestand kann jedem verfügbaren Lagerort entnommen werden."
|
||||
|
||||
#: build/templates/build/detail.html:41
|
||||
#: order/templates/order/order_base.html:71
|
||||
#: stock/templates/stock/item_base.html:174
|
||||
msgid "Status"
|
||||
msgstr "Status"
|
||||
|
||||
#: build/templates/build/detail.html:47
|
||||
#: stock/templates/stock/item_base.html:114
|
||||
#: build/templates/build/detail.html:48
|
||||
#: stock/templates/stock/item_base.html:115
|
||||
msgid "Batch"
|
||||
msgstr "Los"
|
||||
|
||||
#: build/templates/build/detail.html:54
|
||||
#: build/templates/build/detail.html:55
|
||||
#: company/templates/company/supplier_part_base.html:47
|
||||
#: company/templates/company/supplier_part_detail.html:24
|
||||
#: part/templates/part/detail.html:67 part/templates/part/part_base.html:84
|
||||
#: stock/templates/stock/item_base.html:142
|
||||
#: part/templates/part/detail.html:67 part/templates/part/part_base.html:85
|
||||
#: stock/templates/stock/item_base.html:143
|
||||
msgid "External Link"
|
||||
msgstr ""
|
||||
|
||||
#: build/templates/build/detail.html:60
|
||||
#: order/templates/order/order_base.html:83
|
||||
#: build/templates/build/detail.html:61
|
||||
#: order/templates/order/order_base.html:84
|
||||
msgid "Created"
|
||||
msgstr "Erstellt"
|
||||
|
||||
#: build/templates/build/detail.html:66
|
||||
#: build/templates/build/detail.html:67
|
||||
msgid "Enough Parts?"
|
||||
msgstr "Genügend Teile?"
|
||||
|
||||
#: build/templates/build/detail.html:69
|
||||
#: build/templates/build/detail.html:70
|
||||
msgid "Yes"
|
||||
msgstr "Ja"
|
||||
|
||||
#: build/templates/build/detail.html:71
|
||||
#: build/templates/build/detail.html:72
|
||||
msgid "No"
|
||||
msgstr "Nein"
|
||||
|
||||
#: build/templates/build/detail.html:79
|
||||
#: build/templates/build/detail.html:80
|
||||
msgid "Completed"
|
||||
msgstr "Fertig"
|
||||
|
||||
@ -622,7 +649,7 @@ msgstr "Zulieferer auswählen"
|
||||
msgid "Supplier stock keeping unit"
|
||||
msgstr "Stock Keeping Units (SKU) des Zulieferers"
|
||||
|
||||
#: company/models.py:256 company/templates/company/detail_part.html:81
|
||||
#: company/models.py:256 company/templates/company/detail_part.html:96
|
||||
#: company/templates/company/supplier_part_base.html:53
|
||||
#: company/templates/company/supplier_part_detail.html:30
|
||||
msgid "Manufacturer"
|
||||
@ -680,7 +707,7 @@ msgid "Company Details"
|
||||
msgstr "Firmenbemerkungen"
|
||||
|
||||
#: company/templates/company/detail.html:16
|
||||
#: stock/templates/stock/item_base.html:135
|
||||
#: stock/templates/stock/item_base.html:136
|
||||
msgid "Customer"
|
||||
msgstr "Kunde"
|
||||
|
||||
@ -688,9 +715,9 @@ msgstr "Kunde"
|
||||
#: company/templates/company/index.html:46
|
||||
#: company/templates/company/supplier_part_base.html:44
|
||||
#: company/templates/company/supplier_part_detail.html:21
|
||||
#: order/templates/order/order_base.html:66
|
||||
#: order/templates/order/order_base.html:67
|
||||
#: order/templates/order/order_wizard/select_pos.html:30
|
||||
#: stock/templates/stock/item_base.html:149
|
||||
#: stock/templates/stock/item_base.html:150
|
||||
msgid "Supplier"
|
||||
msgstr "Zulieferer"
|
||||
|
||||
@ -716,13 +743,13 @@ msgstr ""
|
||||
msgid "Delete Parts"
|
||||
msgstr "Anhang löschen"
|
||||
|
||||
#: company/templates/company/detail_part.html:73
|
||||
#: company/templates/company/detail_part.html:88
|
||||
#: company/templates/company/supplier_part_base.html:45
|
||||
#: company/templates/company/supplier_part_detail.html:22
|
||||
msgid "SKU"
|
||||
msgstr ""
|
||||
|
||||
#: company/templates/company/detail_part.html:90
|
||||
#: company/templates/company/detail_part.html:105
|
||||
msgid "Link"
|
||||
msgstr ""
|
||||
|
||||
@ -783,7 +810,7 @@ msgstr ""
|
||||
|
||||
#: company/templates/company/supplier_part_base.html:6
|
||||
#: company/templates/company/supplier_part_base.html:13
|
||||
#: stock/templates/stock/item_base.html:154
|
||||
#: stock/templates/stock/item_base.html:155
|
||||
msgid "Supplier Part"
|
||||
msgstr "Zulieferer-Teil"
|
||||
|
||||
@ -810,7 +837,7 @@ msgstr "IPN (Interne Produktnummer)"
|
||||
|
||||
#: company/templates/company/supplier_part_base.html:57
|
||||
#: company/templates/company/supplier_part_detail.html:34
|
||||
#: order/templates/order/purchase_order_detail.html:33
|
||||
#: order/templates/order/purchase_order_detail.html:34
|
||||
msgid "Note"
|
||||
msgstr "Notiz"
|
||||
|
||||
@ -1030,7 +1057,7 @@ msgstr "Position - Referenz"
|
||||
msgid "Line item notes"
|
||||
msgstr "Position - Notizen"
|
||||
|
||||
#: order/models.py:298 stock/templates/stock/item_base.html:128
|
||||
#: order/models.py:298 stock/templates/stock/item_base.html:129
|
||||
msgid "Purchase Order"
|
||||
msgstr "Kaufvertrag"
|
||||
|
||||
@ -1042,16 +1069,16 @@ msgstr "Zulieferer-Teil"
|
||||
msgid "Number of items received"
|
||||
msgstr "Empfangene Objekt-Anzahl"
|
||||
|
||||
#: order/templates/order/order_base.html:61
|
||||
#: order/templates/order/order_base.html:62
|
||||
msgid "Purchase Order Details"
|
||||
msgstr "Bestelldetails"
|
||||
|
||||
#: order/templates/order/order_base.html:89
|
||||
#: order/templates/order/order_base.html:90
|
||||
msgid "Issued"
|
||||
msgstr "Aufgegeben"
|
||||
|
||||
#: order/templates/order/order_base.html:96
|
||||
#: order/templates/order/purchase_order_detail.html:31
|
||||
#: order/templates/order/order_base.html:97
|
||||
#: order/templates/order/purchase_order_detail.html:32
|
||||
msgid "Received"
|
||||
msgstr "Empfangen"
|
||||
|
||||
@ -1152,23 +1179,23 @@ msgid "Are you sure you want to delete this attachment?"
|
||||
msgstr ""
|
||||
"Sind Sie sicher, dass sie die folgenden Zulieferer-Teile löschen möchten?"
|
||||
|
||||
#: order/templates/order/purchase_order_detail.html:15 order/views.py:825
|
||||
#: order/templates/order/purchase_order_detail.html:16 order/views.py:825
|
||||
msgid "Add Line Item"
|
||||
msgstr "Position hinzufügen"
|
||||
|
||||
#: order/templates/order/purchase_order_detail.html:19
|
||||
#: order/templates/order/purchase_order_detail.html:20
|
||||
msgid "Order Items"
|
||||
msgstr "Bestellungspositionen"
|
||||
|
||||
#: order/templates/order/purchase_order_detail.html:24
|
||||
#: order/templates/order/purchase_order_detail.html:25
|
||||
msgid "Line"
|
||||
msgstr "Position"
|
||||
|
||||
#: order/templates/order/purchase_order_detail.html:27
|
||||
#: order/templates/order/purchase_order_detail.html:28
|
||||
msgid "Order Code"
|
||||
msgstr "Bestellnummer"
|
||||
|
||||
#: order/templates/order/purchase_order_detail.html:28
|
||||
#: order/templates/order/purchase_order_detail.html:29
|
||||
msgid "Reference"
|
||||
msgstr "Referenz"
|
||||
|
||||
@ -1475,63 +1502,63 @@ msgstr "Bemerkungen - unterstüzt Markdown-Formatierung"
|
||||
msgid "Stored BOM checksum"
|
||||
msgstr "Prüfsumme der Stückliste gespeichert"
|
||||
|
||||
#: part/models.py:1040
|
||||
#: part/models.py:1049
|
||||
msgid "Parameter template name must be unique"
|
||||
msgstr "Vorlagen-Name des Parameters muss eindeutig sein"
|
||||
|
||||
#: part/models.py:1045
|
||||
#: part/models.py:1054
|
||||
msgid "Parameter Name"
|
||||
msgstr "Name des Parameters"
|
||||
|
||||
#: part/models.py:1047
|
||||
#: part/models.py:1056
|
||||
msgid "Parameter Units"
|
||||
msgstr "Parameter Einheit"
|
||||
|
||||
#: part/models.py:1073
|
||||
#: part/models.py:1082
|
||||
msgid "Parent Part"
|
||||
msgstr "Ausgangsteil"
|
||||
|
||||
#: part/models.py:1075
|
||||
#: part/models.py:1084
|
||||
msgid "Parameter Template"
|
||||
msgstr "Parameter Vorlage"
|
||||
|
||||
#: part/models.py:1077
|
||||
#: part/models.py:1086
|
||||
msgid "Parameter Value"
|
||||
msgstr "Parameter Wert"
|
||||
|
||||
#: part/models.py:1101
|
||||
#: part/models.py:1110
|
||||
msgid "Select parent part"
|
||||
msgstr "Ausgangsteil auswählen"
|
||||
|
||||
#: part/models.py:1110
|
||||
#: part/models.py:1119
|
||||
msgid "Select part to be used in BOM"
|
||||
msgstr "Teil für die Nutzung in der Stückliste auswählen"
|
||||
|
||||
#: part/models.py:1117
|
||||
#: part/models.py:1126
|
||||
msgid "BOM quantity for this BOM item"
|
||||
msgstr "Stücklisten-Anzahl für dieses Stücklisten-Teil"
|
||||
|
||||
#: part/models.py:1120
|
||||
#: part/models.py:1129
|
||||
msgid "Estimated build wastage quantity (absolute or percentage)"
|
||||
msgstr "Geschätzter Ausschuss (absolut oder prozentual)"
|
||||
|
||||
#: part/models.py:1123
|
||||
#: part/models.py:1132
|
||||
msgid "BOM item reference"
|
||||
msgstr "Referenz des Objekts auf der Stückliste"
|
||||
|
||||
#: part/models.py:1126
|
||||
#: part/models.py:1135
|
||||
msgid "BOM item notes"
|
||||
msgstr "Notizen zum Stücklisten-Objekt"
|
||||
|
||||
#: part/models.py:1128
|
||||
#: part/models.py:1137
|
||||
msgid "BOM line checksum"
|
||||
msgstr "Prüfsumme der Stückliste"
|
||||
|
||||
#: part/models.py:1191
|
||||
#: part/models.py:1200
|
||||
msgid "Part cannot be added to its own Bill of Materials"
|
||||
msgstr "Teil kann nicht zu seiner eigenen Stückliste hinzugefügt werden"
|
||||
|
||||
#: part/models.py:1198
|
||||
#: part/models.py:1207
|
||||
#, python-brace-format
|
||||
msgid "Part '{p1}' is used in BOM for '{p2}' (recursive)"
|
||||
msgstr "Teil '{p1}' wird in Stückliste für Teil '{p2}' benutzt (rekursiv)"
|
||||
@ -1581,7 +1608,7 @@ msgstr "Teile (inklusive Unter-Kategorien)"
|
||||
msgid "Part Details"
|
||||
msgstr "Teile-Details"
|
||||
|
||||
#: part/templates/part/detail.html:25 part/templates/part/part_base.html:77
|
||||
#: part/templates/part/detail.html:25 part/templates/part/part_base.html:78
|
||||
msgid "IPN"
|
||||
msgstr "IPN (Interne Produktnummer)"
|
||||
|
||||
@ -1637,7 +1664,7 @@ msgstr "Teil ist virtuell (kein physisches Teil)"
|
||||
msgid "Part is not a virtual part"
|
||||
msgstr "Teil ist nicht virtuell"
|
||||
|
||||
#: part/templates/part/detail.html:132
|
||||
#: part/templates/part/detail.html:132 templates/table_filters.html:86
|
||||
msgid "Assembly"
|
||||
msgstr "Baugruppe"
|
||||
|
||||
@ -1649,7 +1676,7 @@ msgstr "Teil kann aus anderen Teilen angefertigt werden"
|
||||
msgid "Part cannot be assembled from other parts"
|
||||
msgstr "Teil kann nicht aus anderen Teilen angefertigt werden"
|
||||
|
||||
#: part/templates/part/detail.html:141
|
||||
#: part/templates/part/detail.html:141 templates/table_filters.html:90
|
||||
msgid "Component"
|
||||
msgstr "Komponente"
|
||||
|
||||
@ -1713,31 +1740,47 @@ msgstr "Teile"
|
||||
msgid "This part is not active"
|
||||
msgstr "Dieses Teil ist nicht aktiv"
|
||||
|
||||
#: part/templates/part/part_base.html:37
|
||||
#: part/templates/part/part_base.html:16
|
||||
#, fuzzy
|
||||
#| msgid "Is this part a template part?"
|
||||
msgid "This part is a template part."
|
||||
msgstr "Ist dieses Teil eine Vorlage?"
|
||||
|
||||
#: part/templates/part/part_base.html:18
|
||||
msgid "It is not a real part, but real parts can be based on this template."
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/part_base.html:23
|
||||
#, fuzzy
|
||||
#| msgid "This part is not active"
|
||||
msgid "This part is a variant of"
|
||||
msgstr "Dieses Teil ist nicht aktiv"
|
||||
|
||||
#: part/templates/part/part_base.html:38
|
||||
msgid "Star this part"
|
||||
msgstr "Teil favorisieren"
|
||||
|
||||
#: part/templates/part/part_base.html:43
|
||||
#: part/templates/part/part_base.html:44
|
||||
msgid "Show pricing information"
|
||||
msgstr "Kosteninformationen ansehen"
|
||||
|
||||
#: part/templates/part/part_base.html:98
|
||||
#: part/templates/part/part_base.html:101
|
||||
msgid "Available Stock"
|
||||
msgstr "Verfügbarer Lagerbestand"
|
||||
|
||||
#: part/templates/part/part_base.html:103
|
||||
#: part/templates/part/part_base.html:107
|
||||
msgid "In Stock"
|
||||
msgstr "Auf Lager"
|
||||
|
||||
#: part/templates/part/part_base.html:124
|
||||
#: part/templates/part/part_base.html:131
|
||||
msgid "Build Status"
|
||||
msgstr "Bau-Status"
|
||||
|
||||
#: part/templates/part/part_base.html:128
|
||||
#: part/templates/part/part_base.html:136
|
||||
msgid "Can Build"
|
||||
msgstr "Herstellbar?"
|
||||
|
||||
#: part/templates/part/part_base.html:133
|
||||
#: part/templates/part/part_base.html:142
|
||||
msgid "Underway"
|
||||
msgstr "unterwegs"
|
||||
|
||||
@ -1787,11 +1830,6 @@ msgstr "Varianten"
|
||||
msgid "BOM"
|
||||
msgstr "Stückliste"
|
||||
|
||||
#: part/templates/part/tabs.html:28 stock/templates/stock/item_base.html:121
|
||||
#: templates/navbar.html:12
|
||||
msgid "Build"
|
||||
msgstr "Bau"
|
||||
|
||||
#: part/templates/part/tabs.html:32
|
||||
msgid "Used In"
|
||||
msgstr "Benutzt in"
|
||||
@ -2164,11 +2202,11 @@ msgstr "Link auf externe Seite für weitere Informationen"
|
||||
msgid "Stock Tracking Information"
|
||||
msgstr "Informationen zum Lagerbestands-Tracking"
|
||||
|
||||
#: stock/templates/stock/item_base.html:10
|
||||
#: stock/templates/stock/item_base.html:11
|
||||
msgid "Stock Item Details"
|
||||
msgstr "Lagerbestands-Details"
|
||||
|
||||
#: stock/templates/stock/item_base.html:55
|
||||
#: stock/templates/stock/item_base.html:56
|
||||
msgid ""
|
||||
"This stock item is serialized - it has a unique serial number and the "
|
||||
"quantity cannot be adjusted."
|
||||
@ -2176,45 +2214,45 @@ msgstr ""
|
||||
"Dieses Lagerobjekt ist serialisiert. Es hat eine eindeutige Seriennummer und "
|
||||
"die Anzahl kann nicht angepasst werden."
|
||||
|
||||
#: stock/templates/stock/item_base.html:59
|
||||
#: stock/templates/stock/item_base.html:60
|
||||
#, fuzzy
|
||||
#| msgid "Stock item cannot be created for a template Part"
|
||||
msgid "This stock item cannot be deleted as it has child items"
|
||||
msgstr "Lagerobjekt kann nicht für Vorlagen-Teile angelegt werden"
|
||||
|
||||
#: stock/templates/stock/item_base.html:63
|
||||
#: stock/templates/stock/item_base.html:64
|
||||
msgid ""
|
||||
"This stock item will be automatically deleted when all stock is depleted."
|
||||
msgstr ""
|
||||
"Dieses Lagerobjekt wird automatisch gelöscht wenn der Lagerbestand "
|
||||
"aufgebraucht ist."
|
||||
|
||||
#: stock/templates/stock/item_base.html:68
|
||||
#: stock/templates/stock/item_base.html:69
|
||||
msgid "This stock item was split from "
|
||||
msgstr ""
|
||||
|
||||
#: stock/templates/stock/item_base.html:88
|
||||
#: stock/templates/stock/item_base.html:89
|
||||
msgid "Belongs To"
|
||||
msgstr "Gehört zu"
|
||||
|
||||
#: stock/templates/stock/item_base.html:94
|
||||
#: stock/templates/stock/item_base.html:95
|
||||
#: stock/templates/stock/stock_adjust.html:17
|
||||
msgid "Location"
|
||||
msgstr "Standort"
|
||||
|
||||
#: stock/templates/stock/item_base.html:101
|
||||
#: stock/templates/stock/item_base.html:102
|
||||
msgid "Serial Number"
|
||||
msgstr "Seriennummer"
|
||||
|
||||
#: stock/templates/stock/item_base.html:160
|
||||
#: stock/templates/stock/item_base.html:161
|
||||
msgid "Last Updated"
|
||||
msgstr "Zuletzt aktualisiert"
|
||||
|
||||
#: stock/templates/stock/item_base.html:165
|
||||
#: stock/templates/stock/item_base.html:166
|
||||
msgid "Last Stocktake"
|
||||
msgstr "Letzte Inventur"
|
||||
|
||||
#: stock/templates/stock/item_base.html:169
|
||||
#: stock/templates/stock/item_base.html:170
|
||||
msgid "No stocktake performed"
|
||||
msgstr "Keine Inventur ausgeführt"
|
||||
|
||||
@ -2540,6 +2578,80 @@ msgstr "bestellt"
|
||||
msgid "Delete Stock"
|
||||
msgstr "Lagerobjekt löschen"
|
||||
|
||||
#: templates/table_filters.html:22
|
||||
#, fuzzy
|
||||
#| msgid "Include stock items in sub locations"
|
||||
msgid "Include sublocations"
|
||||
msgstr "Lagerobjekte in untergeordneten Lagerorten einschließen"
|
||||
|
||||
#: templates/table_filters.html:23
|
||||
#, fuzzy
|
||||
#| msgid "Include stock items in sub locations"
|
||||
msgid "Include stock in sublocations"
|
||||
msgstr "Lagerobjekte in untergeordneten Lagerorten einschließen"
|
||||
|
||||
#: templates/table_filters.html:27
|
||||
#, fuzzy
|
||||
#| msgid "Required Parts"
|
||||
msgid "Active parts"
|
||||
msgstr "benötigte Teile"
|
||||
|
||||
#: templates/table_filters.html:28
|
||||
msgid "Show stock for active parts"
|
||||
msgstr ""
|
||||
|
||||
#: templates/table_filters.html:32 templates/table_filters.html:33
|
||||
#, fuzzy
|
||||
#| msgid "Stock Details"
|
||||
msgid "Stock status"
|
||||
msgstr "Objekt-Details"
|
||||
|
||||
#: templates/table_filters.html:53
|
||||
#, fuzzy
|
||||
#| msgid "Order Parts"
|
||||
msgid "Order status"
|
||||
msgstr "Teile bestellen"
|
||||
|
||||
#: templates/table_filters.html:64
|
||||
#, fuzzy
|
||||
#| msgid "Parts (Including subcategories)"
|
||||
msgid "Include subcategories"
|
||||
msgstr "Teile (inklusive Unter-Kategorien)"
|
||||
|
||||
#: templates/table_filters.html:65
|
||||
#, fuzzy
|
||||
#| msgid "Parts (Including subcategories)"
|
||||
msgid "Include parts in subcategories"
|
||||
msgstr "Teile (inklusive Unter-Kategorien)"
|
||||
|
||||
#: templates/table_filters.html:69
|
||||
msgid "Active"
|
||||
msgstr ""
|
||||
|
||||
#: templates/table_filters.html:70
|
||||
#, fuzzy
|
||||
#| msgid "Build to allocate parts"
|
||||
msgid "Show active parts"
|
||||
msgstr "Bau starten um Teile zuzuweisen"
|
||||
|
||||
#: templates/table_filters.html:74
|
||||
#, fuzzy
|
||||
#| msgid "Parameter Template"
|
||||
msgid "Template"
|
||||
msgstr "Parameter Vorlage"
|
||||
|
||||
#: templates/table_filters.html:78
|
||||
#, fuzzy
|
||||
#| msgid "Available"
|
||||
msgid "Stock available"
|
||||
msgstr "verfügbar"
|
||||
|
||||
#: templates/table_filters.html:82
|
||||
#, fuzzy
|
||||
#| msgid "Stock"
|
||||
msgid "Low stock"
|
||||
msgstr "Lagerbestand"
|
||||
|
||||
#~ msgid "URL"
|
||||
#~ msgstr "URL"
|
||||
|
||||
|
@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2020-04-09 15:04+0000\n"
|
||||
"POT-Creation-Date: 2020-04-11 15:00+0000\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
@ -18,30 +18,30 @@ msgstr ""
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
#: InvenTree/helpers.py:240 order/models.py:164 order/models.py:215
|
||||
#: InvenTree/helpers.py:259 order/models.py:164 order/models.py:215
|
||||
msgid "Invalid quantity provided"
|
||||
msgstr ""
|
||||
|
||||
#: InvenTree/helpers.py:243
|
||||
#: InvenTree/helpers.py:262
|
||||
msgid "Empty serial number string"
|
||||
msgstr ""
|
||||
|
||||
#: InvenTree/helpers.py:264 InvenTree/helpers.py:281
|
||||
#: InvenTree/helpers.py:283 InvenTree/helpers.py:300
|
||||
#, python-brace-format
|
||||
msgid "Duplicate serial: {n}"
|
||||
msgstr ""
|
||||
|
||||
#: InvenTree/helpers.py:268 InvenTree/helpers.py:271 InvenTree/helpers.py:274
|
||||
#: InvenTree/helpers.py:285
|
||||
#: InvenTree/helpers.py:287 InvenTree/helpers.py:290 InvenTree/helpers.py:293
|
||||
#: InvenTree/helpers.py:304
|
||||
#, python-brace-format
|
||||
msgid "Invalid group: {g}"
|
||||
msgstr ""
|
||||
|
||||
#: InvenTree/helpers.py:291
|
||||
#: InvenTree/helpers.py:310
|
||||
msgid "No serial numbers found"
|
||||
msgstr ""
|
||||
|
||||
#: InvenTree/helpers.py:295
|
||||
#: InvenTree/helpers.py:314
|
||||
#, python-brace-format
|
||||
msgid "Number of unique serial number ({s}) must match quantity ({q})"
|
||||
msgstr ""
|
||||
@ -70,49 +70,49 @@ msgstr ""
|
||||
msgid "Polish"
|
||||
msgstr ""
|
||||
|
||||
#: InvenTree/status_codes.py:36 InvenTree/status_codes.py:97
|
||||
#: InvenTree/status_codes.py:86 InvenTree/status_codes.py:162
|
||||
msgid "Pending"
|
||||
msgstr ""
|
||||
|
||||
#: InvenTree/status_codes.py:37
|
||||
#: InvenTree/status_codes.py:87
|
||||
msgid "Placed"
|
||||
msgstr ""
|
||||
|
||||
#: InvenTree/status_codes.py:38 InvenTree/status_codes.py:100
|
||||
#: InvenTree/status_codes.py:88 InvenTree/status_codes.py:165
|
||||
msgid "Complete"
|
||||
msgstr ""
|
||||
|
||||
#: InvenTree/status_codes.py:39 InvenTree/status_codes.py:99
|
||||
#: InvenTree/status_codes.py:89 InvenTree/status_codes.py:164
|
||||
msgid "Cancelled"
|
||||
msgstr ""
|
||||
|
||||
#: InvenTree/status_codes.py:40 InvenTree/status_codes.py:71
|
||||
#: InvenTree/status_codes.py:90 InvenTree/status_codes.py:130
|
||||
msgid "Lost"
|
||||
msgstr ""
|
||||
|
||||
#: InvenTree/status_codes.py:41
|
||||
#: InvenTree/status_codes.py:91
|
||||
msgid "Returned"
|
||||
msgstr ""
|
||||
|
||||
#: InvenTree/status_codes.py:67
|
||||
#: InvenTree/status_codes.py:126
|
||||
msgid "OK"
|
||||
msgstr ""
|
||||
|
||||
#: InvenTree/status_codes.py:68
|
||||
#: InvenTree/status_codes.py:127
|
||||
msgid "Attention needed"
|
||||
msgstr ""
|
||||
|
||||
#: InvenTree/status_codes.py:69
|
||||
#: InvenTree/status_codes.py:128
|
||||
msgid "Damaged"
|
||||
msgstr ""
|
||||
|
||||
#: InvenTree/status_codes.py:70
|
||||
#: InvenTree/status_codes.py:129
|
||||
msgid "Destroyed"
|
||||
msgstr ""
|
||||
|
||||
#: InvenTree/status_codes.py:98 build/templates/build/allocate_edit.html:28
|
||||
#: InvenTree/status_codes.py:163 build/templates/build/allocate_edit.html:28
|
||||
#: build/templates/build/allocate_view.html:21
|
||||
#: part/templates/part/part_base.html:109 part/templates/part/tabs.html:21
|
||||
#: part/templates/part/part_base.html:114 part/templates/part/tabs.html:21
|
||||
msgid "Allocated"
|
||||
msgstr ""
|
||||
|
||||
@ -175,7 +175,7 @@ msgstr ""
|
||||
msgid "Number of parts to build"
|
||||
msgstr ""
|
||||
|
||||
#: build/models.py:82
|
||||
#: build/models.py:82 templates/table_filters.html:42
|
||||
msgid "Build status"
|
||||
msgstr ""
|
||||
|
||||
@ -227,10 +227,10 @@ msgstr ""
|
||||
|
||||
#: build/templates/build/allocate_edit.html:19
|
||||
#: build/templates/build/allocate_view.html:17
|
||||
#: build/templates/build/detail.html:21
|
||||
#: build/templates/build/detail.html:22
|
||||
#: company/templates/company/detail_part.html:65
|
||||
#: order/templates/order/order_wizard/select_parts.html:30
|
||||
#: order/templates/order/purchase_order_detail.html:25
|
||||
#: order/templates/order/purchase_order_detail.html:26
|
||||
#: part/templates/part/part_app_base.html:7
|
||||
msgid "Part"
|
||||
msgstr ""
|
||||
@ -262,16 +262,58 @@ msgstr ""
|
||||
#: company/templates/company/index.html:54
|
||||
#: company/templates/company/supplier_part_base.html:50
|
||||
#: company/templates/company/supplier_part_detail.html:27
|
||||
#: order/templates/order/purchase_order_detail.html:26
|
||||
#: order/templates/order/purchase_order_detail.html:27
|
||||
#: part/templates/part/detail.html:38
|
||||
msgid "Description"
|
||||
msgstr ""
|
||||
|
||||
#: build/templates/build/allocate_view.html:22
|
||||
#: part/templates/part/part_base.html:115
|
||||
#: part/templates/part/part_base.html:121
|
||||
msgid "On Order"
|
||||
msgstr ""
|
||||
|
||||
#: build/templates/build/build_base.html:27 part/templates/part/tabs.html:28
|
||||
#: stock/templates/stock/item_base.html:122 templates/navbar.html:12
|
||||
msgid "Build"
|
||||
msgstr ""
|
||||
|
||||
#: build/templates/build/build_base.html:52 build/templates/build/detail.html:9
|
||||
msgid "Build Details"
|
||||
msgstr ""
|
||||
|
||||
#: build/templates/build/build_base.html:56
|
||||
msgid "Build Title"
|
||||
msgstr ""
|
||||
|
||||
#: build/templates/build/build_base.html:66
|
||||
#: build/templates/build/detail.html:27
|
||||
#: company/templates/company/supplier_part_pricing.html:27
|
||||
#: order/templates/order/order_wizard/select_parts.html:32
|
||||
#: order/templates/order/purchase_order_detail.html:30
|
||||
#: stock/templates/stock/item_base.html:108
|
||||
#: stock/templates/stock/stock_adjust.html:18
|
||||
msgid "Quantity"
|
||||
msgstr ""
|
||||
|
||||
#: build/templates/build/build_base.html:71
|
||||
#: build/templates/build/detail.html:42
|
||||
#: order/templates/order/order_base.html:72
|
||||
#: stock/templates/stock/item_base.html:175
|
||||
msgid "Status"
|
||||
msgstr ""
|
||||
|
||||
#: build/templates/build/build_base.html:76
|
||||
msgid "BOM Price"
|
||||
msgstr ""
|
||||
|
||||
#: build/templates/build/build_base.html:81
|
||||
msgid "BOM pricing is incomplete"
|
||||
msgstr ""
|
||||
|
||||
#: build/templates/build/build_base.html:84
|
||||
msgid "No pricing information"
|
||||
msgstr ""
|
||||
|
||||
#: build/templates/build/build_output.html:9
|
||||
msgid "Build Outputs"
|
||||
msgstr ""
|
||||
@ -280,68 +322,49 @@ msgstr ""
|
||||
msgid "Are you sure you want to unallocate these parts?"
|
||||
msgstr ""
|
||||
|
||||
#: build/templates/build/detail.html:8
|
||||
msgid "Build Details"
|
||||
msgstr ""
|
||||
|
||||
#: build/templates/build/detail.html:16
|
||||
#: build/templates/build/detail.html:17
|
||||
msgid "Title"
|
||||
msgstr ""
|
||||
|
||||
#: build/templates/build/detail.html:26
|
||||
#: company/templates/company/supplier_part_pricing.html:27
|
||||
#: order/templates/order/order_wizard/select_parts.html:32
|
||||
#: order/templates/order/purchase_order_detail.html:29
|
||||
#: stock/templates/stock/item_base.html:107
|
||||
#: stock/templates/stock/stock_adjust.html:18
|
||||
msgid "Quantity"
|
||||
msgstr ""
|
||||
|
||||
#: build/templates/build/detail.html:30
|
||||
#: build/templates/build/detail.html:31
|
||||
msgid "Stock Source"
|
||||
msgstr ""
|
||||
|
||||
#: build/templates/build/detail.html:35
|
||||
#: build/templates/build/detail.html:36
|
||||
msgid "Stock can be taken from any available location."
|
||||
msgstr ""
|
||||
|
||||
#: build/templates/build/detail.html:41
|
||||
#: order/templates/order/order_base.html:71
|
||||
#: stock/templates/stock/item_base.html:174
|
||||
msgid "Status"
|
||||
msgstr ""
|
||||
|
||||
#: build/templates/build/detail.html:47
|
||||
#: stock/templates/stock/item_base.html:114
|
||||
#: build/templates/build/detail.html:48
|
||||
#: stock/templates/stock/item_base.html:115
|
||||
msgid "Batch"
|
||||
msgstr ""
|
||||
|
||||
#: build/templates/build/detail.html:54
|
||||
#: build/templates/build/detail.html:55
|
||||
#: company/templates/company/supplier_part_base.html:47
|
||||
#: company/templates/company/supplier_part_detail.html:24
|
||||
#: part/templates/part/detail.html:67 part/templates/part/part_base.html:84
|
||||
#: stock/templates/stock/item_base.html:142
|
||||
#: part/templates/part/detail.html:67 part/templates/part/part_base.html:85
|
||||
#: stock/templates/stock/item_base.html:143
|
||||
msgid "External Link"
|
||||
msgstr ""
|
||||
|
||||
#: build/templates/build/detail.html:60
|
||||
#: order/templates/order/order_base.html:83
|
||||
#: build/templates/build/detail.html:61
|
||||
#: order/templates/order/order_base.html:84
|
||||
msgid "Created"
|
||||
msgstr ""
|
||||
|
||||
#: build/templates/build/detail.html:66
|
||||
#: build/templates/build/detail.html:67
|
||||
msgid "Enough Parts?"
|
||||
msgstr ""
|
||||
|
||||
#: build/templates/build/detail.html:69
|
||||
#: build/templates/build/detail.html:70
|
||||
msgid "Yes"
|
||||
msgstr ""
|
||||
|
||||
#: build/templates/build/detail.html:71
|
||||
#: build/templates/build/detail.html:72
|
||||
msgid "No"
|
||||
msgstr ""
|
||||
|
||||
#: build/templates/build/detail.html:79
|
||||
#: build/templates/build/detail.html:80
|
||||
msgid "Completed"
|
||||
msgstr ""
|
||||
|
||||
@ -581,7 +604,7 @@ msgstr ""
|
||||
msgid "Supplier stock keeping unit"
|
||||
msgstr ""
|
||||
|
||||
#: company/models.py:256 company/templates/company/detail_part.html:81
|
||||
#: company/models.py:256 company/templates/company/detail_part.html:96
|
||||
#: company/templates/company/supplier_part_base.html:53
|
||||
#: company/templates/company/supplier_part_detail.html:30
|
||||
msgid "Manufacturer"
|
||||
@ -637,7 +660,7 @@ msgid "Company Details"
|
||||
msgstr ""
|
||||
|
||||
#: company/templates/company/detail.html:16
|
||||
#: stock/templates/stock/item_base.html:135
|
||||
#: stock/templates/stock/item_base.html:136
|
||||
msgid "Customer"
|
||||
msgstr ""
|
||||
|
||||
@ -645,9 +668,9 @@ msgstr ""
|
||||
#: company/templates/company/index.html:46
|
||||
#: company/templates/company/supplier_part_base.html:44
|
||||
#: company/templates/company/supplier_part_detail.html:21
|
||||
#: order/templates/order/order_base.html:66
|
||||
#: order/templates/order/order_base.html:67
|
||||
#: order/templates/order/order_wizard/select_pos.html:30
|
||||
#: stock/templates/stock/item_base.html:149
|
||||
#: stock/templates/stock/item_base.html:150
|
||||
msgid "Supplier"
|
||||
msgstr ""
|
||||
|
||||
@ -669,13 +692,13 @@ msgstr ""
|
||||
msgid "Delete Parts"
|
||||
msgstr ""
|
||||
|
||||
#: company/templates/company/detail_part.html:73
|
||||
#: company/templates/company/detail_part.html:88
|
||||
#: company/templates/company/supplier_part_base.html:45
|
||||
#: company/templates/company/supplier_part_detail.html:22
|
||||
msgid "SKU"
|
||||
msgstr ""
|
||||
|
||||
#: company/templates/company/detail_part.html:90
|
||||
#: company/templates/company/detail_part.html:105
|
||||
msgid "Link"
|
||||
msgstr ""
|
||||
|
||||
@ -727,7 +750,7 @@ msgstr ""
|
||||
|
||||
#: company/templates/company/supplier_part_base.html:6
|
||||
#: company/templates/company/supplier_part_base.html:13
|
||||
#: stock/templates/stock/item_base.html:154
|
||||
#: stock/templates/stock/item_base.html:155
|
||||
msgid "Supplier Part"
|
||||
msgstr ""
|
||||
|
||||
@ -748,7 +771,7 @@ msgstr ""
|
||||
|
||||
#: company/templates/company/supplier_part_base.html:57
|
||||
#: company/templates/company/supplier_part_detail.html:34
|
||||
#: order/templates/order/purchase_order_detail.html:33
|
||||
#: order/templates/order/purchase_order_detail.html:34
|
||||
msgid "Note"
|
||||
msgstr ""
|
||||
|
||||
@ -930,7 +953,7 @@ msgstr ""
|
||||
msgid "Line item notes"
|
||||
msgstr ""
|
||||
|
||||
#: order/models.py:298 stock/templates/stock/item_base.html:128
|
||||
#: order/models.py:298 stock/templates/stock/item_base.html:129
|
||||
msgid "Purchase Order"
|
||||
msgstr ""
|
||||
|
||||
@ -942,16 +965,16 @@ msgstr ""
|
||||
msgid "Number of items received"
|
||||
msgstr ""
|
||||
|
||||
#: order/templates/order/order_base.html:61
|
||||
#: order/templates/order/order_base.html:62
|
||||
msgid "Purchase Order Details"
|
||||
msgstr ""
|
||||
|
||||
#: order/templates/order/order_base.html:89
|
||||
#: order/templates/order/order_base.html:90
|
||||
msgid "Issued"
|
||||
msgstr ""
|
||||
|
||||
#: order/templates/order/order_base.html:96
|
||||
#: order/templates/order/purchase_order_detail.html:31
|
||||
#: order/templates/order/order_base.html:97
|
||||
#: order/templates/order/purchase_order_detail.html:32
|
||||
msgid "Received"
|
||||
msgstr ""
|
||||
|
||||
@ -1035,23 +1058,23 @@ msgstr ""
|
||||
msgid "Are you sure you want to delete this attachment?"
|
||||
msgstr ""
|
||||
|
||||
#: order/templates/order/purchase_order_detail.html:15 order/views.py:825
|
||||
#: order/templates/order/purchase_order_detail.html:16 order/views.py:825
|
||||
msgid "Add Line Item"
|
||||
msgstr ""
|
||||
|
||||
#: order/templates/order/purchase_order_detail.html:19
|
||||
#: order/templates/order/purchase_order_detail.html:20
|
||||
msgid "Order Items"
|
||||
msgstr ""
|
||||
|
||||
#: order/templates/order/purchase_order_detail.html:24
|
||||
#: order/templates/order/purchase_order_detail.html:25
|
||||
msgid "Line"
|
||||
msgstr ""
|
||||
|
||||
#: order/templates/order/purchase_order_detail.html:27
|
||||
#: order/templates/order/purchase_order_detail.html:28
|
||||
msgid "Order Code"
|
||||
msgstr ""
|
||||
|
||||
#: order/templates/order/purchase_order_detail.html:28
|
||||
#: order/templates/order/purchase_order_detail.html:29
|
||||
msgid "Reference"
|
||||
msgstr ""
|
||||
|
||||
@ -1324,63 +1347,63 @@ msgstr ""
|
||||
msgid "Stored BOM checksum"
|
||||
msgstr ""
|
||||
|
||||
#: part/models.py:1040
|
||||
#: part/models.py:1049
|
||||
msgid "Parameter template name must be unique"
|
||||
msgstr ""
|
||||
|
||||
#: part/models.py:1045
|
||||
#: part/models.py:1054
|
||||
msgid "Parameter Name"
|
||||
msgstr ""
|
||||
|
||||
#: part/models.py:1047
|
||||
#: part/models.py:1056
|
||||
msgid "Parameter Units"
|
||||
msgstr ""
|
||||
|
||||
#: part/models.py:1073
|
||||
#: part/models.py:1082
|
||||
msgid "Parent Part"
|
||||
msgstr ""
|
||||
|
||||
#: part/models.py:1075
|
||||
#: part/models.py:1084
|
||||
msgid "Parameter Template"
|
||||
msgstr ""
|
||||
|
||||
#: part/models.py:1077
|
||||
#: part/models.py:1086
|
||||
msgid "Parameter Value"
|
||||
msgstr ""
|
||||
|
||||
#: part/models.py:1101
|
||||
#: part/models.py:1110
|
||||
msgid "Select parent part"
|
||||
msgstr ""
|
||||
|
||||
#: part/models.py:1110
|
||||
#: part/models.py:1119
|
||||
msgid "Select part to be used in BOM"
|
||||
msgstr ""
|
||||
|
||||
#: part/models.py:1117
|
||||
#: part/models.py:1126
|
||||
msgid "BOM quantity for this BOM item"
|
||||
msgstr ""
|
||||
|
||||
#: part/models.py:1120
|
||||
#: part/models.py:1129
|
||||
msgid "Estimated build wastage quantity (absolute or percentage)"
|
||||
msgstr ""
|
||||
|
||||
#: part/models.py:1123
|
||||
#: part/models.py:1132
|
||||
msgid "BOM item reference"
|
||||
msgstr ""
|
||||
|
||||
#: part/models.py:1126
|
||||
#: part/models.py:1135
|
||||
msgid "BOM item notes"
|
||||
msgstr ""
|
||||
|
||||
#: part/models.py:1128
|
||||
#: part/models.py:1137
|
||||
msgid "BOM line checksum"
|
||||
msgstr ""
|
||||
|
||||
#: part/models.py:1191
|
||||
#: part/models.py:1200
|
||||
msgid "Part cannot be added to its own Bill of Materials"
|
||||
msgstr ""
|
||||
|
||||
#: part/models.py:1198
|
||||
#: part/models.py:1207
|
||||
#, python-brace-format
|
||||
msgid "Part '{p1}' is used in BOM for '{p2}' (recursive)"
|
||||
msgstr ""
|
||||
@ -1430,7 +1453,7 @@ msgstr ""
|
||||
msgid "Part Details"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/detail.html:25 part/templates/part/part_base.html:77
|
||||
#: part/templates/part/detail.html:25 part/templates/part/part_base.html:78
|
||||
msgid "IPN"
|
||||
msgstr ""
|
||||
|
||||
@ -1482,7 +1505,7 @@ msgstr ""
|
||||
msgid "Part is not a virtual part"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/detail.html:132
|
||||
#: part/templates/part/detail.html:132 templates/table_filters.html:86
|
||||
msgid "Assembly"
|
||||
msgstr ""
|
||||
|
||||
@ -1494,7 +1517,7 @@ msgstr ""
|
||||
msgid "Part cannot be assembled from other parts"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/detail.html:141
|
||||
#: part/templates/part/detail.html:141 templates/table_filters.html:90
|
||||
msgid "Component"
|
||||
msgstr ""
|
||||
|
||||
@ -1554,31 +1577,43 @@ msgstr ""
|
||||
msgid "This part is not active"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/part_base.html:37
|
||||
#: part/templates/part/part_base.html:16
|
||||
msgid "This part is a template part."
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/part_base.html:18
|
||||
msgid "It is not a real part, but real parts can be based on this template."
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/part_base.html:23
|
||||
msgid "This part is a variant of"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/part_base.html:38
|
||||
msgid "Star this part"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/part_base.html:43
|
||||
#: part/templates/part/part_base.html:44
|
||||
msgid "Show pricing information"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/part_base.html:98
|
||||
#: part/templates/part/part_base.html:101
|
||||
msgid "Available Stock"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/part_base.html:103
|
||||
#: part/templates/part/part_base.html:107
|
||||
msgid "In Stock"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/part_base.html:124
|
||||
#: part/templates/part/part_base.html:131
|
||||
msgid "Build Status"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/part_base.html:128
|
||||
#: part/templates/part/part_base.html:136
|
||||
msgid "Can Build"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/part_base.html:133
|
||||
#: part/templates/part/part_base.html:142
|
||||
msgid "Underway"
|
||||
msgstr ""
|
||||
|
||||
@ -1618,11 +1653,6 @@ msgstr ""
|
||||
msgid "BOM"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/tabs.html:28 stock/templates/stock/item_base.html:121
|
||||
#: templates/navbar.html:12
|
||||
msgid "Build"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/tabs.html:32
|
||||
msgid "Used In"
|
||||
msgstr ""
|
||||
@ -1938,51 +1968,51 @@ msgstr ""
|
||||
msgid "Stock Tracking Information"
|
||||
msgstr ""
|
||||
|
||||
#: stock/templates/stock/item_base.html:10
|
||||
#: stock/templates/stock/item_base.html:11
|
||||
msgid "Stock Item Details"
|
||||
msgstr ""
|
||||
|
||||
#: stock/templates/stock/item_base.html:55
|
||||
#: stock/templates/stock/item_base.html:56
|
||||
msgid ""
|
||||
"This stock item is serialized - it has a unique serial number and the "
|
||||
"quantity cannot be adjusted."
|
||||
msgstr ""
|
||||
|
||||
#: stock/templates/stock/item_base.html:59
|
||||
#: stock/templates/stock/item_base.html:60
|
||||
msgid "This stock item cannot be deleted as it has child items"
|
||||
msgstr ""
|
||||
|
||||
#: stock/templates/stock/item_base.html:63
|
||||
#: stock/templates/stock/item_base.html:64
|
||||
msgid ""
|
||||
"This stock item will be automatically deleted when all stock is depleted."
|
||||
msgstr ""
|
||||
|
||||
#: stock/templates/stock/item_base.html:68
|
||||
#: stock/templates/stock/item_base.html:69
|
||||
msgid "This stock item was split from "
|
||||
msgstr ""
|
||||
|
||||
#: stock/templates/stock/item_base.html:88
|
||||
#: stock/templates/stock/item_base.html:89
|
||||
msgid "Belongs To"
|
||||
msgstr ""
|
||||
|
||||
#: stock/templates/stock/item_base.html:94
|
||||
#: stock/templates/stock/item_base.html:95
|
||||
#: stock/templates/stock/stock_adjust.html:17
|
||||
msgid "Location"
|
||||
msgstr ""
|
||||
|
||||
#: stock/templates/stock/item_base.html:101
|
||||
#: stock/templates/stock/item_base.html:102
|
||||
msgid "Serial Number"
|
||||
msgstr ""
|
||||
|
||||
#: stock/templates/stock/item_base.html:160
|
||||
#: stock/templates/stock/item_base.html:161
|
||||
msgid "Last Updated"
|
||||
msgstr ""
|
||||
|
||||
#: stock/templates/stock/item_base.html:165
|
||||
#: stock/templates/stock/item_base.html:166
|
||||
msgid "Last Stocktake"
|
||||
msgstr ""
|
||||
|
||||
#: stock/templates/stock/item_base.html:169
|
||||
#: stock/templates/stock/item_base.html:170
|
||||
msgid "No stocktake performed"
|
||||
msgstr ""
|
||||
|
||||
@ -2280,3 +2310,55 @@ msgstr ""
|
||||
#: templates/stock_table.html:17
|
||||
msgid "Delete Stock"
|
||||
msgstr ""
|
||||
|
||||
#: templates/table_filters.html:22
|
||||
msgid "Include sublocations"
|
||||
msgstr ""
|
||||
|
||||
#: templates/table_filters.html:23
|
||||
msgid "Include stock in sublocations"
|
||||
msgstr ""
|
||||
|
||||
#: templates/table_filters.html:27
|
||||
msgid "Active parts"
|
||||
msgstr ""
|
||||
|
||||
#: templates/table_filters.html:28
|
||||
msgid "Show stock for active parts"
|
||||
msgstr ""
|
||||
|
||||
#: templates/table_filters.html:32 templates/table_filters.html:33
|
||||
msgid "Stock status"
|
||||
msgstr ""
|
||||
|
||||
#: templates/table_filters.html:53
|
||||
msgid "Order status"
|
||||
msgstr ""
|
||||
|
||||
#: templates/table_filters.html:64
|
||||
msgid "Include subcategories"
|
||||
msgstr ""
|
||||
|
||||
#: templates/table_filters.html:65
|
||||
msgid "Include parts in subcategories"
|
||||
msgstr ""
|
||||
|
||||
#: templates/table_filters.html:69
|
||||
msgid "Active"
|
||||
msgstr ""
|
||||
|
||||
#: templates/table_filters.html:70
|
||||
msgid "Show active parts"
|
||||
msgstr ""
|
||||
|
||||
#: templates/table_filters.html:74
|
||||
msgid "Template"
|
||||
msgstr ""
|
||||
|
||||
#: templates/table_filters.html:78
|
||||
msgid "Stock available"
|
||||
msgstr ""
|
||||
|
||||
#: templates/table_filters.html:82
|
||||
msgid "Low stock"
|
||||
msgstr ""
|
||||
|
@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2020-04-09 15:04+0000\n"
|
||||
"POT-Creation-Date: 2020-04-11 15:00+0000\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
@ -18,30 +18,30 @@ msgstr ""
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
#: InvenTree/helpers.py:240 order/models.py:164 order/models.py:215
|
||||
#: InvenTree/helpers.py:259 order/models.py:164 order/models.py:215
|
||||
msgid "Invalid quantity provided"
|
||||
msgstr ""
|
||||
|
||||
#: InvenTree/helpers.py:243
|
||||
#: InvenTree/helpers.py:262
|
||||
msgid "Empty serial number string"
|
||||
msgstr ""
|
||||
|
||||
#: InvenTree/helpers.py:264 InvenTree/helpers.py:281
|
||||
#: InvenTree/helpers.py:283 InvenTree/helpers.py:300
|
||||
#, python-brace-format
|
||||
msgid "Duplicate serial: {n}"
|
||||
msgstr ""
|
||||
|
||||
#: InvenTree/helpers.py:268 InvenTree/helpers.py:271 InvenTree/helpers.py:274
|
||||
#: InvenTree/helpers.py:285
|
||||
#: InvenTree/helpers.py:287 InvenTree/helpers.py:290 InvenTree/helpers.py:293
|
||||
#: InvenTree/helpers.py:304
|
||||
#, python-brace-format
|
||||
msgid "Invalid group: {g}"
|
||||
msgstr ""
|
||||
|
||||
#: InvenTree/helpers.py:291
|
||||
#: InvenTree/helpers.py:310
|
||||
msgid "No serial numbers found"
|
||||
msgstr ""
|
||||
|
||||
#: InvenTree/helpers.py:295
|
||||
#: InvenTree/helpers.py:314
|
||||
#, python-brace-format
|
||||
msgid "Number of unique serial number ({s}) must match quantity ({q})"
|
||||
msgstr ""
|
||||
@ -70,49 +70,49 @@ msgstr ""
|
||||
msgid "Polish"
|
||||
msgstr ""
|
||||
|
||||
#: InvenTree/status_codes.py:36 InvenTree/status_codes.py:97
|
||||
#: InvenTree/status_codes.py:86 InvenTree/status_codes.py:162
|
||||
msgid "Pending"
|
||||
msgstr ""
|
||||
|
||||
#: InvenTree/status_codes.py:37
|
||||
#: InvenTree/status_codes.py:87
|
||||
msgid "Placed"
|
||||
msgstr ""
|
||||
|
||||
#: InvenTree/status_codes.py:38 InvenTree/status_codes.py:100
|
||||
#: InvenTree/status_codes.py:88 InvenTree/status_codes.py:165
|
||||
msgid "Complete"
|
||||
msgstr ""
|
||||
|
||||
#: InvenTree/status_codes.py:39 InvenTree/status_codes.py:99
|
||||
#: InvenTree/status_codes.py:89 InvenTree/status_codes.py:164
|
||||
msgid "Cancelled"
|
||||
msgstr ""
|
||||
|
||||
#: InvenTree/status_codes.py:40 InvenTree/status_codes.py:71
|
||||
#: InvenTree/status_codes.py:90 InvenTree/status_codes.py:130
|
||||
msgid "Lost"
|
||||
msgstr ""
|
||||
|
||||
#: InvenTree/status_codes.py:41
|
||||
#: InvenTree/status_codes.py:91
|
||||
msgid "Returned"
|
||||
msgstr ""
|
||||
|
||||
#: InvenTree/status_codes.py:67
|
||||
#: InvenTree/status_codes.py:126
|
||||
msgid "OK"
|
||||
msgstr ""
|
||||
|
||||
#: InvenTree/status_codes.py:68
|
||||
#: InvenTree/status_codes.py:127
|
||||
msgid "Attention needed"
|
||||
msgstr ""
|
||||
|
||||
#: InvenTree/status_codes.py:69
|
||||
#: InvenTree/status_codes.py:128
|
||||
msgid "Damaged"
|
||||
msgstr ""
|
||||
|
||||
#: InvenTree/status_codes.py:70
|
||||
#: InvenTree/status_codes.py:129
|
||||
msgid "Destroyed"
|
||||
msgstr ""
|
||||
|
||||
#: InvenTree/status_codes.py:98 build/templates/build/allocate_edit.html:28
|
||||
#: InvenTree/status_codes.py:163 build/templates/build/allocate_edit.html:28
|
||||
#: build/templates/build/allocate_view.html:21
|
||||
#: part/templates/part/part_base.html:109 part/templates/part/tabs.html:21
|
||||
#: part/templates/part/part_base.html:114 part/templates/part/tabs.html:21
|
||||
msgid "Allocated"
|
||||
msgstr ""
|
||||
|
||||
@ -175,7 +175,7 @@ msgstr ""
|
||||
msgid "Number of parts to build"
|
||||
msgstr ""
|
||||
|
||||
#: build/models.py:82
|
||||
#: build/models.py:82 templates/table_filters.html:42
|
||||
msgid "Build status"
|
||||
msgstr ""
|
||||
|
||||
@ -227,10 +227,10 @@ msgstr ""
|
||||
|
||||
#: build/templates/build/allocate_edit.html:19
|
||||
#: build/templates/build/allocate_view.html:17
|
||||
#: build/templates/build/detail.html:21
|
||||
#: build/templates/build/detail.html:22
|
||||
#: company/templates/company/detail_part.html:65
|
||||
#: order/templates/order/order_wizard/select_parts.html:30
|
||||
#: order/templates/order/purchase_order_detail.html:25
|
||||
#: order/templates/order/purchase_order_detail.html:26
|
||||
#: part/templates/part/part_app_base.html:7
|
||||
msgid "Part"
|
||||
msgstr ""
|
||||
@ -262,16 +262,58 @@ msgstr ""
|
||||
#: company/templates/company/index.html:54
|
||||
#: company/templates/company/supplier_part_base.html:50
|
||||
#: company/templates/company/supplier_part_detail.html:27
|
||||
#: order/templates/order/purchase_order_detail.html:26
|
||||
#: order/templates/order/purchase_order_detail.html:27
|
||||
#: part/templates/part/detail.html:38
|
||||
msgid "Description"
|
||||
msgstr ""
|
||||
|
||||
#: build/templates/build/allocate_view.html:22
|
||||
#: part/templates/part/part_base.html:115
|
||||
#: part/templates/part/part_base.html:121
|
||||
msgid "On Order"
|
||||
msgstr ""
|
||||
|
||||
#: build/templates/build/build_base.html:27 part/templates/part/tabs.html:28
|
||||
#: stock/templates/stock/item_base.html:122 templates/navbar.html:12
|
||||
msgid "Build"
|
||||
msgstr ""
|
||||
|
||||
#: build/templates/build/build_base.html:52 build/templates/build/detail.html:9
|
||||
msgid "Build Details"
|
||||
msgstr ""
|
||||
|
||||
#: build/templates/build/build_base.html:56
|
||||
msgid "Build Title"
|
||||
msgstr ""
|
||||
|
||||
#: build/templates/build/build_base.html:66
|
||||
#: build/templates/build/detail.html:27
|
||||
#: company/templates/company/supplier_part_pricing.html:27
|
||||
#: order/templates/order/order_wizard/select_parts.html:32
|
||||
#: order/templates/order/purchase_order_detail.html:30
|
||||
#: stock/templates/stock/item_base.html:108
|
||||
#: stock/templates/stock/stock_adjust.html:18
|
||||
msgid "Quantity"
|
||||
msgstr ""
|
||||
|
||||
#: build/templates/build/build_base.html:71
|
||||
#: build/templates/build/detail.html:42
|
||||
#: order/templates/order/order_base.html:72
|
||||
#: stock/templates/stock/item_base.html:175
|
||||
msgid "Status"
|
||||
msgstr ""
|
||||
|
||||
#: build/templates/build/build_base.html:76
|
||||
msgid "BOM Price"
|
||||
msgstr ""
|
||||
|
||||
#: build/templates/build/build_base.html:81
|
||||
msgid "BOM pricing is incomplete"
|
||||
msgstr ""
|
||||
|
||||
#: build/templates/build/build_base.html:84
|
||||
msgid "No pricing information"
|
||||
msgstr ""
|
||||
|
||||
#: build/templates/build/build_output.html:9
|
||||
msgid "Build Outputs"
|
||||
msgstr ""
|
||||
@ -280,68 +322,49 @@ msgstr ""
|
||||
msgid "Are you sure you want to unallocate these parts?"
|
||||
msgstr ""
|
||||
|
||||
#: build/templates/build/detail.html:8
|
||||
msgid "Build Details"
|
||||
msgstr ""
|
||||
|
||||
#: build/templates/build/detail.html:16
|
||||
#: build/templates/build/detail.html:17
|
||||
msgid "Title"
|
||||
msgstr ""
|
||||
|
||||
#: build/templates/build/detail.html:26
|
||||
#: company/templates/company/supplier_part_pricing.html:27
|
||||
#: order/templates/order/order_wizard/select_parts.html:32
|
||||
#: order/templates/order/purchase_order_detail.html:29
|
||||
#: stock/templates/stock/item_base.html:107
|
||||
#: stock/templates/stock/stock_adjust.html:18
|
||||
msgid "Quantity"
|
||||
msgstr ""
|
||||
|
||||
#: build/templates/build/detail.html:30
|
||||
#: build/templates/build/detail.html:31
|
||||
msgid "Stock Source"
|
||||
msgstr ""
|
||||
|
||||
#: build/templates/build/detail.html:35
|
||||
#: build/templates/build/detail.html:36
|
||||
msgid "Stock can be taken from any available location."
|
||||
msgstr ""
|
||||
|
||||
#: build/templates/build/detail.html:41
|
||||
#: order/templates/order/order_base.html:71
|
||||
#: stock/templates/stock/item_base.html:174
|
||||
msgid "Status"
|
||||
msgstr ""
|
||||
|
||||
#: build/templates/build/detail.html:47
|
||||
#: stock/templates/stock/item_base.html:114
|
||||
#: build/templates/build/detail.html:48
|
||||
#: stock/templates/stock/item_base.html:115
|
||||
msgid "Batch"
|
||||
msgstr ""
|
||||
|
||||
#: build/templates/build/detail.html:54
|
||||
#: build/templates/build/detail.html:55
|
||||
#: company/templates/company/supplier_part_base.html:47
|
||||
#: company/templates/company/supplier_part_detail.html:24
|
||||
#: part/templates/part/detail.html:67 part/templates/part/part_base.html:84
|
||||
#: stock/templates/stock/item_base.html:142
|
||||
#: part/templates/part/detail.html:67 part/templates/part/part_base.html:85
|
||||
#: stock/templates/stock/item_base.html:143
|
||||
msgid "External Link"
|
||||
msgstr ""
|
||||
|
||||
#: build/templates/build/detail.html:60
|
||||
#: order/templates/order/order_base.html:83
|
||||
#: build/templates/build/detail.html:61
|
||||
#: order/templates/order/order_base.html:84
|
||||
msgid "Created"
|
||||
msgstr ""
|
||||
|
||||
#: build/templates/build/detail.html:66
|
||||
#: build/templates/build/detail.html:67
|
||||
msgid "Enough Parts?"
|
||||
msgstr ""
|
||||
|
||||
#: build/templates/build/detail.html:69
|
||||
#: build/templates/build/detail.html:70
|
||||
msgid "Yes"
|
||||
msgstr ""
|
||||
|
||||
#: build/templates/build/detail.html:71
|
||||
#: build/templates/build/detail.html:72
|
||||
msgid "No"
|
||||
msgstr ""
|
||||
|
||||
#: build/templates/build/detail.html:79
|
||||
#: build/templates/build/detail.html:80
|
||||
msgid "Completed"
|
||||
msgstr ""
|
||||
|
||||
@ -581,7 +604,7 @@ msgstr ""
|
||||
msgid "Supplier stock keeping unit"
|
||||
msgstr ""
|
||||
|
||||
#: company/models.py:256 company/templates/company/detail_part.html:81
|
||||
#: company/models.py:256 company/templates/company/detail_part.html:96
|
||||
#: company/templates/company/supplier_part_base.html:53
|
||||
#: company/templates/company/supplier_part_detail.html:30
|
||||
msgid "Manufacturer"
|
||||
@ -637,7 +660,7 @@ msgid "Company Details"
|
||||
msgstr ""
|
||||
|
||||
#: company/templates/company/detail.html:16
|
||||
#: stock/templates/stock/item_base.html:135
|
||||
#: stock/templates/stock/item_base.html:136
|
||||
msgid "Customer"
|
||||
msgstr ""
|
||||
|
||||
@ -645,9 +668,9 @@ msgstr ""
|
||||
#: company/templates/company/index.html:46
|
||||
#: company/templates/company/supplier_part_base.html:44
|
||||
#: company/templates/company/supplier_part_detail.html:21
|
||||
#: order/templates/order/order_base.html:66
|
||||
#: order/templates/order/order_base.html:67
|
||||
#: order/templates/order/order_wizard/select_pos.html:30
|
||||
#: stock/templates/stock/item_base.html:149
|
||||
#: stock/templates/stock/item_base.html:150
|
||||
msgid "Supplier"
|
||||
msgstr ""
|
||||
|
||||
@ -669,13 +692,13 @@ msgstr ""
|
||||
msgid "Delete Parts"
|
||||
msgstr ""
|
||||
|
||||
#: company/templates/company/detail_part.html:73
|
||||
#: company/templates/company/detail_part.html:88
|
||||
#: company/templates/company/supplier_part_base.html:45
|
||||
#: company/templates/company/supplier_part_detail.html:22
|
||||
msgid "SKU"
|
||||
msgstr ""
|
||||
|
||||
#: company/templates/company/detail_part.html:90
|
||||
#: company/templates/company/detail_part.html:105
|
||||
msgid "Link"
|
||||
msgstr ""
|
||||
|
||||
@ -727,7 +750,7 @@ msgstr ""
|
||||
|
||||
#: company/templates/company/supplier_part_base.html:6
|
||||
#: company/templates/company/supplier_part_base.html:13
|
||||
#: stock/templates/stock/item_base.html:154
|
||||
#: stock/templates/stock/item_base.html:155
|
||||
msgid "Supplier Part"
|
||||
msgstr ""
|
||||
|
||||
@ -748,7 +771,7 @@ msgstr ""
|
||||
|
||||
#: company/templates/company/supplier_part_base.html:57
|
||||
#: company/templates/company/supplier_part_detail.html:34
|
||||
#: order/templates/order/purchase_order_detail.html:33
|
||||
#: order/templates/order/purchase_order_detail.html:34
|
||||
msgid "Note"
|
||||
msgstr ""
|
||||
|
||||
@ -930,7 +953,7 @@ msgstr ""
|
||||
msgid "Line item notes"
|
||||
msgstr ""
|
||||
|
||||
#: order/models.py:298 stock/templates/stock/item_base.html:128
|
||||
#: order/models.py:298 stock/templates/stock/item_base.html:129
|
||||
msgid "Purchase Order"
|
||||
msgstr ""
|
||||
|
||||
@ -942,16 +965,16 @@ msgstr ""
|
||||
msgid "Number of items received"
|
||||
msgstr ""
|
||||
|
||||
#: order/templates/order/order_base.html:61
|
||||
#: order/templates/order/order_base.html:62
|
||||
msgid "Purchase Order Details"
|
||||
msgstr ""
|
||||
|
||||
#: order/templates/order/order_base.html:89
|
||||
#: order/templates/order/order_base.html:90
|
||||
msgid "Issued"
|
||||
msgstr ""
|
||||
|
||||
#: order/templates/order/order_base.html:96
|
||||
#: order/templates/order/purchase_order_detail.html:31
|
||||
#: order/templates/order/order_base.html:97
|
||||
#: order/templates/order/purchase_order_detail.html:32
|
||||
msgid "Received"
|
||||
msgstr ""
|
||||
|
||||
@ -1035,23 +1058,23 @@ msgstr ""
|
||||
msgid "Are you sure you want to delete this attachment?"
|
||||
msgstr ""
|
||||
|
||||
#: order/templates/order/purchase_order_detail.html:15 order/views.py:825
|
||||
#: order/templates/order/purchase_order_detail.html:16 order/views.py:825
|
||||
msgid "Add Line Item"
|
||||
msgstr ""
|
||||
|
||||
#: order/templates/order/purchase_order_detail.html:19
|
||||
#: order/templates/order/purchase_order_detail.html:20
|
||||
msgid "Order Items"
|
||||
msgstr ""
|
||||
|
||||
#: order/templates/order/purchase_order_detail.html:24
|
||||
#: order/templates/order/purchase_order_detail.html:25
|
||||
msgid "Line"
|
||||
msgstr ""
|
||||
|
||||
#: order/templates/order/purchase_order_detail.html:27
|
||||
#: order/templates/order/purchase_order_detail.html:28
|
||||
msgid "Order Code"
|
||||
msgstr ""
|
||||
|
||||
#: order/templates/order/purchase_order_detail.html:28
|
||||
#: order/templates/order/purchase_order_detail.html:29
|
||||
msgid "Reference"
|
||||
msgstr ""
|
||||
|
||||
@ -1324,63 +1347,63 @@ msgstr ""
|
||||
msgid "Stored BOM checksum"
|
||||
msgstr ""
|
||||
|
||||
#: part/models.py:1040
|
||||
#: part/models.py:1049
|
||||
msgid "Parameter template name must be unique"
|
||||
msgstr ""
|
||||
|
||||
#: part/models.py:1045
|
||||
#: part/models.py:1054
|
||||
msgid "Parameter Name"
|
||||
msgstr ""
|
||||
|
||||
#: part/models.py:1047
|
||||
#: part/models.py:1056
|
||||
msgid "Parameter Units"
|
||||
msgstr ""
|
||||
|
||||
#: part/models.py:1073
|
||||
#: part/models.py:1082
|
||||
msgid "Parent Part"
|
||||
msgstr ""
|
||||
|
||||
#: part/models.py:1075
|
||||
#: part/models.py:1084
|
||||
msgid "Parameter Template"
|
||||
msgstr ""
|
||||
|
||||
#: part/models.py:1077
|
||||
#: part/models.py:1086
|
||||
msgid "Parameter Value"
|
||||
msgstr ""
|
||||
|
||||
#: part/models.py:1101
|
||||
#: part/models.py:1110
|
||||
msgid "Select parent part"
|
||||
msgstr ""
|
||||
|
||||
#: part/models.py:1110
|
||||
#: part/models.py:1119
|
||||
msgid "Select part to be used in BOM"
|
||||
msgstr ""
|
||||
|
||||
#: part/models.py:1117
|
||||
#: part/models.py:1126
|
||||
msgid "BOM quantity for this BOM item"
|
||||
msgstr ""
|
||||
|
||||
#: part/models.py:1120
|
||||
#: part/models.py:1129
|
||||
msgid "Estimated build wastage quantity (absolute or percentage)"
|
||||
msgstr ""
|
||||
|
||||
#: part/models.py:1123
|
||||
#: part/models.py:1132
|
||||
msgid "BOM item reference"
|
||||
msgstr ""
|
||||
|
||||
#: part/models.py:1126
|
||||
#: part/models.py:1135
|
||||
msgid "BOM item notes"
|
||||
msgstr ""
|
||||
|
||||
#: part/models.py:1128
|
||||
#: part/models.py:1137
|
||||
msgid "BOM line checksum"
|
||||
msgstr ""
|
||||
|
||||
#: part/models.py:1191
|
||||
#: part/models.py:1200
|
||||
msgid "Part cannot be added to its own Bill of Materials"
|
||||
msgstr ""
|
||||
|
||||
#: part/models.py:1198
|
||||
#: part/models.py:1207
|
||||
#, python-brace-format
|
||||
msgid "Part '{p1}' is used in BOM for '{p2}' (recursive)"
|
||||
msgstr ""
|
||||
@ -1430,7 +1453,7 @@ msgstr ""
|
||||
msgid "Part Details"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/detail.html:25 part/templates/part/part_base.html:77
|
||||
#: part/templates/part/detail.html:25 part/templates/part/part_base.html:78
|
||||
msgid "IPN"
|
||||
msgstr ""
|
||||
|
||||
@ -1482,7 +1505,7 @@ msgstr ""
|
||||
msgid "Part is not a virtual part"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/detail.html:132
|
||||
#: part/templates/part/detail.html:132 templates/table_filters.html:86
|
||||
msgid "Assembly"
|
||||
msgstr ""
|
||||
|
||||
@ -1494,7 +1517,7 @@ msgstr ""
|
||||
msgid "Part cannot be assembled from other parts"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/detail.html:141
|
||||
#: part/templates/part/detail.html:141 templates/table_filters.html:90
|
||||
msgid "Component"
|
||||
msgstr ""
|
||||
|
||||
@ -1554,31 +1577,43 @@ msgstr ""
|
||||
msgid "This part is not active"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/part_base.html:37
|
||||
#: part/templates/part/part_base.html:16
|
||||
msgid "This part is a template part."
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/part_base.html:18
|
||||
msgid "It is not a real part, but real parts can be based on this template."
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/part_base.html:23
|
||||
msgid "This part is a variant of"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/part_base.html:38
|
||||
msgid "Star this part"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/part_base.html:43
|
||||
#: part/templates/part/part_base.html:44
|
||||
msgid "Show pricing information"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/part_base.html:98
|
||||
#: part/templates/part/part_base.html:101
|
||||
msgid "Available Stock"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/part_base.html:103
|
||||
#: part/templates/part/part_base.html:107
|
||||
msgid "In Stock"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/part_base.html:124
|
||||
#: part/templates/part/part_base.html:131
|
||||
msgid "Build Status"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/part_base.html:128
|
||||
#: part/templates/part/part_base.html:136
|
||||
msgid "Can Build"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/part_base.html:133
|
||||
#: part/templates/part/part_base.html:142
|
||||
msgid "Underway"
|
||||
msgstr ""
|
||||
|
||||
@ -1618,11 +1653,6 @@ msgstr ""
|
||||
msgid "BOM"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/tabs.html:28 stock/templates/stock/item_base.html:121
|
||||
#: templates/navbar.html:12
|
||||
msgid "Build"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/tabs.html:32
|
||||
msgid "Used In"
|
||||
msgstr ""
|
||||
@ -1938,51 +1968,51 @@ msgstr ""
|
||||
msgid "Stock Tracking Information"
|
||||
msgstr ""
|
||||
|
||||
#: stock/templates/stock/item_base.html:10
|
||||
#: stock/templates/stock/item_base.html:11
|
||||
msgid "Stock Item Details"
|
||||
msgstr ""
|
||||
|
||||
#: stock/templates/stock/item_base.html:55
|
||||
#: stock/templates/stock/item_base.html:56
|
||||
msgid ""
|
||||
"This stock item is serialized - it has a unique serial number and the "
|
||||
"quantity cannot be adjusted."
|
||||
msgstr ""
|
||||
|
||||
#: stock/templates/stock/item_base.html:59
|
||||
#: stock/templates/stock/item_base.html:60
|
||||
msgid "This stock item cannot be deleted as it has child items"
|
||||
msgstr ""
|
||||
|
||||
#: stock/templates/stock/item_base.html:63
|
||||
#: stock/templates/stock/item_base.html:64
|
||||
msgid ""
|
||||
"This stock item will be automatically deleted when all stock is depleted."
|
||||
msgstr ""
|
||||
|
||||
#: stock/templates/stock/item_base.html:68
|
||||
#: stock/templates/stock/item_base.html:69
|
||||
msgid "This stock item was split from "
|
||||
msgstr ""
|
||||
|
||||
#: stock/templates/stock/item_base.html:88
|
||||
#: stock/templates/stock/item_base.html:89
|
||||
msgid "Belongs To"
|
||||
msgstr ""
|
||||
|
||||
#: stock/templates/stock/item_base.html:94
|
||||
#: stock/templates/stock/item_base.html:95
|
||||
#: stock/templates/stock/stock_adjust.html:17
|
||||
msgid "Location"
|
||||
msgstr ""
|
||||
|
||||
#: stock/templates/stock/item_base.html:101
|
||||
#: stock/templates/stock/item_base.html:102
|
||||
msgid "Serial Number"
|
||||
msgstr ""
|
||||
|
||||
#: stock/templates/stock/item_base.html:160
|
||||
#: stock/templates/stock/item_base.html:161
|
||||
msgid "Last Updated"
|
||||
msgstr ""
|
||||
|
||||
#: stock/templates/stock/item_base.html:165
|
||||
#: stock/templates/stock/item_base.html:166
|
||||
msgid "Last Stocktake"
|
||||
msgstr ""
|
||||
|
||||
#: stock/templates/stock/item_base.html:169
|
||||
#: stock/templates/stock/item_base.html:170
|
||||
msgid "No stocktake performed"
|
||||
msgstr ""
|
||||
|
||||
@ -2280,3 +2310,55 @@ msgstr ""
|
||||
#: templates/stock_table.html:17
|
||||
msgid "Delete Stock"
|
||||
msgstr ""
|
||||
|
||||
#: templates/table_filters.html:22
|
||||
msgid "Include sublocations"
|
||||
msgstr ""
|
||||
|
||||
#: templates/table_filters.html:23
|
||||
msgid "Include stock in sublocations"
|
||||
msgstr ""
|
||||
|
||||
#: templates/table_filters.html:27
|
||||
msgid "Active parts"
|
||||
msgstr ""
|
||||
|
||||
#: templates/table_filters.html:28
|
||||
msgid "Show stock for active parts"
|
||||
msgstr ""
|
||||
|
||||
#: templates/table_filters.html:32 templates/table_filters.html:33
|
||||
msgid "Stock status"
|
||||
msgstr ""
|
||||
|
||||
#: templates/table_filters.html:53
|
||||
msgid "Order status"
|
||||
msgstr ""
|
||||
|
||||
#: templates/table_filters.html:64
|
||||
msgid "Include subcategories"
|
||||
msgstr ""
|
||||
|
||||
#: templates/table_filters.html:65
|
||||
msgid "Include parts in subcategories"
|
||||
msgstr ""
|
||||
|
||||
#: templates/table_filters.html:69
|
||||
msgid "Active"
|
||||
msgstr ""
|
||||
|
||||
#: templates/table_filters.html:70
|
||||
msgid "Show active parts"
|
||||
msgstr ""
|
||||
|
||||
#: templates/table_filters.html:74
|
||||
msgid "Template"
|
||||
msgstr ""
|
||||
|
||||
#: templates/table_filters.html:78
|
||||
msgid "Stock available"
|
||||
msgstr ""
|
||||
|
||||
#: templates/table_filters.html:82
|
||||
msgid "Low stock"
|
||||
msgstr ""
|
||||
|
@ -3,6 +3,7 @@
|
||||
{% load i18n %}
|
||||
{% load static %}
|
||||
{% load inventree_extras %}
|
||||
{% load status_codes %}
|
||||
|
||||
{% block page_title %}
|
||||
InvenTree | {{ order }}
|
||||
@ -69,7 +70,7 @@ InvenTree | {{ order }}
|
||||
<tr>
|
||||
<td><span class='fas fa-info'></span></td>
|
||||
<td>{% trans "Status" %}</td>
|
||||
<td>{% include "order/order_status.html" %}</td>
|
||||
<td>{% order_status order.status %}</td>
|
||||
</tr>
|
||||
{% if order.link %}
|
||||
<tr>
|
||||
|
@ -1,13 +0,0 @@
|
||||
{% if order.status == OrderStatus.PENDING %}
|
||||
<span class='label label-large label-info'>
|
||||
{% elif order.status == OrderStatus.PLACED %}
|
||||
<span class='label label-large label-primary'>
|
||||
{% elif order.status == OrderStatus.COMPLETE %}
|
||||
<span class='label label-large label-success'>
|
||||
{% elif order.status == OrderStatus.CANCELLED or order.status == OrderStatus.RETURNED %}
|
||||
<span class='label label-large label-warning'>
|
||||
{% else %}
|
||||
<span class='label label-large label-danger'>
|
||||
{% endif %}
|
||||
{{ order.get_status_display }}
|
||||
</span>
|
@ -1,22 +0,0 @@
|
||||
<table class='table table-striped table-condensed po-table' id='po-table' {% if toolbar %}data-toolbar='{{ toolbar }}'{% endif %}>
|
||||
<thead>
|
||||
<tr>
|
||||
<th data-field='company' data-sortable='true' data-searchable='true'>Company</th>
|
||||
<th data-field='reference' data-sortable='true' data-searchable='true'>Order Reference</th>
|
||||
<th data-field='description' data-sortable='true' data-searchable='true'>Description</th>
|
||||
<th data-field='status' data-sortable='true'>Status</th>
|
||||
<th data-field='items' data-sortable='true'>Items</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for order in orders %}
|
||||
<tr>
|
||||
<td>{% include "hover_image.html" with image=order.supplier.image hover=True %}<a href="{{ order.supplier.get_absolute_url }}purchase-orders/">{{ order.supplier.name }}</a></td>
|
||||
<td><a href="{% url 'po-detail' order.id %}">{{ order }}</a></td>
|
||||
<td>{{ order.description }}</td>
|
||||
<td>{% include "order/order_status.html" %}</td>
|
||||
<td>{{ order.lines.count }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
@ -1,6 +1,7 @@
|
||||
{% extends "order/order_base.html" %}
|
||||
|
||||
{% load inventree_extras %}
|
||||
{% load status_codes %}
|
||||
{% load i18n %}
|
||||
{% load static %}
|
||||
|
||||
|
@ -13,8 +13,11 @@ InvenTree | Purchase Orders
|
||||
<hr>
|
||||
|
||||
<div id='table-buttons'>
|
||||
<div class='btn-group' style='float: right;'>
|
||||
<div class='button-toolbar container-fluid' style='float: right;'>
|
||||
<button class='btn btn-primary' type='button' id='po-create' title='Create new purchase order'>New Purchase Order</button>
|
||||
<div class='filter-list' id='filter-list-order'>
|
||||
<!-- An empty div in which the filter list will be constructed -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
44
InvenTree/order/test_api.py
Normal file
44
InvenTree/order/test_api.py
Normal file
@ -0,0 +1,44 @@
|
||||
"""
|
||||
Tests for the Order API
|
||||
"""
|
||||
|
||||
from rest_framework.test import APITestCase
|
||||
from rest_framework import status
|
||||
|
||||
from django.urls import reverse
|
||||
from django.contrib.auth import get_user_model
|
||||
|
||||
|
||||
class OrderTest(APITestCase):
|
||||
|
||||
fixtures = [
|
||||
'category',
|
||||
'part',
|
||||
'company',
|
||||
'location',
|
||||
'supplier_part',
|
||||
'stock',
|
||||
]
|
||||
|
||||
def setUp(self):
|
||||
|
||||
# Create a user for auth
|
||||
User = get_user_model()
|
||||
User.objects.create_user('testuser', 'test@testing.com', 'password')
|
||||
self.client.login(username='testuser', password='password')
|
||||
|
||||
def doGet(self, url, options=''):
|
||||
|
||||
return self.client.get(url + "?" + options, format='json')
|
||||
|
||||
def test_po_list(self,):
|
||||
|
||||
url = reverse('api-po-list')
|
||||
|
||||
# List all order items
|
||||
response = self.doGet(url)
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
|
||||
# Filter by stuff
|
||||
response = self.doGet(url, 'status=10&part=1&supplier_part=1')
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
@ -8,7 +8,8 @@ from __future__ import unicode_literals
|
||||
from django_filters.rest_framework import DjangoFilterBackend
|
||||
from django.conf import settings
|
||||
|
||||
from django.db.models import Q, Sum, Count
|
||||
from django.db.models import Q, F, Sum, Count
|
||||
from django.db.models.functions import Coalesce
|
||||
|
||||
from rest_framework import status
|
||||
from rest_framework.response import Response
|
||||
@ -19,6 +20,7 @@ from django.conf.urls import url, include
|
||||
from django.urls import reverse
|
||||
|
||||
import os
|
||||
from decimal import Decimal
|
||||
|
||||
from .models import Part, PartCategory, BomItem, PartStar
|
||||
from .models import PartParameter, PartParameterTemplate
|
||||
@ -147,6 +149,18 @@ class PartList(generics.ListCreateAPIView):
|
||||
|
||||
- GET: Return list of objects
|
||||
- POST: Create a new Part object
|
||||
|
||||
The Part object list can be filtered by:
|
||||
- category: Filter by PartCategory reference
|
||||
- cascade: If true, include parts from sub-categories
|
||||
- is_template: Is the part a template part?
|
||||
- variant_of: Filter by variant_of Part reference
|
||||
- assembly: Filter by assembly field
|
||||
- component: Filter by component field
|
||||
- trackable: Filter by trackable field
|
||||
- purchaseable: Filter by purcahseable field
|
||||
- salable: Filter by salable field
|
||||
- active: Filter by active field
|
||||
"""
|
||||
|
||||
serializer_class = part_serializers.PartSerializer
|
||||
@ -210,11 +224,39 @@ class PartList(generics.ListCreateAPIView):
|
||||
'active',
|
||||
).annotate(
|
||||
# Quantity of items which are "in stock"
|
||||
in_stock=Sum('stock_items__quantity', filter=stock_filter),
|
||||
on_order=Sum('supplier_parts__purchase_order_line_items__quantity', filter=order_filter),
|
||||
building=Sum('builds__quantity', filter=build_filter),
|
||||
in_stock=Coalesce(Sum('stock_items__quantity', filter=stock_filter), Decimal(0)),
|
||||
on_order=Coalesce(Sum('supplier_parts__purchase_order_line_items__quantity', filter=order_filter), Decimal(0)),
|
||||
building=Coalesce(Sum('builds__quantity', filter=build_filter), Decimal(0)),
|
||||
)
|
||||
|
||||
# If we are filtering by 'has_stock' status
|
||||
has_stock = self.request.query_params.get('has_stock', None)
|
||||
|
||||
if has_stock is not None:
|
||||
has_stock = str2bool(has_stock)
|
||||
|
||||
if has_stock:
|
||||
# Filter items which have a non-null 'in_stock' quantity above zero
|
||||
data = data.filter(in_stock__gt=0)
|
||||
else:
|
||||
# Filter items which a null or zero 'in_stock' quantity
|
||||
data = data.filter(Q(in_stock__lte=0))
|
||||
|
||||
# If we are filtering by 'low_stock' status
|
||||
low_stock = self.request.query_params.get('low_stock', None)
|
||||
|
||||
if low_stock is not None:
|
||||
low_stock = str2bool(low_stock)
|
||||
|
||||
if low_stock:
|
||||
# Ignore any parts which do not have a specified 'minimum_stock' level
|
||||
data = data.exclude(minimum_stock=0)
|
||||
# Filter items which have an 'in_stock' level lower than 'minimum_stock'
|
||||
data = data.filter(Q(in_stock__lt=F('minimum_stock')))
|
||||
else:
|
||||
# Filter items which have an 'in_stock' level higher than 'minimum_stock'
|
||||
data = data.filter(Q(in_stock__gte=F('minimum_stock')))
|
||||
|
||||
# Reduce the number of lookups we need to do for the part categories
|
||||
categories = {}
|
||||
|
||||
@ -261,13 +303,13 @@ class PartList(generics.ListCreateAPIView):
|
||||
|
||||
cascade = str2bool(self.request.query_params.get('cascade', False))
|
||||
|
||||
if cat_id is not None:
|
||||
|
||||
if isNull(cat_id):
|
||||
if cat_id is None:
|
||||
# Top-level parts
|
||||
if not cascade:
|
||||
parts_list = parts_list.filter(category=None)
|
||||
|
||||
else:
|
||||
try:
|
||||
cat_id = int(cat_id)
|
||||
category = PartCategory.objects.get(pk=cat_id)
|
||||
|
||||
# If '?cascade=true' then include parts which exist in sub-categories
|
||||
@ -443,6 +485,19 @@ class BomList(generics.ListCreateAPIView):
|
||||
def get_queryset(self):
|
||||
queryset = BomItem.objects.all()
|
||||
queryset = self.get_serializer_class().setup_eager_loading(queryset)
|
||||
|
||||
# Filter by part?
|
||||
part = self.request.query_params.get('part', None)
|
||||
|
||||
if part is not None:
|
||||
queryset = queryset.filter(part=part)
|
||||
|
||||
# Filter by sub-part?
|
||||
sub_part = self.request.query_params.get('sub_part', None)
|
||||
|
||||
if sub_part is not None:
|
||||
queryset = queryset.filter(sub_part=sub_part)
|
||||
|
||||
return queryset
|
||||
|
||||
permission_classes = [
|
||||
@ -456,8 +511,6 @@ class BomList(generics.ListCreateAPIView):
|
||||
]
|
||||
|
||||
filter_fields = [
|
||||
'part',
|
||||
'sub_part',
|
||||
]
|
||||
|
||||
|
||||
|
@ -37,7 +37,7 @@ from InvenTree import helpers
|
||||
from InvenTree import validators
|
||||
from InvenTree.models import InvenTreeTree, InvenTreeAttachment
|
||||
from InvenTree.fields import InvenTreeURLField
|
||||
from InvenTree.helpers import decimal2string
|
||||
from InvenTree.helpers import decimal2string, normalize
|
||||
|
||||
from InvenTree.status_codes import BuildStatus, StockStatus, OrderStatus
|
||||
|
||||
@ -659,7 +659,7 @@ class Part(models.Model):
|
||||
if total:
|
||||
return total
|
||||
else:
|
||||
return 0
|
||||
return Decimal(0)
|
||||
|
||||
@property
|
||||
def has_bom(self):
|
||||
@ -781,6 +781,9 @@ class Part(models.Model):
|
||||
if min_price == max_price:
|
||||
return min_price
|
||||
|
||||
min_price = normalize(min_price)
|
||||
max_price = normalize(max_price)
|
||||
|
||||
return "{a} - {b}".format(a=min_price, b=max_price)
|
||||
|
||||
def get_supplier_price_range(self, quantity=1):
|
||||
@ -804,6 +807,9 @@ class Part(models.Model):
|
||||
if min_price is None or max_price is None:
|
||||
return None
|
||||
|
||||
min_price = normalize(min_price)
|
||||
max_price = normalize(max_price)
|
||||
|
||||
return (min_price, max_price)
|
||||
|
||||
def get_bom_price_range(self, quantity=1):
|
||||
@ -837,6 +843,9 @@ class Part(models.Model):
|
||||
if min_price is None or max_price is None:
|
||||
return None
|
||||
|
||||
min_price = normalize(min_price)
|
||||
max_price = normalize(max_price)
|
||||
|
||||
return (min_price, max_price)
|
||||
|
||||
def get_price_range(self, quantity=1, buy=True, bom=True):
|
||||
|
@ -1,5 +1,6 @@
|
||||
{% extends "part/part_base.html" %}
|
||||
{% block details %}
|
||||
{% load status_codes %}
|
||||
|
||||
{% include "part/tabs.html" with tab="allocation" %}
|
||||
|
||||
@ -17,7 +18,7 @@
|
||||
<td><a href="{% url 'build-detail' allocation.build.id %}">{{ allocation.build.title }}</a></td>
|
||||
<td>{{ allocation.build.quantity }} × <a href="{% url 'part-detail' allocation.build.part.id %}">{{ allocation.build.part.full_name }}</a></td>
|
||||
<td>{{ allocation.quantity }}</td>
|
||||
<td>{% include "build_status.html" with build=allocation.build %}</td>
|
||||
<td>{% build_status allocation.build.status %}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
|
@ -7,9 +7,14 @@
|
||||
<h3>Part Builds</h3>
|
||||
|
||||
<div id='button-toolbar'>
|
||||
<div class='button-toolbar container-flui' style='float: right';>
|
||||
{% if part.active %}
|
||||
<button class="btn btn-success" id='start-build'>Start New Build</button>
|
||||
{% endif %}
|
||||
<div class='filter-list' id='filter-list-build'>
|
||||
<!-- Empty div for filters -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<table class='table table-striped table-condensed' data-toolbar='#button-toolbar' id='build-table'>
|
||||
@ -31,64 +36,12 @@
|
||||
});
|
||||
});
|
||||
|
||||
$("#build-table").inventreeTable({
|
||||
queryParams: function(p) {
|
||||
return {
|
||||
loadBuildTable($("#build-table"), {
|
||||
url: "{% url 'api-build-list' %}",
|
||||
params: {
|
||||
part_detail: "true",
|
||||
part: {{ part.id }},
|
||||
}
|
||||
},
|
||||
columns: [
|
||||
{
|
||||
field: 'pk',
|
||||
title: 'ID',
|
||||
visible: false,
|
||||
},
|
||||
{
|
||||
field: 'title',
|
||||
title: 'Title',
|
||||
formatter: function(value, row, index, field) {
|
||||
return renderLink(value, row.url);
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'quantity',
|
||||
title: 'Quantity',
|
||||
},
|
||||
{
|
||||
field: 'status',
|
||||
title: 'Status',
|
||||
formatter: function(value, row, index, field) {
|
||||
|
||||
var color = '';
|
||||
|
||||
switch (value) {
|
||||
case 10: // Pending
|
||||
color = 'label-info';
|
||||
break;
|
||||
case 20: // Allocated
|
||||
color = 'label-primary';
|
||||
break;
|
||||
case 30: // Cancelled
|
||||
color = 'label-danger';
|
||||
break;
|
||||
case 40: // Complete
|
||||
color = 'label-success';
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
var html = "<span class='label " + color + " label-large'>" + row.status_text + "</span>";
|
||||
|
||||
return html;
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'completion_date',
|
||||
title: 'Completed'
|
||||
}
|
||||
],
|
||||
url: "{% url 'api-build-list' %}",
|
||||
});
|
||||
|
||||
{% endblock %}
|
@ -1,10 +0,0 @@
|
||||
{% for build in builds %}
|
||||
<tr>
|
||||
<td><a href="{% url 'build-detail' build.id %}">{{ build.title }}</a></td>
|
||||
<td>{{ build.quantity }}</td>
|
||||
<td>
|
||||
{% include "build_status.html" with build=build %}
|
||||
</td>
|
||||
<td>{% if build.completion_date %}{{ build.completion_date }}{% endif %}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
@ -99,7 +99,7 @@
|
||||
<div class='button-toolbar container-fluid' style="float: right;">
|
||||
<button class='btn btn-default' id='part-export' title='Export Part Data'>Export</button>
|
||||
<button class='btn btn-success' id='part-create'>New Part</button>
|
||||
<div class='dropdown' style='float: right;'>
|
||||
<div class='btn dropdown'>
|
||||
<button id='part-options' class='btn btn-primary dropdown-toggle' type='button' data-toggle="dropdown">Options<span class='caret'></span></button>
|
||||
<ul class='dropdown-menu'>
|
||||
<li><a href='#' id='multi-part-category' title='Set category'>Set Category</a></li>
|
||||
@ -107,6 +107,9 @@
|
||||
<li><a href='#' id='multi-part-export' title='Export'>Export Data</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class='filter-list' id='filter-list-parts'>
|
||||
<!-- Empty div -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -8,18 +8,19 @@
|
||||
|
||||
{% if part.active == False %}
|
||||
<div class='alert alert-danger alert-block'>
|
||||
{% trans "This part is not active" %}"
|
||||
{% trans "This part is not active" %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if part.is_template %}
|
||||
<div class='alert alert-info alert-block'>
|
||||
This part is a <i>template part</i>.<br>
|
||||
It is not a <i>real</i> part, but real parts can be based on this template.
|
||||
{% trans "This part is a template part." %}
|
||||
<br>
|
||||
{% trans "It is not a real part, but real parts can be based on this template." %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if part.variant_of %}
|
||||
<div class='alert alert-info alert-block'>
|
||||
This part is a variant of <b><a href="{% url 'part-detail' part.variant_of.id %}">{{ part.variant_of.full_name }}</a></b>
|
||||
{% trans "This part is a variant of" %} <b><a href="{% url 'part-detail' part.variant_of.id %}">{{ part.variant_of.full_name }}</a></b>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
@ -93,25 +94,30 @@
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<table class="table table-striped">
|
||||
<col width='25'>
|
||||
<tr>
|
||||
<td><span class='fas fa-boxes'></span></td>
|
||||
<td>
|
||||
<h4>{% trans "Available Stock" %}</h4>
|
||||
</td>
|
||||
<td><h4>{% decimal part.available_stock %} {{ part.units }}</h4></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><span class='fas fa-map-marker-alt'></span></td>
|
||||
<td>{% trans "In Stock" %}</td>
|
||||
<td>{% include "part/stock_count.html" %}</td>
|
||||
</tr>
|
||||
{% if not part.is_template %}
|
||||
{% if part.allocation_count > 0 %}
|
||||
<tr>
|
||||
<td><span class='fas fa-dolly'></span></td>
|
||||
<td>{% trans "Allocated" %}</td>
|
||||
<td>{% decimal part.allocation_count %}</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% if part.on_order > 0 %}
|
||||
<tr>
|
||||
<td><span class='fas fa-shopping-cart'></span></td>
|
||||
<td>{% trans "On Order" %}</td>
|
||||
<td>{% decimal part.on_order %}</td>
|
||||
</tr>
|
||||
@ -120,16 +126,19 @@
|
||||
{% if not part.is_template %}
|
||||
{% if part.assembly %}
|
||||
<tr>
|
||||
<td><span class='fas fa-tools'></span></td>
|
||||
<td colspan='2'>
|
||||
<b>{% trans "Build Status" %}</b>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td>{% trans "Can Build" %}</td>
|
||||
<td>{% decimal part.can_build %}</td>
|
||||
</tr>
|
||||
{% if part.quantity_being_built > 0 %}
|
||||
<tr>
|
||||
<td></td>
|
||||
<td>{% trans "Underway" %}</td>
|
||||
<td>{% decimal part.quantity_being_built %}</td>
|
||||
</tr>
|
||||
|
@ -4,7 +4,7 @@
|
||||
{% decimal part.total_stock %}
|
||||
|
||||
{% if part.total_stock == 0 %}
|
||||
<span class='label label-danger'>{% trans "No Stock" %}</span>
|
||||
<span class='label label-danger label-right'>{% trans "No Stock" %}</span>
|
||||
{% elif part.total_stock < part.minimum_stock %}
|
||||
<span class='label label-warning'>{% trans "Low Stock" %}</span>
|
||||
<span class='label label-warning label-right'>{% trans "Low Stock" %}</span>
|
||||
{% endif %}
|
@ -25,7 +25,7 @@
|
||||
<li{% ifequal tab 'bom' %} class="active"{% endifequal %}>
|
||||
<a href="{% url 'part-bom' part.id %}">{% trans "BOM" %}<span class="badge{% if part.is_bom_valid == False %} badge-alert{% endif %}">{{ part.bom_count }}</span></a></li>
|
||||
<li{% ifequal tab 'build' %} class="active"{% endifequal %}>
|
||||
<a href="{% url 'part-build' part.id %}">{% trans "Build" %}<span class='badge'>{{ part.active_builds|length }}</span></a></li>
|
||||
<a href="{% url 'part-build' part.id %}">{% trans "Build" %}<span class='badge'>{{ part.builds|length }}</span></a></li>
|
||||
{% endif %}
|
||||
{% if part.component or part.used_in_count > 0 %}
|
||||
<li{% ifequal tab 'used' %} class="active"{% endifequal %}>
|
||||
|
@ -35,7 +35,7 @@
|
||||
title: 'Part',
|
||||
sortable: true,
|
||||
formatter: function(value, row, index, field) {
|
||||
var html = imageHoverIcon(row.part_detail.image_url) + renderLink(value.full_name, value.url + 'bom/');
|
||||
var html = imageHoverIcon(row.part_detail.thumbnail) + renderLink(value.full_name, value.url + 'bom/');
|
||||
|
||||
if (!row.part_detail.active) {
|
||||
html += "<span class='label label-warning' style='float: right;'>INACTIVE</span>";
|
||||
|
38
InvenTree/part/templatetags/status_codes.py
Normal file
38
InvenTree/part/templatetags/status_codes.py
Normal file
@ -0,0 +1,38 @@
|
||||
"""
|
||||
Provide templates for the various model status codes.
|
||||
"""
|
||||
|
||||
from django import template
|
||||
from django.utils.safestring import mark_safe
|
||||
from InvenTree.status_codes import OrderStatus, StockStatus, BuildStatus
|
||||
|
||||
register = template.Library()
|
||||
|
||||
|
||||
@register.simple_tag
|
||||
def order_status(key, *args, **kwargs):
|
||||
return mark_safe(OrderStatus.render(key))
|
||||
|
||||
|
||||
@register.simple_tag
|
||||
def stock_status(key, *args, **kwargs):
|
||||
return mark_safe(StockStatus.render(key))
|
||||
|
||||
|
||||
@register.simple_tag
|
||||
def build_status(key, *args, **kwargs):
|
||||
return mark_safe(BuildStatus.render(key))
|
||||
|
||||
|
||||
@register.simple_tag(takes_context=True)
|
||||
def load_status_codes(context):
|
||||
"""
|
||||
Make the various StatusCodes available to the page context
|
||||
"""
|
||||
|
||||
context['order_status_codes'] = OrderStatus.list()
|
||||
context['stock_status_codes'] = StockStatus.list()
|
||||
context['build_status_codes'] = BuildStatus.list()
|
||||
|
||||
# Need to return something as the result is rendered to the page
|
||||
return ''
|
@ -82,7 +82,8 @@ class PartAPITest(APITestCase):
|
||||
|
||||
def test_get_all_parts(self):
|
||||
url = reverse('api-part-list')
|
||||
response = self.client.get(url, format='json')
|
||||
data = {'cascade': True}
|
||||
response = self.client.get(url, data, format='json')
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(len(response.data), 8)
|
||||
|
||||
|
@ -410,8 +410,16 @@ class StockList(generics.ListCreateAPIView):
|
||||
# Start with all objects
|
||||
stock_list = super(StockList, self).get_queryset()
|
||||
|
||||
# Filter out parts which are not actually "in stock"
|
||||
stock_list = stock_list.filter(customer=None, belongs_to=None)
|
||||
|
||||
# Do we wish to filter by "active parts"
|
||||
active = self.request.query_params.get('active', None)
|
||||
|
||||
if active is not None:
|
||||
active = str2bool(active)
|
||||
stock_list = stock_list.filter(part__active=active)
|
||||
|
||||
# Does the client wish to filter by the Part ID?
|
||||
part_id = self.request.query_params.get('part', None)
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
{% extends "stock/stock_app_base.html" %}
|
||||
{% load static %}
|
||||
{% load inventree_extras %}
|
||||
{% load status_codes %}
|
||||
{% load i18n %}
|
||||
{% block content %}
|
||||
|
||||
@ -172,7 +173,7 @@
|
||||
<tr>
|
||||
<td><span class='fas fa-info'></span></td>
|
||||
<td>{% trans "Status" %}</td>
|
||||
<td>{{ item.get_status_display }}</td>
|
||||
<td>{% stock_status item.status %}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
@ -1,4 +1,5 @@
|
||||
{% load static %}
|
||||
{% load i18n %}
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
@ -102,7 +103,9 @@ InvenTree
|
||||
<script type='text/javascript' src="{% static 'script/inventree/inventree.js' %}"></script>
|
||||
<script type='text/javascript' src="{% static 'script/inventree/api.js' %}"></script>
|
||||
<script type='text/javascript' src="{% static 'script/inventree/bom.js' %}"></script>
|
||||
<script type='text/javascript' src="{% static 'script/inventree/filters.js' %}"></script>
|
||||
<script type='text/javascript' src="{% static 'script/inventree/tables.js' %}"></script>
|
||||
<script type='text/javascript' src="{% static 'script/inventree/build.js' %}"></script>
|
||||
<script type='text/javascript' src="{% static 'script/inventree/modals.js' %}"></script>
|
||||
<script type='text/javascript' src="{% static 'script/inventree/order.js' %}"></script>
|
||||
<script type='text/javascript' src="{% static 'script/inventree/notification.js' %}"></script>
|
||||
@ -115,7 +118,10 @@ InvenTree
|
||||
{% block js_load %}
|
||||
{% endblock %}
|
||||
|
||||
{% include "table_filters.html" %}
|
||||
|
||||
<script type='text/javascript'>
|
||||
|
||||
$(document).ready(function () {
|
||||
{% block js_ready %}
|
||||
{% endblock %}
|
||||
@ -124,6 +130,7 @@ $(document).ready(function () {
|
||||
|
||||
showCachedAlerts();
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
{% block js %}
|
||||
|
@ -1,11 +0,0 @@
|
||||
{% if build.status == BuildStatus.PENDING %}
|
||||
<span class='label label-large label-info'>
|
||||
{% elif build.status == BuildStatus.ALLOCATED %}
|
||||
<span class='label label-large label-primary'>
|
||||
{% elif build.status == BuildStatus.CANCELLED %}
|
||||
<span class='label label-large label-danger'>
|
||||
{% elif build.status == BuildStatus.COMPLETE %}
|
||||
<span class='label label-large label-success'>
|
||||
{% endif %}
|
||||
{{ build.get_status_display }}
|
||||
</span>
|
35
InvenTree/templates/status_codes.html
Normal file
35
InvenTree/templates/status_codes.html
Normal file
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Status codes for the {{ label }} model.
|
||||
*/
|
||||
var {{ label }}Codes = {
|
||||
{% for opt in options %}'{{ opt.key }}': {
|
||||
key: '{{ opt.key }}',
|
||||
value: '{{ opt.value }}',{% if opt.label %}
|
||||
label: '{{ opt.label }}',{% endif %}
|
||||
},{% endfor %}
|
||||
};
|
||||
|
||||
/*
|
||||
* Render the status for a {{ label }} object.
|
||||
* Uses the values specified in "status_codes.py"
|
||||
* This function is generated by the "status_codes.html" template
|
||||
*/
|
||||
function {{ label }}StatusDisplay(key) {
|
||||
|
||||
key = String(key);
|
||||
|
||||
var label = {{ label }}Codes[key].label;
|
||||
|
||||
var value = {{ label }}Codes[key].value;
|
||||
|
||||
if (value == null || value.length == 0) {
|
||||
value = key;
|
||||
}
|
||||
|
||||
// Label not found, return the original string
|
||||
if (label == null || label.length == 0) {
|
||||
return value;
|
||||
}
|
||||
|
||||
return `<span class='label label-${label}'>${value}</span>`;
|
||||
}
|
@ -6,7 +6,7 @@
|
||||
{% if read_only %}
|
||||
{% else %}
|
||||
<button class="btn btn-success" id='item-create'>{% trans "New Stock Item" %}</button>
|
||||
<div class="dropdown" style='float: right;'>
|
||||
<div class="btn dropdown">
|
||||
<button id='stock-options' class="btn btn-primary dropdown-toggle" type="button" data-toggle="dropdown">{% trans "Options" %}<span class="caret"></span></button>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a href="#" id='multi-item-add' title='Add to selected stock items'>{% trans "Add stock" %}</a></li>
|
||||
@ -18,6 +18,9 @@
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class='filter-list' id='filter-list-stock'>
|
||||
<!-- An empty div in which the filter list will be constructed -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
98
InvenTree/templates/table_filters.html
Normal file
98
InvenTree/templates/table_filters.html
Normal file
@ -0,0 +1,98 @@
|
||||
{% load i18n %}
|
||||
{% load status_codes %}
|
||||
|
||||
{% load_status_codes %}
|
||||
|
||||
<script type='text/javascript'>
|
||||
|
||||
{% include "status_codes.html" with label='stock' options=stock_status_codes %}
|
||||
{% include "status_codes.html" with label='build' options=build_status_codes %}
|
||||
{% include "status_codes.html" with label='order' options=order_status_codes %}
|
||||
|
||||
|
||||
function getAvailableTableFilters(tableKey) {
|
||||
|
||||
tableKey = tableKey.toLowerCase();
|
||||
|
||||
// Filters for the "Stock" table
|
||||
if (tableKey == 'stock') {
|
||||
return {
|
||||
cascade: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Include sublocations" %}',
|
||||
description: '{% trans "Include stock in sublocations" %}',
|
||||
},
|
||||
active: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Active parts" %}',
|
||||
description: '{% trans "Show stock for active parts" %}',
|
||||
},
|
||||
'status': {
|
||||
options: stockCodes,
|
||||
title: '{% trans "Stock status" %}',
|
||||
description: '{% trans "Stock status" %}',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// Filters for the "Build" table
|
||||
if (tableKey == 'build') {
|
||||
return {
|
||||
status: {
|
||||
title: '{% trans "Build status" %}',
|
||||
options: buildCodes,
|
||||
},
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
// Filters for the "Order" table
|
||||
if (tableKey == "order") {
|
||||
return {
|
||||
status: {
|
||||
title: '{% trans "Order status" %}',
|
||||
options: orderCodes,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// Filters for the "Parts" table
|
||||
if (tableKey == "parts") {
|
||||
return {
|
||||
cascade: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Include subcategories" %}',
|
||||
description: '{% trans "Include parts in subcategories" %}',
|
||||
},
|
||||
active: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Active" %}',
|
||||
description: '{% trans "Show active parts" %}',
|
||||
},
|
||||
is_template: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Template" %}',
|
||||
},
|
||||
has_stock: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Stock available" %}'
|
||||
},
|
||||
low_stock: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Low stock" %}',
|
||||
},
|
||||
assembly: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Assembly" %}',
|
||||
},
|
||||
component: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Component" %}',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// Finally, no matching key
|
||||
return {};
|
||||
}
|
||||
</script>
|
Loading…
Reference in New Issue
Block a user