Compare commits

...

5 Commits

Author SHA1 Message Date
github-actions[bot] 9793bba77a
Fix stocktake bug for counting serialized items (#12280) (#12282)
* Fix stocktake bug for counting serialized items

* Add unit test

(cherry picked from commit 414aac0224)

Co-authored-by: Oliver <oliver.henry.walters@gmail.com>
2026-06-30 17:09:23 +10:00
github-actions[bot] ffc60cb189
[UI] Stock column fix (#12268) (#12269)
* [UI] Fix StockColumn component

* stock table rendering tweaks

(cherry picked from commit 1da71ca3b9)

Co-authored-by: Oliver <oliver.henry.walters@gmail.com>
2026-06-27 12:25:10 +10:00
github-actions[bot] 2ac8f608d8
Bug fix for exception handler (#12257) (#12261)
(cherry picked from commit e847d96a88)

Co-authored-by: Oliver <oliver.henry.walters@gmail.com>
2026-06-26 09:55:52 +10:00
Oliver 9ea33df847
Bump version to 1.4.1 (#12242) 2026-06-24 15:47:31 +10:00
Oliver 0a9a8b1c54
Add release entry for 1.4.0 (#12237)
* Add release entry for 1.4.0

* Mark version as 1.4.0
2026-06-24 13:53:55 +10:00
8 changed files with 50 additions and 22 deletions

View File

@ -5,7 +5,7 @@ All major notable changes to this project will be documented in this file (start
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## Unreleased - YYYY-MM-DD
## 1.4.0 - 2026-06-24
### Breaking Changes

View File

@ -15,7 +15,7 @@ from datetime import timedelta as td
from .api_version import INVENTREE_API_TEXT, INVENTREE_API_VERSION
# InvenTree software version
INVENTREE_SW_VERSION = '1.4.0 dev'
INVENTREE_SW_VERSION = '1.4.1'
# Minimum supported Python version
MIN_PYTHON_VERSION = (3, 11)

View File

@ -6,6 +6,7 @@ from datetime import datetime
from typing import Optional
from django.contrib.auth.models import User
from django.core.exceptions import FieldDoesNotExist
from django.core.exceptions import ValidationError as DjangoValidationError
from django.core.validators import FileExtensionValidator
from django.db import models
@ -200,7 +201,7 @@ class DataImportSession(models.Model):
related_field = model_class._meta.get_field(field_name)
model = related_field.remote_field.model
return model
except (AttributeError, models.FieldDoesNotExist):
except (AttributeError, FieldDoesNotExist):
return None
def extract_columns(self) -> None:

View File

@ -2651,8 +2651,8 @@ class StockItem(
)
tracking_info['old_status_logical'] = old_status_logical
if self.updateQuantity(count):
tracking_info['quantity'] = float(count)
if self.serialized or self.updateQuantity(count):
tracking_info['quantity'] = 1 if self.serialized else float(count)
self.stocktake_date = InvenTree.helpers.current_date()
self.stocktake_user = user
@ -2672,12 +2672,12 @@ class StockItem(
deltas=tracking_info,
)
trigger_event(
StockEvents.ITEM_COUNTED,
'stockitem.counted',
id=self.id,
quantity=float(self.quantity),
)
trigger_event(
StockEvents.ITEM_COUNTED,
'stockitem.counted',
id=self.id,
quantity=1 if self.serialized else float(self.quantity),
)
return True

View File

@ -2289,6 +2289,32 @@ class StocktakeTest(StockAPITestCase):
self.assertEqual(response.data['items'][0]['pk'], 1234)
self.assertEqual(response.data['items'][0]['quantity'], target[endpoint])
def test_count_serialized(self):
"""Test that counting a serialized stock item correctly updates stocktake_date."""
import datetime
# Fixture item pk=501 is a serialized item (serial=1)
item = StockItem.objects.get(pk=501)
self.assertTrue(item.serialized)
self.assertEqual(item.quantity, 1)
# Clear any existing stocktake date so we can verify it gets set
item.stocktake_date = None
item.save()
url = reverse('api-stock-count')
# Count the serialized item — quantity must be 1
data = {'items': [{'pk': item.pk, 'quantity': 1}]}
response = self.post(url, data, expected_code=201)
self.assertEqual(response.data['items'][0]['pk'], item.pk)
self.assertEqual(response.data['items'][0]['quantity'], '1.00000')
# stocktake_date must have been set to today
item.refresh_from_db()
self.assertEqual(item.stocktake_date, datetime.date.today())
def test_transfer(self):
"""Test stock transfers."""
stock_item = StockItem.objects.get(pk=1234)

View File

@ -156,7 +156,7 @@ export function StockColumn(props: StockColumnProps): TableColumn {
text = `# ${stock_item.serial}`;
}
if (record.is_building) {
if (stock_item.is_building) {
color = 'blue';
extra.push(
<Text
@ -164,35 +164,35 @@ export function StockColumn(props: StockColumnProps): TableColumn {
size='sm'
>{t`This stock item is in production`}</Text>
);
} else if (record.sales_order) {
} else if (stock_item.sales_order) {
extra.push(
<Text
key='sales-order'
size='sm'
>{t`This stock item has been assigned to a sales order`}</Text>
);
} else if (record.customer) {
} else if (stock_item.customer) {
extra.push(
<Text
key='customer'
size='sm'
>{t`This stock item has been assigned to a customer`}</Text>
);
} else if (record.belongs_to) {
} else if (stock_item.belongs_to) {
extra.push(
<Text
key='belongs-to'
size='sm'
>{t`This stock item is installed in another stock item`}</Text>
);
} else if (record.consumed_by) {
} else if (stock_item.consumed_by) {
extra.push(
<Text
key='consumed-by'
size='sm'
>{t`This stock item has been consumed by a build order`}</Text>
);
} else if (!record.in_stock) {
} else if (!stock_item.in_stock) {
extra.push(
<Text
key='unavailable'
@ -201,17 +201,17 @@ export function StockColumn(props: StockColumnProps): TableColumn {
);
}
if (record.expired) {
if (stock_item.expired) {
extra.push(
<Text key='expired' size='sm'>{t`This stock item has expired`}</Text>
);
} else if (record.stale) {
} else if (stock_item.stale) {
extra.push(
<Text key='stale' size='sm'>{t`This stock item is stale`}</Text>
);
}
if (record.in_stock) {
if (stock_item.in_stock) {
if (allocated > 0) {
if (allocated > quantity) {
color = 'red';
@ -267,7 +267,7 @@ export function StockColumn(props: StockColumnProps): TableColumn {
}
}
if (!record.in_stock) {
if (!stock_item.in_stock) {
color = 'red';
}

View File

@ -68,7 +68,7 @@ export function TableHoverCard({
zIndex={zIndex}
>
<HoverCard.Target>
<Group gap='xs' justify='space-between'>
<Group gap='xs' justify='space-between' wrap='nowrap'>
{value}
<InvenTreeIcon
icon={icon ?? 'info'}

View File

@ -86,6 +86,7 @@ function stockItemTableColumns({
'allocated',
'consumed',
'installed',
'in_stock',
'sent_to_customer'
]
}),