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
17eb8237da
@ -79,7 +79,7 @@ function updateStock(items, options={}) {
|
|||||||
html += "max='" + vMax + "' ";
|
html += "max='" + vMax + "' ";
|
||||||
}
|
}
|
||||||
|
|
||||||
html += "type='number' id='q-" + item.pk + "'/></td>";
|
html += "type='number' id='q-update-" + item.pk + "'/></td>";
|
||||||
|
|
||||||
html += '</tr>';
|
html += '</tr>';
|
||||||
}
|
}
|
||||||
@ -128,7 +128,7 @@ function updateStock(items, options={}) {
|
|||||||
for (idx = 0; idx < items.length; idx++) {
|
for (idx = 0; idx < items.length; idx++) {
|
||||||
var item = items[idx];
|
var item = items[idx];
|
||||||
|
|
||||||
var q = $(modal).find("#q-" + item.pk).val();
|
var q = $(modal).find("#q-update-" + item.pk).val();
|
||||||
|
|
||||||
stocktake.push({
|
stocktake.push({
|
||||||
pk: item.pk,
|
pk: item.pk,
|
||||||
@ -229,7 +229,7 @@ function moveStockItems(items, options) {
|
|||||||
inventreePut("/api/stock/move/",
|
inventreePut("/api/stock/move/",
|
||||||
{
|
{
|
||||||
location: location,
|
location: location,
|
||||||
'parts[]': parts,
|
'stock': parts,
|
||||||
'notes': notes,
|
'notes': notes,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -247,7 +247,6 @@ function moveStockItems(items, options) {
|
|||||||
{
|
{
|
||||||
success: function(response) {
|
success: function(response) {
|
||||||
|
|
||||||
|
|
||||||
// Extact part row info
|
// Extact part row info
|
||||||
var parts = [];
|
var parts = [];
|
||||||
|
|
||||||
@ -267,21 +266,42 @@ function moveStockItems(items, options) {
|
|||||||
|
|
||||||
html += "<p class='warning-msg' id='note-warning'><i>Note field must be filled</i></p>";
|
html += "<p class='warning-msg' id='note-warning'><i>Note field must be filled</i></p>";
|
||||||
|
|
||||||
html += "<hr>The following stock items will be moved:<br><ul class='list-group'>\n";
|
html += "<hr>The following stock items will be moved:<hr>";
|
||||||
|
|
||||||
|
html += `
|
||||||
|
<table class='table table-striped table-condensed'>
|
||||||
|
<tr>
|
||||||
|
<th>Part</th>
|
||||||
|
<th>Location</th>
|
||||||
|
<th>Available</th>
|
||||||
|
<th>Moving</th>
|
||||||
|
</tr>
|
||||||
|
`;
|
||||||
|
|
||||||
for (i = 0; i < items.length; i++) {
|
for (i = 0; i < items.length; i++) {
|
||||||
parts.push(items[i].pk);
|
|
||||||
|
|
||||||
html += "<li class='list-group-item'>" + items[i].quantity + " × " + items[i].part.name;
|
parts.push({
|
||||||
|
pk: items[i].pk,
|
||||||
|
quantity: items[i].quantity,
|
||||||
|
});
|
||||||
|
|
||||||
if (items[i].location) {
|
var item = items[i];
|
||||||
html += " (" + items[i].location.name + ")";
|
|
||||||
}
|
|
||||||
|
|
||||||
html += "</li>\n";
|
html += "<tr>";
|
||||||
|
|
||||||
|
html += "<td>" + item.part.name + "</td>";
|
||||||
|
html += "<td>" + item.location.pathstring + "</td>";
|
||||||
|
html += "<td>" + item.quantity + "</td>";
|
||||||
|
|
||||||
|
html += "<td>";
|
||||||
|
html += "<input class='form-control' min='0' max='" + item.quantity + "'";
|
||||||
|
html += " value='" + item.quantity + "'";
|
||||||
|
html += "type='number' id='q-move-" + item.pk + "'/></td>";
|
||||||
|
|
||||||
|
html += "</tr>";
|
||||||
}
|
}
|
||||||
|
|
||||||
html += "</ul>\n";
|
html += "</table>";
|
||||||
|
|
||||||
openModal({
|
openModal({
|
||||||
modal: modal,
|
modal: modal,
|
||||||
@ -307,6 +327,15 @@ function moveStockItems(items, options) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update the quantity for each item
|
||||||
|
for (var ii = 0; ii < parts.length; ii++) {
|
||||||
|
var pk = parts[ii].pk;
|
||||||
|
|
||||||
|
var q = $(modal).find('#q-move-' + pk).val();
|
||||||
|
|
||||||
|
parts[ii].quantity = q;
|
||||||
|
}
|
||||||
|
|
||||||
doMove(locId, parts, notes);
|
doMove(locId, parts, notes);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@ -151,46 +151,50 @@ class StockMove(APIView):
|
|||||||
|
|
||||||
data = request.data
|
data = request.data
|
||||||
|
|
||||||
if u'location' not in data:
|
if 'location' not in data:
|
||||||
raise ValidationError({'location': 'Destination must be specified'})
|
raise ValidationError({'location': 'Destination must be specified'})
|
||||||
|
|
||||||
loc_id = data.get(u'location')
|
try:
|
||||||
|
loc_id = int(data.get('location'))
|
||||||
|
except ValueError:
|
||||||
|
raise ValidationError({'location': 'Integer ID required'})
|
||||||
|
|
||||||
try:
|
try:
|
||||||
location = StockLocation.objects.get(pk=loc_id)
|
location = StockLocation.objects.get(pk=loc_id)
|
||||||
except StockLocation.DoesNotExist:
|
except StockLocation.DoesNotExist:
|
||||||
raise ValidationError({'location': 'Location does not exist'})
|
raise ValidationError({'location': 'Location does not exist'})
|
||||||
|
|
||||||
if u'parts[]' not in data:
|
if 'stock' not in data:
|
||||||
raise ValidationError({'parts[]': 'Parts list must be specified'})
|
raise ValidationError({'stock': 'Stock list must be specified'})
|
||||||
|
|
||||||
part_list = data.get(u'parts[]')
|
stock_list = data.get('stock')
|
||||||
|
|
||||||
parts = []
|
if type(stock_list) is not list:
|
||||||
|
raise ValidationError({'stock': 'Stock must be supplied as a list'})
|
||||||
|
|
||||||
errors = []
|
if 'notes' not in data:
|
||||||
|
raise ValidationError({'notes': 'Notes field must be supplied'})
|
||||||
|
|
||||||
if u'notes' not in data:
|
for item in stock_list:
|
||||||
errors.append({'notes': 'Notes field must be supplied'})
|
|
||||||
|
|
||||||
for pid in part_list:
|
|
||||||
try:
|
try:
|
||||||
part = StockItem.objects.get(pk=pid)
|
stock_id = int(item['pk'])
|
||||||
parts.append(part)
|
quantity = int(item['quantity'])
|
||||||
|
except ValueError:
|
||||||
|
# Ignore this one
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Ignore a zero quantity movement
|
||||||
|
if quantity <= 0:
|
||||||
|
continue
|
||||||
|
|
||||||
|
try:
|
||||||
|
stock = StockItem.objects.get(pk=stock_id)
|
||||||
except StockItem.DoesNotExist:
|
except StockItem.DoesNotExist:
|
||||||
errors.append({'part': 'Part {id} does not exist'.format(id=pid)})
|
continue
|
||||||
|
|
||||||
if len(errors) > 0:
|
stock.move(location, data.get('notes'), request.user, quantity=quantity)
|
||||||
raise ValidationError(errors)
|
|
||||||
|
|
||||||
n = 0
|
return Response({'success': 'Moved parts to {loc}'.format(
|
||||||
|
|
||||||
for part in parts:
|
|
||||||
if part.move(location, data.get('notes'), request.user):
|
|
||||||
n += 1
|
|
||||||
|
|
||||||
return Response({'success': 'Moved {n} parts to {loc}'.format(
|
|
||||||
n=n,
|
|
||||||
loc=str(location)
|
loc=str(location)
|
||||||
)})
|
)})
|
||||||
|
|
||||||
|
@ -281,7 +281,64 @@ class StockItem(models.Model):
|
|||||||
track.save()
|
track.save()
|
||||||
|
|
||||||
@transaction.atomic
|
@transaction.atomic
|
||||||
def move(self, location, notes, user):
|
def splitStock(self, quantity, user):
|
||||||
|
""" Split this stock item into two items, in the same location.
|
||||||
|
Stock tracking notes for this StockItem will be duplicated,
|
||||||
|
and added to the new StockItem.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
quantity: Number of stock items to remove from this entity, and pass to the next
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
The provided quantity will be subtracted from this item and given to the new one.
|
||||||
|
The new item will have a different StockItem ID, while this will remain the same.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Doesn't make sense for a zero quantity
|
||||||
|
if quantity <= 0:
|
||||||
|
return
|
||||||
|
|
||||||
|
# Also doesn't make sense to split the full amount
|
||||||
|
if quantity >= self.quantity:
|
||||||
|
return
|
||||||
|
|
||||||
|
# Create a new StockItem object, duplicating relevant fields
|
||||||
|
new_stock = StockItem.objects.create(
|
||||||
|
part=self.part,
|
||||||
|
quantity=quantity,
|
||||||
|
supplier_part=self.supplier_part,
|
||||||
|
location=self.location,
|
||||||
|
batch=self.batch,
|
||||||
|
delete_on_deplete=self.delete_on_deplete
|
||||||
|
)
|
||||||
|
|
||||||
|
new_stock.save()
|
||||||
|
|
||||||
|
# Add a new tracking item for the new stock item
|
||||||
|
new_stock.addTransactionNote(
|
||||||
|
"Split from existing stock",
|
||||||
|
user,
|
||||||
|
"Split {n} from existing stock item".format(n=quantity))
|
||||||
|
|
||||||
|
# Remove the specified quantity from THIS stock item
|
||||||
|
self.take_stock(quantity, user, 'Split {n} items into new stock item'.format(n=quantity))
|
||||||
|
|
||||||
|
@transaction.atomic
|
||||||
|
def move(self, location, notes, user, **kwargs):
|
||||||
|
""" Move part to a new location.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
location: Destination location (cannot be null)
|
||||||
|
notes: User notes
|
||||||
|
user: Who is performing the move
|
||||||
|
kwargs:
|
||||||
|
quantity: If provided, override the quantity (default = total stock quantity)
|
||||||
|
"""
|
||||||
|
|
||||||
|
quantity = int(kwargs.get('quantity', self.quantity))
|
||||||
|
|
||||||
|
if quantity <= 0:
|
||||||
|
return False
|
||||||
|
|
||||||
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)
|
||||||
@ -290,6 +347,13 @@ class StockItem(models.Model):
|
|||||||
# TODO - Raise appropriate error (cannot move to same location)
|
# TODO - Raise appropriate error (cannot move to same location)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
# Test for a partial movement
|
||||||
|
if quantity < self.quantity:
|
||||||
|
# We need to split the stock!
|
||||||
|
|
||||||
|
# Leave behind certain quantity
|
||||||
|
self.splitStock(self.quantity - quantity, user)
|
||||||
|
|
||||||
msg = "Moved to {loc}".format(loc=str(location))
|
msg = "Moved to {loc}".format(loc=str(location))
|
||||||
|
|
||||||
if self.location:
|
if self.location:
|
||||||
|
Loading…
Reference in New Issue
Block a user