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 information."""
|
||||||
|
|
||||||
# InvenTree API version
|
# 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."""
|
"""Increment this API version number whenever there is a significant change to the API that any clients need to know about."""
|
||||||
|
|
||||||
INVENTREE_API_TEXT = """
|
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
|
v508 -> 2026-06-17 : https://github.com/inventree/InvenTree/pull/11982
|
||||||
- An order's "status_custom_key" can be updated via PATCH API endpoint
|
- 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:
|
if self.status != DataImportStatusCode.COMPLETE.value:
|
||||||
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()
|
self.save()
|
||||||
|
|
||||||
# Clear staging data now that all rows have been imported
|
# Clear staging data now that all rows have been imported
|
||||||
self.rows.all().delete()
|
self.rows.all().delete()
|
||||||
self.column_mappings.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 the number of completed rows for this session."""
|
||||||
return self.rows.filter(complete=True).count()
|
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):
|
def available_fields(self):
|
||||||
"""Returns information on the available fields.
|
"""Returns information on the available fields.
|
||||||
|
|
||||||
|
|||||||
@@ -62,6 +62,8 @@ class DataImportSessionSerializer(InvenTreeModelSerializer):
|
|||||||
'field_filters',
|
'field_filters',
|
||||||
'row_count',
|
'row_count',
|
||||||
'completed_row_count',
|
'completed_row_count',
|
||||||
|
'completed_row_count_history',
|
||||||
|
'row_count_history',
|
||||||
]
|
]
|
||||||
read_only_fields = ['pk', 'user', 'status', 'columns']
|
read_only_fields = ['pk', 'user', 'status', 'columns']
|
||||||
|
|
||||||
@@ -220,6 +222,8 @@ class DataImportAcceptRowSerializer(serializers.Serializer):
|
|||||||
row.validate(commit=True, request=request)
|
row.validate(commit=True, request=request)
|
||||||
|
|
||||||
if session := self.context.get('session', None):
|
if session := self.context.get('session', None):
|
||||||
|
# ensure current state is available
|
||||||
|
session.refresh_from_db()
|
||||||
session.check_complete()
|
session.check_complete()
|
||||||
|
|
||||||
return rows
|
return rows
|
||||||
|
|||||||
@@ -783,7 +783,7 @@ export default function BuildDetail() {
|
|||||||
? []
|
? []
|
||||||
: [
|
: [
|
||||||
<StatusRenderer
|
<StatusRenderer
|
||||||
status={build.status_custom_key}
|
status={build.status_custom_key || build.status}
|
||||||
type={ModelType.build}
|
type={ModelType.build}
|
||||||
options={{ size: 'lg' }}
|
options={{ size: 'lg' }}
|
||||||
/>,
|
/>,
|
||||||
|
|||||||
@@ -543,7 +543,7 @@ export default function PurchaseOrderDetail() {
|
|||||||
? []
|
? []
|
||||||
: [
|
: [
|
||||||
<StatusRenderer
|
<StatusRenderer
|
||||||
status={order.status_custom_key}
|
status={order.status_custom_key || order.status}
|
||||||
type={ModelType.purchaseorder}
|
type={ModelType.purchaseorder}
|
||||||
options={{ size: 'lg' }}
|
options={{ size: 'lg' }}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -390,7 +390,7 @@ export default function ReturnOrderDetail() {
|
|||||||
? []
|
? []
|
||||||
: [
|
: [
|
||||||
<StatusRenderer
|
<StatusRenderer
|
||||||
status={order.status_custom_key}
|
status={order.status_custom_key || order.status}
|
||||||
type={ModelType.returnorder}
|
type={ModelType.returnorder}
|
||||||
options={{ size: 'lg' }}
|
options={{ size: 'lg' }}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -605,7 +605,7 @@ export default function SalesOrderDetail() {
|
|||||||
? []
|
? []
|
||||||
: [
|
: [
|
||||||
<StatusRenderer
|
<StatusRenderer
|
||||||
status={order.status_custom_key}
|
status={order.status_custom_key || order.status}
|
||||||
type={ModelType.salesorder}
|
type={ModelType.salesorder}
|
||||||
options={{ size: 'lg' }}
|
options={{ size: 'lg' }}
|
||||||
key={order.pk}
|
key={order.pk}
|
||||||
|
|||||||
@@ -356,7 +356,7 @@ export default function TransferOrderDetail() {
|
|||||||
? []
|
? []
|
||||||
: [
|
: [
|
||||||
<StatusRenderer
|
<StatusRenderer
|
||||||
status={order.status_custom_key}
|
status={order.status_custom_key || order.status}
|
||||||
type={ModelType.transferorder}
|
type={ModelType.transferorder}
|
||||||
options={{ size: 'lg' }}
|
options={{ size: 'lg' }}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import {
|
|||||||
useCreateApiFormModal,
|
useCreateApiFormModal,
|
||||||
useDeleteApiFormModal
|
useDeleteApiFormModal
|
||||||
} from '../../hooks/UseForm';
|
} from '../../hooks/UseForm';
|
||||||
|
import useStatusCodes from '../../hooks/UseStatusCodes';
|
||||||
import { useImporterState } from '../../states/ImporterState';
|
import { useImporterState } from '../../states/ImporterState';
|
||||||
import { DateColumn, StatusColumn } from '../ColumnRenderers';
|
import { DateColumn, StatusColumn } from '../ColumnRenderers';
|
||||||
import { StatusFilterOptions, UserFilter } from '../Filter';
|
import { StatusFilterOptions, UserFilter } from '../Filter';
|
||||||
@@ -26,6 +27,9 @@ import { InvenTreeTable } from '../InvenTreeTable';
|
|||||||
export default function ImportSessionTable() {
|
export default function ImportSessionTable() {
|
||||||
const table = useTable('importsession');
|
const table = useTable('importsession');
|
||||||
const openImporter = useImporterState((state) => state.openImporter);
|
const openImporter = useImporterState((state) => state.openImporter);
|
||||||
|
const importSessionStatus = useStatusCodes({
|
||||||
|
modelType: ModelType.importsession
|
||||||
|
});
|
||||||
|
|
||||||
const [selectedSession, setSelectedSession] = useState<number | undefined>(
|
const [selectedSession, setSelectedSession] = useState<number | undefined>(
|
||||||
undefined
|
undefined
|
||||||
@@ -82,13 +86,20 @@ export default function ImportSessionTable() {
|
|||||||
sortable: false,
|
sortable: false,
|
||||||
accessor: 'row_count',
|
accessor: 'row_count',
|
||||||
title: t`Imported Rows`,
|
title: t`Imported Rows`,
|
||||||
render: (record: any) => (
|
render: (record: any) =>
|
||||||
<ProgressBar
|
record.status == importSessionStatus.COMPLETE ? (
|
||||||
progressLabel={true}
|
<ProgressBar
|
||||||
value={record.completed_row_count}
|
progressLabel={true}
|
||||||
maximum={record.row_count}
|
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();
|
await page.getByRole('button', { name: 'Close' }).click();
|
||||||
|
|
||||||
// Confirmation of full import success
|
// 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
|
// Manually delete records
|
||||||
await page.getByRole('checkbox', { name: 'Select all records' }).check();
|
await page.getByRole('checkbox', { name: 'Select all records' }).check();
|
||||||
|
|||||||
Reference in New Issue
Block a user