mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
Merge remote-tracking branch 'inventree/master'
This commit is contained in:
commit
bb4c25ba68
@ -308,7 +308,10 @@ STATICFILES_DIRS = [
|
|||||||
MEDIA_URL = '/media/'
|
MEDIA_URL = '/media/'
|
||||||
|
|
||||||
# The filesystem location for served static files
|
# The filesystem location for served static files
|
||||||
MEDIA_ROOT = CONFIG.get('media_root', os.path.join(BASE_DIR, 'media'))
|
MEDIA_ROOT = os.path.abspath(CONFIG.get('media_root', os.path.join(BASE_DIR, 'media')))
|
||||||
|
|
||||||
|
if DEBUG:
|
||||||
|
print("MEDIA_ROOT:", MEDIA_ROOT)
|
||||||
|
|
||||||
# crispy forms use the bootstrap templates
|
# crispy forms use the bootstrap templates
|
||||||
CRISPY_TEMPLATE_PACK = 'bootstrap'
|
CRISPY_TEMPLATE_PACK = 'bootstrap'
|
||||||
|
@ -183,6 +183,24 @@
|
|||||||
-webkit-opacity: 10%;
|
-webkit-opacity: 10%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* grid display for part images */
|
||||||
|
|
||||||
|
.table-img-grid tr {
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-img-grid td {
|
||||||
|
padding: 10px;
|
||||||
|
margin: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-img-grid .grid-image {
|
||||||
|
|
||||||
|
height: 128px;
|
||||||
|
width: 128px;
|
||||||
|
object-fit: contain;
|
||||||
|
background: #eee;
|
||||||
|
}
|
||||||
|
|
||||||
.btn-glyph {
|
.btn-glyph {
|
||||||
padding-left: 6px;
|
padding-left: 6px;
|
||||||
@ -211,6 +229,20 @@
|
|||||||
object-fit: contain;
|
object-fit: contain;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.part-thumb-container:hover .part-thumb-overlay {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.part-thumb-overlay {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
opacity: 0;
|
||||||
|
transition: .25s ease;
|
||||||
|
padding: 15px;
|
||||||
|
margin: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
.checkbox {
|
.checkbox {
|
||||||
margin-left: 20px;
|
margin-left: 20px;
|
||||||
}
|
}
|
||||||
|
18
InvenTree/build/migrations/0009_auto_20200210_1032.py
Normal file
18
InvenTree/build/migrations/0009_auto_20200210_1032.py
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 2.2.9 on 2020-02-10 10:32
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('build', '0008_auto_20200201_1247'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='build',
|
||||||
|
name='creation_date',
|
||||||
|
field=models.DateField(auto_now_add=True),
|
||||||
|
),
|
||||||
|
]
|
@ -82,7 +82,7 @@ class Build(models.Model):
|
|||||||
batch = models.CharField(max_length=100, blank=True, null=True,
|
batch = models.CharField(max_length=100, blank=True, null=True,
|
||||||
help_text=_('Batch code for this build output'))
|
help_text=_('Batch code for this build output'))
|
||||||
|
|
||||||
creation_date = models.DateField(auto_now=True, editable=False)
|
creation_date = models.DateField(auto_now_add=True, editable=False)
|
||||||
|
|
||||||
completion_date = models.DateField(null=True, blank=True)
|
completion_date = models.DateField(null=True, blank=True)
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: \n"
|
"Project-Id-Version: \n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2020-02-03 10:28+0000\n"
|
"POT-Creation-Date: 2020-02-10 11:09+0000\n"
|
||||||
"PO-Revision-Date: 2020-02-02 08:07+0100\n"
|
"PO-Revision-Date: 2020-02-02 08:07+0100\n"
|
||||||
"Last-Translator: Christian Schlüter <chschlue@gmail.com>\n"
|
"Last-Translator: Christian Schlüter <chschlue@gmail.com>\n"
|
||||||
"Language-Team: C <kde-i18n-doc@kde.org>\n"
|
"Language-Team: C <kde-i18n-doc@kde.org>\n"
|
||||||
@ -706,7 +706,7 @@ msgstr "Link auf externe Seite"
|
|||||||
msgid "Order notes"
|
msgid "Order notes"
|
||||||
msgstr "Bestell-Notizen"
|
msgstr "Bestell-Notizen"
|
||||||
|
|
||||||
#: order/models.py:159 order/models.py:210 part/views.py:1067
|
#: order/models.py:159 order/models.py:210 part/views.py:1080
|
||||||
#: stock/models.py:440
|
#: stock/models.py:440
|
||||||
msgid "Quantity must be greater than zero"
|
msgid "Quantity must be greater than zero"
|
||||||
msgstr "Anzahl muss größer Null sein"
|
msgstr "Anzahl muss größer Null sein"
|
||||||
@ -1277,56 +1277,208 @@ msgstr "Tracking"
|
|||||||
msgid "Attachments"
|
msgid "Attachments"
|
||||||
msgstr "Anhänge"
|
msgstr "Anhänge"
|
||||||
|
|
||||||
|
#: part/views.py:77
|
||||||
|
#, fuzzy
|
||||||
|
#| msgid "Add Attachment"
|
||||||
|
msgid "Added attachment"
|
||||||
|
msgstr "Anhang hinzufügen"
|
||||||
|
|
||||||
|
#: part/views.py:119
|
||||||
|
#, fuzzy
|
||||||
|
#| msgid "Part Attachments"
|
||||||
|
msgid "Part attachment updated"
|
||||||
|
msgstr "Anhänge"
|
||||||
|
|
||||||
#: part/views.py:196
|
#: part/views.py:196
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Set category for {n} parts"
|
msgid "Set category for {n} parts"
|
||||||
msgstr "Kategorie für {n} Teile setzen"
|
msgstr "Kategorie für {n} Teile setzen"
|
||||||
|
|
||||||
#: part/views.py:808
|
#: part/views.py:306
|
||||||
|
#, fuzzy
|
||||||
|
#| msgid "Supplier part"
|
||||||
|
msgid "Copied part"
|
||||||
|
msgstr "Zulieferer-Teil"
|
||||||
|
|
||||||
|
#: part/views.py:414
|
||||||
|
#, fuzzy
|
||||||
|
#| msgid "Create new Stock Item"
|
||||||
|
msgid "Create new part"
|
||||||
|
msgstr "Neues Lagerobjekt hinzufügen"
|
||||||
|
|
||||||
|
#: part/views.py:419
|
||||||
|
#, fuzzy
|
||||||
|
#| msgid "Created new stock item"
|
||||||
|
msgid "Created new part"
|
||||||
|
msgstr "Neues Lagerobjekt erstellt"
|
||||||
|
|
||||||
|
#: part/views.py:609
|
||||||
|
msgid "Upload Part Image"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: part/views.py:614
|
||||||
|
msgid "Updated part image"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: part/views.py:623
|
||||||
|
#, fuzzy
|
||||||
|
#| msgid "Select part"
|
||||||
|
msgid "Select Part Image"
|
||||||
|
msgstr "Teil auswählen"
|
||||||
|
|
||||||
|
#: part/views.py:627
|
||||||
|
#, fuzzy
|
||||||
|
#| msgid "Select part"
|
||||||
|
msgid "Selected part image"
|
||||||
|
msgstr "Teil auswählen"
|
||||||
|
|
||||||
|
#: part/views.py:637
|
||||||
|
#, fuzzy
|
||||||
|
#| msgid "Edit notes"
|
||||||
|
msgid "Edit Part Properties"
|
||||||
|
msgstr "Bermerkungen bearbeiten"
|
||||||
|
|
||||||
|
#: part/views.py:659
|
||||||
|
msgid "Validate BOM"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: part/views.py:821
|
||||||
msgid "No BOM file provided"
|
msgid "No BOM file provided"
|
||||||
msgstr "Keine Stückliste angegeben"
|
msgstr "Keine Stückliste angegeben"
|
||||||
|
|
||||||
#: part/views.py:1069
|
#: part/views.py:1082
|
||||||
msgid "Enter a valid quantity"
|
msgid "Enter a valid quantity"
|
||||||
msgstr "Bitte eine gültige Anzahl eingeben"
|
msgstr "Bitte eine gültige Anzahl eingeben"
|
||||||
|
|
||||||
#: part/views.py:1093 part/views.py:1096
|
#: part/views.py:1106 part/views.py:1109
|
||||||
msgid "Select valid part"
|
msgid "Select valid part"
|
||||||
msgstr "Bitte ein gültiges Teil auswählen"
|
msgstr "Bitte ein gültiges Teil auswählen"
|
||||||
|
|
||||||
#: part/views.py:1102
|
#: part/views.py:1115
|
||||||
msgid "Duplicate part selected"
|
msgid "Duplicate part selected"
|
||||||
msgstr "Teil doppelt ausgewählt"
|
msgstr "Teil doppelt ausgewählt"
|
||||||
|
|
||||||
#: part/views.py:1130
|
#: part/views.py:1143
|
||||||
msgid "Select a part"
|
msgid "Select a part"
|
||||||
msgstr "Teil auswählen"
|
msgstr "Teil auswählen"
|
||||||
|
|
||||||
#: part/views.py:1134
|
#: part/views.py:1147
|
||||||
msgid "Specify quantity"
|
msgid "Specify quantity"
|
||||||
msgstr "Anzahl angeben"
|
msgstr "Anzahl angeben"
|
||||||
|
|
||||||
#: stock/forms.py:92
|
#: part/views.py:1324
|
||||||
|
#, fuzzy
|
||||||
|
#| msgid "Confirm part creation"
|
||||||
|
msgid "Confirm Part Deletion"
|
||||||
|
msgstr "Erstellen des Teils bestätigen"
|
||||||
|
|
||||||
|
#: part/views.py:1331
|
||||||
|
msgid "Part was deleted"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: part/views.py:1340
|
||||||
|
#, fuzzy
|
||||||
|
#| msgid "Part packaging"
|
||||||
|
msgid "Part Pricing"
|
||||||
|
msgstr "Teile-Packaging"
|
||||||
|
|
||||||
|
#: part/views.py:1462
|
||||||
|
#, fuzzy
|
||||||
|
#| msgid "Parameter Template"
|
||||||
|
msgid "Create Part Parameter Template"
|
||||||
|
msgstr "Parameter Vorlage"
|
||||||
|
|
||||||
|
#: part/views.py:1470
|
||||||
|
#, fuzzy
|
||||||
|
#| msgid "Parameter Template"
|
||||||
|
msgid "Edit Part Parameter Template"
|
||||||
|
msgstr "Parameter Vorlage"
|
||||||
|
|
||||||
|
#: part/views.py:1477
|
||||||
|
#, fuzzy
|
||||||
|
#| msgid "Parameter Template"
|
||||||
|
msgid "Delete Part Parameter Template"
|
||||||
|
msgstr "Parameter Vorlage"
|
||||||
|
|
||||||
|
#: part/views.py:1485
|
||||||
|
msgid "Create Part Parameter"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: part/views.py:1535
|
||||||
|
#, fuzzy
|
||||||
|
#| msgid "Edit attachment"
|
||||||
|
msgid "Edit Part Parameter"
|
||||||
|
msgstr "Anhang bearbeiten"
|
||||||
|
|
||||||
|
#: part/views.py:1549
|
||||||
|
#, fuzzy
|
||||||
|
#| msgid "Delete attachment"
|
||||||
|
msgid "Delete Part Parameter"
|
||||||
|
msgstr "Anhang löschen"
|
||||||
|
|
||||||
|
#: part/views.py:1565
|
||||||
|
#, fuzzy
|
||||||
|
#| msgid "Part category"
|
||||||
|
msgid "Edit Part Category"
|
||||||
|
msgstr "Teile-Kategorie"
|
||||||
|
|
||||||
|
#: part/views.py:1600
|
||||||
|
#, fuzzy
|
||||||
|
#| msgid "Select part category"
|
||||||
|
msgid "Delete Part Category"
|
||||||
|
msgstr "Teilekategorie wählen"
|
||||||
|
|
||||||
|
#: part/views.py:1606
|
||||||
|
#, fuzzy
|
||||||
|
#| msgid "Part category"
|
||||||
|
msgid "Part category was deleted"
|
||||||
|
msgstr "Teile-Kategorie"
|
||||||
|
|
||||||
|
#: part/views.py:1614
|
||||||
|
#, fuzzy
|
||||||
|
#| msgid "Select part category"
|
||||||
|
msgid "Create new part category"
|
||||||
|
msgstr "Teilekategorie wählen"
|
||||||
|
|
||||||
|
#: part/views.py:1665
|
||||||
|
#, fuzzy
|
||||||
|
#| msgid "Created new stock item"
|
||||||
|
msgid "Create BOM item"
|
||||||
|
msgstr "Neues Lagerobjekt erstellt"
|
||||||
|
|
||||||
|
#: part/views.py:1731
|
||||||
|
#, fuzzy
|
||||||
|
#| msgid "Edit Stock Item"
|
||||||
|
msgid "Edit BOM item"
|
||||||
|
msgstr "Lagerobjekt bearbeiten"
|
||||||
|
|
||||||
|
#: part/views.py:1779
|
||||||
|
#, fuzzy
|
||||||
|
#| msgid "Confirm build completion"
|
||||||
|
msgid "Confim BOM item deletion"
|
||||||
|
msgstr "Bau-Fertigstellung bestätigen"
|
||||||
|
|
||||||
|
#: stock/forms.py:91
|
||||||
msgid "File Format"
|
msgid "File Format"
|
||||||
msgstr "Dateiformat"
|
msgstr "Dateiformat"
|
||||||
|
|
||||||
#: stock/forms.py:92
|
#: stock/forms.py:91
|
||||||
msgid "Select output file format"
|
msgid "Select output file format"
|
||||||
msgstr "Ausgabe-Dateiformat auswählen"
|
msgstr "Ausgabe-Dateiformat auswählen"
|
||||||
|
|
||||||
#: stock/forms.py:94
|
#: stock/forms.py:93
|
||||||
msgid "Include stock items in sub locations"
|
msgid "Include stock items in sub locations"
|
||||||
msgstr "Lagerobjekte in untergeordneten Lagerorten einschließen"
|
msgstr "Lagerobjekte in untergeordneten Lagerorten einschließen"
|
||||||
|
|
||||||
#: stock/forms.py:127
|
#: stock/forms.py:126
|
||||||
msgid "Destination stock location"
|
msgid "Destination stock location"
|
||||||
msgstr "Ziel-Lagerbestand"
|
msgstr "Ziel-Lagerbestand"
|
||||||
|
|
||||||
#: stock/forms.py:133
|
#: stock/forms.py:132
|
||||||
msgid "Confirm movement of stock items"
|
msgid "Confirm movement of stock items"
|
||||||
msgstr "Bewegung der Lagerobjekte bestätigen"
|
msgstr "Bewegung der Lagerobjekte bestätigen"
|
||||||
|
|
||||||
#: stock/forms.py:135
|
#: stock/forms.py:134
|
||||||
msgid "Set the destination as the default location for selected parts"
|
msgid "Set the destination as the default location for selected parts"
|
||||||
msgstr "Setze das Ziel als Standard-Ziel für ausgewählte Teile"
|
msgstr "Setze das Ziel als Standard-Ziel für ausgewählte Teile"
|
||||||
|
|
||||||
@ -1652,27 +1804,33 @@ msgstr "Ungültige Menge"
|
|||||||
msgid "Invalid part selection"
|
msgid "Invalid part selection"
|
||||||
msgstr "Ungültige Teileauswahl"
|
msgstr "Ungültige Teileauswahl"
|
||||||
|
|
||||||
#: stock/views.py:925
|
#: stock/views.py:910
|
||||||
|
#, fuzzy, python-brace-format
|
||||||
|
#| msgid "Created new stock item"
|
||||||
|
msgid "Created {n} new stock items"
|
||||||
|
msgstr "Neues Lagerobjekt erstellt"
|
||||||
|
|
||||||
|
#: stock/views.py:927 stock/views.py:940
|
||||||
msgid "Created new stock item"
|
msgid "Created new stock item"
|
||||||
msgstr "Neues Lagerobjekt erstellt"
|
msgstr "Neues Lagerobjekt erstellt"
|
||||||
|
|
||||||
#: stock/views.py:942
|
#: stock/views.py:957
|
||||||
msgid "Delete Stock Location"
|
msgid "Delete Stock Location"
|
||||||
msgstr "Standort löschen"
|
msgstr "Standort löschen"
|
||||||
|
|
||||||
#: stock/views.py:955
|
#: stock/views.py:970
|
||||||
msgid "Delete Stock Item"
|
msgid "Delete Stock Item"
|
||||||
msgstr "Lagerobjekt löschen"
|
msgstr "Lagerobjekt löschen"
|
||||||
|
|
||||||
#: stock/views.py:966
|
#: stock/views.py:981
|
||||||
msgid "Delete Stock Tracking Entry"
|
msgid "Delete Stock Tracking Entry"
|
||||||
msgstr "Lagerbestands-Tracking-Eintrag löschen"
|
msgstr "Lagerbestands-Tracking-Eintrag löschen"
|
||||||
|
|
||||||
#: stock/views.py:983
|
#: stock/views.py:998
|
||||||
msgid "Edit Stock Tracking Entry"
|
msgid "Edit Stock Tracking Entry"
|
||||||
msgstr "Lagerbestands-Tracking-Eintrag bearbeiten"
|
msgstr "Lagerbestands-Tracking-Eintrag bearbeiten"
|
||||||
|
|
||||||
#: stock/views.py:992
|
#: stock/views.py:1007
|
||||||
msgid "Add Stock Tracking Entry"
|
msgid "Add Stock Tracking Entry"
|
||||||
msgstr "Lagerbestands-Tracking-Eintrag hinzufügen"
|
msgstr "Lagerbestands-Tracking-Eintrag hinzufügen"
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2020-02-03 10:28+0000\n"
|
"POT-Creation-Date: 2020-02-10 11:09+0000\n"
|
||||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
@ -675,7 +675,7 @@ msgstr ""
|
|||||||
msgid "Order notes"
|
msgid "Order notes"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: order/models.py:159 order/models.py:210 part/views.py:1067
|
#: order/models.py:159 order/models.py:210 part/views.py:1080
|
||||||
#: stock/models.py:440
|
#: stock/models.py:440
|
||||||
msgid "Quantity must be greater than zero"
|
msgid "Quantity must be greater than zero"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -1242,56 +1242,164 @@ msgstr ""
|
|||||||
msgid "Attachments"
|
msgid "Attachments"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: part/views.py:77
|
||||||
|
msgid "Added attachment"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: part/views.py:119
|
||||||
|
msgid "Part attachment updated"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: part/views.py:196
|
#: part/views.py:196
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Set category for {n} parts"
|
msgid "Set category for {n} parts"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: part/views.py:808
|
#: part/views.py:306
|
||||||
|
msgid "Copied part"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: part/views.py:414
|
||||||
|
msgid "Create new part"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: part/views.py:419
|
||||||
|
msgid "Created new part"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: part/views.py:609
|
||||||
|
msgid "Upload Part Image"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: part/views.py:614
|
||||||
|
msgid "Updated part image"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: part/views.py:623
|
||||||
|
msgid "Select Part Image"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: part/views.py:627
|
||||||
|
msgid "Selected part image"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: part/views.py:637
|
||||||
|
msgid "Edit Part Properties"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: part/views.py:659
|
||||||
|
msgid "Validate BOM"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: part/views.py:821
|
||||||
msgid "No BOM file provided"
|
msgid "No BOM file provided"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: part/views.py:1069
|
#: part/views.py:1082
|
||||||
msgid "Enter a valid quantity"
|
msgid "Enter a valid quantity"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: part/views.py:1093 part/views.py:1096
|
#: part/views.py:1106 part/views.py:1109
|
||||||
msgid "Select valid part"
|
msgid "Select valid part"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: part/views.py:1102
|
#: part/views.py:1115
|
||||||
msgid "Duplicate part selected"
|
msgid "Duplicate part selected"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: part/views.py:1130
|
#: part/views.py:1143
|
||||||
msgid "Select a part"
|
msgid "Select a part"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: part/views.py:1134
|
#: part/views.py:1147
|
||||||
msgid "Specify quantity"
|
msgid "Specify quantity"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: stock/forms.py:92
|
#: part/views.py:1324
|
||||||
|
msgid "Confirm Part Deletion"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: part/views.py:1331
|
||||||
|
msgid "Part was deleted"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: part/views.py:1340
|
||||||
|
msgid "Part Pricing"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: part/views.py:1462
|
||||||
|
msgid "Create Part Parameter Template"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: part/views.py:1470
|
||||||
|
msgid "Edit Part Parameter Template"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: part/views.py:1477
|
||||||
|
msgid "Delete Part Parameter Template"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: part/views.py:1485
|
||||||
|
msgid "Create Part Parameter"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: part/views.py:1535
|
||||||
|
msgid "Edit Part Parameter"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: part/views.py:1549
|
||||||
|
msgid "Delete Part Parameter"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: part/views.py:1565
|
||||||
|
msgid "Edit Part Category"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: part/views.py:1600
|
||||||
|
msgid "Delete Part Category"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: part/views.py:1606
|
||||||
|
msgid "Part category was deleted"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: part/views.py:1614
|
||||||
|
msgid "Create new part category"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: part/views.py:1665
|
||||||
|
msgid "Create BOM item"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: part/views.py:1731
|
||||||
|
msgid "Edit BOM item"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: part/views.py:1779
|
||||||
|
msgid "Confim BOM item deletion"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: stock/forms.py:91
|
||||||
msgid "File Format"
|
msgid "File Format"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: stock/forms.py:92
|
#: stock/forms.py:91
|
||||||
msgid "Select output file format"
|
msgid "Select output file format"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: stock/forms.py:94
|
#: stock/forms.py:93
|
||||||
msgid "Include stock items in sub locations"
|
msgid "Include stock items in sub locations"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: stock/forms.py:127
|
#: stock/forms.py:126
|
||||||
msgid "Destination stock location"
|
msgid "Destination stock location"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: stock/forms.py:133
|
#: stock/forms.py:132
|
||||||
msgid "Confirm movement of stock items"
|
msgid "Confirm movement of stock items"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: stock/forms.py:135
|
#: stock/forms.py:134
|
||||||
msgid "Set the destination as the default location for selected parts"
|
msgid "Set the destination as the default location for selected parts"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -1610,27 +1718,32 @@ msgstr ""
|
|||||||
msgid "Invalid part selection"
|
msgid "Invalid part selection"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: stock/views.py:925
|
#: stock/views.py:910
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "Created {n} new stock items"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: stock/views.py:927 stock/views.py:940
|
||||||
msgid "Created new stock item"
|
msgid "Created new stock item"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: stock/views.py:942
|
#: stock/views.py:957
|
||||||
msgid "Delete Stock Location"
|
msgid "Delete Stock Location"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: stock/views.py:955
|
#: stock/views.py:970
|
||||||
msgid "Delete Stock Item"
|
msgid "Delete Stock Item"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: stock/views.py:966
|
#: stock/views.py:981
|
||||||
msgid "Delete Stock Tracking Entry"
|
msgid "Delete Stock Tracking Entry"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: stock/views.py:983
|
#: stock/views.py:998
|
||||||
msgid "Edit Stock Tracking Entry"
|
msgid "Edit Stock Tracking Entry"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: stock/views.py:992
|
#: stock/views.py:1007
|
||||||
msgid "Add Stock Tracking Entry"
|
msgid "Add Stock Tracking Entry"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2020-02-03 10:28+0000\n"
|
"POT-Creation-Date: 2020-02-10 11:09+0000\n"
|
||||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
@ -675,7 +675,7 @@ msgstr ""
|
|||||||
msgid "Order notes"
|
msgid "Order notes"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: order/models.py:159 order/models.py:210 part/views.py:1067
|
#: order/models.py:159 order/models.py:210 part/views.py:1080
|
||||||
#: stock/models.py:440
|
#: stock/models.py:440
|
||||||
msgid "Quantity must be greater than zero"
|
msgid "Quantity must be greater than zero"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -1242,56 +1242,164 @@ msgstr ""
|
|||||||
msgid "Attachments"
|
msgid "Attachments"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: part/views.py:77
|
||||||
|
msgid "Added attachment"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: part/views.py:119
|
||||||
|
msgid "Part attachment updated"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: part/views.py:196
|
#: part/views.py:196
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Set category for {n} parts"
|
msgid "Set category for {n} parts"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: part/views.py:808
|
#: part/views.py:306
|
||||||
|
msgid "Copied part"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: part/views.py:414
|
||||||
|
msgid "Create new part"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: part/views.py:419
|
||||||
|
msgid "Created new part"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: part/views.py:609
|
||||||
|
msgid "Upload Part Image"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: part/views.py:614
|
||||||
|
msgid "Updated part image"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: part/views.py:623
|
||||||
|
msgid "Select Part Image"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: part/views.py:627
|
||||||
|
msgid "Selected part image"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: part/views.py:637
|
||||||
|
msgid "Edit Part Properties"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: part/views.py:659
|
||||||
|
msgid "Validate BOM"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: part/views.py:821
|
||||||
msgid "No BOM file provided"
|
msgid "No BOM file provided"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: part/views.py:1069
|
#: part/views.py:1082
|
||||||
msgid "Enter a valid quantity"
|
msgid "Enter a valid quantity"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: part/views.py:1093 part/views.py:1096
|
#: part/views.py:1106 part/views.py:1109
|
||||||
msgid "Select valid part"
|
msgid "Select valid part"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: part/views.py:1102
|
#: part/views.py:1115
|
||||||
msgid "Duplicate part selected"
|
msgid "Duplicate part selected"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: part/views.py:1130
|
#: part/views.py:1143
|
||||||
msgid "Select a part"
|
msgid "Select a part"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: part/views.py:1134
|
#: part/views.py:1147
|
||||||
msgid "Specify quantity"
|
msgid "Specify quantity"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: stock/forms.py:92
|
#: part/views.py:1324
|
||||||
|
msgid "Confirm Part Deletion"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: part/views.py:1331
|
||||||
|
msgid "Part was deleted"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: part/views.py:1340
|
||||||
|
msgid "Part Pricing"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: part/views.py:1462
|
||||||
|
msgid "Create Part Parameter Template"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: part/views.py:1470
|
||||||
|
msgid "Edit Part Parameter Template"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: part/views.py:1477
|
||||||
|
msgid "Delete Part Parameter Template"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: part/views.py:1485
|
||||||
|
msgid "Create Part Parameter"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: part/views.py:1535
|
||||||
|
msgid "Edit Part Parameter"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: part/views.py:1549
|
||||||
|
msgid "Delete Part Parameter"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: part/views.py:1565
|
||||||
|
msgid "Edit Part Category"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: part/views.py:1600
|
||||||
|
msgid "Delete Part Category"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: part/views.py:1606
|
||||||
|
msgid "Part category was deleted"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: part/views.py:1614
|
||||||
|
msgid "Create new part category"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: part/views.py:1665
|
||||||
|
msgid "Create BOM item"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: part/views.py:1731
|
||||||
|
msgid "Edit BOM item"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: part/views.py:1779
|
||||||
|
msgid "Confim BOM item deletion"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: stock/forms.py:91
|
||||||
msgid "File Format"
|
msgid "File Format"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: stock/forms.py:92
|
#: stock/forms.py:91
|
||||||
msgid "Select output file format"
|
msgid "Select output file format"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: stock/forms.py:94
|
#: stock/forms.py:93
|
||||||
msgid "Include stock items in sub locations"
|
msgid "Include stock items in sub locations"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: stock/forms.py:127
|
#: stock/forms.py:126
|
||||||
msgid "Destination stock location"
|
msgid "Destination stock location"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: stock/forms.py:133
|
#: stock/forms.py:132
|
||||||
msgid "Confirm movement of stock items"
|
msgid "Confirm movement of stock items"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: stock/forms.py:135
|
#: stock/forms.py:134
|
||||||
msgid "Set the destination as the default location for selected parts"
|
msgid "Set the destination as the default location for selected parts"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -1610,27 +1718,32 @@ msgstr ""
|
|||||||
msgid "Invalid part selection"
|
msgid "Invalid part selection"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: stock/views.py:925
|
#: stock/views.py:910
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "Created {n} new stock items"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: stock/views.py:927 stock/views.py:940
|
||||||
msgid "Created new stock item"
|
msgid "Created new stock item"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: stock/views.py:942
|
#: stock/views.py:957
|
||||||
msgid "Delete Stock Location"
|
msgid "Delete Stock Location"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: stock/views.py:955
|
#: stock/views.py:970
|
||||||
msgid "Delete Stock Item"
|
msgid "Delete Stock Item"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: stock/views.py:966
|
#: stock/views.py:981
|
||||||
msgid "Delete Stock Tracking Entry"
|
msgid "Delete Stock Tracking Entry"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: stock/views.py:983
|
#: stock/views.py:998
|
||||||
msgid "Edit Stock Tracking Entry"
|
msgid "Edit Stock Tracking Entry"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: stock/views.py:992
|
#: stock/views.py:1007
|
||||||
msgid "Add Stock Tracking Entry"
|
msgid "Add Stock Tracking Entry"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ from __future__ import unicode_literals
|
|||||||
from django_filters.rest_framework import DjangoFilterBackend
|
from django_filters.rest_framework import DjangoFilterBackend
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|
||||||
from django.db.models import Sum
|
from django.db.models import Sum, Count
|
||||||
|
|
||||||
from rest_framework import status
|
from rest_framework import status
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
@ -23,10 +23,7 @@ import os
|
|||||||
from .models import Part, PartCategory, BomItem, PartStar
|
from .models import Part, PartCategory, BomItem, PartStar
|
||||||
from .models import PartParameter, PartParameterTemplate
|
from .models import PartParameter, PartParameterTemplate
|
||||||
|
|
||||||
from .serializers import PartSerializer, BomItemSerializer
|
from . import serializers as part_serializers
|
||||||
from .serializers import CategorySerializer
|
|
||||||
from .serializers import PartStarSerializer
|
|
||||||
from .serializers import PartParameterSerializer, PartParameterTemplateSerializer
|
|
||||||
|
|
||||||
from InvenTree.views import TreeSerializer
|
from InvenTree.views import TreeSerializer
|
||||||
from InvenTree.helpers import str2bool
|
from InvenTree.helpers import str2bool
|
||||||
@ -53,7 +50,7 @@ class CategoryList(generics.ListCreateAPIView):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
queryset = PartCategory.objects.all()
|
queryset = PartCategory.objects.all()
|
||||||
serializer_class = CategorySerializer
|
serializer_class = part_serializers.CategorySerializer
|
||||||
|
|
||||||
permission_classes = [
|
permission_classes = [
|
||||||
permissions.IsAuthenticated,
|
permissions.IsAuthenticated,
|
||||||
@ -83,14 +80,37 @@ class CategoryList(generics.ListCreateAPIView):
|
|||||||
|
|
||||||
class CategoryDetail(generics.RetrieveUpdateDestroyAPIView):
|
class CategoryDetail(generics.RetrieveUpdateDestroyAPIView):
|
||||||
""" API endpoint for detail view of a single PartCategory object """
|
""" API endpoint for detail view of a single PartCategory object """
|
||||||
serializer_class = CategorySerializer
|
serializer_class = part_serializers.CategorySerializer
|
||||||
queryset = PartCategory.objects.all()
|
queryset = PartCategory.objects.all()
|
||||||
|
|
||||||
|
|
||||||
|
class PartThumbs(generics.ListAPIView):
|
||||||
|
""" API endpoint for retrieving information on available Part thumbnails """
|
||||||
|
|
||||||
|
serializer_class = part_serializers.PartThumbSerializer
|
||||||
|
|
||||||
|
def list(self, reguest, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
Serialize the available Part images.
|
||||||
|
- Images may be used for multiple parts!
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Get all Parts which have an associated image
|
||||||
|
queryset = Part.objects.all().exclude(image='')
|
||||||
|
|
||||||
|
# Return the most popular parts first
|
||||||
|
data = queryset.values(
|
||||||
|
'image',
|
||||||
|
).annotate(count=Count('image')).order_by('-count')
|
||||||
|
|
||||||
|
return Response(data)
|
||||||
|
|
||||||
|
|
||||||
class PartDetail(generics.RetrieveUpdateAPIView):
|
class PartDetail(generics.RetrieveUpdateAPIView):
|
||||||
""" API endpoint for detail view of a single Part object """
|
""" API endpoint for detail view of a single Part object """
|
||||||
|
|
||||||
queryset = Part.objects.all()
|
queryset = Part.objects.all()
|
||||||
serializer_class = PartSerializer
|
serializer_class = part_serializers.PartSerializer
|
||||||
|
|
||||||
permission_classes = [
|
permission_classes = [
|
||||||
permissions.IsAuthenticated,
|
permissions.IsAuthenticated,
|
||||||
@ -104,12 +124,12 @@ class PartList(generics.ListCreateAPIView):
|
|||||||
- POST: Create a new Part object
|
- POST: Create a new Part object
|
||||||
"""
|
"""
|
||||||
|
|
||||||
serializer_class = PartSerializer
|
serializer_class = part_serializers.PartSerializer
|
||||||
|
|
||||||
def list(self, request, *args, **kwargs):
|
def list(self, request, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
Instead of using the DRF serialiser to LIST,
|
Instead of using the DRF serialiser to LIST,
|
||||||
we serialize the objects manuually.
|
we serialize the objects manually.
|
||||||
This turns out to be significantly faster.
|
This turns out to be significantly faster.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@ -218,7 +238,7 @@ class PartStarDetail(generics.RetrieveDestroyAPIView):
|
|||||||
""" API endpoint for viewing or removing a PartStar object """
|
""" API endpoint for viewing or removing a PartStar object """
|
||||||
|
|
||||||
queryset = PartStar.objects.all()
|
queryset = PartStar.objects.all()
|
||||||
serializer_class = PartStarSerializer
|
serializer_class = part_serializers.PartStarSerializer
|
||||||
|
|
||||||
|
|
||||||
class PartStarList(generics.ListCreateAPIView):
|
class PartStarList(generics.ListCreateAPIView):
|
||||||
@ -229,7 +249,7 @@ class PartStarList(generics.ListCreateAPIView):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
queryset = PartStar.objects.all()
|
queryset = PartStar.objects.all()
|
||||||
serializer_class = PartStarSerializer
|
serializer_class = part_serializers.PartStarSerializer
|
||||||
|
|
||||||
def create(self, request, *args, **kwargs):
|
def create(self, request, *args, **kwargs):
|
||||||
|
|
||||||
@ -271,7 +291,7 @@ class PartParameterTemplateList(generics.ListCreateAPIView):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
queryset = PartParameterTemplate.objects.all()
|
queryset = PartParameterTemplate.objects.all()
|
||||||
serializer_class = PartParameterTemplateSerializer
|
serializer_class = part_serializers.PartParameterTemplateSerializer
|
||||||
|
|
||||||
permission_classes = [
|
permission_classes = [
|
||||||
permissions.IsAuthenticated,
|
permissions.IsAuthenticated,
|
||||||
@ -294,7 +314,7 @@ class PartParameterList(generics.ListCreateAPIView):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
queryset = PartParameter.objects.all()
|
queryset = PartParameter.objects.all()
|
||||||
serializer_class = PartParameterSerializer
|
serializer_class = part_serializers.PartParameterSerializer
|
||||||
|
|
||||||
permission_classes = [
|
permission_classes = [
|
||||||
permissions.IsAuthenticated,
|
permissions.IsAuthenticated,
|
||||||
@ -317,7 +337,7 @@ class BomList(generics.ListCreateAPIView):
|
|||||||
- POST: Create a new BomItem object
|
- POST: Create a new BomItem object
|
||||||
"""
|
"""
|
||||||
|
|
||||||
serializer_class = BomItemSerializer
|
serializer_class = part_serializers.BomItemSerializer
|
||||||
|
|
||||||
def get_serializer(self, *args, **kwargs):
|
def get_serializer(self, *args, **kwargs):
|
||||||
|
|
||||||
@ -360,7 +380,7 @@ class BomDetail(generics.RetrieveUpdateDestroyAPIView):
|
|||||||
""" API endpoint for detail view of a single BomItem object """
|
""" API endpoint for detail view of a single BomItem object """
|
||||||
|
|
||||||
queryset = BomItem.objects.all()
|
queryset = BomItem.objects.all()
|
||||||
serializer_class = BomItemSerializer
|
serializer_class = part_serializers.BomItemSerializer
|
||||||
|
|
||||||
permission_classes = [
|
permission_classes = [
|
||||||
permissions.IsAuthenticated,
|
permissions.IsAuthenticated,
|
||||||
@ -424,6 +444,8 @@ part_api_urls = [
|
|||||||
url(r'^star/', include(part_star_api_urls)),
|
url(r'^star/', include(part_star_api_urls)),
|
||||||
url(r'^parameter/', include(part_param_api_urls)),
|
url(r'^parameter/', include(part_param_api_urls)),
|
||||||
|
|
||||||
|
url(r'^thumbs/', PartThumbs.as_view(), name='api-part-thumbs'),
|
||||||
|
|
||||||
url(r'^(?P<pk>\d+)/?', PartDetail.as_view(), name='api-part-detail'),
|
url(r'^(?P<pk>\d+)/?', PartDetail.as_view(), name='api-part-detail'),
|
||||||
|
|
||||||
url(r'^.*$', PartList.as_view(), name='api-part-list'),
|
url(r'^.*$', PartList.as_view(), name='api-part-list'),
|
||||||
|
@ -12,7 +12,6 @@ from django.core.exceptions import ValidationError
|
|||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|
||||||
from django.core.files.base import ContentFile
|
|
||||||
from django.db import models, transaction
|
from django.db import models, transaction
|
||||||
from django.db.models import Sum
|
from django.db.models import Sum
|
||||||
from django.db.models import prefetch_related_objects
|
from django.db.models import prefetch_related_objects
|
||||||
@ -24,6 +23,8 @@ from django.dispatch import receiver
|
|||||||
|
|
||||||
from markdownx.models import MarkdownxField
|
from markdownx.models import MarkdownxField
|
||||||
|
|
||||||
|
from django_cleanup import cleanup
|
||||||
|
|
||||||
from mptt.models import TreeForeignKey
|
from mptt.models import TreeForeignKey
|
||||||
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
@ -136,18 +137,9 @@ def rename_part_image(instance, filename):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
base = 'part_images'
|
base = 'part_images'
|
||||||
|
fname = os.path.basename(filename)
|
||||||
|
|
||||||
if filename.count('.') > 0:
|
return os.path.join(base, fname)
|
||||||
ext = filename.split('.')[-1]
|
|
||||||
else:
|
|
||||||
ext = ''
|
|
||||||
|
|
||||||
fn = 'part_{pk}_img'.format(pk=instance.pk)
|
|
||||||
|
|
||||||
if ext:
|
|
||||||
fn += '.' + ext
|
|
||||||
|
|
||||||
return os.path.join(base, fn)
|
|
||||||
|
|
||||||
|
|
||||||
def match_part_names(match, threshold=80, reverse=True, compare_length=False):
|
def match_part_names(match, threshold=80, reverse=True, compare_length=False):
|
||||||
@ -201,6 +193,7 @@ def match_part_names(match, threshold=80, reverse=True, compare_length=False):
|
|||||||
return matches
|
return matches
|
||||||
|
|
||||||
|
|
||||||
|
@cleanup.ignore
|
||||||
class Part(models.Model):
|
class Part(models.Model):
|
||||||
""" The Part object represents an abstract part, the 'concept' of an actual entity.
|
""" The Part object represents an abstract part, the 'concept' of an actual entity.
|
||||||
|
|
||||||
@ -237,6 +230,26 @@ class Part(models.Model):
|
|||||||
verbose_name = "Part"
|
verbose_name = "Part"
|
||||||
verbose_name_plural = "Parts"
|
verbose_name_plural = "Parts"
|
||||||
|
|
||||||
|
def save(self, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
Overrides the save() function for the Part model.
|
||||||
|
If the part image has been updated,
|
||||||
|
then check if the "old" (previous) image is still used by another part.
|
||||||
|
If not, it is considered "orphaned" and will be deleted.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if self.pk:
|
||||||
|
previous = Part.objects.get(pk=self.pk)
|
||||||
|
|
||||||
|
if previous.image and not self.image == previous.image:
|
||||||
|
# Are there any (other) parts which reference the image?
|
||||||
|
n_refs = Part.objects.filter(image=previous.image).exclude(pk=self.pk).count()
|
||||||
|
|
||||||
|
if n_refs == 0:
|
||||||
|
previous.image.delete(save=False)
|
||||||
|
|
||||||
|
super().save(*args, **kwargs)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "{n} - {d}".format(n=self.full_name, d=self.description)
|
return "{n} - {d}".format(n=self.full_name, d=self.description)
|
||||||
|
|
||||||
@ -832,10 +845,8 @@ class Part(models.Model):
|
|||||||
# Copy the part image
|
# Copy the part image
|
||||||
if kwargs.get('image', True):
|
if kwargs.get('image', True):
|
||||||
if other.image:
|
if other.image:
|
||||||
image_file = ContentFile(other.image.read())
|
# Reference the other image from this Part
|
||||||
image_file.name = rename_part_image(self, other.image.url)
|
self.image = other.image
|
||||||
|
|
||||||
self.image = image_file
|
|
||||||
|
|
||||||
# Copy the BOM data
|
# Copy the BOM data
|
||||||
if kwargs.get('bom', False):
|
if kwargs.get('bom', False):
|
||||||
|
@ -30,6 +30,16 @@ class CategorySerializer(InvenTreeModelSerializer):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class PartThumbSerializer(serializers.Serializer):
|
||||||
|
"""
|
||||||
|
Serializer for the 'image' field of the Part model.
|
||||||
|
Used to serve and display existing Part images.
|
||||||
|
"""
|
||||||
|
|
||||||
|
image = serializers.URLField(read_only=True)
|
||||||
|
count = serializers.IntegerField(read_only=True)
|
||||||
|
|
||||||
|
|
||||||
class PartBriefSerializer(InvenTreeModelSerializer):
|
class PartBriefSerializer(InvenTreeModelSerializer):
|
||||||
""" Serializer for Part (brief detail) """
|
""" Serializer for Part (brief detail) """
|
||||||
|
|
||||||
|
@ -25,17 +25,7 @@
|
|||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-sm-6">
|
<div class="col-sm-6">
|
||||||
<div class="media">
|
{% include "part/part_thumb.html" %}
|
||||||
<div class="media-left">
|
|
||||||
<div class='dropzone' id='part-thumb'>
|
|
||||||
<img class="part-thumb"
|
|
||||||
{% if part.image %}
|
|
||||||
src="{{ part.image.url }}"
|
|
||||||
{% else %}
|
|
||||||
src="{% static 'img/blank_image.png' %}"
|
|
||||||
{% endif %}/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="media-body">
|
<div class="media-body">
|
||||||
<h4>
|
<h4>
|
||||||
{{ part.full_name }}
|
{{ part.full_name }}
|
||||||
@ -163,7 +153,7 @@
|
|||||||
|
|
||||||
enableDragAndDrop(
|
enableDragAndDrop(
|
||||||
'#part-thumb',
|
'#part-thumb',
|
||||||
"{% url 'part-image' part.id %}",
|
"{% url 'part-image-upload' part.id %}",
|
||||||
{
|
{
|
||||||
label: 'image',
|
label: 'image',
|
||||||
success: function(data, status, xhr) {
|
success: function(data, status, xhr) {
|
||||||
@ -208,13 +198,54 @@
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
$("#part-thumb").click(function() {
|
$("#part-image-upload").click(function() {
|
||||||
launchModalForm(
|
launchModalForm("{% url 'part-image-upload' part.id %}",
|
||||||
"{% url 'part-image' part.id %}",
|
{
|
||||||
|
reload: true
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
function onSelectImage(response) {
|
||||||
|
// Callback when the image-selection modal form is displayed
|
||||||
|
// Populate the form with image data (requested via AJAX)
|
||||||
|
|
||||||
|
$("#modal-form").find("#image-select-table").bootstrapTable({
|
||||||
|
pagination: true,
|
||||||
|
pageSize: 25,
|
||||||
|
url: "{% url 'api-part-thumbs' %}",
|
||||||
|
showHeader: false,
|
||||||
|
clickToSelect: true,
|
||||||
|
singleSelect: true,
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
checkbox: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'image',
|
||||||
|
title: 'Image',
|
||||||
|
formatter: function(value, row, index, field) {
|
||||||
|
return "<img src='/media/" + value + "' class='grid-image'/>"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
onCheck: function(row, element) {
|
||||||
|
|
||||||
|
// Update the selected image in the form
|
||||||
|
var ipt = $("#modal-form").find("#image-input");
|
||||||
|
ipt.val(row.image);
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
$("#part-image-select").click(function() {
|
||||||
|
launchModalForm("{% url 'part-image-select' part.id %}",
|
||||||
{
|
{
|
||||||
reload: true
|
reload: true,
|
||||||
}
|
after_render: onSelectImage
|
||||||
);
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
$("#part-edit").click(function() {
|
$("#part-edit").click(function() {
|
||||||
|
20
InvenTree/part/templates/part/part_thumb.html
Normal file
20
InvenTree/part/templates/part/part_thumb.html
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
{% load static %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
<div class="media">
|
||||||
|
<div class="media-left part-thumb-container">
|
||||||
|
<div class='dropzone' id='part-thumb'>
|
||||||
|
<img class="part-thumb"
|
||||||
|
{% if part.image %}
|
||||||
|
src="{{ part.image.url }}"
|
||||||
|
{% else %}
|
||||||
|
src="{% static 'img/blank_image.png' %}"
|
||||||
|
{% endif %}/>
|
||||||
|
</div>
|
||||||
|
<div class='btn-row part-thumb-overlay'>
|
||||||
|
<div class='btn-group'>
|
||||||
|
<button type='button' class='btn btn-default btn-glyph' title="{% trans 'Select from existing images' %}" id='part-image-select'><span class='glyphicon glyphicon-th'></span></button>
|
||||||
|
<button type='button' class='btn btn-default btn-glyph' title="{% trans 'Upload new image' %}" id='part-image-upload'><span class='glyphicon glyphicon-upload'></span></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
21
InvenTree/part/templates/part/select_image.html
Normal file
21
InvenTree/part/templates/part/select_image.html
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
{% extends "modal_form.html" %}
|
||||||
|
|
||||||
|
{% block pre_form_content %}
|
||||||
|
|
||||||
|
{{ block.super }}
|
||||||
|
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block form %}
|
||||||
|
<form method='post' action='' class='js-modal-form' enctype='multipart/form-data'>
|
||||||
|
{% csrf_token %}
|
||||||
|
{% load crispy_forms_tags %}
|
||||||
|
|
||||||
|
<input id='image-input' name='image' type='hidden' value="{{ part.image }}">
|
||||||
|
|
||||||
|
<table id='image-select-table' class='table table-striped table-condensed table-img-grid'>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
</form>
|
||||||
|
{% endblock %}
|
@ -23,7 +23,7 @@ class TemplateTagTest(TestCase):
|
|||||||
|
|
||||||
def test_hash(self):
|
def test_hash(self):
|
||||||
hash = inventree_extras.inventree_commit_hash()
|
hash = inventree_extras.inventree_commit_hash()
|
||||||
self.assertEqual(len(hash), 7)
|
self.assertGreater(len(hash), 5)
|
||||||
|
|
||||||
def test_date(self):
|
def test_date(self):
|
||||||
d = inventree_extras.inventree_commit_date()
|
d = inventree_extras.inventree_commit_date()
|
||||||
@ -68,11 +68,8 @@ class PartTest(TestCase):
|
|||||||
|
|
||||||
def test_rename_img(self):
|
def test_rename_img(self):
|
||||||
img = rename_part_image(self.R1, 'hello.png')
|
img = rename_part_image(self.R1, 'hello.png')
|
||||||
self.assertEqual(img, os.path.join('part_images', 'part_3_img.png'))
|
self.assertEqual(img, os.path.join('part_images', 'hello.png'))
|
||||||
|
|
||||||
img = rename_part_image(self.R2, 'test')
|
|
||||||
self.assertEqual(img, os.path.join('part_images', 'part_4_img'))
|
|
||||||
|
|
||||||
def test_stock(self):
|
def test_stock(self):
|
||||||
# No stock of any resistors
|
# No stock of any resistors
|
||||||
res = Part.objects.filter(description__contains='resistor')
|
res = Part.objects.filter(description__contains='resistor')
|
||||||
|
@ -57,7 +57,8 @@ part_detail_urls = [
|
|||||||
url(r'^qr_code/?', views.PartQRCode.as_view(), name='part-qr'),
|
url(r'^qr_code/?', views.PartQRCode.as_view(), name='part-qr'),
|
||||||
|
|
||||||
# Normal thumbnail with form
|
# Normal thumbnail with form
|
||||||
url(r'^thumbnail/?', views.PartImage.as_view(), name='part-image'),
|
url(r'^thumbnail/?', views.PartImageUpload.as_view(), name='part-image-upload'),
|
||||||
|
url(r'^thumb-select/?', views.PartImageSelect.as_view(), name='part-image-select'),
|
||||||
|
|
||||||
# Any other URLs go to the part detail page
|
# Any other URLs go to the part detail page
|
||||||
url(r'^.*$', views.PartDetail.as_view(), name='part-detail'),
|
url(r'^.*$', views.PartDetail.as_view(), name='part-detail'),
|
||||||
|
@ -14,6 +14,9 @@ from django.urls import reverse, reverse_lazy
|
|||||||
from django.views.generic import DetailView, ListView, FormView, UpdateView
|
from django.views.generic import DetailView, ListView, FormView, UpdateView
|
||||||
from django.forms.models import model_to_dict
|
from django.forms.models import model_to_dict
|
||||||
from django.forms import HiddenInput, CheckboxInput
|
from django.forms import HiddenInput, CheckboxInput
|
||||||
|
from django.conf import settings
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
from fuzzywuzzy import fuzz
|
from fuzzywuzzy import fuzz
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
@ -74,7 +77,7 @@ class PartAttachmentCreate(AjaxCreateView):
|
|||||||
|
|
||||||
def get_data(self):
|
def get_data(self):
|
||||||
return {
|
return {
|
||||||
'success': 'Added attachment'
|
'success': _('Added attachment')
|
||||||
}
|
}
|
||||||
|
|
||||||
def get_initial(self):
|
def get_initial(self):
|
||||||
@ -116,7 +119,7 @@ class PartAttachmentEdit(AjaxUpdateView):
|
|||||||
|
|
||||||
def get_data(self):
|
def get_data(self):
|
||||||
return {
|
return {
|
||||||
'success': 'Part attachment updated'
|
'success': _('Part attachment updated')
|
||||||
}
|
}
|
||||||
|
|
||||||
def get_form(self):
|
def get_form(self):
|
||||||
@ -303,7 +306,7 @@ class PartDuplicate(AjaxCreateView):
|
|||||||
|
|
||||||
def get_data(self):
|
def get_data(self):
|
||||||
return {
|
return {
|
||||||
'success': 'Copied part'
|
'success': _('Copied part')
|
||||||
}
|
}
|
||||||
|
|
||||||
def get_part_to_copy(self):
|
def get_part_to_copy(self):
|
||||||
@ -411,12 +414,12 @@ class PartCreate(AjaxCreateView):
|
|||||||
model = Part
|
model = Part
|
||||||
form_class = part_forms.EditPartForm
|
form_class = part_forms.EditPartForm
|
||||||
|
|
||||||
ajax_form_title = 'Create new part'
|
ajax_form_title = _('Create new part')
|
||||||
ajax_template_name = 'part/create_part.html'
|
ajax_template_name = 'part/create_part.html'
|
||||||
|
|
||||||
def get_data(self):
|
def get_data(self):
|
||||||
return {
|
return {
|
||||||
'success': "Created new part",
|
'success': _("Created new part"),
|
||||||
}
|
}
|
||||||
|
|
||||||
def get_category_id(self):
|
def get_category_id(self):
|
||||||
@ -601,27 +604,66 @@ class PartQRCode(QRCodeView):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
class PartImage(AjaxUpdateView):
|
class PartImageUpload(AjaxUpdateView):
|
||||||
""" View for uploading Part image """
|
""" View for uploading a new Part image """
|
||||||
|
|
||||||
model = Part
|
model = Part
|
||||||
ajax_template_name = 'modal_form.html'
|
ajax_template_name = 'modal_form.html'
|
||||||
ajax_form_title = 'Upload Part Image'
|
ajax_form_title = _('Upload Part Image')
|
||||||
form_class = part_forms.PartImageForm
|
form_class = part_forms.PartImageForm
|
||||||
|
|
||||||
def get_data(self):
|
def get_data(self):
|
||||||
return {
|
return {
|
||||||
'success': 'Updated part image',
|
'success': _('Updated part image'),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class PartImageSelect(AjaxUpdateView):
|
||||||
|
""" View for selecting Part image from existing images. """
|
||||||
|
|
||||||
|
model = Part
|
||||||
|
ajax_template_name = 'part/select_image.html'
|
||||||
|
ajax_form_title = _('Select Part Image')
|
||||||
|
|
||||||
|
fields = [
|
||||||
|
'image',
|
||||||
|
]
|
||||||
|
|
||||||
|
def post(self, request, *args, **kwargs):
|
||||||
|
|
||||||
|
part = self.get_object()
|
||||||
|
form = self.get_form()
|
||||||
|
|
||||||
|
img = request.POST.get('image', '')
|
||||||
|
|
||||||
|
img = os.path.basename(img)
|
||||||
|
|
||||||
|
data = {}
|
||||||
|
|
||||||
|
if img:
|
||||||
|
img_path = os.path.join(settings.MEDIA_ROOT, 'part_images', img)
|
||||||
|
|
||||||
|
# Ensure that the image already exists
|
||||||
|
if os.path.exists(img_path):
|
||||||
|
|
||||||
|
part.image = os.path.join('part_images', img)
|
||||||
|
part.save()
|
||||||
|
|
||||||
|
data['success'] = _('Updated part image')
|
||||||
|
|
||||||
|
if 'success' not in data:
|
||||||
|
data['error'] = _('Part image not found')
|
||||||
|
|
||||||
|
return self.renderJsonResponse(request, form, data)
|
||||||
|
|
||||||
|
|
||||||
class PartEdit(AjaxUpdateView):
|
class PartEdit(AjaxUpdateView):
|
||||||
""" View for editing Part object """
|
""" View for editing Part object """
|
||||||
|
|
||||||
model = Part
|
model = Part
|
||||||
form_class = part_forms.EditPartForm
|
form_class = part_forms.EditPartForm
|
||||||
ajax_template_name = 'modal_form.html'
|
ajax_template_name = 'modal_form.html'
|
||||||
ajax_form_title = 'Edit Part Properties'
|
ajax_form_title = _('Edit Part Properties')
|
||||||
context_object_name = 'part'
|
context_object_name = 'part'
|
||||||
|
|
||||||
def get_form(self):
|
def get_form(self):
|
||||||
@ -643,7 +685,7 @@ class BomValidate(AjaxUpdateView):
|
|||||||
""" Modal form view for validating a part BOM """
|
""" Modal form view for validating a part BOM """
|
||||||
|
|
||||||
model = Part
|
model = Part
|
||||||
ajax_form_title = "Validate BOM"
|
ajax_form_title = _("Validate BOM")
|
||||||
ajax_template_name = 'part/bom_validate.html'
|
ajax_template_name = 'part/bom_validate.html'
|
||||||
context_object_name = 'part'
|
context_object_name = 'part'
|
||||||
form_class = part_forms.BomValidateForm
|
form_class = part_forms.BomValidateForm
|
||||||
@ -1308,14 +1350,14 @@ class PartDelete(AjaxDeleteView):
|
|||||||
|
|
||||||
model = Part
|
model = Part
|
||||||
ajax_template_name = 'part/partial_delete.html'
|
ajax_template_name = 'part/partial_delete.html'
|
||||||
ajax_form_title = 'Confirm Part Deletion'
|
ajax_form_title = _('Confirm Part Deletion')
|
||||||
context_object_name = 'part'
|
context_object_name = 'part'
|
||||||
|
|
||||||
success_url = '/part/'
|
success_url = '/part/'
|
||||||
|
|
||||||
def get_data(self):
|
def get_data(self):
|
||||||
return {
|
return {
|
||||||
'danger': 'Part was deleted',
|
'danger': _('Part was deleted'),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1324,7 +1366,7 @@ class PartPricing(AjaxView):
|
|||||||
|
|
||||||
model = Part
|
model = Part
|
||||||
ajax_template_name = "part/part_pricing.html"
|
ajax_template_name = "part/part_pricing.html"
|
||||||
ajax_form_title = "Part Pricing"
|
ajax_form_title = _("Part Pricing")
|
||||||
form_class = part_forms.PartPriceForm
|
form_class = part_forms.PartPriceForm
|
||||||
|
|
||||||
def get_part(self):
|
def get_part(self):
|
||||||
@ -1446,7 +1488,7 @@ class PartParameterTemplateCreate(AjaxCreateView):
|
|||||||
|
|
||||||
model = PartParameterTemplate
|
model = PartParameterTemplate
|
||||||
form_class = part_forms.EditPartParameterTemplateForm
|
form_class = part_forms.EditPartParameterTemplateForm
|
||||||
ajax_form_title = 'Create Part Parameter Template'
|
ajax_form_title = _('Create Part Parameter Template')
|
||||||
|
|
||||||
|
|
||||||
class PartParameterTemplateEdit(AjaxUpdateView):
|
class PartParameterTemplateEdit(AjaxUpdateView):
|
||||||
@ -1454,14 +1496,14 @@ class PartParameterTemplateEdit(AjaxUpdateView):
|
|||||||
|
|
||||||
model = PartParameterTemplate
|
model = PartParameterTemplate
|
||||||
form_class = part_forms.EditPartParameterTemplateForm
|
form_class = part_forms.EditPartParameterTemplateForm
|
||||||
ajax_form_title = 'Edit Part Parameter Template'
|
ajax_form_title = _('Edit Part Parameter Template')
|
||||||
|
|
||||||
|
|
||||||
class PartParameterTemplateDelete(AjaxDeleteView):
|
class PartParameterTemplateDelete(AjaxDeleteView):
|
||||||
""" View for deleting an existing PartParameterTemplate """
|
""" View for deleting an existing PartParameterTemplate """
|
||||||
|
|
||||||
model = PartParameterTemplate
|
model = PartParameterTemplate
|
||||||
ajax_form_title = "Delete Part Parameter Template"
|
ajax_form_title = _("Delete Part Parameter Template")
|
||||||
|
|
||||||
|
|
||||||
class PartParameterCreate(AjaxCreateView):
|
class PartParameterCreate(AjaxCreateView):
|
||||||
@ -1469,7 +1511,7 @@ class PartParameterCreate(AjaxCreateView):
|
|||||||
|
|
||||||
model = PartParameter
|
model = PartParameter
|
||||||
form_class = part_forms.EditPartParameterForm
|
form_class = part_forms.EditPartParameterForm
|
||||||
ajax_form_title = 'Create Part Parameter'
|
ajax_form_title = _('Create Part Parameter')
|
||||||
|
|
||||||
def get_initial(self):
|
def get_initial(self):
|
||||||
|
|
||||||
@ -1519,7 +1561,7 @@ class PartParameterEdit(AjaxUpdateView):
|
|||||||
|
|
||||||
model = PartParameter
|
model = PartParameter
|
||||||
form_class = part_forms.EditPartParameterForm
|
form_class = part_forms.EditPartParameterForm
|
||||||
ajax_form_title = 'Edit Part Parameter'
|
ajax_form_title = _('Edit Part Parameter')
|
||||||
|
|
||||||
def get_form(self):
|
def get_form(self):
|
||||||
|
|
||||||
@ -1533,7 +1575,7 @@ class PartParameterDelete(AjaxDeleteView):
|
|||||||
|
|
||||||
model = PartParameter
|
model = PartParameter
|
||||||
ajax_template_name = 'part/param_delete.html'
|
ajax_template_name = 'part/param_delete.html'
|
||||||
ajax_form_title = 'Delete Part Parameter'
|
ajax_form_title = _('Delete Part Parameter')
|
||||||
|
|
||||||
|
|
||||||
class CategoryDetail(DetailView):
|
class CategoryDetail(DetailView):
|
||||||
@ -1549,7 +1591,7 @@ class CategoryEdit(AjaxUpdateView):
|
|||||||
model = PartCategory
|
model = PartCategory
|
||||||
form_class = part_forms.EditCategoryForm
|
form_class = part_forms.EditCategoryForm
|
||||||
ajax_template_name = 'modal_form.html'
|
ajax_template_name = 'modal_form.html'
|
||||||
ajax_form_title = 'Edit Part Category'
|
ajax_form_title = _('Edit Part Category')
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super(CategoryEdit, self).get_context_data(**kwargs).copy()
|
context = super(CategoryEdit, self).get_context_data(**kwargs).copy()
|
||||||
@ -1584,13 +1626,13 @@ class CategoryDelete(AjaxDeleteView):
|
|||||||
""" Delete view to delete a PartCategory """
|
""" Delete view to delete a PartCategory """
|
||||||
model = PartCategory
|
model = PartCategory
|
||||||
ajax_template_name = 'part/category_delete.html'
|
ajax_template_name = 'part/category_delete.html'
|
||||||
ajax_form_title = 'Delete Part Category'
|
ajax_form_title = _('Delete Part Category')
|
||||||
context_object_name = 'category'
|
context_object_name = 'category'
|
||||||
success_url = '/part/'
|
success_url = '/part/'
|
||||||
|
|
||||||
def get_data(self):
|
def get_data(self):
|
||||||
return {
|
return {
|
||||||
'danger': 'Part category was deleted',
|
'danger': _('Part category was deleted'),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1598,7 +1640,7 @@ class CategoryCreate(AjaxCreateView):
|
|||||||
""" Create view to make a new PartCategory """
|
""" Create view to make a new PartCategory """
|
||||||
model = PartCategory
|
model = PartCategory
|
||||||
ajax_form_action = reverse_lazy('category-create')
|
ajax_form_action = reverse_lazy('category-create')
|
||||||
ajax_form_title = 'Create new part category'
|
ajax_form_title = _('Create new part category')
|
||||||
ajax_template_name = 'modal_form.html'
|
ajax_template_name = 'modal_form.html'
|
||||||
form_class = part_forms.EditCategoryForm
|
form_class = part_forms.EditCategoryForm
|
||||||
|
|
||||||
@ -1649,7 +1691,7 @@ class BomItemCreate(AjaxCreateView):
|
|||||||
model = BomItem
|
model = BomItem
|
||||||
form_class = part_forms.EditBomItemForm
|
form_class = part_forms.EditBomItemForm
|
||||||
ajax_template_name = 'modal_form.html'
|
ajax_template_name = 'modal_form.html'
|
||||||
ajax_form_title = 'Create BOM item'
|
ajax_form_title = _('Create BOM item')
|
||||||
|
|
||||||
def get_form(self):
|
def get_form(self):
|
||||||
""" Override get_form() method to reduce Part selection options.
|
""" Override get_form() method to reduce Part selection options.
|
||||||
@ -1715,7 +1757,7 @@ class BomItemEdit(AjaxUpdateView):
|
|||||||
model = BomItem
|
model = BomItem
|
||||||
form_class = part_forms.EditBomItemForm
|
form_class = part_forms.EditBomItemForm
|
||||||
ajax_template_name = 'modal_form.html'
|
ajax_template_name = 'modal_form.html'
|
||||||
ajax_form_title = 'Edit BOM item'
|
ajax_form_title = _('Edit BOM item')
|
||||||
|
|
||||||
def get_form(self):
|
def get_form(self):
|
||||||
""" Override get_form() method to filter part selection options
|
""" Override get_form() method to filter part selection options
|
||||||
@ -1763,4 +1805,4 @@ class BomItemDelete(AjaxDeleteView):
|
|||||||
model = BomItem
|
model = BomItem
|
||||||
ajax_template_name = 'part/bom-delete.html'
|
ajax_template_name = 'part/bom-delete.html'
|
||||||
context_object_name = 'item'
|
context_object_name = 'item'
|
||||||
ajax_form_title = 'Confim BOM item deletion'
|
ajax_form_title = _('Confim BOM item deletion')
|
||||||
|
@ -44,7 +44,6 @@ class CreateStockItemForm(HelperForm):
|
|||||||
'serial_numbers',
|
'serial_numbers',
|
||||||
'delete_on_deplete',
|
'delete_on_deplete',
|
||||||
'status',
|
'status',
|
||||||
'notes',
|
|
||||||
'URL',
|
'URL',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
19
InvenTree/stock/migrations/0020_auto_20200206_1213.py
Normal file
19
InvenTree/stock/migrations/0020_auto_20200206_1213.py
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
# Generated by Django 2.2.9 on 2020-02-06 12:13
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
import markdownx.models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('stock', '0019_auto_20200202_1024'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='stockitem',
|
||||||
|
name='notes',
|
||||||
|
field=markdownx.models.MarkdownxField(blank=True, help_text='Stock Item Notes', null=True),
|
||||||
|
),
|
||||||
|
]
|
@ -360,7 +360,7 @@ class StockItem(models.Model):
|
|||||||
choices=StockStatus.items(),
|
choices=StockStatus.items(),
|
||||||
validators=[MinValueValidator(0)])
|
validators=[MinValueValidator(0)])
|
||||||
|
|
||||||
notes = MarkdownxField(blank=True, help_text=_('Stock Item Notes'))
|
notes = MarkdownxField(blank=True, null=True, help_text=_('Stock Item Notes'))
|
||||||
|
|
||||||
# If stock item is incoming, an (optional) ETA field
|
# If stock item is incoming, an (optional) ETA field
|
||||||
# expected_arrival = models.DateField(null=True, blank=True)
|
# expected_arrival = models.DateField(null=True, blank=True)
|
||||||
|
@ -884,34 +884,49 @@ class StockItemCreate(AjaxCreateView):
|
|||||||
form.errors['serial_numbers'] = [_('The following serial numbers already exist: ({sn})'.format(sn=exists))]
|
form.errors['serial_numbers'] = [_('The following serial numbers already exist: ({sn})'.format(sn=exists))]
|
||||||
valid = False
|
valid = False
|
||||||
|
|
||||||
# At this point we have a list of serial numbers which we know are valid,
|
else:
|
||||||
# and do not currently exist
|
# At this point we have a list of serial numbers which we know are valid,
|
||||||
form.clean()
|
# and do not currently exist
|
||||||
|
form.clean()
|
||||||
|
|
||||||
data = form.cleaned_data
|
form_data = form.cleaned_data
|
||||||
|
|
||||||
for serial in serials:
|
for serial in serials:
|
||||||
# Create a new stock item for each serial number
|
# Create a new stock item for each serial number
|
||||||
item = StockItem(
|
item = StockItem(
|
||||||
part=part,
|
part=part,
|
||||||
quantity=1,
|
quantity=1,
|
||||||
serial=serial,
|
serial=serial,
|
||||||
supplier_part=data.get('supplier_part'),
|
supplier_part=form_data.get('supplier_part'),
|
||||||
location=data.get('location'),
|
location=form_data.get('location'),
|
||||||
batch=data.get('batch'),
|
batch=form_data.get('batch'),
|
||||||
delete_on_deplete=False,
|
delete_on_deplete=False,
|
||||||
status=data.get('status'),
|
status=form_data.get('status'),
|
||||||
notes=data.get('notes'),
|
URL=form_data.get('URL'),
|
||||||
URL=data.get('URL'),
|
)
|
||||||
)
|
|
||||||
|
|
||||||
item.save(user=request.user)
|
item.save(user=request.user)
|
||||||
|
|
||||||
|
data['success'] = _('Created {n} new stock items'.format(n=len(serials)))
|
||||||
|
valid = True
|
||||||
|
|
||||||
except ValidationError as e:
|
except ValidationError as e:
|
||||||
form.errors['serial_numbers'] = e.messages
|
form.errors['serial_numbers'] = e.messages
|
||||||
valid = False
|
valid = False
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
# We have a serialized part, but no serial numbers specified...
|
||||||
|
form.clean()
|
||||||
|
form._post_clean()
|
||||||
|
|
||||||
|
item = form.save(commit=False)
|
||||||
|
item.save(user=request.user)
|
||||||
|
|
||||||
|
data['pk'] = item.pk
|
||||||
|
data['url'] = item.get_absolute_url()
|
||||||
|
data['success'] = _("Created new stock item")
|
||||||
|
|
||||||
|
else: # Referenced Part object is not marked as "trackable"
|
||||||
# For non-serialized items, simply save the form.
|
# For non-serialized items, simply save the form.
|
||||||
# We need to call _post_clean() here because it is prevented in the form implementation
|
# We need to call _post_clean() here because it is prevented in the form implementation
|
||||||
form.clean()
|
form.clean()
|
||||||
|
Loading…
Reference in New Issue
Block a user