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:
Oliver 2024-05-10 12:04:26 +10:00 committed by GitHub
parent b88457a39e
commit 29fa5cfafa
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 48 additions and 6 deletions

View File

@ -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': _(

View File

@ -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:

View File

@ -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)

View File

@ -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>

View File

@ -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>

View File

@ -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'
]}
/>

View File

@ -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