mirror of
https://github.com/inventree/InvenTree.git
synced 2026-07-04 06:00:38 +00:00
fix(backend): import session metadata (#12184)
* add storage for historic import metadata for reporting and display purposes fixes breakage fromhttps://github.com/inventree/InvenTree/pull/12169 * add api bump * re-enable test * fix migration * ensure session is not overwritten * fix statusrender without custom key
This commit is contained in:
@@ -1,11 +1,14 @@
|
||||
"""InvenTree API version information."""
|
||||
|
||||
# InvenTree API version
|
||||
INVENTREE_API_VERSION = 508
|
||||
INVENTREE_API_VERSION = 509
|
||||
"""Increment this API version number whenever there is a significant change to the API that any clients need to know about."""
|
||||
|
||||
INVENTREE_API_TEXT = """
|
||||
|
||||
v509 -> 2026-06-17 : https://github.com/inventree/InvenTree/pull/12184
|
||||
- Adds "completed_row_count_history" and "row_count_history" fields to the DataImportSession model, which store the historic count of completed rows and total rows for a data import session.
|
||||
|
||||
v508 -> 2026-06-17 : https://github.com/inventree/InvenTree/pull/11982
|
||||
- An order's "status_custom_key" can be updated via PATCH API endpoint
|
||||
|
||||
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
# Generated by Django 5.2.15 on 2026-06-17 05:57
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('importer', '0006_dataimportcolumnmap_lookup_field'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='dataimportsession',
|
||||
name='completed_row_count_history',
|
||||
field=models.PositiveIntegerField(blank=True, null=True, verbose_name='Completed Row Count History'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='dataimportsession',
|
||||
name='row_count_history',
|
||||
field=models.PositiveIntegerField(blank=True, null=True, verbose_name='Row Count History'),
|
||||
),
|
||||
]
|
||||
@@ -385,7 +385,13 @@ class DataImportSession(models.Model):
|
||||
|
||||
if self.status != DataImportStatusCode.COMPLETE.value:
|
||||
self.status = DataImportStatusCode.COMPLETE.value
|
||||
|
||||
# persist historic count values for reporting purposes
|
||||
self.completed_row_count_history = self.completed_row_count
|
||||
self.row_count_history = self.row_count
|
||||
|
||||
self.save()
|
||||
|
||||
# Clear staging data now that all rows have been imported
|
||||
self.rows.all().delete()
|
||||
self.column_mappings.all().delete()
|
||||
@@ -402,6 +408,14 @@ class DataImportSession(models.Model):
|
||||
"""Return the number of completed rows for this session."""
|
||||
return self.rows.filter(complete=True).count()
|
||||
|
||||
# Historic values for reporting purposes
|
||||
completed_row_count_history = models.PositiveIntegerField(
|
||||
blank=True, null=True, verbose_name=_('Completed Row Count History')
|
||||
)
|
||||
row_count_history = models.PositiveIntegerField(
|
||||
blank=True, null=True, verbose_name=_('Row Count History')
|
||||
)
|
||||
|
||||
def available_fields(self):
|
||||
"""Returns information on the available fields.
|
||||
|
||||
|
||||
@@ -62,6 +62,8 @@ class DataImportSessionSerializer(InvenTreeModelSerializer):
|
||||
'field_filters',
|
||||
'row_count',
|
||||
'completed_row_count',
|
||||
'completed_row_count_history',
|
||||
'row_count_history',
|
||||
]
|
||||
read_only_fields = ['pk', 'user', 'status', 'columns']
|
||||
|
||||
@@ -220,6 +222,8 @@ class DataImportAcceptRowSerializer(serializers.Serializer):
|
||||
row.validate(commit=True, request=request)
|
||||
|
||||
if session := self.context.get('session', None):
|
||||
# ensure current state is available
|
||||
session.refresh_from_db()
|
||||
session.check_complete()
|
||||
|
||||
return rows
|
||||
|
||||
@@ -783,7 +783,7 @@ export default function BuildDetail() {
|
||||
? []
|
||||
: [
|
||||
<StatusRenderer
|
||||
status={build.status_custom_key}
|
||||
status={build.status_custom_key || build.status}
|
||||
type={ModelType.build}
|
||||
options={{ size: 'lg' }}
|
||||
/>,
|
||||
|
||||
@@ -543,7 +543,7 @@ export default function PurchaseOrderDetail() {
|
||||
? []
|
||||
: [
|
||||
<StatusRenderer
|
||||
status={order.status_custom_key}
|
||||
status={order.status_custom_key || order.status}
|
||||
type={ModelType.purchaseorder}
|
||||
options={{ size: 'lg' }}
|
||||
/>
|
||||
|
||||
@@ -390,7 +390,7 @@ export default function ReturnOrderDetail() {
|
||||
? []
|
||||
: [
|
||||
<StatusRenderer
|
||||
status={order.status_custom_key}
|
||||
status={order.status_custom_key || order.status}
|
||||
type={ModelType.returnorder}
|
||||
options={{ size: 'lg' }}
|
||||
/>
|
||||
|
||||
@@ -605,7 +605,7 @@ export default function SalesOrderDetail() {
|
||||
? []
|
||||
: [
|
||||
<StatusRenderer
|
||||
status={order.status_custom_key}
|
||||
status={order.status_custom_key || order.status}
|
||||
type={ModelType.salesorder}
|
||||
options={{ size: 'lg' }}
|
||||
key={order.pk}
|
||||
|
||||
@@ -356,7 +356,7 @@ export default function TransferOrderDetail() {
|
||||
? []
|
||||
: [
|
||||
<StatusRenderer
|
||||
status={order.status_custom_key}
|
||||
status={order.status_custom_key || order.status}
|
||||
type={ModelType.transferorder}
|
||||
options={{ size: 'lg' }}
|
||||
/>
|
||||
|
||||
@@ -18,6 +18,7 @@ import {
|
||||
useCreateApiFormModal,
|
||||
useDeleteApiFormModal
|
||||
} from '../../hooks/UseForm';
|
||||
import useStatusCodes from '../../hooks/UseStatusCodes';
|
||||
import { useImporterState } from '../../states/ImporterState';
|
||||
import { DateColumn, StatusColumn } from '../ColumnRenderers';
|
||||
import { StatusFilterOptions, UserFilter } from '../Filter';
|
||||
@@ -26,6 +27,9 @@ import { InvenTreeTable } from '../InvenTreeTable';
|
||||
export default function ImportSessionTable() {
|
||||
const table = useTable('importsession');
|
||||
const openImporter = useImporterState((state) => state.openImporter);
|
||||
const importSessionStatus = useStatusCodes({
|
||||
modelType: ModelType.importsession
|
||||
});
|
||||
|
||||
const [selectedSession, setSelectedSession] = useState<number | undefined>(
|
||||
undefined
|
||||
@@ -82,13 +86,20 @@ export default function ImportSessionTable() {
|
||||
sortable: false,
|
||||
accessor: 'row_count',
|
||||
title: t`Imported Rows`,
|
||||
render: (record: any) => (
|
||||
<ProgressBar
|
||||
progressLabel={true}
|
||||
value={record.completed_row_count}
|
||||
maximum={record.row_count}
|
||||
/>
|
||||
)
|
||||
render: (record: any) =>
|
||||
record.status == importSessionStatus.COMPLETE ? (
|
||||
<ProgressBar
|
||||
progressLabel={true}
|
||||
value={record.completed_row_count_history}
|
||||
maximum={record.row_count_history}
|
||||
/>
|
||||
) : (
|
||||
<ProgressBar
|
||||
progressLabel={true}
|
||||
value={record.completed_row_count}
|
||||
maximum={record.row_count}
|
||||
/>
|
||||
)
|
||||
}
|
||||
];
|
||||
}, []);
|
||||
|
||||
@@ -60,7 +60,7 @@ test('Importing - Admin Center', async ({ browser }) => {
|
||||
await page.getByRole('button', { name: 'Close' }).click();
|
||||
|
||||
// Confirmation of full import success
|
||||
// await page.getByRole('cell', { name: '3 / 3' }).first().waitFor();
|
||||
await page.getByRole('cell', { name: '3 / 3' }).first().waitFor();
|
||||
|
||||
// Manually delete records
|
||||
await page.getByRole('checkbox', { name: 'Select all records' }).check();
|
||||
|
||||
Reference in New Issue
Block a user