This commit is contained in:
Oliver Walters 2020-08-18 21:29:48 +10:00
commit 50460ed527
10 changed files with 992 additions and 552 deletions

View File

@ -6,7 +6,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-08-16 02:11+0000\n"
"POT-Creation-Date: 2020-08-18 04:01+0000\n"
"PO-Revision-Date: 2020-05-03 11:32+0200\n"
"Last-Translator: Christian Schlüter <chschlue@gmail.com>\n"
"Language-Team: C <kde-i18n-doc@kde.org>\n"
@ -45,30 +45,30 @@ msgstr "Neues Teil anlegen"
msgid "Confirm new password"
msgstr ""
#: InvenTree/helpers.py:328 order/models.py:187 order/models.py:261
#: InvenTree/helpers.py:336 order/models.py:187 order/models.py:261
msgid "Invalid quantity provided"
msgstr "Keine gültige Menge"
#: InvenTree/helpers.py:331
#: InvenTree/helpers.py:339
msgid "Empty serial number string"
msgstr "Keine Seriennummer angegeben"
#: InvenTree/helpers.py:352 InvenTree/helpers.py:369
#: InvenTree/helpers.py:360 InvenTree/helpers.py:377
#, python-brace-format
msgid "Duplicate serial: {n}"
msgstr "Doppelte Seriennummer: {n}"
#: InvenTree/helpers.py:356 InvenTree/helpers.py:359 InvenTree/helpers.py:362
#: InvenTree/helpers.py:373
#: InvenTree/helpers.py:364 InvenTree/helpers.py:367 InvenTree/helpers.py:370
#: InvenTree/helpers.py:381
#, python-brace-format
msgid "Invalid group: {g}"
msgstr "Ungültige Gruppe: {g}"
#: InvenTree/helpers.py:379
#: InvenTree/helpers.py:387
msgid "No serial numbers found"
msgstr "Keine Seriennummern gefunden"
#: InvenTree/helpers.py:383
#: InvenTree/helpers.py:391
#, python-brace-format
msgid "Number of unique serial number ({s}) must match quantity ({q})"
msgstr ""
@ -341,7 +341,7 @@ msgstr "Bau-Status"
msgid "Build status code"
msgstr "Bau-Statuscode"
#: build/models.py:136 stock/models.py:376
#: build/models.py:136 stock/models.py:371
msgid "Batch Code"
msgstr "Losnummer"
@ -353,11 +353,11 @@ msgstr "Chargennummer für diese Bau-Ausgabe"
#: company/templates/company/supplier_part_base.html:60
#: company/templates/company/supplier_part_detail.html:24
#: part/templates/part/detail.html:74 part/templates/part/part_base.html:86
#: stock/models.py:370 stock/templates/stock/item_base.html:237
#: stock/models.py:365 stock/templates/stock/item_base.html:234
msgid "External Link"
msgstr "Externer Link"
#: build/models.py:156 stock/models.py:372
#: build/models.py:156 stock/models.py:367
msgid "Link to external URL"
msgstr "Link zu einer externen URL"
@ -365,7 +365,7 @@ msgstr "Link zu einer externen URL"
#: company/templates/company/tabs.html:33 order/templates/order/po_tabs.html:15
#: order/templates/order/purchase_order_detail.html:200
#: order/templates/order/so_tabs.html:23 part/templates/part/tabs.html:64
#: stock/models.py:438 stock/models.py:1287 stock/templates/stock/tabs.html:26
#: stock/models.py:433 stock/models.py:1282 stock/templates/stock/tabs.html:26
#: templates/js/barcode.html:391 templates/js/bom.html:229
#: templates/js/stock.html:114 templates/js/stock.html:526
msgid "Notes"
@ -433,8 +433,8 @@ msgstr "Neues Lagerobjekt"
#: build/templates/build/allocate.html:161
#: order/templates/order/sales_order_detail.html:68
#: order/templates/order/sales_order_detail.html:150 stock/models.py:364
#: stock/templates/stock/item_base.html:153
#: order/templates/order/sales_order_detail.html:150 stock/models.py:359
#: stock/templates/stock/item_base.html:150
msgid "Serial Number"
msgstr "Seriennummer"
@ -451,7 +451,7 @@ msgstr "Seriennummer"
#: part/templates/part/allocation.html:49
#: stock/templates/stock/item_base.html:26
#: stock/templates/stock/item_base.html:32
#: stock/templates/stock/item_base.html:159
#: stock/templates/stock/item_base.html:156
#: stock/templates/stock/stock_adjust.html:18 templates/js/barcode.html:338
#: templates/js/bom.html:172 templates/js/build.html:52
#: templates/js/stock.html:673
@ -460,7 +460,7 @@ msgstr "Anzahl"
#: build/templates/build/allocate.html:177
#: build/templates/build/auto_allocate.html:20
#: stock/templates/stock/item_base.html:191
#: stock/templates/stock/item_base.html:188
#: stock/templates/stock/stock_adjust.html:17 templates/js/barcode.html:337
#: templates/js/stock.html:508
msgid "Location"
@ -498,7 +498,7 @@ msgstr "Beschreibung"
msgid "Reference"
msgstr "Referenz"
#: build/templates/build/allocate.html:338 part/models.py:1270
#: build/templates/build/allocate.html:338 part/models.py:1271
#: templates/js/part.html:359 templates/js/table_filters.html:100
msgid "Required"
msgstr "benötigt"
@ -543,7 +543,7 @@ msgstr "Keine Lagerobjekt gefunden, die diesem Bau zugewiesen werden können"
#: build/templates/build/build_base.html:8
#: build/templates/build/build_base.html:34
#: build/templates/build/complete.html:6
#: stock/templates/stock/item_base.html:216 templates/js/build.html:33
#: stock/templates/stock/item_base.html:213 templates/js/build.html:33
#: templates/navbar.html:12
msgid "Build"
msgstr "Bau"
@ -563,7 +563,7 @@ msgstr "Bau-Status"
#: build/templates/build/build_base.html:80
#: build/templates/build/detail.html:42
#: order/templates/order/receive_parts.html:24
#: stock/templates/stock/item_base.html:269 templates/js/barcode.html:42
#: stock/templates/stock/item_base.html:266 templates/js/barcode.html:42
#: templates/js/build.html:57 templates/js/order.html:162
#: templates/js/order.html:235 templates/js/stock.html:495
msgid "Status"
@ -575,7 +575,7 @@ msgstr "Status"
#: order/templates/order/sales_order_notes.html:10
#: order/templates/order/sales_order_ship.html:25
#: part/templates/part/allocation.html:27
#: stock/templates/stock/item_base.html:179 templates/js/order.html:209
#: stock/templates/stock/item_base.html:176 templates/js/order.html:209
msgid "Sales Order"
msgstr "Bestellung"
@ -644,7 +644,7 @@ msgid "Stock can be taken from any available location."
msgstr "Bestand kann jedem verfügbaren Lagerort entnommen werden."
#: build/templates/build/detail.html:48
#: stock/templates/stock/item_base.html:209 templates/js/stock.html:503
#: stock/templates/stock/item_base.html:206 templates/js/stock.html:503
msgid "Batch"
msgstr "Los"
@ -942,8 +942,8 @@ msgstr "Kaufen Sie Teile von dieser Firma?"
msgid "Does this company manufacture parts?"
msgstr "Produziert diese Firma Teile?"
#: company/models.py:279 stock/models.py:324
#: stock/templates/stock/item_base.html:145
#: company/models.py:279 stock/models.py:319
#: stock/templates/stock/item_base.html:142
msgid "Base Part"
msgstr "Basisteil"
@ -1016,14 +1016,14 @@ msgstr "Hersteller"
#: company/templates/company/supplier_part_detail.html:21 order/models.py:148
#: order/templates/order/order_base.html:74
#: order/templates/order/order_wizard/select_pos.html:30
#: stock/templates/stock/item_base.html:244 templates/js/company.html:52
#: stock/templates/stock/item_base.html:241 templates/js/company.html:52
#: templates/js/company.html:134 templates/js/order.html:144
msgid "Supplier"
msgstr "Zulieferer"
#: company/templates/company/detail.html:26 order/models.py:314
#: order/templates/order/sales_order_base.html:73 stock/models.py:359
#: stock/models.py:360 stock/templates/stock/item_base.html:166
#: order/templates/order/sales_order_base.html:73 stock/models.py:354
#: stock/models.py:355 stock/templates/stock/item_base.html:163
#: templates/js/company.html:44 templates/js/order.html:217
msgid "Customer"
msgstr "Kunde"
@ -1137,8 +1137,8 @@ msgid "New Sales Order"
msgstr "Neuer Auftrag"
#: company/templates/company/supplier_part_base.html:6
#: company/templates/company/supplier_part_base.html:19 stock/models.py:333
#: stock/templates/stock/item_base.html:249 templates/js/company.html:150
#: company/templates/company/supplier_part_base.html:19 stock/models.py:328
#: stock/templates/stock/item_base.html:246 templates/js/company.html:150
msgid "Supplier Part"
msgstr "Zulieferer-Teil"
@ -1410,8 +1410,8 @@ msgstr "Bestell-Notizen"
msgid "Supplier order reference code"
msgstr "Bestellreferenz"
#: order/models.py:185 order/models.py:259 part/views.py:1167
#: stock/models.py:238 stock/models.py:687
#: order/models.py:185 order/models.py:259 part/views.py:1250
#: stock/models.py:239 stock/models.py:682
msgid "Quantity must be greater than zero"
msgstr "Anzahl muss größer Null sein"
@ -1445,7 +1445,7 @@ msgstr "Position - Notizen"
#: order/models.py:466 order/templates/order/order_base.html:9
#: order/templates/order/order_base.html:23
#: stock/templates/stock/item_base.html:223 templates/js/order.html:136
#: stock/templates/stock/item_base.html:220 templates/js/order.html:136
msgid "Purchase Order"
msgstr "Kaufvertrag"
@ -1869,16 +1869,16 @@ msgstr "Zuordnung bearbeiten"
msgid "Remove allocation"
msgstr "Zuordnung entfernen"
#: part/bom.py:140
#: part/bom.py:144
#, python-brace-format
msgid "Unsupported file format: {f}"
msgstr "Nicht unterstütztes Dateiformat: {f}"
#: part/bom.py:145
#: part/bom.py:149
msgid "Error reading BOM file (invalid data)"
msgstr "Fehler beim Lesen der Stückliste (ungültige Daten)"
#: part/bom.py:147
#: part/bom.py:151
msgid "Error reading BOM file (incorrect row size)"
msgstr "Fehler beim Lesen der Stückliste (ungültige Zeilengröße)"
@ -1898,33 +1898,41 @@ msgstr "Kaskadierend"
msgid "Download cascading / multi-level BOM"
msgstr "Kaskadierende Stückliste herunterladen"
#: part/forms.py:76
#: part/forms.py:59
msgid "Levels"
msgstr ""
#: part/forms.py:59
msgid "Select maximum number of BOM levels to export (0 = all levels)"
msgstr ""
#: part/forms.py:78
msgid "Confirm that the BOM is correct"
msgstr "Bestätigen, dass die Stückliste korrekt ist"
#: part/forms.py:88
#: part/forms.py:90
msgid "Select BOM file to upload"
msgstr "Stücklisten-Datei zum Upload auswählen"
#: part/forms.py:112
#: part/forms.py:114
msgid "Select part category"
msgstr "Teilekategorie wählen"
#: part/forms.py:126
#: part/forms.py:128
msgid "Perform 'deep copy' which will duplicate all BOM data for this part"
msgstr ""
"Tiefe Kopie ausführen. Dies wird alle Daten der Stückliste für dieses Teil "
"duplizieren"
#: part/forms.py:131
#: part/forms.py:133
msgid "Confirm part creation"
msgstr "Erstellen des Teils bestätigen"
#: part/forms.py:221
#: part/forms.py:223
msgid "Input quantity for price calculation"
msgstr "Eintragsmenge zur Preisberechnung"
#: part/forms.py:224
#: part/forms.py:226
msgid "Select currency for price calculation"
msgstr "Währung zur Preisberechnung wählen"
@ -2037,13 +2045,13 @@ msgstr "Bemerkungen - unterstüzt Markdown-Formatierung"
msgid "Stored BOM checksum"
msgstr "Prüfsumme der Stückliste gespeichert"
#: part/models.py:1222
#: part/models.py:1223
#, fuzzy
#| msgid "Stock item cannot be created for a template Part"
msgid "Test templates can only be created for trackable parts"
msgstr "Lagerobjekt kann nicht für Vorlagen-Teile angelegt werden"
#: part/models.py:1239
#: part/models.py:1240
#, fuzzy
#| msgid ""
#| "A stock item with this serial number already exists for template part "
@ -2053,122 +2061,123 @@ msgstr ""
"Ein Teil mit dieser Seriennummer existiert bereits für die Teilevorlage "
"{part}"
#: part/models.py:1258 templates/js/part.html:350 templates/js/stock.html:90
#: part/models.py:1259 templates/js/part.html:350 templates/js/stock.html:90
#, fuzzy
#| msgid "Instance Name"
msgid "Test Name"
msgstr "Instanzname"
#: part/models.py:1259
#: part/models.py:1260
#, fuzzy
#| msgid "Serial number for this item"
msgid "Enter a name for the test"
msgstr "Seriennummer für dieses Teil"
#: part/models.py:1264
#: part/models.py:1265
#, fuzzy
#| msgid "Description"
msgid "Test Description"
msgstr "Beschreibung"
#: part/models.py:1265
#: part/models.py:1266
#, fuzzy
#| msgid "Brief description of the build"
msgid "Enter description for this test"
msgstr "Kurze Beschreibung des Baus"
#: part/models.py:1271
#: part/models.py:1272
msgid "Is this test required to pass?"
msgstr ""
#: part/models.py:1276 templates/js/part.html:367
#: part/models.py:1277 templates/js/part.html:367
#, fuzzy
#| msgid "Required Parts"
msgid "Requires Value"
msgstr "benötigte Teile"
#: part/models.py:1277
#: part/models.py:1278
msgid "Does this test require a value when adding a test result?"
msgstr ""
#: part/models.py:1282 templates/js/part.html:374
#: part/models.py:1283 templates/js/part.html:374
#, fuzzy
#| msgid "Delete Attachment"
msgid "Requires Attachment"
msgstr "Anhang löschen"
#: part/models.py:1283
#: part/models.py:1284
msgid "Does this test require a file attachment when adding a test result?"
msgstr ""
#: part/models.py:1316
#: part/models.py:1317
msgid "Parameter template name must be unique"
msgstr "Vorlagen-Name des Parameters muss eindeutig sein"
#: part/models.py:1321
#: part/models.py:1322
msgid "Parameter Name"
msgstr "Name des Parameters"
#: part/models.py:1323
#: part/models.py:1324
msgid "Parameter Units"
msgstr "Parameter Einheit"
#: part/models.py:1349
#: part/models.py:1350
msgid "Parent Part"
msgstr "Ausgangsteil"
#: part/models.py:1351
#: part/models.py:1352
msgid "Parameter Template"
msgstr "Parameter Vorlage"
#: part/models.py:1353
#: part/models.py:1354
msgid "Parameter Value"
msgstr "Parameter Wert"
#: part/models.py:1382
#: part/models.py:1383
msgid "Select parent part"
msgstr "Ausgangsteil auswählen"
#: part/models.py:1390
#: part/models.py:1391
msgid "Select part to be used in BOM"
msgstr "Teil für die Nutzung in der Stückliste auswählen"
#: part/models.py:1396
#: part/models.py:1397
msgid "BOM quantity for this BOM item"
msgstr "Stücklisten-Anzahl für dieses Stücklisten-Teil"
#: part/models.py:1399
#: part/models.py:1400
msgid "Estimated build wastage quantity (absolute or percentage)"
msgstr "Geschätzter Ausschuss (absolut oder prozentual)"
#: part/models.py:1402
#: part/models.py:1403
msgid "BOM item reference"
msgstr "Referenz des Objekts auf der Stückliste"
#: part/models.py:1405
#: part/models.py:1406
msgid "BOM item notes"
msgstr "Notizen zum Stücklisten-Objekt"
#: part/models.py:1407
#: part/models.py:1408
msgid "BOM line checksum"
msgstr "Prüfsumme der Stückliste"
#: part/models.py:1471 stock/models.py:228
#: part/models.py:1472 part/views.py:1256 part/views.py:1308
#: stock/models.py:229
#, fuzzy
#| msgid "Overage must be an integer value or a percentage"
msgid "Quantity must be integer value for trackable parts"
msgstr "Überschuss muss eine Ganzzahl oder ein Prozentwert sein"
#: part/models.py:1480
#: part/models.py:1481
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:1487
#: part/models.py:1488
#, 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)"
#: part/models.py:1494
#: part/models.py:1495
#, fuzzy
#| msgid "New BOM Item"
msgid "BOM Item"
@ -2189,14 +2198,14 @@ msgstr "Bestellung"
#: part/templates/part/allocation.html:45
#: stock/templates/stock/item_base.html:8
#: stock/templates/stock/item_base.html:58
#: stock/templates/stock/item_base.html:231
#: stock/templates/stock/item_base.html:228
#: stock/templates/stock/stock_adjust.html:16 templates/js/build.html:106
#: templates/js/stock.html:643
msgid "Stock Item"
msgstr "Lagerobjekt"
#: part/templates/part/allocation.html:20
#: stock/templates/stock/item_base.html:185
#: stock/templates/stock/item_base.html:182
msgid "Build Order"
msgstr "Bauauftrag"
@ -2232,10 +2241,94 @@ msgstr "Stückliste bearbeiten"
msgid "Validate Bill of Materials"
msgstr "Stückliste validieren"
#: part/templates/part/bom.html:46 part/views.py:1412
#: part/templates/part/bom.html:46 part/views.py:1523
msgid "Export Bill of Materials"
msgstr "Stückliste exportieren"
#: part/templates/part/bom_upload/select_fields.html:8
#: part/templates/part/bom_upload/select_parts.html:8
#: part/templates/part/bom_upload/upload_file.html:10
#, fuzzy
#| msgid "Export Bill of Materials"
msgid "Upload Bill of Materials"
msgstr "Stückliste exportieren"
#: part/templates/part/bom_upload/select_fields.html:10
#, fuzzy
#| msgid "Step 1 of 2 - Select Part Suppliers"
msgid "Step 2 - Select Fields"
msgstr "Schritt 1 von 2 - Zulieferer auswählen"
#: part/templates/part/bom_upload/select_fields.html:15
msgid "Missing selections for the following required columns"
msgstr ""
#: part/templates/part/bom_upload/select_fields.html:26
#, fuzzy
#| msgid "Sublocations"
msgid "Submit Selections"
msgstr "Sub-Standorte"
#: part/templates/part/bom_upload/select_fields.html:35
msgid "File Fields"
msgstr ""
#: part/templates/part/bom_upload/select_fields.html:52
msgid "Match Fields"
msgstr ""
#: part/templates/part/bom_upload/select_parts.html:10
#, fuzzy
#| msgid "Step 1 of 2 - Select Part Suppliers"
msgid "Step 3 - Select Parts"
msgstr "Schritt 1 von 2 - Zulieferer auswählen"
#: part/templates/part/bom_upload/select_parts.html:15
msgid "Errors exist in the submitted data"
msgstr ""
#: part/templates/part/bom_upload/select_parts.html:21
#, fuzzy
#| msgid "Edit BOM"
msgid "Submit BOM"
msgstr "Stückliste bearbeiten"
#: part/templates/part/bom_upload/select_parts.html:33
msgid "Row"
msgstr ""
#: part/templates/part/bom_upload/select_parts.html:34
#: part/templates/part/bom_upload/select_parts.html:63
#, fuzzy
#| msgid "Select part"
msgid "Select Part"
msgstr "Teil auswählen"
#: part/templates/part/bom_upload/upload_file.html:13
#, fuzzy
#| msgid "Step 1 of 2 - Select Part Suppliers"
msgid "Step 1 - Select BOM File"
msgstr "Schritt 1 von 2 - Zulieferer auswählen"
#: part/templates/part/bom_upload/upload_file.html:16
msgid "Requirements for BOM upload"
msgstr ""
#: part/templates/part/bom_upload/upload_file.html:18
msgid ""
"The BOM file must contain the required named columns as provided in the "
msgstr ""
#: part/templates/part/bom_upload/upload_file.html:18
#, fuzzy
#| msgid "Upload new image"
msgid "BOM Upload Template"
msgstr "Neues Bild hochladen"
#: part/templates/part/bom_upload/upload_file.html:19
msgid "Each part must already exist in the database"
msgstr ""
#: part/templates/part/category.html:14
msgid "All parts"
msgstr "Alle Teile"
@ -2440,7 +2533,7 @@ msgstr "Parameter hinzufügen"
msgid "New Parameter"
msgstr "Neuer Parameter"
#: part/templates/part/params.html:21 stock/models.py:1274
#: part/templates/part/params.html:21 stock/models.py:1269
#: templates/js/stock.html:110
msgid "Value"
msgstr "Wert"
@ -2647,7 +2740,7 @@ msgstr "Stückliste"
msgid "Used In"
msgstr "Benutzt in"
#: part/templates/part/tabs.html:55 stock/templates/stock/item_base.html:275
#: part/templates/part/tabs.html:55 stock/templates/stock/item_base.html:272
msgid "Tests"
msgstr ""
@ -2758,91 +2851,91 @@ msgstr "Teileigenschaften bearbeiten"
msgid "Validate BOM"
msgstr "BOM validieren"
#: part/views.py:908
#: part/views.py:909
msgid "No BOM file provided"
msgstr "Keine Stückliste angegeben"
#: part/views.py:1169
#: part/views.py:1259
msgid "Enter a valid quantity"
msgstr "Bitte eine gültige Anzahl eingeben"
#: part/views.py:1193 part/views.py:1196
#: part/views.py:1284 part/views.py:1287
msgid "Select valid part"
msgstr "Bitte ein gültiges Teil auswählen"
#: part/views.py:1202
#: part/views.py:1293
msgid "Duplicate part selected"
msgstr "Teil doppelt ausgewählt"
#: part/views.py:1230
#: part/views.py:1329
msgid "Select a part"
msgstr "Teil auswählen"
#: part/views.py:1234
#: part/views.py:1333
msgid "Specify quantity"
msgstr "Anzahl angeben"
#: part/views.py:1450
#: part/views.py:1565
msgid "Confirm Part Deletion"
msgstr "Löschen des Teils bestätigen"
#: part/views.py:1457
#: part/views.py:1572
msgid "Part was deleted"
msgstr "Teil wurde gelöscht"
#: part/views.py:1466
#: part/views.py:1581
msgid "Part Pricing"
msgstr "Teilbepreisung"
#: part/views.py:1588
#: part/views.py:1703
msgid "Create Part Parameter Template"
msgstr "Teilparametervorlage anlegen"
#: part/views.py:1596
#: part/views.py:1711
msgid "Edit Part Parameter Template"
msgstr "Teilparametervorlage bearbeiten"
#: part/views.py:1603
#: part/views.py:1718
msgid "Delete Part Parameter Template"
msgstr "Teilparametervorlage löschen"
#: part/views.py:1611
#: part/views.py:1726
msgid "Create Part Parameter"
msgstr "Teilparameter anlegen"
#: part/views.py:1661
#: part/views.py:1776
msgid "Edit Part Parameter"
msgstr "Teilparameter bearbeiten"
#: part/views.py:1675
#: part/views.py:1790
msgid "Delete Part Parameter"
msgstr "Teilparameter löschen"
#: part/views.py:1691
#: part/views.py:1806
msgid "Edit Part Category"
msgstr "Teilkategorie bearbeiten"
#: part/views.py:1726
#: part/views.py:1841
msgid "Delete Part Category"
msgstr "Teilkategorie löschen"
#: part/views.py:1732
#: part/views.py:1847
msgid "Part category was deleted"
msgstr "Teilekategorie wurde gelöscht"
#: part/views.py:1740
#: part/views.py:1855
msgid "Create new part category"
msgstr "Teilkategorie anlegen"
#: part/views.py:1791
#: part/views.py:1906
msgid "Create BOM item"
msgstr "BOM-Position anlegen"
#: part/views.py:1857
#: part/views.py:1972
msgid "Edit BOM item"
msgstr "BOM-Position beaarbeiten"
#: part/views.py:1905
#: part/views.py:2020
msgid "Confim BOM item deletion"
msgstr "Löschung von BOM-Position bestätigen"
@ -2902,226 +2995,226 @@ msgstr "Bewegung der Lagerobjekte bestätigen"
msgid "Set the destination as the default location for selected parts"
msgstr "Setze das Ziel als Standard-Ziel für ausgewählte Teile"
#: stock/models.py:209
#: stock/models.py:210
#, fuzzy
#| msgid "A stock item with this serial number already exists"
msgid "StockItem with this serial number already exists"
msgstr "Ein Teil mit dieser Seriennummer existiert bereits"
#: stock/models.py:245
#: stock/models.py:246
#, python-brace-format
msgid "Part type ('{pf}') must be {pe}"
msgstr "Teile-Typ ('{pf}') muss {pe} sein"
#: stock/models.py:255 stock/models.py:264
#: stock/models.py:256 stock/models.py:265
msgid "Quantity must be 1 for item with a serial number"
msgstr "Anzahl muss für Objekte mit Seriennummer \"1\" sein"
#: stock/models.py:256
#: stock/models.py:257
msgid "Serial number cannot be set if quantity greater than 1"
msgstr ""
"Seriennummer kann nicht gesetzt werden wenn die Anzahl größer als \"1\" ist"
#: stock/models.py:277
#: stock/models.py:278
msgid "Item cannot belong to itself"
msgstr "Teil kann nicht zu sich selbst gehören"
#: stock/models.py:316
#: stock/models.py:311
msgid "Parent Stock Item"
msgstr "Eltern-Lagerobjekt"
#: stock/models.py:325
#: stock/models.py:320
msgid "Base part"
msgstr "Basis-Teil"
#: stock/models.py:334
#: stock/models.py:329
msgid "Select a matching supplier part for this stock item"
msgstr "Passenden Zulieferer für dieses Lagerobjekt auswählen"
#: stock/models.py:339 stock/templates/stock/stock_app_base.html:7
#: stock/models.py:334 stock/templates/stock/stock_app_base.html:7
msgid "Stock Location"
msgstr "Lagerort"
#: stock/models.py:342
#: stock/models.py:337
msgid "Where is this stock item located?"
msgstr "Wo wird dieses Teil normalerweise gelagert?"
#: stock/models.py:347
#: stock/models.py:342
msgid "Installed In"
msgstr "Installiert in"
#: stock/models.py:350
#: stock/models.py:345
msgid "Is this item installed in another item?"
msgstr "Ist dieses Teil in einem anderen verbaut?"
#: stock/models.py:366
#: stock/models.py:361
msgid "Serial number for this item"
msgstr "Seriennummer für dieses Teil"
#: stock/models.py:378
#: stock/models.py:373
msgid "Batch code for this stock item"
msgstr "Losnummer für dieses Lagerobjekt"
#: stock/models.py:382
#: stock/models.py:377
msgid "Stock Quantity"
msgstr "Bestand"
#: stock/models.py:391
#: stock/models.py:386
msgid "Source Build"
msgstr "Quellbau"
#: stock/models.py:393
#: stock/models.py:388
msgid "Build for this stock item"
msgstr "Bau für dieses Lagerobjekt"
#: stock/models.py:400
#: stock/models.py:395
msgid "Source Purchase Order"
msgstr "Quellbestellung"
#: stock/models.py:403
#: stock/models.py:398
msgid "Purchase order for this stock item"
msgstr "Bestellung für dieses Teil"
#: stock/models.py:409
#: stock/models.py:404
msgid "Destination Sales Order"
msgstr "Zielauftrag"
#: stock/models.py:416
#: stock/models.py:411
msgid "Destination Build Order"
msgstr "Zielbauauftrag"
#: stock/models.py:429
#: stock/models.py:424
msgid "Delete this Stock Item when stock is depleted"
msgstr "Objekt löschen wenn Lagerbestand aufgebraucht"
#: stock/models.py:439 stock/templates/stock/item_notes.html:14
#: stock/models.py:434 stock/templates/stock/item_notes.html:14
#: stock/templates/stock/item_notes.html:30
msgid "Stock Item Notes"
msgstr "Lagerobjekt-Notizen"
#: stock/models.py:490
#: stock/models.py:485
#, fuzzy
#| msgid "Item assigned to customer?"
msgid "Assigned to Customer"
msgstr "Ist dieses Objekt einem Kunden zugeteilt?"
#: stock/models.py:492
#: stock/models.py:487
#, fuzzy
#| msgid "Item assigned to customer?"
msgid "Manually assigned to customer"
msgstr "Ist dieses Objekt einem Kunden zugeteilt?"
#: stock/models.py:505
#: stock/models.py:500
#, fuzzy
#| msgid "Item assigned to customer?"
msgid "Returned from customer"
msgstr "Ist dieses Objekt einem Kunden zugeteilt?"
#: stock/models.py:507
#: stock/models.py:502
#, fuzzy
#| msgid "Create new stock location"
msgid "Returned to location"
msgstr "Neuen Lagerort anlegen"
#: stock/models.py:678
#: stock/models.py:673
#, fuzzy
#| msgid "Part is not a virtual part"
msgid "Part is not set as trackable"
msgstr "Teil ist nicht virtuell"
#: stock/models.py:684
#: stock/models.py:679
msgid "Quantity must be integer"
msgstr "Anzahl muss eine Ganzzahl sein"
#: stock/models.py:690
#: stock/models.py:685
#, python-brace-format
msgid "Quantity must not exceed available stock quantity ({n})"
msgstr "Anzahl darf nicht die verfügbare Anzahl überschreiten ({n})"
#: stock/models.py:693 stock/models.py:696
#: stock/models.py:688 stock/models.py:691
msgid "Serial numbers must be a list of integers"
msgstr "Seriennummern muss eine Liste von Ganzzahlen sein"
#: stock/models.py:699
#: stock/models.py:694
msgid "Quantity does not match serial numbers"
msgstr "Anzahl stimmt nicht mit den Seriennummern überein"
#: stock/models.py:709
#: stock/models.py:704
msgid "Serial numbers already exist: "
msgstr "Seriennummern existieren bereits:"
#: stock/models.py:734
#: stock/models.py:729
msgid "Add serial number"
msgstr "Seriennummer hinzufügen"
#: stock/models.py:737
#: stock/models.py:732
#, python-brace-format
msgid "Serialized {n} items"
msgstr "{n} Teile serialisiert"
#: stock/models.py:848
#: stock/models.py:843
msgid "StockItem cannot be moved as it is not in stock"
msgstr "Lagerobjekt kann nicht bewegt werden, da kein Bestand vorhanden ist"
#: stock/models.py:1175
#: stock/models.py:1170
msgid "Tracking entry title"
msgstr "Name des Eintrags-Trackings"
#: stock/models.py:1177
#: stock/models.py:1172
msgid "Entry notes"
msgstr "Eintrags-Notizen"
#: stock/models.py:1179
#: stock/models.py:1174
msgid "Link to external page for further information"
msgstr "Link auf externe Seite für weitere Informationen"
#: stock/models.py:1239
#: stock/models.py:1234
#, fuzzy
#| msgid "Serial number for this item"
msgid "Value must be provided for this test"
msgstr "Seriennummer für dieses Teil"
#: stock/models.py:1245
#: stock/models.py:1240
msgid "Attachment must be uploaded for this test"
msgstr ""
#: stock/models.py:1262
#: stock/models.py:1257
msgid "Test"
msgstr ""
#: stock/models.py:1263
#: stock/models.py:1258
#, fuzzy
#| msgid "Part name"
msgid "Test name"
msgstr "Name des Teils"
#: stock/models.py:1268
#: stock/models.py:1263
#, fuzzy
#| msgid "Search Results"
msgid "Result"
msgstr "Suchergebnisse"
#: stock/models.py:1269 templates/js/table_filters.html:90
#: stock/models.py:1264 templates/js/table_filters.html:90
msgid "Test result"
msgstr ""
#: stock/models.py:1275
#: stock/models.py:1270
msgid "Test output value"
msgstr ""
#: stock/models.py:1281
#: stock/models.py:1276
#, fuzzy
#| msgid "Attachments"
msgid "Attachment"
msgstr "Anhänge"
#: stock/models.py:1282
#: stock/models.py:1277
#, fuzzy
#| msgid "Delete attachment"
msgid "Test result attachment"
msgstr "Anhang löschen"
#: stock/models.py:1288
#: stock/models.py:1283
#, fuzzy
#| msgid "Edit notes"
msgid "Test notes"
@ -3257,41 +3350,37 @@ msgstr "Lagerobjekt löschen"
msgid "Generate test report"
msgstr ""
#: stock/templates/stock/item_base.html:132
msgid "Print labels"
msgstr ""
#: stock/templates/stock/item_base.html:140
#: stock/templates/stock/item_base.html:137
msgid "Stock Item Details"
msgstr "Lagerbestands-Details"
#: stock/templates/stock/item_base.html:173
#: stock/templates/stock/item_base.html:170
msgid "Belongs To"
msgstr "Gehört zu"
#: stock/templates/stock/item_base.html:195
#: stock/templates/stock/item_base.html:192
#, fuzzy
#| msgid "No stock location set"
msgid "No location set"
msgstr "Kein Lagerort gesetzt"
#: stock/templates/stock/item_base.html:202
#: stock/templates/stock/item_base.html:199
msgid "Unique Identifier"
msgstr "Eindeutiger Bezeichner"
#: stock/templates/stock/item_base.html:230
#: stock/templates/stock/item_base.html:227
msgid "Parent Item"
msgstr "Elternposition"
#: stock/templates/stock/item_base.html:255
#: stock/templates/stock/item_base.html:252
msgid "Last Updated"
msgstr "Zuletzt aktualisiert"
#: stock/templates/stock/item_base.html:260
#: stock/templates/stock/item_base.html:257
msgid "Last Stocktake"
msgstr "Letzte Inventur"
#: stock/templates/stock/item_base.html:264
#: stock/templates/stock/item_base.html:261
msgid "No stocktake performed"
msgstr "Keine Inventur ausgeführt"

View File

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-08-16 02:11+0000\n"
"POT-Creation-Date: 2020-08-18 04:01+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"
@ -42,30 +42,30 @@ msgstr ""
msgid "Confirm new password"
msgstr ""
#: InvenTree/helpers.py:328 order/models.py:187 order/models.py:261
#: InvenTree/helpers.py:336 order/models.py:187 order/models.py:261
msgid "Invalid quantity provided"
msgstr ""
#: InvenTree/helpers.py:331
#: InvenTree/helpers.py:339
msgid "Empty serial number string"
msgstr ""
#: InvenTree/helpers.py:352 InvenTree/helpers.py:369
#: InvenTree/helpers.py:360 InvenTree/helpers.py:377
#, python-brace-format
msgid "Duplicate serial: {n}"
msgstr ""
#: InvenTree/helpers.py:356 InvenTree/helpers.py:359 InvenTree/helpers.py:362
#: InvenTree/helpers.py:373
#: InvenTree/helpers.py:364 InvenTree/helpers.py:367 InvenTree/helpers.py:370
#: InvenTree/helpers.py:381
#, python-brace-format
msgid "Invalid group: {g}"
msgstr ""
#: InvenTree/helpers.py:379
#: InvenTree/helpers.py:387
msgid "No serial numbers found"
msgstr ""
#: InvenTree/helpers.py:383
#: InvenTree/helpers.py:391
#, python-brace-format
msgid "Number of unique serial number ({s}) must match quantity ({q})"
msgstr ""
@ -322,7 +322,7 @@ msgstr ""
msgid "Build status code"
msgstr ""
#: build/models.py:136 stock/models.py:376
#: build/models.py:136 stock/models.py:371
msgid "Batch Code"
msgstr ""
@ -334,11 +334,11 @@ msgstr ""
#: company/templates/company/supplier_part_base.html:60
#: company/templates/company/supplier_part_detail.html:24
#: part/templates/part/detail.html:74 part/templates/part/part_base.html:86
#: stock/models.py:370 stock/templates/stock/item_base.html:237
#: stock/models.py:365 stock/templates/stock/item_base.html:234
msgid "External Link"
msgstr ""
#: build/models.py:156 stock/models.py:372
#: build/models.py:156 stock/models.py:367
msgid "Link to external URL"
msgstr ""
@ -346,7 +346,7 @@ msgstr ""
#: company/templates/company/tabs.html:33 order/templates/order/po_tabs.html:15
#: order/templates/order/purchase_order_detail.html:200
#: order/templates/order/so_tabs.html:23 part/templates/part/tabs.html:64
#: stock/models.py:438 stock/models.py:1287 stock/templates/stock/tabs.html:26
#: stock/models.py:433 stock/models.py:1282 stock/templates/stock/tabs.html:26
#: templates/js/barcode.html:391 templates/js/bom.html:229
#: templates/js/stock.html:114 templates/js/stock.html:526
msgid "Notes"
@ -413,8 +413,8 @@ msgstr ""
#: build/templates/build/allocate.html:161
#: order/templates/order/sales_order_detail.html:68
#: order/templates/order/sales_order_detail.html:150 stock/models.py:364
#: stock/templates/stock/item_base.html:153
#: order/templates/order/sales_order_detail.html:150 stock/models.py:359
#: stock/templates/stock/item_base.html:150
msgid "Serial Number"
msgstr ""
@ -431,7 +431,7 @@ msgstr ""
#: part/templates/part/allocation.html:49
#: stock/templates/stock/item_base.html:26
#: stock/templates/stock/item_base.html:32
#: stock/templates/stock/item_base.html:159
#: stock/templates/stock/item_base.html:156
#: stock/templates/stock/stock_adjust.html:18 templates/js/barcode.html:338
#: templates/js/bom.html:172 templates/js/build.html:52
#: templates/js/stock.html:673
@ -440,7 +440,7 @@ msgstr ""
#: build/templates/build/allocate.html:177
#: build/templates/build/auto_allocate.html:20
#: stock/templates/stock/item_base.html:191
#: stock/templates/stock/item_base.html:188
#: stock/templates/stock/stock_adjust.html:17 templates/js/barcode.html:337
#: templates/js/stock.html:508
msgid "Location"
@ -478,7 +478,7 @@ msgstr ""
msgid "Reference"
msgstr ""
#: build/templates/build/allocate.html:338 part/models.py:1270
#: build/templates/build/allocate.html:338 part/models.py:1271
#: templates/js/part.html:359 templates/js/table_filters.html:100
msgid "Required"
msgstr ""
@ -522,7 +522,7 @@ msgstr ""
#: build/templates/build/build_base.html:8
#: build/templates/build/build_base.html:34
#: build/templates/build/complete.html:6
#: stock/templates/stock/item_base.html:216 templates/js/build.html:33
#: stock/templates/stock/item_base.html:213 templates/js/build.html:33
#: templates/navbar.html:12
msgid "Build"
msgstr ""
@ -542,7 +542,7 @@ msgstr ""
#: build/templates/build/build_base.html:80
#: build/templates/build/detail.html:42
#: order/templates/order/receive_parts.html:24
#: stock/templates/stock/item_base.html:269 templates/js/barcode.html:42
#: stock/templates/stock/item_base.html:266 templates/js/barcode.html:42
#: templates/js/build.html:57 templates/js/order.html:162
#: templates/js/order.html:235 templates/js/stock.html:495
msgid "Status"
@ -554,7 +554,7 @@ msgstr ""
#: order/templates/order/sales_order_notes.html:10
#: order/templates/order/sales_order_ship.html:25
#: part/templates/part/allocation.html:27
#: stock/templates/stock/item_base.html:179 templates/js/order.html:209
#: stock/templates/stock/item_base.html:176 templates/js/order.html:209
msgid "Sales Order"
msgstr ""
@ -621,7 +621,7 @@ msgid "Stock can be taken from any available location."
msgstr ""
#: build/templates/build/detail.html:48
#: stock/templates/stock/item_base.html:209 templates/js/stock.html:503
#: stock/templates/stock/item_base.html:206 templates/js/stock.html:503
msgid "Batch"
msgstr ""
@ -910,8 +910,8 @@ msgstr ""
msgid "Does this company manufacture parts?"
msgstr ""
#: company/models.py:279 stock/models.py:324
#: stock/templates/stock/item_base.html:145
#: company/models.py:279 stock/models.py:319
#: stock/templates/stock/item_base.html:142
msgid "Base Part"
msgstr ""
@ -982,14 +982,14 @@ msgstr ""
#: company/templates/company/supplier_part_detail.html:21 order/models.py:148
#: order/templates/order/order_base.html:74
#: order/templates/order/order_wizard/select_pos.html:30
#: stock/templates/stock/item_base.html:244 templates/js/company.html:52
#: stock/templates/stock/item_base.html:241 templates/js/company.html:52
#: templates/js/company.html:134 templates/js/order.html:144
msgid "Supplier"
msgstr ""
#: company/templates/company/detail.html:26 order/models.py:314
#: order/templates/order/sales_order_base.html:73 stock/models.py:359
#: stock/models.py:360 stock/templates/stock/item_base.html:166
#: order/templates/order/sales_order_base.html:73 stock/models.py:354
#: stock/models.py:355 stock/templates/stock/item_base.html:163
#: templates/js/company.html:44 templates/js/order.html:217
msgid "Customer"
msgstr ""
@ -1102,8 +1102,8 @@ msgid "New Sales Order"
msgstr ""
#: company/templates/company/supplier_part_base.html:6
#: company/templates/company/supplier_part_base.html:19 stock/models.py:333
#: stock/templates/stock/item_base.html:249 templates/js/company.html:150
#: company/templates/company/supplier_part_base.html:19 stock/models.py:328
#: stock/templates/stock/item_base.html:246 templates/js/company.html:150
msgid "Supplier Part"
msgstr ""
@ -1367,8 +1367,8 @@ msgstr ""
msgid "Supplier order reference code"
msgstr ""
#: order/models.py:185 order/models.py:259 part/views.py:1167
#: stock/models.py:238 stock/models.py:687
#: order/models.py:185 order/models.py:259 part/views.py:1250
#: stock/models.py:239 stock/models.py:682
msgid "Quantity must be greater than zero"
msgstr ""
@ -1402,7 +1402,7 @@ msgstr ""
#: order/models.py:466 order/templates/order/order_base.html:9
#: order/templates/order/order_base.html:23
#: stock/templates/stock/item_base.html:223 templates/js/order.html:136
#: stock/templates/stock/item_base.html:220 templates/js/order.html:136
msgid "Purchase Order"
msgstr ""
@ -1819,16 +1819,16 @@ msgstr ""
msgid "Remove allocation"
msgstr ""
#: part/bom.py:140
#: part/bom.py:144
#, python-brace-format
msgid "Unsupported file format: {f}"
msgstr ""
#: part/bom.py:145
#: part/bom.py:149
msgid "Error reading BOM file (invalid data)"
msgstr ""
#: part/bom.py:147
#: part/bom.py:151
msgid "Error reading BOM file (incorrect row size)"
msgstr ""
@ -1848,31 +1848,39 @@ msgstr ""
msgid "Download cascading / multi-level BOM"
msgstr ""
#: part/forms.py:76
#: part/forms.py:59
msgid "Levels"
msgstr ""
#: part/forms.py:59
msgid "Select maximum number of BOM levels to export (0 = all levels)"
msgstr ""
#: part/forms.py:78
msgid "Confirm that the BOM is correct"
msgstr ""
#: part/forms.py:88
#: part/forms.py:90
msgid "Select BOM file to upload"
msgstr ""
#: part/forms.py:112
#: part/forms.py:114
msgid "Select part category"
msgstr ""
#: part/forms.py:126
#: part/forms.py:128
msgid "Perform 'deep copy' which will duplicate all BOM data for this part"
msgstr ""
#: part/forms.py:131
#: part/forms.py:133
msgid "Confirm part creation"
msgstr ""
#: part/forms.py:221
#: part/forms.py:223
msgid "Input quantity for price calculation"
msgstr ""
#: part/forms.py:224
#: part/forms.py:226
msgid "Select currency for price calculation"
msgstr ""
@ -1985,116 +1993,117 @@ msgstr ""
msgid "Stored BOM checksum"
msgstr ""
#: part/models.py:1222
#: part/models.py:1223
msgid "Test templates can only be created for trackable parts"
msgstr ""
#: part/models.py:1239
#: part/models.py:1240
msgid "Test with this name already exists for this part"
msgstr ""
#: part/models.py:1258 templates/js/part.html:350 templates/js/stock.html:90
#: part/models.py:1259 templates/js/part.html:350 templates/js/stock.html:90
msgid "Test Name"
msgstr ""
#: part/models.py:1259
#: part/models.py:1260
msgid "Enter a name for the test"
msgstr ""
#: part/models.py:1264
#: part/models.py:1265
msgid "Test Description"
msgstr ""
#: part/models.py:1265
#: part/models.py:1266
msgid "Enter description for this test"
msgstr ""
#: part/models.py:1271
#: part/models.py:1272
msgid "Is this test required to pass?"
msgstr ""
#: part/models.py:1276 templates/js/part.html:367
#: part/models.py:1277 templates/js/part.html:367
msgid "Requires Value"
msgstr ""
#: part/models.py:1277
#: part/models.py:1278
msgid "Does this test require a value when adding a test result?"
msgstr ""
#: part/models.py:1282 templates/js/part.html:374
#: part/models.py:1283 templates/js/part.html:374
msgid "Requires Attachment"
msgstr ""
#: part/models.py:1283
#: part/models.py:1284
msgid "Does this test require a file attachment when adding a test result?"
msgstr ""
#: part/models.py:1316
#: part/models.py:1317
msgid "Parameter template name must be unique"
msgstr ""
#: part/models.py:1321
#: part/models.py:1322
msgid "Parameter Name"
msgstr ""
#: part/models.py:1323
#: part/models.py:1324
msgid "Parameter Units"
msgstr ""
#: part/models.py:1349
#: part/models.py:1350
msgid "Parent Part"
msgstr ""
#: part/models.py:1351
#: part/models.py:1352
msgid "Parameter Template"
msgstr ""
#: part/models.py:1353
#: part/models.py:1354
msgid "Parameter Value"
msgstr ""
#: part/models.py:1382
#: part/models.py:1383
msgid "Select parent part"
msgstr ""
#: part/models.py:1390
#: part/models.py:1391
msgid "Select part to be used in BOM"
msgstr ""
#: part/models.py:1396
#: part/models.py:1397
msgid "BOM quantity for this BOM item"
msgstr ""
#: part/models.py:1399
#: part/models.py:1400
msgid "Estimated build wastage quantity (absolute or percentage)"
msgstr ""
#: part/models.py:1402
#: part/models.py:1403
msgid "BOM item reference"
msgstr ""
#: part/models.py:1405
#: part/models.py:1406
msgid "BOM item notes"
msgstr ""
#: part/models.py:1407
#: part/models.py:1408
msgid "BOM line checksum"
msgstr ""
#: part/models.py:1471 stock/models.py:228
#: part/models.py:1472 part/views.py:1256 part/views.py:1308
#: stock/models.py:229
msgid "Quantity must be integer value for trackable parts"
msgstr ""
#: part/models.py:1480
#: part/models.py:1481
msgid "Part cannot be added to its own Bill of Materials"
msgstr ""
#: part/models.py:1487
#: part/models.py:1488
#, python-brace-format
msgid "Part '{p1}' is used in BOM for '{p2}' (recursive)"
msgstr ""
#: part/models.py:1494
#: part/models.py:1495
msgid "BOM Item"
msgstr ""
@ -2113,14 +2122,14 @@ msgstr ""
#: part/templates/part/allocation.html:45
#: stock/templates/stock/item_base.html:8
#: stock/templates/stock/item_base.html:58
#: stock/templates/stock/item_base.html:231
#: stock/templates/stock/item_base.html:228
#: stock/templates/stock/stock_adjust.html:16 templates/js/build.html:106
#: templates/js/stock.html:643
msgid "Stock Item"
msgstr ""
#: part/templates/part/allocation.html:20
#: stock/templates/stock/item_base.html:185
#: stock/templates/stock/item_base.html:182
msgid "Build Order"
msgstr ""
@ -2156,10 +2165,78 @@ msgstr ""
msgid "Validate Bill of Materials"
msgstr ""
#: part/templates/part/bom.html:46 part/views.py:1412
#: part/templates/part/bom.html:46 part/views.py:1523
msgid "Export Bill of Materials"
msgstr ""
#: part/templates/part/bom_upload/select_fields.html:8
#: part/templates/part/bom_upload/select_parts.html:8
#: part/templates/part/bom_upload/upload_file.html:10
msgid "Upload Bill of Materials"
msgstr ""
#: part/templates/part/bom_upload/select_fields.html:10
msgid "Step 2 - Select Fields"
msgstr ""
#: part/templates/part/bom_upload/select_fields.html:15
msgid "Missing selections for the following required columns"
msgstr ""
#: part/templates/part/bom_upload/select_fields.html:26
msgid "Submit Selections"
msgstr ""
#: part/templates/part/bom_upload/select_fields.html:35
msgid "File Fields"
msgstr ""
#: part/templates/part/bom_upload/select_fields.html:52
msgid "Match Fields"
msgstr ""
#: part/templates/part/bom_upload/select_parts.html:10
msgid "Step 3 - Select Parts"
msgstr ""
#: part/templates/part/bom_upload/select_parts.html:15
msgid "Errors exist in the submitted data"
msgstr ""
#: part/templates/part/bom_upload/select_parts.html:21
msgid "Submit BOM"
msgstr ""
#: part/templates/part/bom_upload/select_parts.html:33
msgid "Row"
msgstr ""
#: part/templates/part/bom_upload/select_parts.html:34
#: part/templates/part/bom_upload/select_parts.html:63
msgid "Select Part"
msgstr ""
#: part/templates/part/bom_upload/upload_file.html:13
msgid "Step 1 - Select BOM File"
msgstr ""
#: part/templates/part/bom_upload/upload_file.html:16
msgid "Requirements for BOM upload"
msgstr ""
#: part/templates/part/bom_upload/upload_file.html:18
msgid ""
"The BOM file must contain the required named columns as provided in the "
msgstr ""
#: part/templates/part/bom_upload/upload_file.html:18
msgid "BOM Upload Template"
msgstr ""
#: part/templates/part/bom_upload/upload_file.html:19
msgid "Each part must already exist in the database"
msgstr ""
#: part/templates/part/category.html:14
msgid "All parts"
msgstr ""
@ -2354,7 +2431,7 @@ msgstr ""
msgid "New Parameter"
msgstr ""
#: part/templates/part/params.html:21 stock/models.py:1274
#: part/templates/part/params.html:21 stock/models.py:1269
#: templates/js/stock.html:110
msgid "Value"
msgstr ""
@ -2535,7 +2612,7 @@ msgstr ""
msgid "Used In"
msgstr ""
#: part/templates/part/tabs.html:55 stock/templates/stock/item_base.html:275
#: part/templates/part/tabs.html:55 stock/templates/stock/item_base.html:272
msgid "Tests"
msgstr ""
@ -2640,91 +2717,91 @@ msgstr ""
msgid "Validate BOM"
msgstr ""
#: part/views.py:908
#: part/views.py:909
msgid "No BOM file provided"
msgstr ""
#: part/views.py:1169
#: part/views.py:1259
msgid "Enter a valid quantity"
msgstr ""
#: part/views.py:1193 part/views.py:1196
#: part/views.py:1284 part/views.py:1287
msgid "Select valid part"
msgstr ""
#: part/views.py:1202
#: part/views.py:1293
msgid "Duplicate part selected"
msgstr ""
#: part/views.py:1230
#: part/views.py:1329
msgid "Select a part"
msgstr ""
#: part/views.py:1234
#: part/views.py:1333
msgid "Specify quantity"
msgstr ""
#: part/views.py:1450
#: part/views.py:1565
msgid "Confirm Part Deletion"
msgstr ""
#: part/views.py:1457
#: part/views.py:1572
msgid "Part was deleted"
msgstr ""
#: part/views.py:1466
#: part/views.py:1581
msgid "Part Pricing"
msgstr ""
#: part/views.py:1588
#: part/views.py:1703
msgid "Create Part Parameter Template"
msgstr ""
#: part/views.py:1596
#: part/views.py:1711
msgid "Edit Part Parameter Template"
msgstr ""
#: part/views.py:1603
#: part/views.py:1718
msgid "Delete Part Parameter Template"
msgstr ""
#: part/views.py:1611
#: part/views.py:1726
msgid "Create Part Parameter"
msgstr ""
#: part/views.py:1661
#: part/views.py:1776
msgid "Edit Part Parameter"
msgstr ""
#: part/views.py:1675
#: part/views.py:1790
msgid "Delete Part Parameter"
msgstr ""
#: part/views.py:1691
#: part/views.py:1806
msgid "Edit Part Category"
msgstr ""
#: part/views.py:1726
#: part/views.py:1841
msgid "Delete Part Category"
msgstr ""
#: part/views.py:1732
#: part/views.py:1847
msgid "Part category was deleted"
msgstr ""
#: part/views.py:1740
#: part/views.py:1855
msgid "Create new part category"
msgstr ""
#: part/views.py:1791
#: part/views.py:1906
msgid "Create BOM item"
msgstr ""
#: part/views.py:1857
#: part/views.py:1972
msgid "Edit BOM item"
msgstr ""
#: part/views.py:1905
#: part/views.py:2020
msgid "Confim BOM item deletion"
msgstr ""
@ -2776,203 +2853,203 @@ msgstr ""
msgid "Set the destination as the default location for selected parts"
msgstr ""
#: stock/models.py:209
#: stock/models.py:210
msgid "StockItem with this serial number already exists"
msgstr ""
#: stock/models.py:245
#: stock/models.py:246
#, python-brace-format
msgid "Part type ('{pf}') must be {pe}"
msgstr ""
#: stock/models.py:255 stock/models.py:264
#: stock/models.py:256 stock/models.py:265
msgid "Quantity must be 1 for item with a serial number"
msgstr ""
#: stock/models.py:256
#: stock/models.py:257
msgid "Serial number cannot be set if quantity greater than 1"
msgstr ""
#: stock/models.py:277
#: stock/models.py:278
msgid "Item cannot belong to itself"
msgstr ""
#: stock/models.py:316
#: stock/models.py:311
msgid "Parent Stock Item"
msgstr ""
#: stock/models.py:325
#: stock/models.py:320
msgid "Base part"
msgstr ""
#: stock/models.py:334
#: stock/models.py:329
msgid "Select a matching supplier part for this stock item"
msgstr ""
#: stock/models.py:339 stock/templates/stock/stock_app_base.html:7
#: stock/models.py:334 stock/templates/stock/stock_app_base.html:7
msgid "Stock Location"
msgstr ""
#: stock/models.py:342
#: stock/models.py:337
msgid "Where is this stock item located?"
msgstr ""
#: stock/models.py:347
#: stock/models.py:342
msgid "Installed In"
msgstr ""
#: stock/models.py:350
#: stock/models.py:345
msgid "Is this item installed in another item?"
msgstr ""
#: stock/models.py:366
#: stock/models.py:361
msgid "Serial number for this item"
msgstr ""
#: stock/models.py:378
#: stock/models.py:373
msgid "Batch code for this stock item"
msgstr ""
#: stock/models.py:382
#: stock/models.py:377
msgid "Stock Quantity"
msgstr ""
#: stock/models.py:391
#: stock/models.py:386
msgid "Source Build"
msgstr ""
#: stock/models.py:393
#: stock/models.py:388
msgid "Build for this stock item"
msgstr ""
#: stock/models.py:400
#: stock/models.py:395
msgid "Source Purchase Order"
msgstr ""
#: stock/models.py:403
#: stock/models.py:398
msgid "Purchase order for this stock item"
msgstr ""
#: stock/models.py:409
#: stock/models.py:404
msgid "Destination Sales Order"
msgstr ""
#: stock/models.py:416
#: stock/models.py:411
msgid "Destination Build Order"
msgstr ""
#: stock/models.py:429
#: stock/models.py:424
msgid "Delete this Stock Item when stock is depleted"
msgstr ""
#: stock/models.py:439 stock/templates/stock/item_notes.html:14
#: stock/models.py:434 stock/templates/stock/item_notes.html:14
#: stock/templates/stock/item_notes.html:30
msgid "Stock Item Notes"
msgstr ""
#: stock/models.py:490
#: stock/models.py:485
msgid "Assigned to Customer"
msgstr ""
#: stock/models.py:492
#: stock/models.py:487
msgid "Manually assigned to customer"
msgstr ""
#: stock/models.py:505
#: stock/models.py:500
msgid "Returned from customer"
msgstr ""
#: stock/models.py:507
#: stock/models.py:502
msgid "Returned to location"
msgstr ""
#: stock/models.py:678
#: stock/models.py:673
msgid "Part is not set as trackable"
msgstr ""
#: stock/models.py:684
#: stock/models.py:679
msgid "Quantity must be integer"
msgstr ""
#: stock/models.py:690
#: stock/models.py:685
#, python-brace-format
msgid "Quantity must not exceed available stock quantity ({n})"
msgstr ""
#: stock/models.py:693 stock/models.py:696
#: stock/models.py:688 stock/models.py:691
msgid "Serial numbers must be a list of integers"
msgstr ""
#: stock/models.py:699
#: stock/models.py:694
msgid "Quantity does not match serial numbers"
msgstr ""
#: stock/models.py:709
#: stock/models.py:704
msgid "Serial numbers already exist: "
msgstr ""
#: stock/models.py:734
#: stock/models.py:729
msgid "Add serial number"
msgstr ""
#: stock/models.py:737
#: stock/models.py:732
#, python-brace-format
msgid "Serialized {n} items"
msgstr ""
#: stock/models.py:848
#: stock/models.py:843
msgid "StockItem cannot be moved as it is not in stock"
msgstr ""
#: stock/models.py:1175
#: stock/models.py:1170
msgid "Tracking entry title"
msgstr ""
#: stock/models.py:1177
#: stock/models.py:1172
msgid "Entry notes"
msgstr ""
#: stock/models.py:1179
#: stock/models.py:1174
msgid "Link to external page for further information"
msgstr ""
#: stock/models.py:1239
#: stock/models.py:1234
msgid "Value must be provided for this test"
msgstr ""
#: stock/models.py:1245
#: stock/models.py:1240
msgid "Attachment must be uploaded for this test"
msgstr ""
#: stock/models.py:1262
#: stock/models.py:1257
msgid "Test"
msgstr ""
#: stock/models.py:1263
#: stock/models.py:1258
msgid "Test name"
msgstr ""
#: stock/models.py:1268
#: stock/models.py:1263
msgid "Result"
msgstr ""
#: stock/models.py:1269 templates/js/table_filters.html:90
#: stock/models.py:1264 templates/js/table_filters.html:90
msgid "Test result"
msgstr ""
#: stock/models.py:1275
#: stock/models.py:1270
msgid "Test output value"
msgstr ""
#: stock/models.py:1281
#: stock/models.py:1276
msgid "Attachment"
msgstr ""
#: stock/models.py:1282
#: stock/models.py:1277
msgid "Test result attachment"
msgstr ""
#: stock/models.py:1288
#: stock/models.py:1283
msgid "Test notes"
msgstr ""
@ -3078,39 +3155,35 @@ msgstr ""
msgid "Generate test report"
msgstr ""
#: stock/templates/stock/item_base.html:132
msgid "Print labels"
msgstr ""
#: stock/templates/stock/item_base.html:140
#: stock/templates/stock/item_base.html:137
msgid "Stock Item Details"
msgstr ""
#: stock/templates/stock/item_base.html:173
#: stock/templates/stock/item_base.html:170
msgid "Belongs To"
msgstr ""
#: stock/templates/stock/item_base.html:195
#: stock/templates/stock/item_base.html:192
msgid "No location set"
msgstr ""
#: stock/templates/stock/item_base.html:202
#: stock/templates/stock/item_base.html:199
msgid "Unique Identifier"
msgstr ""
#: stock/templates/stock/item_base.html:230
#: stock/templates/stock/item_base.html:227
msgid "Parent Item"
msgstr ""
#: stock/templates/stock/item_base.html:255
#: stock/templates/stock/item_base.html:252
msgid "Last Updated"
msgstr ""
#: stock/templates/stock/item_base.html:260
#: stock/templates/stock/item_base.html:257
msgid "Last Stocktake"
msgstr ""
#: stock/templates/stock/item_base.html:264
#: stock/templates/stock/item_base.html:261
msgid "No stocktake performed"
msgstr ""

View File

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-08-16 02:11+0000\n"
"POT-Creation-Date: 2020-08-18 04:01+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"
@ -42,30 +42,30 @@ msgstr ""
msgid "Confirm new password"
msgstr ""
#: InvenTree/helpers.py:328 order/models.py:187 order/models.py:261
#: InvenTree/helpers.py:336 order/models.py:187 order/models.py:261
msgid "Invalid quantity provided"
msgstr ""
#: InvenTree/helpers.py:331
#: InvenTree/helpers.py:339
msgid "Empty serial number string"
msgstr ""
#: InvenTree/helpers.py:352 InvenTree/helpers.py:369
#: InvenTree/helpers.py:360 InvenTree/helpers.py:377
#, python-brace-format
msgid "Duplicate serial: {n}"
msgstr ""
#: InvenTree/helpers.py:356 InvenTree/helpers.py:359 InvenTree/helpers.py:362
#: InvenTree/helpers.py:373
#: InvenTree/helpers.py:364 InvenTree/helpers.py:367 InvenTree/helpers.py:370
#: InvenTree/helpers.py:381
#, python-brace-format
msgid "Invalid group: {g}"
msgstr ""
#: InvenTree/helpers.py:379
#: InvenTree/helpers.py:387
msgid "No serial numbers found"
msgstr ""
#: InvenTree/helpers.py:383
#: InvenTree/helpers.py:391
#, python-brace-format
msgid "Number of unique serial number ({s}) must match quantity ({q})"
msgstr ""
@ -322,7 +322,7 @@ msgstr ""
msgid "Build status code"
msgstr ""
#: build/models.py:136 stock/models.py:376
#: build/models.py:136 stock/models.py:371
msgid "Batch Code"
msgstr ""
@ -334,11 +334,11 @@ msgstr ""
#: company/templates/company/supplier_part_base.html:60
#: company/templates/company/supplier_part_detail.html:24
#: part/templates/part/detail.html:74 part/templates/part/part_base.html:86
#: stock/models.py:370 stock/templates/stock/item_base.html:237
#: stock/models.py:365 stock/templates/stock/item_base.html:234
msgid "External Link"
msgstr ""
#: build/models.py:156 stock/models.py:372
#: build/models.py:156 stock/models.py:367
msgid "Link to external URL"
msgstr ""
@ -346,7 +346,7 @@ msgstr ""
#: company/templates/company/tabs.html:33 order/templates/order/po_tabs.html:15
#: order/templates/order/purchase_order_detail.html:200
#: order/templates/order/so_tabs.html:23 part/templates/part/tabs.html:64
#: stock/models.py:438 stock/models.py:1287 stock/templates/stock/tabs.html:26
#: stock/models.py:433 stock/models.py:1282 stock/templates/stock/tabs.html:26
#: templates/js/barcode.html:391 templates/js/bom.html:229
#: templates/js/stock.html:114 templates/js/stock.html:526
msgid "Notes"
@ -413,8 +413,8 @@ msgstr ""
#: build/templates/build/allocate.html:161
#: order/templates/order/sales_order_detail.html:68
#: order/templates/order/sales_order_detail.html:150 stock/models.py:364
#: stock/templates/stock/item_base.html:153
#: order/templates/order/sales_order_detail.html:150 stock/models.py:359
#: stock/templates/stock/item_base.html:150
msgid "Serial Number"
msgstr ""
@ -431,7 +431,7 @@ msgstr ""
#: part/templates/part/allocation.html:49
#: stock/templates/stock/item_base.html:26
#: stock/templates/stock/item_base.html:32
#: stock/templates/stock/item_base.html:159
#: stock/templates/stock/item_base.html:156
#: stock/templates/stock/stock_adjust.html:18 templates/js/barcode.html:338
#: templates/js/bom.html:172 templates/js/build.html:52
#: templates/js/stock.html:673
@ -440,7 +440,7 @@ msgstr ""
#: build/templates/build/allocate.html:177
#: build/templates/build/auto_allocate.html:20
#: stock/templates/stock/item_base.html:191
#: stock/templates/stock/item_base.html:188
#: stock/templates/stock/stock_adjust.html:17 templates/js/barcode.html:337
#: templates/js/stock.html:508
msgid "Location"
@ -478,7 +478,7 @@ msgstr ""
msgid "Reference"
msgstr ""
#: build/templates/build/allocate.html:338 part/models.py:1270
#: build/templates/build/allocate.html:338 part/models.py:1271
#: templates/js/part.html:359 templates/js/table_filters.html:100
msgid "Required"
msgstr ""
@ -522,7 +522,7 @@ msgstr ""
#: build/templates/build/build_base.html:8
#: build/templates/build/build_base.html:34
#: build/templates/build/complete.html:6
#: stock/templates/stock/item_base.html:216 templates/js/build.html:33
#: stock/templates/stock/item_base.html:213 templates/js/build.html:33
#: templates/navbar.html:12
msgid "Build"
msgstr ""
@ -542,7 +542,7 @@ msgstr ""
#: build/templates/build/build_base.html:80
#: build/templates/build/detail.html:42
#: order/templates/order/receive_parts.html:24
#: stock/templates/stock/item_base.html:269 templates/js/barcode.html:42
#: stock/templates/stock/item_base.html:266 templates/js/barcode.html:42
#: templates/js/build.html:57 templates/js/order.html:162
#: templates/js/order.html:235 templates/js/stock.html:495
msgid "Status"
@ -554,7 +554,7 @@ msgstr ""
#: order/templates/order/sales_order_notes.html:10
#: order/templates/order/sales_order_ship.html:25
#: part/templates/part/allocation.html:27
#: stock/templates/stock/item_base.html:179 templates/js/order.html:209
#: stock/templates/stock/item_base.html:176 templates/js/order.html:209
msgid "Sales Order"
msgstr ""
@ -621,7 +621,7 @@ msgid "Stock can be taken from any available location."
msgstr ""
#: build/templates/build/detail.html:48
#: stock/templates/stock/item_base.html:209 templates/js/stock.html:503
#: stock/templates/stock/item_base.html:206 templates/js/stock.html:503
msgid "Batch"
msgstr ""
@ -910,8 +910,8 @@ msgstr ""
msgid "Does this company manufacture parts?"
msgstr ""
#: company/models.py:279 stock/models.py:324
#: stock/templates/stock/item_base.html:145
#: company/models.py:279 stock/models.py:319
#: stock/templates/stock/item_base.html:142
msgid "Base Part"
msgstr ""
@ -982,14 +982,14 @@ msgstr ""
#: company/templates/company/supplier_part_detail.html:21 order/models.py:148
#: order/templates/order/order_base.html:74
#: order/templates/order/order_wizard/select_pos.html:30
#: stock/templates/stock/item_base.html:244 templates/js/company.html:52
#: stock/templates/stock/item_base.html:241 templates/js/company.html:52
#: templates/js/company.html:134 templates/js/order.html:144
msgid "Supplier"
msgstr ""
#: company/templates/company/detail.html:26 order/models.py:314
#: order/templates/order/sales_order_base.html:73 stock/models.py:359
#: stock/models.py:360 stock/templates/stock/item_base.html:166
#: order/templates/order/sales_order_base.html:73 stock/models.py:354
#: stock/models.py:355 stock/templates/stock/item_base.html:163
#: templates/js/company.html:44 templates/js/order.html:217
msgid "Customer"
msgstr ""
@ -1102,8 +1102,8 @@ msgid "New Sales Order"
msgstr ""
#: company/templates/company/supplier_part_base.html:6
#: company/templates/company/supplier_part_base.html:19 stock/models.py:333
#: stock/templates/stock/item_base.html:249 templates/js/company.html:150
#: company/templates/company/supplier_part_base.html:19 stock/models.py:328
#: stock/templates/stock/item_base.html:246 templates/js/company.html:150
msgid "Supplier Part"
msgstr ""
@ -1367,8 +1367,8 @@ msgstr ""
msgid "Supplier order reference code"
msgstr ""
#: order/models.py:185 order/models.py:259 part/views.py:1167
#: stock/models.py:238 stock/models.py:687
#: order/models.py:185 order/models.py:259 part/views.py:1250
#: stock/models.py:239 stock/models.py:682
msgid "Quantity must be greater than zero"
msgstr ""
@ -1402,7 +1402,7 @@ msgstr ""
#: order/models.py:466 order/templates/order/order_base.html:9
#: order/templates/order/order_base.html:23
#: stock/templates/stock/item_base.html:223 templates/js/order.html:136
#: stock/templates/stock/item_base.html:220 templates/js/order.html:136
msgid "Purchase Order"
msgstr ""
@ -1819,16 +1819,16 @@ msgstr ""
msgid "Remove allocation"
msgstr ""
#: part/bom.py:140
#: part/bom.py:144
#, python-brace-format
msgid "Unsupported file format: {f}"
msgstr ""
#: part/bom.py:145
#: part/bom.py:149
msgid "Error reading BOM file (invalid data)"
msgstr ""
#: part/bom.py:147
#: part/bom.py:151
msgid "Error reading BOM file (incorrect row size)"
msgstr ""
@ -1848,31 +1848,39 @@ msgstr ""
msgid "Download cascading / multi-level BOM"
msgstr ""
#: part/forms.py:76
#: part/forms.py:59
msgid "Levels"
msgstr ""
#: part/forms.py:59
msgid "Select maximum number of BOM levels to export (0 = all levels)"
msgstr ""
#: part/forms.py:78
msgid "Confirm that the BOM is correct"
msgstr ""
#: part/forms.py:88
#: part/forms.py:90
msgid "Select BOM file to upload"
msgstr ""
#: part/forms.py:112
#: part/forms.py:114
msgid "Select part category"
msgstr ""
#: part/forms.py:126
#: part/forms.py:128
msgid "Perform 'deep copy' which will duplicate all BOM data for this part"
msgstr ""
#: part/forms.py:131
#: part/forms.py:133
msgid "Confirm part creation"
msgstr ""
#: part/forms.py:221
#: part/forms.py:223
msgid "Input quantity for price calculation"
msgstr ""
#: part/forms.py:224
#: part/forms.py:226
msgid "Select currency for price calculation"
msgstr ""
@ -1985,116 +1993,117 @@ msgstr ""
msgid "Stored BOM checksum"
msgstr ""
#: part/models.py:1222
#: part/models.py:1223
msgid "Test templates can only be created for trackable parts"
msgstr ""
#: part/models.py:1239
#: part/models.py:1240
msgid "Test with this name already exists for this part"
msgstr ""
#: part/models.py:1258 templates/js/part.html:350 templates/js/stock.html:90
#: part/models.py:1259 templates/js/part.html:350 templates/js/stock.html:90
msgid "Test Name"
msgstr ""
#: part/models.py:1259
#: part/models.py:1260
msgid "Enter a name for the test"
msgstr ""
#: part/models.py:1264
#: part/models.py:1265
msgid "Test Description"
msgstr ""
#: part/models.py:1265
#: part/models.py:1266
msgid "Enter description for this test"
msgstr ""
#: part/models.py:1271
#: part/models.py:1272
msgid "Is this test required to pass?"
msgstr ""
#: part/models.py:1276 templates/js/part.html:367
#: part/models.py:1277 templates/js/part.html:367
msgid "Requires Value"
msgstr ""
#: part/models.py:1277
#: part/models.py:1278
msgid "Does this test require a value when adding a test result?"
msgstr ""
#: part/models.py:1282 templates/js/part.html:374
#: part/models.py:1283 templates/js/part.html:374
msgid "Requires Attachment"
msgstr ""
#: part/models.py:1283
#: part/models.py:1284
msgid "Does this test require a file attachment when adding a test result?"
msgstr ""
#: part/models.py:1316
#: part/models.py:1317
msgid "Parameter template name must be unique"
msgstr ""
#: part/models.py:1321
#: part/models.py:1322
msgid "Parameter Name"
msgstr ""
#: part/models.py:1323
#: part/models.py:1324
msgid "Parameter Units"
msgstr ""
#: part/models.py:1349
#: part/models.py:1350
msgid "Parent Part"
msgstr ""
#: part/models.py:1351
#: part/models.py:1352
msgid "Parameter Template"
msgstr ""
#: part/models.py:1353
#: part/models.py:1354
msgid "Parameter Value"
msgstr ""
#: part/models.py:1382
#: part/models.py:1383
msgid "Select parent part"
msgstr ""
#: part/models.py:1390
#: part/models.py:1391
msgid "Select part to be used in BOM"
msgstr ""
#: part/models.py:1396
#: part/models.py:1397
msgid "BOM quantity for this BOM item"
msgstr ""
#: part/models.py:1399
#: part/models.py:1400
msgid "Estimated build wastage quantity (absolute or percentage)"
msgstr ""
#: part/models.py:1402
#: part/models.py:1403
msgid "BOM item reference"
msgstr ""
#: part/models.py:1405
#: part/models.py:1406
msgid "BOM item notes"
msgstr ""
#: part/models.py:1407
#: part/models.py:1408
msgid "BOM line checksum"
msgstr ""
#: part/models.py:1471 stock/models.py:228
#: part/models.py:1472 part/views.py:1256 part/views.py:1308
#: stock/models.py:229
msgid "Quantity must be integer value for trackable parts"
msgstr ""
#: part/models.py:1480
#: part/models.py:1481
msgid "Part cannot be added to its own Bill of Materials"
msgstr ""
#: part/models.py:1487
#: part/models.py:1488
#, python-brace-format
msgid "Part '{p1}' is used in BOM for '{p2}' (recursive)"
msgstr ""
#: part/models.py:1494
#: part/models.py:1495
msgid "BOM Item"
msgstr ""
@ -2113,14 +2122,14 @@ msgstr ""
#: part/templates/part/allocation.html:45
#: stock/templates/stock/item_base.html:8
#: stock/templates/stock/item_base.html:58
#: stock/templates/stock/item_base.html:231
#: stock/templates/stock/item_base.html:228
#: stock/templates/stock/stock_adjust.html:16 templates/js/build.html:106
#: templates/js/stock.html:643
msgid "Stock Item"
msgstr ""
#: part/templates/part/allocation.html:20
#: stock/templates/stock/item_base.html:185
#: stock/templates/stock/item_base.html:182
msgid "Build Order"
msgstr ""
@ -2156,10 +2165,78 @@ msgstr ""
msgid "Validate Bill of Materials"
msgstr ""
#: part/templates/part/bom.html:46 part/views.py:1412
#: part/templates/part/bom.html:46 part/views.py:1523
msgid "Export Bill of Materials"
msgstr ""
#: part/templates/part/bom_upload/select_fields.html:8
#: part/templates/part/bom_upload/select_parts.html:8
#: part/templates/part/bom_upload/upload_file.html:10
msgid "Upload Bill of Materials"
msgstr ""
#: part/templates/part/bom_upload/select_fields.html:10
msgid "Step 2 - Select Fields"
msgstr ""
#: part/templates/part/bom_upload/select_fields.html:15
msgid "Missing selections for the following required columns"
msgstr ""
#: part/templates/part/bom_upload/select_fields.html:26
msgid "Submit Selections"
msgstr ""
#: part/templates/part/bom_upload/select_fields.html:35
msgid "File Fields"
msgstr ""
#: part/templates/part/bom_upload/select_fields.html:52
msgid "Match Fields"
msgstr ""
#: part/templates/part/bom_upload/select_parts.html:10
msgid "Step 3 - Select Parts"
msgstr ""
#: part/templates/part/bom_upload/select_parts.html:15
msgid "Errors exist in the submitted data"
msgstr ""
#: part/templates/part/bom_upload/select_parts.html:21
msgid "Submit BOM"
msgstr ""
#: part/templates/part/bom_upload/select_parts.html:33
msgid "Row"
msgstr ""
#: part/templates/part/bom_upload/select_parts.html:34
#: part/templates/part/bom_upload/select_parts.html:63
msgid "Select Part"
msgstr ""
#: part/templates/part/bom_upload/upload_file.html:13
msgid "Step 1 - Select BOM File"
msgstr ""
#: part/templates/part/bom_upload/upload_file.html:16
msgid "Requirements for BOM upload"
msgstr ""
#: part/templates/part/bom_upload/upload_file.html:18
msgid ""
"The BOM file must contain the required named columns as provided in the "
msgstr ""
#: part/templates/part/bom_upload/upload_file.html:18
msgid "BOM Upload Template"
msgstr ""
#: part/templates/part/bom_upload/upload_file.html:19
msgid "Each part must already exist in the database"
msgstr ""
#: part/templates/part/category.html:14
msgid "All parts"
msgstr ""
@ -2354,7 +2431,7 @@ msgstr ""
msgid "New Parameter"
msgstr ""
#: part/templates/part/params.html:21 stock/models.py:1274
#: part/templates/part/params.html:21 stock/models.py:1269
#: templates/js/stock.html:110
msgid "Value"
msgstr ""
@ -2535,7 +2612,7 @@ msgstr ""
msgid "Used In"
msgstr ""
#: part/templates/part/tabs.html:55 stock/templates/stock/item_base.html:275
#: part/templates/part/tabs.html:55 stock/templates/stock/item_base.html:272
msgid "Tests"
msgstr ""
@ -2640,91 +2717,91 @@ msgstr ""
msgid "Validate BOM"
msgstr ""
#: part/views.py:908
#: part/views.py:909
msgid "No BOM file provided"
msgstr ""
#: part/views.py:1169
#: part/views.py:1259
msgid "Enter a valid quantity"
msgstr ""
#: part/views.py:1193 part/views.py:1196
#: part/views.py:1284 part/views.py:1287
msgid "Select valid part"
msgstr ""
#: part/views.py:1202
#: part/views.py:1293
msgid "Duplicate part selected"
msgstr ""
#: part/views.py:1230
#: part/views.py:1329
msgid "Select a part"
msgstr ""
#: part/views.py:1234
#: part/views.py:1333
msgid "Specify quantity"
msgstr ""
#: part/views.py:1450
#: part/views.py:1565
msgid "Confirm Part Deletion"
msgstr ""
#: part/views.py:1457
#: part/views.py:1572
msgid "Part was deleted"
msgstr ""
#: part/views.py:1466
#: part/views.py:1581
msgid "Part Pricing"
msgstr ""
#: part/views.py:1588
#: part/views.py:1703
msgid "Create Part Parameter Template"
msgstr ""
#: part/views.py:1596
#: part/views.py:1711
msgid "Edit Part Parameter Template"
msgstr ""
#: part/views.py:1603
#: part/views.py:1718
msgid "Delete Part Parameter Template"
msgstr ""
#: part/views.py:1611
#: part/views.py:1726
msgid "Create Part Parameter"
msgstr ""
#: part/views.py:1661
#: part/views.py:1776
msgid "Edit Part Parameter"
msgstr ""
#: part/views.py:1675
#: part/views.py:1790
msgid "Delete Part Parameter"
msgstr ""
#: part/views.py:1691
#: part/views.py:1806
msgid "Edit Part Category"
msgstr ""
#: part/views.py:1726
#: part/views.py:1841
msgid "Delete Part Category"
msgstr ""
#: part/views.py:1732
#: part/views.py:1847
msgid "Part category was deleted"
msgstr ""
#: part/views.py:1740
#: part/views.py:1855
msgid "Create new part category"
msgstr ""
#: part/views.py:1791
#: part/views.py:1906
msgid "Create BOM item"
msgstr ""
#: part/views.py:1857
#: part/views.py:1972
msgid "Edit BOM item"
msgstr ""
#: part/views.py:1905
#: part/views.py:2020
msgid "Confim BOM item deletion"
msgstr ""
@ -2776,203 +2853,203 @@ msgstr ""
msgid "Set the destination as the default location for selected parts"
msgstr ""
#: stock/models.py:209
#: stock/models.py:210
msgid "StockItem with this serial number already exists"
msgstr ""
#: stock/models.py:245
#: stock/models.py:246
#, python-brace-format
msgid "Part type ('{pf}') must be {pe}"
msgstr ""
#: stock/models.py:255 stock/models.py:264
#: stock/models.py:256 stock/models.py:265
msgid "Quantity must be 1 for item with a serial number"
msgstr ""
#: stock/models.py:256
#: stock/models.py:257
msgid "Serial number cannot be set if quantity greater than 1"
msgstr ""
#: stock/models.py:277
#: stock/models.py:278
msgid "Item cannot belong to itself"
msgstr ""
#: stock/models.py:316
#: stock/models.py:311
msgid "Parent Stock Item"
msgstr ""
#: stock/models.py:325
#: stock/models.py:320
msgid "Base part"
msgstr ""
#: stock/models.py:334
#: stock/models.py:329
msgid "Select a matching supplier part for this stock item"
msgstr ""
#: stock/models.py:339 stock/templates/stock/stock_app_base.html:7
#: stock/models.py:334 stock/templates/stock/stock_app_base.html:7
msgid "Stock Location"
msgstr ""
#: stock/models.py:342
#: stock/models.py:337
msgid "Where is this stock item located?"
msgstr ""
#: stock/models.py:347
#: stock/models.py:342
msgid "Installed In"
msgstr ""
#: stock/models.py:350
#: stock/models.py:345
msgid "Is this item installed in another item?"
msgstr ""
#: stock/models.py:366
#: stock/models.py:361
msgid "Serial number for this item"
msgstr ""
#: stock/models.py:378
#: stock/models.py:373
msgid "Batch code for this stock item"
msgstr ""
#: stock/models.py:382
#: stock/models.py:377
msgid "Stock Quantity"
msgstr ""
#: stock/models.py:391
#: stock/models.py:386
msgid "Source Build"
msgstr ""
#: stock/models.py:393
#: stock/models.py:388
msgid "Build for this stock item"
msgstr ""
#: stock/models.py:400
#: stock/models.py:395
msgid "Source Purchase Order"
msgstr ""
#: stock/models.py:403
#: stock/models.py:398
msgid "Purchase order for this stock item"
msgstr ""
#: stock/models.py:409
#: stock/models.py:404
msgid "Destination Sales Order"
msgstr ""
#: stock/models.py:416
#: stock/models.py:411
msgid "Destination Build Order"
msgstr ""
#: stock/models.py:429
#: stock/models.py:424
msgid "Delete this Stock Item when stock is depleted"
msgstr ""
#: stock/models.py:439 stock/templates/stock/item_notes.html:14
#: stock/models.py:434 stock/templates/stock/item_notes.html:14
#: stock/templates/stock/item_notes.html:30
msgid "Stock Item Notes"
msgstr ""
#: stock/models.py:490
#: stock/models.py:485
msgid "Assigned to Customer"
msgstr ""
#: stock/models.py:492
#: stock/models.py:487
msgid "Manually assigned to customer"
msgstr ""
#: stock/models.py:505
#: stock/models.py:500
msgid "Returned from customer"
msgstr ""
#: stock/models.py:507
#: stock/models.py:502
msgid "Returned to location"
msgstr ""
#: stock/models.py:678
#: stock/models.py:673
msgid "Part is not set as trackable"
msgstr ""
#: stock/models.py:684
#: stock/models.py:679
msgid "Quantity must be integer"
msgstr ""
#: stock/models.py:690
#: stock/models.py:685
#, python-brace-format
msgid "Quantity must not exceed available stock quantity ({n})"
msgstr ""
#: stock/models.py:693 stock/models.py:696
#: stock/models.py:688 stock/models.py:691
msgid "Serial numbers must be a list of integers"
msgstr ""
#: stock/models.py:699
#: stock/models.py:694
msgid "Quantity does not match serial numbers"
msgstr ""
#: stock/models.py:709
#: stock/models.py:704
msgid "Serial numbers already exist: "
msgstr ""
#: stock/models.py:734
#: stock/models.py:729
msgid "Add serial number"
msgstr ""
#: stock/models.py:737
#: stock/models.py:732
#, python-brace-format
msgid "Serialized {n} items"
msgstr ""
#: stock/models.py:848
#: stock/models.py:843
msgid "StockItem cannot be moved as it is not in stock"
msgstr ""
#: stock/models.py:1175
#: stock/models.py:1170
msgid "Tracking entry title"
msgstr ""
#: stock/models.py:1177
#: stock/models.py:1172
msgid "Entry notes"
msgstr ""
#: stock/models.py:1179
#: stock/models.py:1174
msgid "Link to external page for further information"
msgstr ""
#: stock/models.py:1239
#: stock/models.py:1234
msgid "Value must be provided for this test"
msgstr ""
#: stock/models.py:1245
#: stock/models.py:1240
msgid "Attachment must be uploaded for this test"
msgstr ""
#: stock/models.py:1262
#: stock/models.py:1257
msgid "Test"
msgstr ""
#: stock/models.py:1263
#: stock/models.py:1258
msgid "Test name"
msgstr ""
#: stock/models.py:1268
#: stock/models.py:1263
msgid "Result"
msgstr ""
#: stock/models.py:1269 templates/js/table_filters.html:90
#: stock/models.py:1264 templates/js/table_filters.html:90
msgid "Test result"
msgstr ""
#: stock/models.py:1275
#: stock/models.py:1270
msgid "Test output value"
msgstr ""
#: stock/models.py:1281
#: stock/models.py:1276
msgid "Attachment"
msgstr ""
#: stock/models.py:1282
#: stock/models.py:1277
msgid "Test result attachment"
msgstr ""
#: stock/models.py:1288
#: stock/models.py:1283
msgid "Test notes"
msgstr ""
@ -3078,39 +3155,35 @@ msgstr ""
msgid "Generate test report"
msgstr ""
#: stock/templates/stock/item_base.html:132
msgid "Print labels"
msgstr ""
#: stock/templates/stock/item_base.html:140
#: stock/templates/stock/item_base.html:137
msgid "Stock Item Details"
msgstr ""
#: stock/templates/stock/item_base.html:173
#: stock/templates/stock/item_base.html:170
msgid "Belongs To"
msgstr ""
#: stock/templates/stock/item_base.html:195
#: stock/templates/stock/item_base.html:192
msgid "No location set"
msgstr ""
#: stock/templates/stock/item_base.html:202
#: stock/templates/stock/item_base.html:199
msgid "Unique Identifier"
msgstr ""
#: stock/templates/stock/item_base.html:230
#: stock/templates/stock/item_base.html:227
msgid "Parent Item"
msgstr ""
#: stock/templates/stock/item_base.html:255
#: stock/templates/stock/item_base.html:252
msgid "Last Updated"
msgstr ""
#: stock/templates/stock/item_base.html:260
#: stock/templates/stock/item_base.html:257
msgid "Last Stocktake"
msgstr ""
#: stock/templates/stock/item_base.html:264
#: stock/templates/stock/item_base.html:261
msgid "No stocktake performed"
msgstr ""

View File

@ -139,16 +139,71 @@ class BomItemResource(ModelResource):
bom_id = Field(attribute='pk')
# ID of the parent part
parent_part_id = Field(attribute='part', widget=widgets.ForeignKeyWidget(Part))
parent_part_name = Field(attribute='part__full_name', readonly=True)
# IPN of the parent part
parent_part_ipn = Field(attribute='part__IPN', readonly=True)
sub_part_id = Field(attribute='sub_part', widget=widgets.ForeignKeyWidget(Part))
# Name of the parent part
parent_part_name = Field(attribute='part__name', readonly=True)
sub_part_name = Field(attribute='sub_part__full_name', readonly=True)
# ID of the sub-part
part_id = Field(attribute='sub_part', widget=widgets.ForeignKeyWidget(Part))
# IPN of the sub-part
part_ipn = Field(attribute='sub_part__IPN', readonly=True)
# Name of the sub-part
part_name = Field(attribute='sub_part__name', readonly=True)
# Description of the sub-part
part_description = Field(attribute='sub_part__description', readonly=True)
# Is the sub-part itself an assembly?
sub_assembly = Field(attribute='sub_part__assembly', readonly=True)
def before_export(self, queryset, *args, **kwargs):
self.is_importing = kwargs.get('importing', False)
def get_fields(self, **kwargs):
"""
If we are exporting for the purposes of generating
a 'bom-import' template, there are some fields which
we are not interested in.
"""
fields = super().get_fields(**kwargs)
# If we are not generating an "import" template,
# just return the complete list of fields
if not self.is_importing:
return fields
# Otherwise, remove some fields we are not interested in
idx = 0
to_remove = [
'level',
'bom_id',
'parent_part_id',
'parent_part_ipn',
'parent_part_name',
'part_description',
'sub_assembly'
]
while idx < len(fields):
if fields[idx].column_name.lower() in to_remove:
del fields[idx]
else:
idx += 1
return fields
class Meta:
model = BomItem
skip_unchanged = True

View File

@ -30,8 +30,14 @@ def MakeBomTemplate(fmt):
if not IsValidBOMFormat(fmt):
fmt = 'csv'
# Create an "empty" queryset, essentially.
# This will then export just the row headers!
query = BomItem.objects.filter(pk=None)
dataset = BomItemResource().export(queryset=query)
dataset = BomItemResource().export(
queryset=query,
importing=True
)
data = dataset.export(fmt)
@ -59,8 +65,8 @@ def ExportBom(part, fmt='csv', cascade=False, max_levels=None):
# Add items at a given layer
for item in items:
item.level = '-' * level
item.level = str(int(level))
# Avoid circular BOM references
if item.pk in uids:
continue
@ -96,26 +102,23 @@ class BomUploadManager:
# Fields which are absolutely necessary for valid upload
REQUIRED_HEADERS = [
'Part',
'Part_Name',
'Quantity'
]
# Fields which would be helpful but are not required
OPTIONAL_HEADERS = [
'Part_IPN',
'Part_ID',
'Reference',
'Notes',
'Note',
'Overage',
'Description',
'Category',
'Supplier',
'Manufacturer',
'MPN',
'IPN',
]
EDITABLE_HEADERS = [
'Reference',
'Notes'
'Note',
'Overage'
]
HEADERS = REQUIRED_HEADERS + OPTIONAL_HEADERS
@ -165,6 +168,11 @@ class BomUploadManager:
if h.lower() == header.lower():
return h
# Try for a case-insensitive match with space replacement
for h in self.HEADERS:
if h.lower() == header.lower().replace(' ', '_'):
return h
# Finally, look for a close match using fuzzy matching
matches = []

View File

@ -271,6 +271,42 @@ class Part(MPTTModel):
def __str__(self):
return "{n} - {d}".format(n=self.full_name, d=self.description)
def checkAddToBOM(self, parent):
"""
Check if this Part can be added to the BOM of another part.
This will fail if:
a) The parent part is the same as this one
b) The parent part is used in the BOM for *this* part
c) The parent part is used in the BOM for any child parts under this one
Failing this check raises a ValidationError!
"""
if parent is None:
return
if self.pk == parent.pk:
raise ValidationError({'sub_part': _("Part '{p1}' is used in BOM for '{p2}' (recursive)".format(
p1=str(self),
p2=str(parent)
))})
# Ensure that the parent part does not appear under any child BOM item!
for item in self.bom_items.all():
# Check for simple match
if item.sub_part == parent:
raise ValidationError({'sub_part': _("Part '{p1}' is used in BOM for '{p2}' (recursive)".format(
p1=str(parent),
p2=str(self)
))})
# And recursively check too
item.sub_part.checkAddToBOM(parent)
def checkIfSerialNumberExists(self, sn):
"""
Check if a serial number exists for this Part.
@ -1357,7 +1393,7 @@ class PartParameter(models.Model):
class BomItem(models.Model):
""" A BomItem links a part to its component items.
A part can have a BOM (bill of materials) which defines
which parts are required (and in what quatity) to make it.
which parts are required (and in what quantity) to make it.
Attributes:
part: Link to the parent part (the part that will be produced)
@ -1474,22 +1510,8 @@ class BomItem(models.Model):
except Part.DoesNotExist:
pass
# A part cannot refer to itself in its BOM
try:
if self.sub_part is not None and self.part is not None:
if self.part == self.sub_part:
raise ValidationError({'sub_part': _('Part cannot be added to its own Bill of Materials')})
# TODO - Make sure that there is no recusion
# Test for simple recursion
for item in self.sub_part.bom_items.all():
if self.part == item.sub_part:
raise ValidationError({'sub_part': _("Part '{p1}' is used in BOM for '{p2}' (recursive)".format(p1=str(self.part), p2=str(self.sub_part)))})
except Part.DoesNotExist:
# A blank Part will be caught elsewhere
pass
# Check for circular BOM references
self.sub_part.checkAddToBOM(self.part)
class Meta:
verbose_name = _("BOM Item")

View File

@ -1,17 +1,18 @@
{% extends "part/part_base.html" %}
{% load static %}
{% load i18n %}
{% load inventree_extras %}
{% block details %}
{% include "part/tabs.html" with tab='bom' %}
<h4>Upload Bill of Materials</h4>
<h4>{% trans "Upload Bill of Materials" %}</h4>
<p>Step 2 - Select Fields</p>
<p>{% trans "Step 2 - Select Fields" %}</p>
<hr>
{% if missing_columns and missing_columns|length > 0 %}
<div class='alert alert-danger alert-block' role='alert'>
Missing selections for the following required columns:
{% trans "Missing selections for the following required columns" %}:
<br>
<ul>
{% for col in missing_columns %}
@ -22,7 +23,7 @@
{% endif %}
<form method="post" action='' class='js-modal-form' enctype="multipart/form-data">
<button type="submit" class="save btn btn-default">Submit Selections</button>
<button type="submit" class="save btn btn-default">{% trans "Submit Selections" %}</button>
{% csrf_token %}
<input type='hidden' name='form_step' value='select_fields'/>
@ -31,7 +32,7 @@
<thead>
<tr>
<th></th>
<th>Row</th>
<th>{% trans "File Fields" %}</th>
{% for col in bom_columns %}
<th>
<div>
@ -48,7 +49,7 @@
<tbody>
<tr>
<td></td>
<td></td>
<td>{% trans "Match Fields" %}</td>
{% for col in bom_columns %}
<td>
<select class='select' id='id_col_{{ forloop.counter0 }}' name='col_guess_{{ forloop.counter0 }}'>

View File

@ -1,23 +1,24 @@
{% extends "part/part_base.html" %}
{% load static %}
{% load i18n %}
{% load inventree_extras %}
{% block details %}
{% include "part/tabs.html" with tab="bom" %}
<h4>Upload Bill of Materials</h4>
<h4>{% trans "Upload Bill of Materials" %}</h4>
<p>Step 3 - Select Parts</p>
<p>{% trans "Step 3 - Select Parts" %}</p>
<hr>
{% if form_errors %}
<div class='alert alert-danger alert-block' role='alert'>
Errors exist in the submitted data.
{% trans "Errors exist in the submitted data" %}
</div>
{% endif %}
<form method="post" action='' class='js-modal-form' enctype="multipart/form-data">
<button type="submit" class="save btn btn-default">Submit BOM</button>
<button type="submit" class="save btn btn-default">{% trans "Submit BOM" %}</button>
{% csrf_token %}
{% load crispy_forms_tags %}
@ -29,7 +30,8 @@
<tr>
<th></th>
<th></th>
<th>Row</th>
<th>{% trans "Row" %}</th>
<th>{% trans "Select Part" %}</th>
{% for col in bom_columns %}
<th>
<input type='hidden' name='col_name_{{ forloop.counter0 }}' value='{{ col.name }}'/>
@ -51,34 +53,39 @@
<span row_id='{{ forloop.counter }}' class='fas fa-trash-alt icon-red'></span>
</button>
</td>
<td>
</td>
<td></td>
<td>{% add row.index 1 %}</td>
<td>
<button class='btn btn-default btn-create' onClick='newPartFromBomWizard()' id='new_part_row_{{ row.index }}' title='Create new part' type='button'>
<span row_id='{{ row.index }}' class='fas fa-plus icon-green'/>
</button>
<select class='select bomselect' id='select_part_{{ row.index }}' name='part_{{ row.index }}'>
<option value=''>--- {% trans "Select Part" %} ---</option>
{% for part in row.part_options %}
<option value='{{ part.id }}' {% if part.id == row.part.id %} selected='selected' {% elif part.id == row.part_match.id %} selected='selected' {% endif %}>
{{ part.full_name }} - {{ part.description }}
</option>
{% endfor %}
</select>
{% if row.errors.part %}
<p class='help-inline'>{{ row.errors.part }}</p>
{% endif %}
</td>
{% for item in row.data %}
<td>
{% if item.column.guess == 'Part' %}
<button class='btn btn-default btn-create' onClick='newPartFromBomWizard()' id='new_part_row_{{ row.index }}' title='Create new part' type='button'>
<span row_id='{{ row.index }}' class='fas fa-trash-alt icon-red'/>
</button>
<select class='select bomselect' id='select_part_{{ row.index }}' name='part_{{ row.index }}'>
<option value=''>---------</option>
{% for part in row.part_options %}
<option value='{{ part.id }}'{% if part.id == row.part.id %} selected='selected'{% endif %}>{{ part.full_name }} - {{ part.description }}</option>
{% endfor %}
</select>
<i>{{ item.cell }}</i>
{% if row.errors.part %}
<p class='help-inline'>{{ row.errors.part }}</p>
{% endif %}
{% elif item.column.guess == 'Quantity' %}
<input name='quantity_{{ row.index }}' class='numberinput' type='number' min='1' value='{{ row.quantity }}'/>
<input name='quantity_{{ row.index }}' class='numberinput' type='number' min='1' step='any' value='{% decimal row.quantity %}'/>
{% if row.errors.quantity %}
<p class='help-inline'>{{ row.errors.quantity }}</p>
{% endif %}
{% elif item.column.guess == 'Reference' %}
<input name='reference_{{ row.index }}' value='{{ row.reference }}'/>
{% elif item.column.guess == 'Notes' %}
{% elif item.column.guess == 'Note' %}
<input name='notes_{{ row.index }}' value='{{ row.notes }}'/>
{% elif item.column.guess == 'Overage' %}
<input name='overage_{{ row.index }}' value='{{ row.overage }}'/>

View File

@ -1,18 +1,23 @@
{% extends "part/part_base.html" %}
{% load static %}
{% load i18n %}
{% load inventree_extras %}
{% block details %}
{% include "part/tabs.html" with tab='bom' %}
<h4>Upload Bill of Materials</h4>
<h4>{% trans "Upload Bill of Materials" %}</h4>
<hr>
<p>Step 1 - Select BOM File</p>
<p>{% trans "Step 1 - Select BOM File" %}</p>
<div class='alert alert-info alert-block'>
<p>The BOM file must contain the required named columns as provided in the <a href="/part/bom_template/">BOM Upload Template</a></a></p>
<b>{% trans "Requirements for BOM upload" %}:</b>
<ul>
<li>{% trans "The BOM file must contain the required named columns as provided in the " %} <b><a href="/part/bom_template/">{% trans "BOM Upload Template" %}</a></b></li>
<li>{% trans "Each part must already exist in the database" %}</li>
</ul>
</div>
<form method="post" action='' class='js-modal-form' enctype="multipart/form-data">

View File

@ -19,7 +19,7 @@ from django.conf import settings
import os
from rapidfuzz import fuzz
from decimal import Decimal
from decimal import Decimal, InvalidOperation
from .models import PartCategory, Part, PartAttachment
from .models import PartParameterTemplate, PartParameter
@ -847,6 +847,7 @@ class BomUpload(FormView):
rows.append({
'index': row.get('index', -1),
'data': data,
'part_match': row.get('part_match', None),
'part_options': row.get('part_options', self.allowed_parts),
# User-input (passed between client and server)
@ -947,51 +948,133 @@ class BomUpload(FormView):
The pre-fill data are then passed through to the part selection form.
"""
# Fields prefixed with "Part_" can be used to do "smart matching" against Part objects in the database
k_idx = self.getColumnIndex('Part_ID')
p_idx = self.getColumnIndex('Part_Name')
i_idx = self.getColumnIndex('Part_IPN')
q_idx = self.getColumnIndex('Quantity')
p_idx = self.getColumnIndex('Part')
d_idx = self.getColumnIndex('Description')
r_idx = self.getColumnIndex('Reference')
n_idx = self.getColumnIndex('Notes')
o_idx = self.getColumnIndex('Overage')
n_idx = self.getColumnIndex('Note')
for row in self.bom_rows:
"""
quantity = 0
part = None
Iterate through each row in the uploaded data,
and see if we can match the row to a "Part" object in the database.
There are three potential ways to match, based on the uploaded data:
a) Use the PK (primary key) field for the part, uploaded in the "Part_ID" field
b) Use the IPN (internal part number) field for the part, uploaded in the "Part_IPN" field
c) Use the name of the part, uploaded in the "Part_Name" field
Notes:
- If using the Part_ID field, we can do an exact match against the PK field
- If using the Part_IPN field, we can do an exact match against the IPN field
- If using the Part_Name field, we can use fuzzy string matching to match "close" values
We also extract other information from the row, for the other non-matched fields:
- Quantity
- Reference
- Overage
- Note
"""
# Initially use a quantity of zero
quantity = Decimal(0)
# Initially we do not have a part to reference
exact_match_part = None
# A list of potential Part matches
part_options = self.allowed_parts
# Check if there is a column corresponding to "quantity"
if q_idx >= 0:
q_val = row['data'][q_idx]
try:
quantity = int(q_val)
except ValueError:
pass
if q_val:
try:
# Attempt to extract a valid quantity from the field
quantity = Decimal(q_val)
except (ValueError, InvalidOperation):
pass
# Store the 'quantity' value
row['quantity'] = quantity
# Check if there is a column corresponding to "PK"
if k_idx >= 0:
pk = row['data'][k_idx]
if pk:
try:
# Attempt Part lookup based on PK value
exact_match_part = Part.objects.get(pk=pk)
except (ValueError, Part.DoesNotExist):
exact_match_part = None
# Check if there is a column corresponding to "Part Name"
if p_idx >= 0:
part_name = row['data'][p_idx]
row['part_name'] = part_name
# Fuzzy match the values and see what happends
matches = []
for part in self.allowed_parts:
ratio = fuzz.partial_ratio(part.name + part.description, part_name)
matches.append({'part': part, 'match': ratio})
# Sort matches by the 'strength' of the match ratio
if len(matches) > 0:
matches = sorted(matches, key=lambda item: item['match'], reverse=True)
if d_idx >= 0:
row['description'] = row['data'][d_idx]
part_options = [m['part'] for m in matches]
# Check if there is a column corresponding to "Part IPN"
if i_idx >= 0:
row['part_ipn'] = row['data'][i_idx]
# Check if there is a column corresponding to "Overage" field
if o_idx >= 0:
row['overage'] = row['data'][o_idx]
# Check if there is a column corresponding to "Reference" field
if r_idx >= 0:
row['reference'] = row['data'][r_idx]
# Check if there is a column corresponding to "Note" field
if n_idx >= 0:
row['notes'] = row['data'][n_idx]
row['note'] = row['data'][n_idx]
# Supply list of part options for each row, sorted by how closely they match the part name
row['part_options'] = part_options
row['quantity'] = quantity
row['part_options'] = [m['part'] for m in matches]
# Unless found, the 'part_match' is blank
row['part_match'] = None
if exact_match_part:
# If there is an exact match based on PK, use that
row['part_match'] = exact_match_part
else:
# Otherwise, check to see if there is a matching IPN
try:
if row['part_ipn']:
part_matches = [part for part in self.allowed_parts if row['part_ipn'].lower() == part.IPN.lower()]
# Check for single match
if len(part_matches) == 1:
row['part_match'] = part_matches[0]
continue
except KeyError:
pass
print(row, row['part_match'], len(row['part_options']))
def extractDataFromFile(self, bom):
""" Read data from the BOM file """
@ -1159,13 +1242,20 @@ class BomUpload(FormView):
if row is None:
continue
q = 1
q = Decimal(1)
try:
q = int(value)
if q <= 0:
q = Decimal(value)
if q < 0:
row['errors']['quantity'] = _('Quantity must be greater than zero')
except ValueError:
if 'part' in row.keys():
if row['part'].trackable:
# Trackable parts must use integer quantities
if not q == int(q):
row['errors']['quantity'] = _('Quantity must be integer value for trackable parts')
except (ValueError, InvalidOperation):
row['errors']['quantity'] = _('Enter a valid quantity')
row['quantity'] = q
@ -1175,6 +1265,7 @@ class BomUpload(FormView):
# Extract part from each row
if key.startswith('part_'):
try:
row_id = int(key.replace('part_', ''))
@ -1208,6 +1299,14 @@ class BomUpload(FormView):
row['part'] = part
if part.trackable:
# For trackable parts, ensure the quantity is an integer value!
if 'quantity' in row.keys():
q = row['quantity']
if not q == int(q):
row['errors']['quantity'] = _('Quantity must be integer value for trackable parts')
# Extract other fields which do not require further validation
for field in ['reference', 'notes']:
if key.startswith(field + '_'):
@ -1226,8 +1325,16 @@ class BomUpload(FormView):
for row in self.bom_rows:
# Has a part been selected for the given row?
if row.get('part', None) is None:
part = row.get('part', None)
if part is None:
row['errors']['part'] = _('Select a part')
else:
# Will the selected part result in a recursive BOM?
try:
part.checkAddToBOM(self.part)
except ValidationError:
row['errors']['part'] = _('Selected part creates a circular BOM')
# Has a quantity been specified?
if row.get('quantity', None) is None: