mirror of
https://github.com/inventree/InvenTree.git
synced 2025-07-01 03:00:54 +00:00
Merge remote-tracking branch 'inventree/master' into simple-qr-codes
This commit is contained in:
@ -121,12 +121,17 @@ class StockAdjust(APIView):
|
||||
- StockAdd: add stock items
|
||||
- StockRemove: remove stock items
|
||||
- StockTransfer: transfer stock items
|
||||
|
||||
# TODO - This needs serious refactoring!!!
|
||||
|
||||
"""
|
||||
|
||||
permission_classes = [
|
||||
permissions.IsAuthenticated,
|
||||
]
|
||||
|
||||
allow_missing_quantity = False
|
||||
|
||||
def get_items(self, request):
|
||||
"""
|
||||
Return a list of items posted to the endpoint.
|
||||
@ -157,10 +162,13 @@ class StockAdjust(APIView):
|
||||
except (ValueError, StockItem.DoesNotExist):
|
||||
raise ValidationError({'pk': 'Each entry must contain a valid pk field'})
|
||||
|
||||
if self.allow_missing_quantity and 'quantity' not in entry:
|
||||
entry['quantity'] = item.quantity
|
||||
|
||||
try:
|
||||
quantity = Decimal(str(entry.get('quantity', None)))
|
||||
except (ValueError, TypeError, InvalidOperation):
|
||||
raise ValidationError({'quantity': 'Each entry must contain a valid quantity field'})
|
||||
raise ValidationError({'quantity': "Each entry must contain a valid quantity value"})
|
||||
|
||||
if quantity < 0:
|
||||
raise ValidationError({'quantity': 'Quantity field must not be less than zero'})
|
||||
@ -234,6 +242,8 @@ class StockTransfer(StockAdjust):
|
||||
API endpoint for performing stock movements
|
||||
"""
|
||||
|
||||
allow_missing_quantity = True
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
|
||||
self.get_items(request)
|
||||
|
@ -195,11 +195,14 @@ class StockItem(MPTTModel):
|
||||
super(StockItem, self).save(*args, **kwargs)
|
||||
|
||||
if add_note:
|
||||
|
||||
note = f"{_('Created new stock item for')} {str(self.part)}"
|
||||
|
||||
# This StockItem is being saved for the first time
|
||||
self.addTransactionNote(
|
||||
_('Created stock item'),
|
||||
user,
|
||||
notes="Created new stock item for part '{p}'".format(p=str(self.part)),
|
||||
note,
|
||||
system=True
|
||||
)
|
||||
|
||||
@ -611,9 +614,9 @@ class StockItem(MPTTModel):
|
||||
"""
|
||||
|
||||
self.addTransactionNote(
|
||||
_("Returned from customer") + " " + self.customer.name,
|
||||
_("Returned from customer") + f" {self.customer.name}",
|
||||
user,
|
||||
notes=_("Returned to location") + " " + location.name,
|
||||
notes=_("Returned to location") + f" {location.name}",
|
||||
system=True
|
||||
)
|
||||
|
||||
@ -1000,12 +1003,17 @@ class StockItem(MPTTModel):
|
||||
|
||||
# Add a new tracking item for the new stock item
|
||||
new_stock.addTransactionNote(
|
||||
"Split from existing stock",
|
||||
_("Split from existing stock"),
|
||||
user,
|
||||
"Split {n} from existing stock item".format(n=quantity))
|
||||
f"{_('Split')} {helpers.normalize(quantity)} {_('items')}"
|
||||
)
|
||||
|
||||
# Remove the specified quantity from THIS stock item
|
||||
self.take_stock(quantity, user, 'Split {n} items into new stock item'.format(n=quantity))
|
||||
self.take_stock(
|
||||
quantity,
|
||||
user,
|
||||
f"{_('Split')} {quantity} {_('items into new stock item')}"
|
||||
)
|
||||
|
||||
# Return a copy of the "new" stock item
|
||||
return new_stock
|
||||
@ -1054,10 +1062,10 @@ class StockItem(MPTTModel):
|
||||
|
||||
return True
|
||||
|
||||
msg = "Moved to {loc}".format(loc=str(location))
|
||||
|
||||
msg = f"{_('Moved to')} {str(location)}"
|
||||
|
||||
if self.location:
|
||||
msg += " (from {loc})".format(loc=str(self.location))
|
||||
msg += f" ({_('from')} {str(self.location)})"
|
||||
|
||||
self.location = location
|
||||
|
||||
@ -1125,10 +1133,16 @@ class StockItem(MPTTModel):
|
||||
|
||||
if self.updateQuantity(count):
|
||||
|
||||
self.addTransactionNote('Stocktake - counted {n} items'.format(n=helpers.normalize(count)),
|
||||
user,
|
||||
notes=notes,
|
||||
system=True)
|
||||
n = helpers.normalize(count)
|
||||
|
||||
text = f"{_('Counted')} {n} {_('items')}"
|
||||
|
||||
self.addTransactionNote(
|
||||
text,
|
||||
user,
|
||||
notes=notes,
|
||||
system=True
|
||||
)
|
||||
|
||||
return True
|
||||
|
||||
@ -1154,10 +1168,15 @@ class StockItem(MPTTModel):
|
||||
|
||||
if self.updateQuantity(self.quantity + quantity):
|
||||
|
||||
self.addTransactionNote('Added {n} items to stock'.format(n=helpers.normalize(quantity)),
|
||||
user,
|
||||
notes=notes,
|
||||
system=True)
|
||||
n = helpers.normalize(quantity)
|
||||
text = f"{_('Added')} {n} {_('items')}"
|
||||
|
||||
self.addTransactionNote(
|
||||
text,
|
||||
user,
|
||||
notes=notes,
|
||||
system=True
|
||||
)
|
||||
|
||||
return True
|
||||
|
||||
@ -1180,7 +1199,10 @@ class StockItem(MPTTModel):
|
||||
|
||||
if self.updateQuantity(self.quantity - quantity):
|
||||
|
||||
self.addTransactionNote('Removed {n} items from stock'.format(n=helpers.normalize(quantity)),
|
||||
q = helpers.normalize(quantity)
|
||||
text = f"{_('Removed')} {q} {_('items')}"
|
||||
|
||||
self.addTransactionNote(text,
|
||||
user,
|
||||
notes=notes,
|
||||
system=True)
|
||||
|
@ -120,6 +120,8 @@ InvenTree | {% trans "Stock Item" %} - {{ item }}
|
||||
</div>
|
||||
|
||||
<div class='btn-group action-buttons' role='group'>
|
||||
{% settings_value 'BARCODE_ENABLE' as barcodes %}
|
||||
{% if barcodes %}
|
||||
<!-- Barcode actions menu -->
|
||||
<div class='btn-group'>
|
||||
<button id='barcode-options' title='{% trans "Barcode actions" %}' class='btn btn-default dropdown-toggle' type='button' data-toggle='dropdown'><span class='fas fa-qrcode'></span> <span class='caret'></span></button>
|
||||
@ -127,24 +129,26 @@ InvenTree | {% trans "Stock Item" %} - {{ item }}
|
||||
<li><a href='#' id='show-qr-code'><span class='fas fa-qrcode'></span> {% trans "Show QR Code" %}</a></li>
|
||||
{% if roles.stock.change %}
|
||||
{% if item.uid %}
|
||||
<li><a href='#' id='unlink-barcode'><span class='fas fa-unlink'></span> {% trans "Unlink Barcode" %}</a></li>
|
||||
{% else %}
|
||||
<li><a href='#' id='link-barcode'><span class='fas fa-link'></span> {% trans "Link Barcode" %}</a></li>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</ul>
|
||||
</div>
|
||||
<!-- Document / label menu -->
|
||||
{% if item.has_labels or item.has_test_reports %}
|
||||
<div class='btn-group'>
|
||||
<button id='document-options' title='{% trans "Printing actions" %}' class='btn btn-default dropdown-toggle' type='button' data-toggle='dropdown'><span class='fas fa-print'></span> <span class='caret'></span></button>
|
||||
<ul class='dropdown-menu' role='menu'>
|
||||
{% if item.has_labels %}
|
||||
<li><a href='#' id='print-label'><span class='fas fa-tag'></span> {% trans "Print Label" %}</a></li>
|
||||
{% endif %}
|
||||
{% if item.has_test_reports %}
|
||||
<li><a href='#' id='stock-test-report'><span class='fas fa-file-pdf'></span> {% trans "Test Report" %}</a></li>
|
||||
{% endif %}
|
||||
<li><a href='#' id='barcode-unlink'><span class='fas fa-unlink'></span> {% trans "Unlink Barcode" %}</a></li>
|
||||
{% else %}
|
||||
<li><a href='#' id='barcode-link'><span class='fas fa-link'></span> {% trans "Link Barcode" %}</a></li>
|
||||
{% endif %}
|
||||
<li><a href='#' id='barcode-scan-into-location'><span class='fas fa-sitemap'></span> {% trans "Scan to Location" %}</a></li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
<!-- Document / label menu -->
|
||||
{% if item.has_labels or item.has_test_reports %}
|
||||
<div class='btn-group'>
|
||||
<button id='document-options' title='{% trans "Printing actions" %}' class='btn btn-default dropdown-toggle' type='button' data-toggle='dropdown'><span class='fas fa-print'></span> <span class='caret'></span></button>
|
||||
<ul class='dropdown-menu' role='menu'>
|
||||
{% if item.has_labels %}
|
||||
<li><a href='#' id='print-label'><span class='fas fa-tag'></span> {% trans "Print Label" %}</a></li>
|
||||
{% endif %}
|
||||
{% if item.has_test_reports %}
|
||||
<li><a href='#' id='stock-test-report'><span class='fas fa-file-pdf'></span> {% trans "Test Report" %}</a></li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
@ -447,14 +451,18 @@ $("#show-qr-code").click(function() {
|
||||
});
|
||||
});
|
||||
|
||||
$("#link-barcode").click(function() {
|
||||
$("#barcode-link").click(function() {
|
||||
linkBarcodeDialog({{ item.id }});
|
||||
});
|
||||
|
||||
$("#unlink-barcode").click(function() {
|
||||
$("#barcode-unlink").click(function() {
|
||||
unlinkBarcode({{ item.id }});
|
||||
});
|
||||
|
||||
$("#barcode-scan-into-location").click(function() {
|
||||
scanItemsIntoLocation([{{ item.id }}]);
|
||||
});
|
||||
|
||||
{% if item.in_stock %}
|
||||
|
||||
$("#stock-assign-to-customer").click(function() {
|
||||
|
@ -37,6 +37,8 @@
|
||||
</button>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% settings_value 'BARCODE_ENABLE' as barcodes %}
|
||||
{% if barcodes %}
|
||||
<!-- Barcode actions menu -->
|
||||
{% if location %}
|
||||
<div class='btn-group'>
|
||||
@ -47,29 +49,30 @@
|
||||
<li><a href='#' id='barcode-check-in'><span class='fas fa-arrow-right'></span> {% trans "Check-in Items" %}</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<!-- Check permissions and owner -->
|
||||
{% if owner_control.value == "False" or owner_control.value == "True" and user in owners or user.is_superuser %}
|
||||
{% if roles.stock.change %}
|
||||
<div class='btn-group'>
|
||||
<button id='stock-actions' title='{% trans "Stock actions" %}' class='btn btn-default dropdown-toggle' type='button' data-toggle='dropdown'><span class='fas fa-boxes'></span> <span class='caret'></span></button>
|
||||
<ul class='dropdown-menu' role='menu'>
|
||||
<li><a href='#' id='location-count'><span class='fas fa-clipboard-list'></span>
|
||||
{% trans "Count stock" %}</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if roles.stock_location.change %}
|
||||
<div class='btn-group'>
|
||||
<button id='location-actions' title='{% trans "Location actions" %}' class='btn btn-default dropdown-toggle' type='button' data-toggle="dropdown"><span class='fas fa-sitemap'></span> <span class='caret'></span></button>
|
||||
<ul class='dropdown-menu' role='menu'>
|
||||
<li><a href='#' id='location-edit'><span class='fas fa-edit icon-green'></span> {% trans "Edit location" %}</a></li>
|
||||
{% if roles.stock.delete %}
|
||||
<li><a href='#' id='location-delete'><span class='fas fa-trash-alt icon-red'></span> {% trans "Delete location" %}</a></li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
<!-- Check permissions and owner -->
|
||||
{% if owner_control.value == "False" or owner_control.value == "True" and user in owners or user.is_superuser %}
|
||||
{% if roles.stock.change %}
|
||||
<div class='btn-group'>
|
||||
<button id='stock-actions' title='{% trans "Stock actions" %}' class='btn btn-default dropdown-toggle' type='button' data-toggle='dropdown'><span class='fas fa-boxes'></span> <span class='caret'></span></button>
|
||||
<ul class='dropdown-menu' role='menu'>
|
||||
<li><a href='#' id='location-count'><span class='fas fa-clipboard-list'></span>
|
||||
{% trans "Count stock" %}</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if roles.stock_location.change %}
|
||||
<div class='btn-group'>
|
||||
<button id='location-actions' title='{% trans "Location actions" %}' class='btn btn-default dropdown-toggle' type='button' data-toggle="dropdown"><span class='fas fa-sitemap'></span> <span class='caret'></span></button>
|
||||
<ul class='dropdown-menu' role='menu'>
|
||||
<li><a href='#' id='location-edit'><span class='fas fa-edit icon-green'></span> {% trans "Edit location" %}</a></li>
|
||||
{% if roles.stock.delete %}
|
||||
<li><a href='#' id='location-delete'><span class='fas fa-trash-alt icon-red'></span> {% trans "Delete location" %}</a></li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -284,7 +284,8 @@ class StockTest(TestCase):
|
||||
# Check that a tracking item was added
|
||||
track = StockItemTracking.objects.filter(item=it).latest('id')
|
||||
|
||||
self.assertIn('Stocktake', track.title)
|
||||
self.assertIn('Counted', track.title)
|
||||
self.assertIn('items', track.title)
|
||||
self.assertIn('Counted items', track.notes)
|
||||
|
||||
n = it.tracking_info.count()
|
||||
|
@ -1114,7 +1114,7 @@ class StockAdjust(AjaxView, FormMixin):
|
||||
return self.do_delete()
|
||||
|
||||
else:
|
||||
return 'No action performed'
|
||||
return _('No action performed')
|
||||
|
||||
def do_add(self):
|
||||
|
||||
@ -1129,7 +1129,7 @@ class StockAdjust(AjaxView, FormMixin):
|
||||
|
||||
count += 1
|
||||
|
||||
return _("Added stock to {n} items".format(n=count))
|
||||
return f"{_('Added stock to ')} {count} {_('items')}"
|
||||
|
||||
def do_take(self):
|
||||
|
||||
@ -1144,7 +1144,7 @@ class StockAdjust(AjaxView, FormMixin):
|
||||
|
||||
count += 1
|
||||
|
||||
return _("Removed stock from {n} items".format(n=count))
|
||||
return f"{_('Removed stock from ')} {count} {_('items')}"
|
||||
|
||||
def do_count(self):
|
||||
|
||||
|
Reference in New Issue
Block a user