Fix test result ordering by test timestamps (#12533)

* Fix test result ordering by test timestamps

* Refractor test result comparison helper

* Apply formatting fixes

* Fix frontend formatting

* Avoid mutating test result records

* Add migration for test result ordering

* Retry documentation build

---------

Co-authored-by: jayasree723 <confidentlehmann@tomorjerry.com>
Co-authored-by: Oliver <oliver.henry.walters@gmail.com>
This commit is contained in:
Jayasree
2026-08-09 09:56:48 +10:00
committed by GitHub
co-authored by jayasree723 Oliver
parent afcc89ecfb
commit 214f472ec9
5 changed files with 84 additions and 22 deletions
@@ -0,0 +1,25 @@
# Generated by Django 5.2.16
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("stock", "0126_serial_number_concurrency_guard"),
]
operations = [
migrations.AlterModelOptions(
name="stockitemtestresult",
options={
"ordering": (
"-finished_datetime",
"-started_datetime",
"-date",
"-pk",
),
"verbose_name": "Stock Item Test Result",
},
),
]
+2
View File
@@ -3820,6 +3820,8 @@ class StockItemTestResult(InvenTree.models.InvenTreeMetadataModel):
verbose_name = _('Stock Item Test Result')
ordering = ('-finished_datetime', '-started_datetime', '-date', '-pk')
def __str__(self):
"""Return string representation."""
return f'{self.test_name} - {self.result}'
+39
View File
@@ -53,3 +53,42 @@ export function isEquivalent(a: any, b: any): boolean {
return false;
}
export function compareTestResults(a: any, b: any) {
const finishedA = a.finished_datetime
? new Date(a.finished_datetime).getTime()
: null;
const finishedB = b.finished_datetime
? new Date(b.finished_datetime).getTime()
: null;
if (finishedA !== finishedB) {
if (finishedA === null) return 1;
if (finishedB === null) return -1;
return finishedB - finishedA;
}
const startedA = a.started_datetime
? new Date(a.started_datetime).getTime()
: null;
const startedB = b.started_datetime
? new Date(b.started_datetime).getTime()
: null;
if (startedA !== startedB) {
if (startedA === null) return 1;
if (startedB === null) return -1;
return startedB - startedA;
}
const uploadTimeA = a.date ? new Date(a.date).getTime() : null;
const uploadTimeB = b.date ? new Date(b.date).getTime() : null;
if (uploadTimeA !== uploadTimeB) {
if (uploadTimeA === null) return 1;
if (uploadTimeB === null) return -1;
return uploadTimeB - uploadTimeA;
}
return b.pk - a.pk;
}
@@ -63,6 +63,7 @@ import {
useStockFields,
useStockItemSerializeFields
} from '../../forms/StockForms';
import { compareTestResults } from '../../functions/comparison';
import { InvenTreeIcon } from '../../functions/icons';
import useBackgroundTask from '../../hooks/UseBackgroundTask';
import {
@@ -277,9 +278,7 @@ export default function BuildOutputTable({
// Find the "newest" result for this template in the returned data
const result = record.tests
?.filter((test: any) => test.template == template.pk)
.sort((a: any, b: any) => {
return a.pk < b.pk ? 1 : -1;
})
.sort(compareTestResults)
.shift();
if (template?.required && result?.result) {
@@ -37,6 +37,7 @@ import RowExpansionIcon from '../../components/tables/RowExpansionIcon';
import { useApi } from '../../contexts/ApiContext';
import { formatDate } from '../../defaults/formatters';
import { useTestResultFields } from '../../forms/StockForms';
import { compareTestResults } from '../../functions/comparison';
import {
useCreateApiFormModal,
useDeleteApiFormModal,
@@ -110,13 +111,9 @@ export default function StockItemTestResultTable({
});
// Iterate through the returned records
// Note that the results are sorted by oldest first,
// to ensure that the most recent result is displayed "on top"
records
.sort((a: any, b: any) => {
return a.pk > b.pk ? 1 : -1;
})
.forEach((record) => {
// Sort test results using the same priority as the backend:
// finished_datetime -> started_datetime -> date -> pk
records.toSorted(compareTestResults).forEach((record) => {
// Find matching template
const idx = results.findIndex(
(r: any) => r.templateId == record.template