diff --git a/docs/docs/assets/images/barcode/barcode_allocate_stock.png b/docs/docs/assets/images/barcode/barcode_allocate_stock.png new file mode 100644 index 0000000000..676efcb1e3 Binary files /dev/null and b/docs/docs/assets/images/barcode/barcode_allocate_stock.png differ diff --git a/docs/docs/barcodes/index.md b/docs/docs/barcodes/index.md index b9e53b38b6..6d5eb04d76 100644 --- a/docs/docs/barcodes/index.md +++ b/docs/docs/barcodes/index.md @@ -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_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: @@ -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") }} +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 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. diff --git a/src/backend/InvenTree/common/setting/user.py b/src/backend/InvenTree/common/setting/user.py index b318a00213..23dee7e5bd 100644 --- a/src/backend/InvenTree/common/setting/user.py +++ b/src/backend/InvenTree/common/setting/user.py @@ -44,7 +44,7 @@ USER_SETTINGS: dict[str, InvenTreeSettingsKeyType] = { 'BARCODE_IN_FORM_FIELDS': { 'name': _('Barcode Scanner in Form Fields'), 'description': _('Allow barcode scanner input in form fields'), - 'default': True, + 'default': False, 'validator': bool, }, 'SEARCH_PREVIEW_SHOW_PARTS': { diff --git a/src/frontend/src/components/buttons/ScanButton.tsx b/src/frontend/src/components/buttons/ScanButton.tsx index 542656aed1..286f3ef4ac 100644 --- a/src/frontend/src/components/buttons/ScanButton.tsx +++ b/src/frontend/src/components/buttons/ScanButton.tsx @@ -29,7 +29,6 @@ export function ScanButton({ aria-label={`barcode-scan-button-${modelType ?? 'any'}`} onClick={open} variant='transparent' - title={t`Open Barcode Scanner`} > diff --git a/src/frontend/tests/api.ts b/src/frontend/tests/api.ts index 7d4e694a65..7c609fd5e4 100644 --- a/src/frontend/tests/api.ts +++ b/src/frontend/tests/api.ts @@ -1,10 +1,16 @@ import { request } from '@playwright/test'; import { adminuser, apiUrl } from './defaults'; -export const createApi = () => +export const createApi = ({ + username, + password +}: { + username?: string; + password?: string; +}) => request.newContext({ baseURL: apiUrl, extraHTTPHeaders: { - Authorization: `Basic ${btoa(`${adminuser.username}:${adminuser.password}`)}` + Authorization: `Basic ${btoa(`${username || adminuser.username}:${password || adminuser.password}`)}` } }); diff --git a/src/frontend/tests/helpers.ts b/src/frontend/tests/helpers.ts index a7dfed6e8a..4cb02129d2 100644 --- a/src/frontend/tests/helpers.ts +++ b/src/frontend/tests/helpers.ts @@ -152,7 +152,7 @@ export const globalSearch = async (page: Page, query: string) => { }; export const deletePart = async (name: string) => { - const api = await createApi(); + const api = await createApi({}); const parts = await api .get('part/', { params: { search: name } diff --git a/src/frontend/tests/pages/pui_scan.spec.ts b/src/frontend/tests/pages/pui_scan.spec.ts index 139e122724..5db51523fc 100644 --- a/src/frontend/tests/pages/pui_scan.spec.ts +++ b/src/frontend/tests/pages/pui_scan.spec.ts @@ -1,4 +1,5 @@ import type { Page } from '@playwright/test'; +import { createApi } from '../api'; import { test } from '../baseFixtures'; import { doCachedLogin } from '../login'; @@ -128,9 +129,33 @@ test('Barcode Scanning - Build', async ({ browser }) => { test('Barcode Scanning - Forms', async ({ browser }) => { const page = await doCachedLogin(browser, { + username: 'admin', + password: 'inventree', 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 await page .getByRole('button', { name: 'action-button-add-stock-item' }) diff --git a/src/frontend/tests/pui_settings.spec.ts b/src/frontend/tests/pui_settings.spec.ts index fbdd590b6c..f30415af2b 100644 --- a/src/frontend/tests/pui_settings.spec.ts +++ b/src/frontend/tests/pui_settings.spec.ts @@ -332,7 +332,7 @@ test('Settings - Admin - Barcode History', async ({ browser }) => { // Scan some barcodes (via API calls) const barcodes = ['ABC1234', 'XYZ5678', 'QRS9012']; - const api = await createApi(); + const api = await createApi({}); for (let i = 0; i < barcodes.length; i++) { const barcode = barcodes[i]; @@ -349,8 +349,8 @@ test('Settings - Admin - Barcode History', async ({ browser }) => { }, timeout: 5000 }) - .then(() => { - result = true; + .then((response) => { + result = response.status() === 200; }); if (result) { @@ -478,8 +478,6 @@ test('Settings - Admin - Parameter', async ({ browser }) => { await page.getByRole('option', { name: 'my custom parameter' }).click(); await page.getByLabel('choice-field-data').fill('2'); await page.getByRole('button', { name: 'Submit' }).click(); - - await page.waitForTimeout(2500); }); test('Settings - Admin - Unauthorized', async ({ browser }) => { diff --git a/src/frontend/tests/settings.ts b/src/frontend/tests/settings.ts index a8fcdc0a68..72d3126808 100644 --- a/src/frontend/tests/settings.ts +++ b/src/frontend/tests/settings.ts @@ -16,7 +16,7 @@ export const setSettingState = async ({ type?: 'global' | 'plugin'; plugin?: string; }) => { - const api = await createApi(); + const api = await createApi({}); const url = type === 'global' ? `settings/global/${setting}/` @@ -37,7 +37,7 @@ export const setPluginState = async ({ plugin: string; state: boolean; }) => { - const api = await createApi(); + const api = await createApi({}); const response = await api.patch(`plugins/${plugin}/activate/`, { data: { active: state