Merge commit '9e125be07e94e0997ce03fd905673c44aad81c00' into block-notes
This commit is contained in:
commit
cec678f88b
|
|
@ -8,7 +8,7 @@ name: Frontend
|
|||
|
||||
on:
|
||||
push:
|
||||
branches-ignore: ["l10*"]
|
||||
branches-ignore: ["l10*", "dependabot/*", "backport/*"]
|
||||
pull_request:
|
||||
branches-ignore: ["l10*"]
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ name: Import / Export
|
|||
|
||||
on:
|
||||
push:
|
||||
branches-ignore: ["l10*"]
|
||||
branches-ignore: ["l10*", "dependabot/*", "backport/*"]
|
||||
pull_request:
|
||||
branches-ignore: ["l10*"]
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ name: QC
|
|||
|
||||
on:
|
||||
push:
|
||||
branches-ignore: ["l10*"]
|
||||
branches-ignore: ["l10*", "dependabot/*", "backport/*"]
|
||||
pull_request:
|
||||
branches-ignore: ["l10*"]
|
||||
|
||||
|
|
|
|||
|
|
@ -42,6 +42,8 @@ jobs:
|
|||
id-token: write
|
||||
contents: write
|
||||
attestations: write
|
||||
artifact-metadata: write
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # pin@v6.0.3
|
||||
with:
|
||||
|
|
@ -73,7 +75,7 @@ jobs:
|
|||
zip -r ../frontend-build.zip * .vite
|
||||
- name: Attest Build Provenance
|
||||
id: attest
|
||||
uses: actions/attest-build-provenance@a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32 # pin@v1
|
||||
uses: actions/attest@59d89421af93a897026c735860bf21b6eb4f7b26 # pin@v4
|
||||
with:
|
||||
subject-path: "${{ github.workspace }}/src/backend/InvenTree/web/static/frontend-build.zip"
|
||||
|
||||
|
|
@ -87,11 +89,15 @@ jobs:
|
|||
with:
|
||||
name: frontend-build
|
||||
path: src/backend/InvenTree/web/static/frontend-build.zip
|
||||
- name: Rename Attestation Bundle
|
||||
run: |
|
||||
mv ${BUNDLE_PATH} src/backend/InvenTree/web/static/frontend-build.intoto.jsonl
|
||||
env:
|
||||
BUNDLE_PATH: ${{ steps.attest.outputs.bundle-path}}
|
||||
- name: Upload Attestation
|
||||
run: gh release upload ${REF} ${BUNDLE_PATH}#frontend-build.intoto.jsonl
|
||||
run: gh release upload ${REF} src/backend/InvenTree/web/static/frontend-build.intoto.jsonl#frontend-build.intoto.jsonl
|
||||
env:
|
||||
REF: ${{ github.ref_name }}
|
||||
BUNDLE_PATH: ${{ steps.attest.outputs.bundle-path}}
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
docs:
|
||||
|
|
@ -148,12 +154,12 @@ jobs:
|
|||
- ubuntu:22.04
|
||||
- ubuntu:24.04
|
||||
- debian:12
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # pin@v6.0.3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
persist-credentials: false
|
||||
|
||||
- name: Get frontend artifact
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # pin@v8.0.1
|
||||
with:
|
||||
|
|
@ -209,7 +215,7 @@ jobs:
|
|||
echo "calculate release channel"
|
||||
pip install --require-hashes -r contrib/dev_reqs/requirements.txt
|
||||
python3 .github/scripts/version_check.py
|
||||
- name: Package
|
||||
- name: Package - current release channel
|
||||
uses: pkgr/action/package@c5666febcd31750da6428042193fc5b2fb765435 # pin@main
|
||||
id: package
|
||||
with:
|
||||
|
|
@ -235,14 +241,6 @@ jobs:
|
|||
repository: inventree/InvenTree
|
||||
channel: ${{ env.pkg_channel }}
|
||||
file: ${{ steps.package.outputs.package_path }}
|
||||
- name: Publish to go.packager.io - stable release channel
|
||||
uses: pkgr/action/publish@3bce081ae512c5020856e237d37b3f5479d4aa71 # pin@main
|
||||
with:
|
||||
target: ${{ matrix.target }}
|
||||
token: ${{ secrets.PACKAGER_RELEASE_TOKEN }}
|
||||
repository: inventree/InvenTree
|
||||
channel: stable
|
||||
file: ${{ steps.package.outputs.package_path }}
|
||||
- name: Publish to artifact
|
||||
run: gh release upload ${REF} ${PACKAGE_PATH}#${PACKAGE_NAME}
|
||||
env:
|
||||
|
|
@ -250,3 +248,29 @@ jobs:
|
|||
PACKAGE_PATH: ${{ steps.package.outputs.package_path }}
|
||||
PACKAGE_NAME: ${{ matrix.target }}-${{ steps.setup.outputs.version }}.tar.gz
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Package - stable release channel
|
||||
uses: pkgr/action/package@c5666febcd31750da6428042193fc5b2fb765435 # pin@main
|
||||
id: package-stable
|
||||
with:
|
||||
target: ${{ matrix.target }}
|
||||
version: ${{ steps.setup.outputs.version }}
|
||||
debug: true
|
||||
cache_prefix: ${{ github.ref_name }}
|
||||
env: |
|
||||
INVENTREE_DB_ENGINE=sqlite3
|
||||
INVENTREE_DB_NAME=database.sqlite3
|
||||
INVENTREE_PLUGINS_ENABLED=true
|
||||
INVENTREE_MEDIA_ROOT=/opt/inventree/media
|
||||
INVENTREE_STATIC_ROOT=/opt/inventree/static
|
||||
INVENTREE_BACKUP_DIR=/opt/inventree/backup
|
||||
INVENTREE_PLUGIN_FILE=/opt/inventree/plugins.txt
|
||||
INVENTREE_CONFIG_FILE=/opt/inventree/config.yaml
|
||||
APP_REPO=inventree/InvenTree
|
||||
- name: Publish to go.packager.io - stable release channel
|
||||
uses: pkgr/action/publish@3bce081ae512c5020856e237d37b3f5479d4aa71 # pin@main
|
||||
with:
|
||||
target: ${{ matrix.target }}
|
||||
token: ${{ secrets.PACKAGER_RELEASE_TOKEN }}
|
||||
repository: inventree/InvenTree
|
||||
channel: stable
|
||||
file: ${{ steps.package-stable.outputs.package_path }}
|
||||
|
|
|
|||
|
|
@ -1,16 +1,22 @@
|
|||
"""InvenTree API version information."""
|
||||
|
||||
# InvenTree API version
|
||||
INVENTREE_API_VERSION = 508
|
||||
INVENTREE_API_VERSION = 510
|
||||
"""Increment this API version number whenever there is a significant change to the API that any clients need to know about."""
|
||||
|
||||
INVENTREE_API_TEXT = """
|
||||
|
||||
v508 -> 2026-06-16 : https://github.com/inventree/InvenTree/pull/11971
|
||||
v510 -> 2026-06-18 : https://github.com/inventree/InvenTree/pull/11971
|
||||
- Removes direct "notes" field from any models which previously supported markdown notes
|
||||
- Adds a generic "Note" model which can be attached to any model type via a generic foreign key relationship
|
||||
- Allow multiple notes to be attached to a single object, and for notes to be created / edited / deleted via the API
|
||||
|
||||
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
|
||||
|
||||
v507 -> 2026-06-16 : https://github.com/inventree/InvenTree/pull/12180
|
||||
- Adds "lookup_field" parameter to the DataImportSessionSerializer, which allows for more flexible lookup of related objects during data import operations
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -590,6 +590,21 @@ class Order(
|
|||
"""Return the Address associated with this order."""
|
||||
return self.address or self.company.primary_address
|
||||
|
||||
@property
|
||||
def status_text(self):
|
||||
"""Return the text representation of the current status. This will consider any custom status."""
|
||||
if self.get_custom_status() is not None:
|
||||
from generic.states.custom import (
|
||||
get_logical_value as get_custom_state_logical_value,
|
||||
)
|
||||
|
||||
custom_status = get_custom_state_logical_value(
|
||||
self.get_custom_status(), model=self._meta.model_name
|
||||
)
|
||||
return custom_status.label
|
||||
else:
|
||||
return self.status_class.label(self.get_status())
|
||||
|
||||
@classmethod
|
||||
def get_status_class(cls):
|
||||
"""Return the enumeration class which represents the 'status' field for this model."""
|
||||
|
|
@ -692,11 +707,6 @@ class PurchaseOrder(TotalPriceMixin, Order):
|
|||
help_text=_('Purchase order status'),
|
||||
)
|
||||
|
||||
@property
|
||||
def status_text(self):
|
||||
"""Return the text representation of the status field."""
|
||||
return PurchaseOrderStatus.text(self.status)
|
||||
|
||||
supplier = models.ForeignKey(
|
||||
Company,
|
||||
on_delete=models.SET_NULL,
|
||||
|
|
@ -1443,11 +1453,6 @@ class SalesOrder(TotalPriceMixin, Order):
|
|||
help_text=_('Sales order status'),
|
||||
)
|
||||
|
||||
@property
|
||||
def status_text(self) -> str:
|
||||
"""Return the text representation of the status field."""
|
||||
return SalesOrderStatus.text(self.status)
|
||||
|
||||
customer_reference = models.CharField(
|
||||
max_length=64,
|
||||
blank=True,
|
||||
|
|
|
|||
|
|
@ -126,6 +126,14 @@ class AbstractOrderSerializer(
|
|||
# status field cannot be set directly
|
||||
status = serializers.IntegerField(read_only=True, label=_('Order Status'))
|
||||
|
||||
# can be set directly, but must be valid for the current order status
|
||||
status_custom_key = serializers.IntegerField(
|
||||
label=_('Custom Status Key'),
|
||||
help_text=_('Update order status to a custom value for this logical value'),
|
||||
allow_null=True,
|
||||
default=None,
|
||||
)
|
||||
|
||||
# Reference string is *required*
|
||||
reference = serializers.CharField(required=True)
|
||||
|
||||
|
|
@ -198,6 +206,31 @@ class AbstractOrderSerializer(
|
|||
self.Meta.model.validate_reference_field(reference)
|
||||
return reference
|
||||
|
||||
def validate_status_custom_key(self, value):
|
||||
"""Validate the status_custom_key field.
|
||||
|
||||
Ensure the custom status key is valid for the logical order status.
|
||||
"""
|
||||
if value is None:
|
||||
return value
|
||||
|
||||
from generic.states.custom import get_logical_value
|
||||
|
||||
if not isinstance(value, int):
|
||||
raise ValidationError(_('Custom status key must be an integer'))
|
||||
|
||||
try:
|
||||
custom_status = get_logical_value(
|
||||
value, model=self.Meta.model._meta.model_name
|
||||
)
|
||||
except:
|
||||
raise ValidationError(_('Invalid custom status key'))
|
||||
|
||||
if custom_status.logical_key is not self.instance.status:
|
||||
raise ValidationError(_('Invalid custom status key for this order status'))
|
||||
|
||||
return value
|
||||
|
||||
@staticmethod
|
||||
def annotate_queryset(queryset):
|
||||
"""Add extra information to the queryset."""
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import json
|
|||
from datetime import date, datetime, timedelta
|
||||
from typing import Optional
|
||||
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.db import connection
|
||||
from django.test.utils import CaptureQueriesContext
|
||||
|
|
@ -240,6 +241,77 @@ class PurchaseOrderTest(OrderTest):
|
|||
self.assertEqual(data['pk'], 1)
|
||||
self.assertEqual(data['description'], 'Ordering some screws')
|
||||
|
||||
def test_po_status_custom_key_options(self):
|
||||
"""Test that status_custom_key is exposed as writable in options."""
|
||||
self.assignRole('purchase_order.add')
|
||||
|
||||
response = self.options(self.LIST_URL, expected_code=200)
|
||||
post = response.data['actions']['POST']
|
||||
|
||||
self.assertIn('status_custom_key', post)
|
||||
self.assertEqual(post['status_custom_key']['required'], False)
|
||||
self.assertEqual(post['status_custom_key']['read_only'], False)
|
||||
|
||||
def test_po_status_custom_key_patch_valid(self):
|
||||
"""Test patching a valid custom status key for the current PO status."""
|
||||
self.assignRole('purchase_order.change')
|
||||
|
||||
po = models.PurchaseOrder.objects.get(pk=1)
|
||||
self.assertEqual(po.status, PurchaseOrderStatus.PENDING.value)
|
||||
|
||||
custom_status = InvenTreeCustomUserStateModel.objects.create(
|
||||
key=901,
|
||||
name='PO Pending Custom',
|
||||
label='PO Pending Custom',
|
||||
color='secondary',
|
||||
logical_key=PurchaseOrderStatus.PENDING.value,
|
||||
model=ContentType.objects.get_for_model(models.PurchaseOrder),
|
||||
reference_status='PurchaseOrderStatus',
|
||||
)
|
||||
|
||||
url = reverse('api-po-detail', kwargs={'pk': po.pk})
|
||||
response = self.patch(
|
||||
url, {'status_custom_key': custom_status.key}, expected_code=200
|
||||
)
|
||||
|
||||
self.assertEqual(response.data['status'], PurchaseOrderStatus.PENDING.value)
|
||||
self.assertEqual(response.data['status_custom_key'], custom_status.key)
|
||||
|
||||
def test_po_status_custom_key_patch_invalid(self):
|
||||
"""Test patching an invalid custom status key for a PO."""
|
||||
self.assignRole('purchase_order.change')
|
||||
|
||||
po = models.PurchaseOrder.objects.get(pk=1)
|
||||
url = reverse('api-po-detail', kwargs={'pk': po.pk})
|
||||
|
||||
response = self.patch(url, {'status_custom_key': 999999}, expected_code=400)
|
||||
|
||||
self.assertIn('status_custom_key', response.data)
|
||||
|
||||
def test_po_status_custom_key_patch_wrong_logical_status(self):
|
||||
"""Test patching a custom key mapped to a different logical status."""
|
||||
self.assignRole('purchase_order.change')
|
||||
|
||||
po = models.PurchaseOrder.objects.get(pk=1)
|
||||
self.assertEqual(po.status, PurchaseOrderStatus.PENDING.value)
|
||||
|
||||
custom_status = InvenTreeCustomUserStateModel.objects.create(
|
||||
key=902,
|
||||
name='PO Placed Custom',
|
||||
label='PO Placed Custom',
|
||||
color='secondary',
|
||||
logical_key=PurchaseOrderStatus.PLACED.value,
|
||||
model=ContentType.objects.get_for_model(models.PurchaseOrder),
|
||||
reference_status='PurchaseOrderStatus',
|
||||
)
|
||||
|
||||
url = reverse('api-po-detail', kwargs={'pk': po.pk})
|
||||
response = self.patch(
|
||||
url, {'status_custom_key': custom_status.key}, expected_code=400
|
||||
)
|
||||
|
||||
self.assertIn('status_custom_key', response.data)
|
||||
|
||||
def test_po_reference(self):
|
||||
"""Test that a reference with a too big / small reference is handled correctly."""
|
||||
# get permissions
|
||||
|
|
@ -1950,6 +2022,63 @@ class SalesOrderTest(OrderTest):
|
|||
reverse('api-so-detail', kwargs={'pk': 1}), ['customer_detail']
|
||||
)
|
||||
|
||||
def test_so_custom_status_query_count(self):
|
||||
"""Test that listing SalesOrders with custom statuses does not cause N+1 queries.
|
||||
|
||||
Ensures that resolving the 'status_text' field for custom status values
|
||||
is O(1) in database queries, not O(N) relative to the number of results.
|
||||
"""
|
||||
so_content_type = ContentType.objects.get_for_model(models.SalesOrder)
|
||||
|
||||
logical_keys = [
|
||||
SalesOrderStatus.PENDING.value,
|
||||
SalesOrderStatus.IN_PROGRESS.value,
|
||||
SalesOrderStatus.SHIPPED.value,
|
||||
SalesOrderStatus.ON_HOLD.value,
|
||||
SalesOrderStatus.COMPLETE.value,
|
||||
SalesOrderStatus.CANCELLED.value,
|
||||
SalesOrderStatus.PENDING.value,
|
||||
SalesOrderStatus.IN_PROGRESS.value,
|
||||
SalesOrderStatus.SHIPPED.value,
|
||||
SalesOrderStatus.ON_HOLD.value,
|
||||
]
|
||||
|
||||
custom_statuses = [
|
||||
InvenTreeCustomUserStateModel.objects.create(
|
||||
key=2000 + i,
|
||||
name=f'SoCustomStatus{i}',
|
||||
label=f'SO Custom Status Label {i}',
|
||||
color='secondary',
|
||||
logical_key=logical_keys[i],
|
||||
model=so_content_type,
|
||||
reference_status='SalesOrderStatus',
|
||||
)
|
||||
for i in range(10)
|
||||
]
|
||||
|
||||
customer = Company.objects.filter(is_customer=True).first()
|
||||
models.SalesOrder.objects.bulk_create([
|
||||
models.SalesOrder(
|
||||
customer=customer,
|
||||
reference=f'SO-QTEST-{i}',
|
||||
status=custom_statuses[i % 10].logical_key,
|
||||
status_custom_key=custom_statuses[i % 10].key,
|
||||
)
|
||||
for i in range(100)
|
||||
])
|
||||
|
||||
for limit in [1, 5, 10, 25, 50, 100]:
|
||||
response = self.get(
|
||||
self.LIST_URL,
|
||||
data={'limit': limit},
|
||||
expected_code=200,
|
||||
max_query_count=50,
|
||||
)
|
||||
|
||||
for result in response.data['results']:
|
||||
self.assertIn('status_text', result)
|
||||
self.assertIsNotNone(result['status_text'])
|
||||
|
||||
|
||||
class SalesOrderLineItemTest(OrderTest):
|
||||
"""Tests for the SalesOrderLineItem API."""
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
Loading…
Reference in New Issue