mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
Transfer out of stock items (#7194)
* Use new setting to determine if item can be moved * Add new setting to front-end * Invert double inversion * Prevent empty stock tracking entry * Updated unit tests * Fix rendering of FailedTasksTable
This commit is contained in:
parent
b88457a39e
commit
29fa5cfafa
@ -1781,6 +1781,14 @@ class InvenTreeSetting(BaseInvenTreeSetting):
|
||||
'default': True,
|
||||
'validator': bool,
|
||||
},
|
||||
'STOCK_ALLOW_OUT_OF_STOCK_TRANSFER': {
|
||||
'name': _('Allow Out of Stock Transfer'),
|
||||
'description': _(
|
||||
'Allow stock items which are not in stock to be transferred between stock locations'
|
||||
),
|
||||
'default': False,
|
||||
'validator': bool,
|
||||
},
|
||||
'BUILDORDER_REFERENCE_PATTERN': {
|
||||
'name': _('Build Order Reference Pattern'),
|
||||
'description': _(
|
||||
|
@ -1419,6 +1419,14 @@ class StockItem(
|
||||
if deltas is None:
|
||||
deltas = {}
|
||||
|
||||
# Prevent empty entry
|
||||
if (
|
||||
entry_type == StockHistoryCode.STOCK_UPDATE
|
||||
and len(deltas) == 0
|
||||
and not notes
|
||||
):
|
||||
return
|
||||
|
||||
# Has a location been specified?
|
||||
location = kwargs.get('location', None)
|
||||
|
||||
@ -1866,7 +1874,11 @@ class StockItem(
|
||||
except InvalidOperation:
|
||||
return False
|
||||
|
||||
if not self.in_stock:
|
||||
allow_out_of_stock_transfer = common.models.InvenTreeSetting.get_setting(
|
||||
'STOCK_ALLOW_OUT_OF_STOCK_TRANSFER', backup_value=False, cache=False
|
||||
)
|
||||
|
||||
if not allow_out_of_stock_transfer and not self.in_stock:
|
||||
raise ValidationError(_('StockItem cannot be moved as it is not in stock'))
|
||||
|
||||
if quantity <= 0:
|
||||
|
@ -1470,6 +1470,14 @@ class StocktakeTest(StockAPITestCase):
|
||||
|
||||
def test_transfer(self):
|
||||
"""Test stock transfers."""
|
||||
stock_item = StockItem.objects.get(pk=1234)
|
||||
|
||||
# Mark this stock item as "quarantined" (cannot be moved)
|
||||
stock_item.status = StockStatus.QUARANTINED.value
|
||||
stock_item.save()
|
||||
|
||||
InvenTreeSetting.set_setting('STOCK_ALLOW_OUT_OF_STOCK_TRANSFER', False)
|
||||
|
||||
data = {
|
||||
'items': [{'pk': 1234, 'quantity': 10}],
|
||||
'location': 1,
|
||||
@ -1478,6 +1486,14 @@ class StocktakeTest(StockAPITestCase):
|
||||
|
||||
url = reverse('api-stock-transfer')
|
||||
|
||||
# First attempt should *fail* - stock item is quarantined
|
||||
response = self.post(url, data, expected_code=400)
|
||||
|
||||
self.assertIn('cannot be moved as it is not in stock', str(response.data))
|
||||
|
||||
# Now, allow transfer of "out of stock" items
|
||||
InvenTreeSetting.set_setting('STOCK_ALLOW_OUT_OF_STOCK_TRANSFER', True)
|
||||
|
||||
# This should succeed
|
||||
response = self.post(url, data, expected_code=201)
|
||||
|
||||
|
@ -23,6 +23,7 @@
|
||||
{% include "InvenTree/settings/setting.html" with key="STOCK_LOCATION_DEFAULT_ICON" icon="fa-icons" %}
|
||||
{% include "InvenTree/settings/setting.html" with key="STOCK_SHOW_INSTALLED_ITEMS" icon="fa-sitemap" %}
|
||||
{% include "InvenTree/settings/setting.html" with key="STOCK_ENFORCE_BOM_INSTALLATION" icon="fa-check-circle" %}
|
||||
{% include "InvenTree/settings/setting.html" with key="STOCK_ALLOW_OUT_OF_STOCK_TRANSFER" icon="fa-dolly" %}
|
||||
{% include "InvenTree/settings/setting.html" with key="TEST_STATION_DATA" icon="fa-network-wired" %}
|
||||
|
||||
</tbody>
|
||||
|
@ -20,7 +20,7 @@ const FailedTasksTable = Loadable(
|
||||
export default function TaskManagementPanel() {
|
||||
return (
|
||||
<Accordion defaultValue="pending">
|
||||
<Accordion.Item value="pending">
|
||||
<Accordion.Item value="pending" key="pending-tasks">
|
||||
<Accordion.Control>
|
||||
<StylishText size="lg">{t`Pending Tasks`}</StylishText>
|
||||
</Accordion.Control>
|
||||
@ -28,7 +28,7 @@ export default function TaskManagementPanel() {
|
||||
<PendingTasksTable />
|
||||
</Accordion.Panel>
|
||||
</Accordion.Item>
|
||||
<Accordion.Item value="scheduled">
|
||||
<Accordion.Item value="scheduled" key="scheduled-tasks">
|
||||
<Accordion.Control>
|
||||
<StylishText size="lg">{t`Scheduled Tasks`}</StylishText>
|
||||
</Accordion.Control>
|
||||
@ -36,7 +36,7 @@ export default function TaskManagementPanel() {
|
||||
<ScheduledTasksTable />
|
||||
</Accordion.Panel>
|
||||
</Accordion.Item>
|
||||
<Accordion.Item value="failed">
|
||||
<Accordion.Item value="failed" key="failed-tasks">
|
||||
<Accordion.Control>
|
||||
<StylishText size="lg">{t`Failed Tasks`}</StylishText>
|
||||
</Accordion.Control>
|
||||
|
@ -212,6 +212,7 @@ export default function SystemSettings() {
|
||||
'STOCK_LOCATION_DEFAULT_ICON',
|
||||
'STOCK_SHOW_INSTALLED_ITEMS',
|
||||
'STOCK_ENFORCE_BOM_INSTALLATION',
|
||||
'STOCK_ALLOW_OUT_OF_STOCK_TRANSFER',
|
||||
'TEST_STATION_DATA'
|
||||
]}
|
||||
/>
|
||||
|
@ -57,8 +57,12 @@ export default function FailedTasksTable() {
|
||||
title={<StylishText>{t`Error Details`}</StylishText>}
|
||||
onClose={close}
|
||||
>
|
||||
{error.split('\n').map((line: string) => {
|
||||
return <Text size="sm">{line}</Text>;
|
||||
{error.split('\n').map((line: string, index: number) => {
|
||||
return (
|
||||
<Text key={`error-${index}`} size="sm">
|
||||
{line}
|
||||
</Text>
|
||||
);
|
||||
})}
|
||||
</Drawer>
|
||||
<InvenTreeTable
|
||||
|
Loading…
Reference in New Issue
Block a user