mirror of
https://github.com/inventree/InvenTree.git
synced 2025-12-14 08:19:54 +00:00
Barcode scan tweaks (#10992)
* Remove duplicate tooltip * Adjust default value * docs update * Tweak unit test * Fix playwright tests
This commit is contained in:
BIN
docs/docs/assets/images/barcode/barcode_allocate_stock.png
Normal file
BIN
docs/docs/assets/images/barcode/barcode_allocate_stock.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 26 KiB |
@@ -69,7 +69,7 @@ To access this page, select *Scan Barcode* from the main navigation menu:
|
|||||||
{{ image("barcode/barcode_nav_menu.png", "Barcode menu item") }}
|
{{ image("barcode/barcode_nav_menu.png", "Barcode menu item") }}
|
||||||
{{ image("barcode/barcode_scan_page.png", "Barcode scan page") }}
|
{{ image("barcode/barcode_scan_page.png", "Barcode scan page") }}
|
||||||
|
|
||||||
### Barcodes in Forms
|
## Barcodes in Forms
|
||||||
|
|
||||||
The InvenTree user interface supports direct scanning of barcodes within certain forms in the web UI. This means that any form field which points to a model which supports barcodes can accept barcode input. If barcode scanning is supported for a particular field, a barcode icon will be displayed next to the input field:
|
The InvenTree user interface supports direct scanning of barcodes within certain forms in the web UI. This means that any form field which points to a model which supports barcodes can accept barcode input. If barcode scanning is supported for a particular field, a barcode icon will be displayed next to the input field:
|
||||||
|
|
||||||
@@ -83,6 +83,14 @@ Once scanned, the form field will be automatically populated with the correct it
|
|||||||
|
|
||||||
{{ image("barcode/barcode_field_filled.png", "Barcode field populated") }}
|
{{ image("barcode/barcode_field_filled.png", "Barcode field populated") }}
|
||||||
|
|
||||||
|
Any field which supports barcode input will have this functionality, such as allocating stock items to an order:
|
||||||
|
|
||||||
|
{{ image("barcode/barcode_allocate_stock.png", "Allocate stock via barcode") }}
|
||||||
|
|
||||||
|
### User Configuration
|
||||||
|
|
||||||
|
By default, barcode scanning in form fields is disabled. Each user can enable this feature via their [user preferences](../settings/user.md#display-settings).
|
||||||
|
|
||||||
## App Integration
|
## App Integration
|
||||||
|
|
||||||
Barcode scanning is a key feature of the [companion mobile app](../app/barcode.md). When running on a device with an integrated camera, the app can scan barcodes directly from the camera feed.
|
Barcode scanning is a key feature of the [companion mobile app](../app/barcode.md). When running on a device with an integrated camera, the app can scan barcodes directly from the camera feed.
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ USER_SETTINGS: dict[str, InvenTreeSettingsKeyType] = {
|
|||||||
'BARCODE_IN_FORM_FIELDS': {
|
'BARCODE_IN_FORM_FIELDS': {
|
||||||
'name': _('Barcode Scanner in Form Fields'),
|
'name': _('Barcode Scanner in Form Fields'),
|
||||||
'description': _('Allow barcode scanner input in form fields'),
|
'description': _('Allow barcode scanner input in form fields'),
|
||||||
'default': True,
|
'default': False,
|
||||||
'validator': bool,
|
'validator': bool,
|
||||||
},
|
},
|
||||||
'SEARCH_PREVIEW_SHOW_PARTS': {
|
'SEARCH_PREVIEW_SHOW_PARTS': {
|
||||||
|
|||||||
@@ -29,7 +29,6 @@ export function ScanButton({
|
|||||||
aria-label={`barcode-scan-button-${modelType ?? 'any'}`}
|
aria-label={`barcode-scan-button-${modelType ?? 'any'}`}
|
||||||
onClick={open}
|
onClick={open}
|
||||||
variant='transparent'
|
variant='transparent'
|
||||||
title={t`Open Barcode Scanner`}
|
|
||||||
>
|
>
|
||||||
<IconQrcode />
|
<IconQrcode />
|
||||||
</ActionIcon>
|
</ActionIcon>
|
||||||
|
|||||||
@@ -1,10 +1,16 @@
|
|||||||
import { request } from '@playwright/test';
|
import { request } from '@playwright/test';
|
||||||
import { adminuser, apiUrl } from './defaults';
|
import { adminuser, apiUrl } from './defaults';
|
||||||
|
|
||||||
export const createApi = () =>
|
export const createApi = ({
|
||||||
|
username,
|
||||||
|
password
|
||||||
|
}: {
|
||||||
|
username?: string;
|
||||||
|
password?: string;
|
||||||
|
}) =>
|
||||||
request.newContext({
|
request.newContext({
|
||||||
baseURL: apiUrl,
|
baseURL: apiUrl,
|
||||||
extraHTTPHeaders: {
|
extraHTTPHeaders: {
|
||||||
Authorization: `Basic ${btoa(`${adminuser.username}:${adminuser.password}`)}`
|
Authorization: `Basic ${btoa(`${username || adminuser.username}:${password || adminuser.password}`)}`
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -152,7 +152,7 @@ export const globalSearch = async (page: Page, query: string) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const deletePart = async (name: string) => {
|
export const deletePart = async (name: string) => {
|
||||||
const api = await createApi();
|
const api = await createApi({});
|
||||||
const parts = await api
|
const parts = await api
|
||||||
.get('part/', {
|
.get('part/', {
|
||||||
params: { search: name }
|
params: { search: name }
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import type { Page } from '@playwright/test';
|
import type { Page } from '@playwright/test';
|
||||||
|
import { createApi } from '../api';
|
||||||
import { test } from '../baseFixtures';
|
import { test } from '../baseFixtures';
|
||||||
import { doCachedLogin } from '../login';
|
import { doCachedLogin } from '../login';
|
||||||
|
|
||||||
@@ -128,9 +129,33 @@ test('Barcode Scanning - Build', async ({ browser }) => {
|
|||||||
|
|
||||||
test('Barcode Scanning - Forms', async ({ browser }) => {
|
test('Barcode Scanning - Forms', async ({ browser }) => {
|
||||||
const page = await doCachedLogin(browser, {
|
const page = await doCachedLogin(browser, {
|
||||||
|
username: 'admin',
|
||||||
|
password: 'inventree',
|
||||||
url: '/stock/location/index/stock-items'
|
url: '/stock/location/index/stock-items'
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Ensure the user setting is enabled
|
||||||
|
const api = await createApi({});
|
||||||
|
|
||||||
|
let patched = false;
|
||||||
|
|
||||||
|
await api
|
||||||
|
.patch('/api/settings/user/BARCODE_IN_FORM_FIELDS/', {
|
||||||
|
data: {
|
||||||
|
value: true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then((response) => {
|
||||||
|
patched = response.status() === 200;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Assert that the setting was patched successfully
|
||||||
|
if (!patched) {
|
||||||
|
throw new Error('Could not patch user setting: BARCODE_IN_FORM_FIELDS');
|
||||||
|
}
|
||||||
|
|
||||||
|
await page.reload();
|
||||||
|
|
||||||
// Open the "Add Stock Item" form
|
// Open the "Add Stock Item" form
|
||||||
await page
|
await page
|
||||||
.getByRole('button', { name: 'action-button-add-stock-item' })
|
.getByRole('button', { name: 'action-button-add-stock-item' })
|
||||||
|
|||||||
@@ -332,7 +332,7 @@ test('Settings - Admin - Barcode History', async ({ browser }) => {
|
|||||||
|
|
||||||
// Scan some barcodes (via API calls)
|
// Scan some barcodes (via API calls)
|
||||||
const barcodes = ['ABC1234', 'XYZ5678', 'QRS9012'];
|
const barcodes = ['ABC1234', 'XYZ5678', 'QRS9012'];
|
||||||
const api = await createApi();
|
const api = await createApi({});
|
||||||
|
|
||||||
for (let i = 0; i < barcodes.length; i++) {
|
for (let i = 0; i < barcodes.length; i++) {
|
||||||
const barcode = barcodes[i];
|
const barcode = barcodes[i];
|
||||||
@@ -349,8 +349,8 @@ test('Settings - Admin - Barcode History', async ({ browser }) => {
|
|||||||
},
|
},
|
||||||
timeout: 5000
|
timeout: 5000
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then((response) => {
|
||||||
result = true;
|
result = response.status() === 200;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (result) {
|
if (result) {
|
||||||
@@ -478,8 +478,6 @@ test('Settings - Admin - Parameter', async ({ browser }) => {
|
|||||||
await page.getByRole('option', { name: 'my custom parameter' }).click();
|
await page.getByRole('option', { name: 'my custom parameter' }).click();
|
||||||
await page.getByLabel('choice-field-data').fill('2');
|
await page.getByLabel('choice-field-data').fill('2');
|
||||||
await page.getByRole('button', { name: 'Submit' }).click();
|
await page.getByRole('button', { name: 'Submit' }).click();
|
||||||
|
|
||||||
await page.waitForTimeout(2500);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Settings - Admin - Unauthorized', async ({ browser }) => {
|
test('Settings - Admin - Unauthorized', async ({ browser }) => {
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ export const setSettingState = async ({
|
|||||||
type?: 'global' | 'plugin';
|
type?: 'global' | 'plugin';
|
||||||
plugin?: string;
|
plugin?: string;
|
||||||
}) => {
|
}) => {
|
||||||
const api = await createApi();
|
const api = await createApi({});
|
||||||
const url =
|
const url =
|
||||||
type === 'global'
|
type === 'global'
|
||||||
? `settings/global/${setting}/`
|
? `settings/global/${setting}/`
|
||||||
@@ -37,7 +37,7 @@ export const setPluginState = async ({
|
|||||||
plugin: string;
|
plugin: string;
|
||||||
state: boolean;
|
state: boolean;
|
||||||
}) => {
|
}) => {
|
||||||
const api = await createApi();
|
const api = await createApi({});
|
||||||
const response = await api.patch(`plugins/${plugin}/activate/`, {
|
const response = await api.patch(`plugins/${plugin}/activate/`, {
|
||||||
data: {
|
data: {
|
||||||
active: state
|
active: state
|
||||||
|
|||||||
Reference in New Issue
Block a user