Merge pull request #711 from SchrodingersGat/tweakers

Table Filtering
This commit is contained in:
Oliver 2020-04-12 01:05:03 +10:00 committed by GitHub
commit a921b3fcee
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
51 changed files with 1934 additions and 735 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View 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;
}

View File

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

View File

@ -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;
if (!options.allowInactive) {
// Only display active parts
query.active = true;
var params = options.parms || {};
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,
});

View File

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

View File

@ -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.
*/

View File

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

View File

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

View File

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

View File

@ -1,6 +1,8 @@
{% extends "base.html" %}
{% load static %}
{% load i18n %}
{% load status_codes %}
{% block page_title %}
InvenTree | Build - {{ build }}
@ -22,61 +24,70 @@ InvenTree | Build - {{ build }}
</div>
</div>
<div class='media-body'>
<h4>Build Details</h4>
<p>
<div class='btn-row'>
<div class='btn-group'>
<button type='button' class='btn btn-default btn-glyph' id='build-edit' title='Edit Build'>
<span class='glyphicon glyphicon-edit'/>
</button>
{% if build.is_active %}
<button type='button' class='btn btn-default btn-glyph' id='build-complete' title="Complete Build">
<span class='glyphicon glyphicon-send'/>
</button>
<button type='button' class='btn btn-default btn-glyph' id='build-cancel' title='Cancel Build'>
<span class='glyphicon glyphicon-remove'/>
</button>
{% endif %}
{% if build.status == BuildStatus.CANCELLED %}
<button type='button' class='btn btn-default btn-glyph' id='build-delete' title='Delete Build'>
<span class='glyphicon glyphicon-trash'/>
</button>
{% endif %}
</div>
<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'>
<span class='glyphicon glyphicon-edit'/>
</button>
{% if build.is_active %}
<button type='button' class='btn btn-default btn-glyph' id='build-complete' title="Complete Build">
<span class='glyphicon glyphicon-send'/>
</button>
<button type='button' class='btn btn-default btn-glyph' id='build-cancel' title='Cancel Build'>
<span class='glyphicon glyphicon-remove'/>
</button>
{% endif %}
{% if build.status == BuildStatus.CANCELLED %}
<button type='button' class='btn btn-default btn-glyph' id='build-delete' title='Delete Build'>
<span class='glyphicon glyphicon-trash'/>
</button>
{% endif %}
</div>
</p>
<table class='table table-striped table-condensed'>
<tr>
<td>{{ build.title }}</td>
<td>{% include "build_status.html" with build=build %}</td>
</tr>
<tr>
<td>Part</td>
<td><a href="{% url 'part-detail' build.part.id %}">{{ build.part.full_name }}</a></td>
</tr>
<tr>
<td>Quantity</td>
<td>{{ build.quantity }}</td>
</tr>
<tr>
<td>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>
{% endif %}
{% else %}
<span class='warning-msg'><i>No pricing information</i></span>
{% endif %}
</td>
</tr>
</table>
</div>
</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>
</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></td>
<td>{% trans "Quantity" %}</td>
<td>{{ build.quantity }}</td>
</tr>
<tr>
<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>{% trans "BOM pricing is incomplete" %}</i></span>
{% endif %}
{% else %}
<span class='warning-msg'><i>{% trans "No pricing information" %}</i></span>
{% endif %}
</td>
</tr>
</table>
</div>
</div>
</div>
<hr>

View File

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

View File

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

View File

@ -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;'>
<button type='button' class="btn btn-success" id='new-build'>Start New Build</button>
</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 %}
@ -38,38 +40,18 @@ InvenTree | Build List
$("#new-build").click(function() {
launchModalForm(
"{% url 'build-create' %}",
{
follow: true
});
"{% url 'build-create' %}",
{
follow: true
}
);
});
$(".build-table").inventreeTable({
formatNoMatches: function() { return 'No builds found'; },
columns: [
{
field: 'name',
title: 'Build',
sortable: true,
},
{
field: 'part',
title: 'Part',
sortable: true,
},
{
title: 'Quantity',
sortable: true,
searchable: false
},
{
title: 'Status',
sortable: true,
},
{
sortable: true,
},
]
loadBuildTable($("#build-table"), {
url: "{% url 'api-build-list' %}",
params: {
part_detail: "true",
},
});
{% endblock %}

View File

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

View File

@ -30,21 +30,21 @@
$("#part-create").click(function () {
launchModalForm(
"{% url 'supplier-part-create' %}",
{
data: {
supplier: {{ company.id }}
},
reload: true,
secondary: [
{
field: 'part',
label: 'New Part',
title: 'Create New Part',
url: "{% url 'part-create' %}"
},
]
});
"{% url 'supplier-part-create' %}",
{
data: {
supplier: {{ company.id }}
},
reload: true,
secondary: [
{
field: 'part',
label: 'New Part',
title: 'Create New Part',
url: "{% url 'part-create' %}"
},
]
});
});
$("#part-table").inventreeTable({
@ -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;
}
},
{

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,6 +1,7 @@
{% extends "order/order_base.html" %}
{% load inventree_extras %}
{% load status_codes %}
{% load i18n %}
{% load static %}

View File

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

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

View File

@ -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,23 +303,23 @@ 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
if cascade:
parts_list = parts_list.filter(category__in=category.getUniqueChildren())
# Just return parts directly in the requested category
else:
parts_list = parts_list.filter(category=cat_id)
except (ValueError, PartCategory.DoesNotExist):
pass
else:
try:
category = PartCategory.objects.get(pk=cat_id)
# If '?cascade=true' then include parts which exist in sub-categories
if cascade:
parts_list = parts_list.filter(category__in=category.getUniqueChildren())
# Just return parts directly in the requested category
else:
parts_list = parts_list.filter(category=cat_id)
except (ValueError, PartCategory.DoesNotExist):
pass
# Ensure that related models are pre-loaded to reduce DB trips
parts_list = self.get_serializer_class().setup_eager_loading(parts_list)
@ -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',
]

View File

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

View File

@ -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 }} &times <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>

View File

@ -7,9 +7,14 @@
<h3>Part Builds</h3>
<div id='button-toolbar'>
{% if part.active %}
<button class="btn btn-success" id='start-build'>Start New Build</button>
{% endif %}
<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 {
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' %}",
loadBuildTable($("#build-table"), {
url: "{% url 'api-build-list' %}",
params: {
part_detail: "true",
part: {{ part.id }},
}
});
{% endblock %}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View 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 ''

View File

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

View File

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

View File

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

View File

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

View File

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

View 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>`;
}

View File

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

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