From 33fe4d186d375ce1f4d1108297a3444b20d9217d Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Wed, 24 Jul 2019 19:48:37 +1000 Subject: [PATCH 1/9] Include more fields when splitting stock --- InvenTree/stock/models.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/InvenTree/stock/models.py b/InvenTree/stock/models.py index 4f4c74d6a2..359e9a0115 100644 --- a/InvenTree/stock/models.py +++ b/InvenTree/stock/models.py @@ -377,6 +377,8 @@ class StockItem(models.Model): quantity=quantity, supplier_part=self.supplier_part, location=self.location, + notes=self.notes, + URL=self.URL, batch=self.batch, delete_on_deplete=self.delete_on_deplete ) From 3058b895dd5ed2dea0ab5f5d6decf5c97fba4984 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Wed, 24 Jul 2019 20:24:12 +1000 Subject: [PATCH 2/9] Prevent auto-delete of stock items which have a serial number --- InvenTree/stock/models.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/InvenTree/stock/models.py b/InvenTree/stock/models.py index 359e9a0115..c92776a2fa 100644 --- a/InvenTree/stock/models.py +++ b/InvenTree/stock/models.py @@ -316,7 +316,15 @@ class StockItem(models.Model): infinite = models.BooleanField(default=False) def can_delete(self): - # TODO - Return FALSE if this item cannot be deleted! + """ Can this stock item be deleted? It can NOT be deleted under the following circumstances: + + - Has a serial number and is tracked + - Is installed inside another StockItem + """ + + if part.trackable and self.serial is not None: + return False + return True @property @@ -457,7 +465,7 @@ class StockItem(models.Model): self.quantity = quantity - if quantity <= 0 and self.delete_on_deplete: + if quantity <= 0 and self.delete_on_deplete and self.can_delete(): self.delete() return False else: From 42e1370e92d1853768477d33e88a32fe3bf602fb Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Thu, 25 Jul 2019 10:36:59 +1000 Subject: [PATCH 3/9] Bug fix --- InvenTree/stock/models.py | 2 +- InvenTree/stock/views.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/InvenTree/stock/models.py b/InvenTree/stock/models.py index c92776a2fa..abc4702e4a 100644 --- a/InvenTree/stock/models.py +++ b/InvenTree/stock/models.py @@ -322,7 +322,7 @@ class StockItem(models.Model): - Is installed inside another StockItem """ - if part.trackable and self.serial is not None: + if self.part.trackable and self.serial is not None: return False return True diff --git a/InvenTree/stock/views.py b/InvenTree/stock/views.py index 7595c550b0..b7fbb9f9e4 100644 --- a/InvenTree/stock/views.py +++ b/InvenTree/stock/views.py @@ -383,8 +383,8 @@ class StockAdjust(AjaxView, FormMixin): if item.new_quantity <= 0: continue - # Do not move to the same location - if destination == item.location: + # Do not move to the same location (unless the quantity is different) + if destination == item.location and item.new_quantity == item.quantity: continue item.move(destination, note, self.request.user, quantity=int(item.new_quantity)) From 94c0102742b2729ffdbec15166a1f744ac8866a6 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Thu, 25 Jul 2019 11:04:45 +1000 Subject: [PATCH 4/9] Improve validation logic for StockItem - Allow tracked items to exist without a serial number (e.g. non-serialized tracked items) --- InvenTree/stock/forms.py | 3 ++- InvenTree/stock/models.py | 27 ++++++++++++++------------- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/InvenTree/stock/forms.py b/InvenTree/stock/forms.py index 9e24b538f3..46a50521dd 100644 --- a/InvenTree/stock/forms.py +++ b/InvenTree/stock/forms.py @@ -118,11 +118,12 @@ class EditStockItemForm(HelperForm): fields = [ 'supplier_part', + 'serial', 'batch', - 'delete_on_deplete', 'status', 'notes', 'URL', + 'delete_on_deplete', ] diff --git a/InvenTree/stock/models.py b/InvenTree/stock/models.py index abc4702e4a..446ab65a56 100644 --- a/InvenTree/stock/models.py +++ b/InvenTree/stock/models.py @@ -190,20 +190,21 @@ class StockItem(models.Model): }) if self.part is not None: - # A trackable part must have a serial number - if self.part.trackable: - if not self.serial: - raise ValidationError({'serial': _('Serial number must be set for trackable items')}) + # A part with a serial number MUST have the quantity set to 1 + if self.serial is not None: + if self.quantity > 1: + raise ValidationError({ + 'quantity': _('Quantity must be 1 for item with a serial number'), + 'serial': _('Serial number cannot be set if quantity greater than 1') + }) + + if self.quantity == 0: + raise ValidationError({ + 'quantity': _('Quantity must be 1 for item with a serial number') + }) if self.delete_on_deplete: - raise ValidationError({'delete_on_deplete': _("Must be set to False for trackable items")}) - - # Serial number cannot be set for items with quantity greater than 1 - if not self.quantity == 1: - raise ValidationError({ - 'quantity': _("Quantity must be set to 1 for item with a serial number"), - 'serial': _("Serial number cannot be set if quantity > 1") - }) + raise ValidationError({'delete_on_deplete': _("Must be set to False for item with a serial number")}) # A template part cannot be instantiated as a StockItem if self.part.is_template: @@ -422,7 +423,7 @@ class StockItem(models.Model): if location is None: # TODO - Raise appropriate error (cannot move to blank location) return False - elif self.location and (location.pk == self.location.pk): + elif self.location and (location.pk == self.location.pk) and (quantity == self.quantity): # TODO - Raise appropriate error (cannot move to same location) return False From fe7392f152e618a8c34ef9e765e7a0a25b2960af Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Thu, 25 Jul 2019 11:05:09 +1000 Subject: [PATCH 5/9] Prevent stock adjustments for serialized stock items --- InvenTree/stock/models.py | 29 +++++++++++++++++++++++ InvenTree/stock/templates/stock/item.html | 15 ++++++++++-- InvenTree/stock/views.py | 3 +++ 3 files changed, 45 insertions(+), 2 deletions(-) diff --git a/InvenTree/stock/models.py b/InvenTree/stock/models.py index 446ab65a56..b5a88d4e66 100644 --- a/InvenTree/stock/models.py +++ b/InvenTree/stock/models.py @@ -122,6 +122,11 @@ class StockItem(models.Model): system=True ) + @property + def serialized(self): + """ Return True if this StockItem is serialized """ + return self.serial is not None and self.quantity == 1 + @classmethod def check_serial_number(cls, part, serial_number): """ Check if a new stock item can be created with the provided part_id @@ -358,6 +363,14 @@ class StockItem(models.Model): track.save() + @transaction.atomic + def serializeStock(self, serials, user): + """ Split this stock item into unique serial numbers. + """ + + # TODO + pass + @transaction.atomic def splitStock(self, quantity, user): """ Split this stock item into two items, in the same location. @@ -372,6 +385,10 @@ class StockItem(models.Model): The new item will have a different StockItem ID, while this will remain the same. """ + # Do not split a serialized part + if self.serialized: + return + # Doesn't make sense for a zero quantity if quantity <= 0: return @@ -461,6 +478,10 @@ class StockItem(models.Model): - False if the StockItem was deleted """ + # Do not adjust quantity of a serialized part + if self.serialized: + return + if quantity < 0: quantity = 0 @@ -504,6 +525,10 @@ class StockItem(models.Model): or by manually adding the items to the stock location """ + # Cannot add items to a serialized part + if self.serialized: + return False + quantity = int(quantity) # Ignore amounts that do not make sense @@ -524,6 +549,10 @@ class StockItem(models.Model): """ Remove items from stock """ + # Cannot remove items from a serialized part + if self.serialized: + return False + quantity = int(quantity) if quantity <= 0 or self.infinite: diff --git a/InvenTree/stock/templates/stock/item.html b/InvenTree/stock/templates/stock/item.html index f6847d5ae1..97895fd4d2 100644 --- a/InvenTree/stock/templates/stock/item.html +++ b/InvenTree/stock/templates/stock/item.html @@ -5,11 +5,16 @@

Stock Item Details

+ {% if item.serialized %} +

{{ item.part.full_name}} # {{ item.serial }}

+ {% else %}

{{ item.quantity }} × {{ item.part.full_name }}

+ {% endif %}

{% include "qr_button.html" %} {% if item.in_stock %} + {% if not item.serialized %} @@ -19,6 +24,7 @@ + {% endif %} @@ -34,6 +40,11 @@

+ {% if item.serialized %} +
+ This stock item is serialized - it has a unique serial number and the quantity cannot be adjusted. +
+ {% endif %}
@@ -54,9 +65,9 @@ {{ item.location.name }} {% endif %} - {% if item.serial %} + {% if item.serialized %} - Serial + Serial Number {{ item.serial }} {% else %} diff --git a/InvenTree/stock/views.py b/InvenTree/stock/views.py index b7fbb9f9e4..6fbea9d885 100644 --- a/InvenTree/stock/views.py +++ b/InvenTree/stock/views.py @@ -429,6 +429,9 @@ class StockItemEdit(AjaxUpdateView): query = query.filter(part=item.part.id) form.fields['supplier_part'].queryset = query + if not item.part.trackable: + form.fields.pop('serial') + return form From c8bf20ad41040161a637f0bb0771d3d9f71c57c3 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Thu, 25 Jul 2019 11:08:22 +1000 Subject: [PATCH 6/9] Add part hover image --- InvenTree/stock/templates/stock/item.html | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/InvenTree/stock/templates/stock/item.html b/InvenTree/stock/templates/stock/item.html index 97895fd4d2..62e46c8f84 100644 --- a/InvenTree/stock/templates/stock/item.html +++ b/InvenTree/stock/templates/stock/item.html @@ -52,7 +52,10 @@ - + {% if item.belongs_to %} From 0a328687a5a783a8b7e165c8f5223039045abf7d Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Fri, 2 Aug 2019 11:11:28 +1000 Subject: [PATCH 7/9] BOM hash includes reference fields --- InvenTree/part/models.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/InvenTree/part/models.py b/InvenTree/part/models.py index e56466c832..9e0b2cea29 100644 --- a/InvenTree/part/models.py +++ b/InvenTree/part/models.py @@ -634,7 +634,8 @@ class Part(models.Model): For hash is calculated from the following fields of each BOM item: - Part.full_name (if the part name changes, the BOM checksum is invalidated) - - quantity + - Quantity + - Reference field - Note field returns a string representation of a hash object which can be compared with a stored value @@ -647,6 +648,7 @@ class Part(models.Model): hash.update(str(item.sub_part.full_name).encode()) hash.update(str(item.quantity).encode()) hash.update(str(item.note).encode()) + hash.update(str(item.reference).encode()) return str(hash.digest()) From e66fd956f970fea820a9bed97412288d1c3d2a9e Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Fri, 2 Aug 2019 15:13:47 +1000 Subject: [PATCH 8/9] Fix available stock display --- InvenTree/part/templates/part/part_base.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/InvenTree/part/templates/part/part_base.html b/InvenTree/part/templates/part/part_base.html index b650f5b33b..8cbae62fec 100644 --- a/InvenTree/part/templates/part/part_base.html +++ b/InvenTree/part/templates/part/part_base.html @@ -102,7 +102,7 @@ - + From 9253ee2c9a1c774bef3ad3f08f02c78778c8dff5 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Fri, 2 Aug 2019 21:51:24 +1000 Subject: [PATCH 9/9] Fix the travis script to prevent requesting user input --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 00d049b7a1..58bdad303f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,7 +10,8 @@ addons: -sqlite3 before_install: - - make install + - make requirements + - make secret - make migrate script:
Part{{ item.part.full_name }} + {% include "hover_image.html" with image=item.part.image hover=True %} + {{ item.part.full_name }} +

Available Stock

{{ part.net_stock }} {{ part.units }}

{{ part.available_stock }} {{ part.units }}

In Stock