2
0
mirror of https://github.com/inventree/InvenTree.git synced 2025-05-01 13:06:45 +00:00

Merge pull request #432 from SchrodingersGat/tweaks

Tweaks
This commit is contained in:
Oliver 2019-08-02 21:55:02 +10:00 committed by GitHub
commit 73eada17bc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 85 additions and 24 deletions

View File

@ -10,7 +10,8 @@ addons:
-sqlite3 -sqlite3
before_install: before_install:
- make install - make requirements
- make secret
- make migrate - make migrate
script: script:

View File

@ -634,7 +634,8 @@ class Part(models.Model):
For hash is calculated from the following fields of each BOM item: 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) - Part.full_name (if the part name changes, the BOM checksum is invalidated)
- quantity - Quantity
- Reference field
- Note field - Note field
returns a string representation of a hash object which can be compared with a stored value 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.sub_part.full_name).encode())
hash.update(str(item.quantity).encode()) hash.update(str(item.quantity).encode())
hash.update(str(item.note).encode()) hash.update(str(item.note).encode())
hash.update(str(item.reference).encode())
return str(hash.digest()) return str(hash.digest())

View File

@ -102,7 +102,7 @@
<td> <td>
<h4>Available Stock</h4> <h4>Available Stock</h4>
</td> </td>
<td><h4>{{ part.net_stock }} {{ part.units }}</h4></td> <td><h4>{{ part.available_stock }} {{ part.units }}</h4></td>
</tr> </tr>
<tr> <tr>
<td>In Stock</td> <td>In Stock</td>

View File

@ -118,11 +118,12 @@ class EditStockItemForm(HelperForm):
fields = [ fields = [
'supplier_part', 'supplier_part',
'serial',
'batch', 'batch',
'delete_on_deplete',
'status', 'status',
'notes', 'notes',
'URL', 'URL',
'delete_on_deplete',
] ]

View File

@ -122,6 +122,11 @@ class StockItem(models.Model):
system=True system=True
) )
@property
def serialized(self):
""" Return True if this StockItem is serialized """
return self.serial is not None and self.quantity == 1
@classmethod @classmethod
def check_serial_number(cls, part, serial_number): def check_serial_number(cls, part, serial_number):
""" Check if a new stock item can be created with the provided part_id """ Check if a new stock item can be created with the provided part_id
@ -190,20 +195,21 @@ class StockItem(models.Model):
}) })
if self.part is not None: if self.part is not None:
# A trackable part must have a serial number # A part with a serial number MUST have the quantity set to 1
if self.part.trackable: if self.serial is not None:
if not self.serial: if self.quantity > 1:
raise ValidationError({'serial': _('Serial number must be set for trackable items')}) 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: if self.delete_on_deplete:
raise ValidationError({'delete_on_deplete': _("Must be set to False for trackable items")}) raise ValidationError({'delete_on_deplete': _("Must be set to False for item with a serial number")})
# 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")
})
# A template part cannot be instantiated as a StockItem # A template part cannot be instantiated as a StockItem
if self.part.is_template: if self.part.is_template:
@ -316,7 +322,15 @@ class StockItem(models.Model):
infinite = models.BooleanField(default=False) infinite = models.BooleanField(default=False)
def can_delete(self): 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 self.part.trackable and self.serial is not None:
return False
return True return True
@property @property
@ -349,6 +363,14 @@ class StockItem(models.Model):
track.save() track.save()
@transaction.atomic
def serializeStock(self, serials, user):
""" Split this stock item into unique serial numbers.
"""
# TODO
pass
@transaction.atomic @transaction.atomic
def splitStock(self, quantity, user): def splitStock(self, quantity, user):
""" Split this stock item into two items, in the same location. """ Split this stock item into two items, in the same location.
@ -363,6 +385,10 @@ class StockItem(models.Model):
The new item will have a different StockItem ID, while this will remain the same. 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 # Doesn't make sense for a zero quantity
if quantity <= 0: if quantity <= 0:
return return
@ -377,6 +403,8 @@ class StockItem(models.Model):
quantity=quantity, quantity=quantity,
supplier_part=self.supplier_part, supplier_part=self.supplier_part,
location=self.location, location=self.location,
notes=self.notes,
URL=self.URL,
batch=self.batch, batch=self.batch,
delete_on_deplete=self.delete_on_deplete delete_on_deplete=self.delete_on_deplete
) )
@ -412,7 +440,7 @@ class StockItem(models.Model):
if location is None: if location is None:
# TODO - Raise appropriate error (cannot move to blank location) # TODO - Raise appropriate error (cannot move to blank location)
return False 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) # TODO - Raise appropriate error (cannot move to same location)
return False return False
@ -450,12 +478,16 @@ class StockItem(models.Model):
- False if the StockItem was deleted - False if the StockItem was deleted
""" """
# Do not adjust quantity of a serialized part
if self.serialized:
return
if quantity < 0: if quantity < 0:
quantity = 0 quantity = 0
self.quantity = quantity 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() self.delete()
return False return False
else: else:
@ -493,6 +525,10 @@ class StockItem(models.Model):
or by manually adding the items to the stock location 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) quantity = int(quantity)
# Ignore amounts that do not make sense # Ignore amounts that do not make sense
@ -513,6 +549,10 @@ class StockItem(models.Model):
""" Remove items from stock """ Remove items from stock
""" """
# Cannot remove items from a serialized part
if self.serialized:
return False
quantity = int(quantity) quantity = int(quantity)
if quantity <= 0 or self.infinite: if quantity <= 0 or self.infinite:

View File

@ -5,11 +5,16 @@
<div class='row'> <div class='row'>
<div class='col-sm-6'> <div class='col-sm-6'>
<h3>Stock Item Details</h3> <h3>Stock Item Details</h3>
{% if item.serialized %}
<p><i>{{ item.part.full_name}} # {{ item.serial }}</i></p>
{% else %}
<p><i>{{ item.quantity }} &times {{ item.part.full_name }}</i></p> <p><i>{{ item.quantity }} &times {{ item.part.full_name }}</i></p>
{% endif %}
<p> <p>
<div class='btn-group'> <div class='btn-group'>
{% include "qr_button.html" %} {% include "qr_button.html" %}
{% if item.in_stock %} {% if item.in_stock %}
{% if not item.serialized %}
<button type='button' class='btn btn-default btn-glyph' id='stock-add' title='Add to stock'> <button type='button' class='btn btn-default btn-glyph' id='stock-add' title='Add to stock'>
<span class='glyphicon glyphicon-plus-sign' style='color: #1a1;'/> <span class='glyphicon glyphicon-plus-sign' style='color: #1a1;'/>
</button> </button>
@ -19,6 +24,7 @@
<button type='button' class='btn btn-default btn-glyph' id='stock-count' title='Count stock'> <button type='button' class='btn btn-default btn-glyph' id='stock-count' title='Count stock'>
<span class='glyphicon glyphicon-ok-circle'/> <span class='glyphicon glyphicon-ok-circle'/>
</button> </button>
{% endif %}
<button type='button' class='btn btn-default btn-glyph' id='stock-move' title='Transfer stock'> <button type='button' class='btn btn-default btn-glyph' id='stock-move' title='Transfer stock'>
<span class='glyphicon glyphicon-transfer' style='color: #11a;'/> <span class='glyphicon glyphicon-transfer' style='color: #11a;'/>
</button> </button>
@ -34,6 +40,11 @@
</button> </button>
</div> </div>
</p> </p>
{% if item.serialized %}
<div class='alert alert-block alert-info'>
This stock item is serialized - it has a unique serial number and the quantity cannot be adjusted.
</div>
{% endif %}
</div> </div>
<div class='row'> <div class='row'>
@ -41,7 +52,10 @@
<table class="table table-striped"> <table class="table table-striped">
<tr> <tr>
<td>Part</td> <td>Part</td>
<td><a href="{% url 'part-stock' item.part.id %}">{{ item.part.full_name }}</td> <td>
{% include "hover_image.html" with image=item.part.image hover=True %}
<a href="{% url 'part-stock' item.part.id %}">{{ item.part.full_name }}
</td>
</tr> </tr>
{% if item.belongs_to %} {% if item.belongs_to %}
<tr> <tr>
@ -54,9 +68,9 @@
<td><a href="{% url 'stock-location-detail' item.location.id %}">{{ item.location.name }}</a></td> <td><a href="{% url 'stock-location-detail' item.location.id %}">{{ item.location.name }}</a></td>
</tr> </tr>
{% endif %} {% endif %}
{% if item.serial %} {% if item.serialized %}
<tr> <tr>
<td>Serial</td> <td>Serial Number</td>
<td>{{ item.serial }}</td> <td>{{ item.serial }}</td>
</tr> </tr>
{% else %} {% else %}

View File

@ -383,8 +383,8 @@ class StockAdjust(AjaxView, FormMixin):
if item.new_quantity <= 0: if item.new_quantity <= 0:
continue continue
# Do not move to the same location # Do not move to the same location (unless the quantity is different)
if destination == item.location: if destination == item.location and item.new_quantity == item.quantity:
continue continue
item.move(destination, note, self.request.user, quantity=int(item.new_quantity)) item.move(destination, note, self.request.user, quantity=int(item.new_quantity))
@ -429,6 +429,9 @@ class StockItemEdit(AjaxUpdateView):
query = query.filter(part=item.part.id) query = query.filter(part=item.part.id)
form.fields['supplier_part'].queryset = query form.fields['supplier_part'].queryset = query
if not item.part.trackable:
form.fields.pop('serial')
return form return form