mirror of
https://github.com/inventree/InvenTree.git
synced 2026-09-02 19:08:47 +00:00
Remember last used label template and printer plugin per model type (#12146)
* Remember last used label template and printer plugin per model type * Added playwright test for printing preference persistence and format code * Used waitFor for react-select assertions for test case fixing and persist plugin key correctly * Updated the problem resolution code and the tests * Fixed printing test assertion to avoid word boundary failure with concatenated text
This commit is contained in:
@@ -11,6 +11,7 @@ import { api } from '../../App';
|
|||||||
import { extractAvailableFields } from '../../functions/forms';
|
import { extractAvailableFields } from '../../functions/forms';
|
||||||
import useDataOutput from '../../hooks/UseDataOutput';
|
import useDataOutput from '../../hooks/UseDataOutput';
|
||||||
import { useCreateApiFormModal } from '../../hooks/UseForm';
|
import { useCreateApiFormModal } from '../../hooks/UseForm';
|
||||||
|
import { useLocalState } from '../../states/LocalState';
|
||||||
import {
|
import {
|
||||||
useGlobalSettingsState,
|
useGlobalSettingsState,
|
||||||
useUserSettingsState
|
useUserSettingsState
|
||||||
@@ -32,6 +33,11 @@ export function PrintingActions({
|
|||||||
}) {
|
}) {
|
||||||
const userSettings = useUserSettingsState();
|
const userSettings = useUserSettingsState();
|
||||||
const globalSettings = useGlobalSettingsState();
|
const globalSettings = useGlobalSettingsState();
|
||||||
|
const localState = useLocalState();
|
||||||
|
|
||||||
|
const lastUsedPrinting = useMemo(() => {
|
||||||
|
return modelType ? localState.lastUsedPrinting[modelType] : undefined;
|
||||||
|
}, [localState.lastUsedPrinting, modelType]);
|
||||||
|
|
||||||
const enabled = useMemo(() => items.length > 0, [items]);
|
const enabled = useMemo(() => items.length > 0, [items]);
|
||||||
|
|
||||||
@@ -118,6 +124,7 @@ export function PrintingActions({
|
|||||||
fields.template = {
|
fields.template = {
|
||||||
...fields.template,
|
...fields.template,
|
||||||
autoFill: true,
|
autoFill: true,
|
||||||
|
value: lastUsedPrinting?.template,
|
||||||
filters: {
|
filters: {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
model_type: modelType,
|
model_type: modelType,
|
||||||
@@ -147,7 +154,14 @@ export function PrintingActions({
|
|||||||
};
|
};
|
||||||
|
|
||||||
return fields;
|
return fields;
|
||||||
}, [defaultLabelPlugin, pluginKey, printingFields.data, itemIdList]);
|
}, [
|
||||||
|
defaultLabelPlugin,
|
||||||
|
pluginKey,
|
||||||
|
printingFields.data,
|
||||||
|
itemIdList,
|
||||||
|
lastUsedPrinting,
|
||||||
|
modelType
|
||||||
|
]);
|
||||||
|
|
||||||
const labelModal = useCreateApiFormModal({
|
const labelModal = useCreateApiFormModal({
|
||||||
url: apiUrl(ApiEndpoints.label_print),
|
url: apiUrl(ApiEndpoints.label_print),
|
||||||
@@ -158,15 +172,21 @@ export function PrintingActions({
|
|||||||
onOpen: () => {
|
onOpen: () => {
|
||||||
setLabelDialogOpen(true);
|
setLabelDialogOpen(true);
|
||||||
setItemIdList(items);
|
setItemIdList(items);
|
||||||
|
setPluginKey(lastUsedPrinting?.plugin ?? null);
|
||||||
},
|
},
|
||||||
onClose: () => {
|
onClose: () => {
|
||||||
setLabelDialogOpen(false);
|
setLabelDialogOpen(false);
|
||||||
setPluginKey('');
|
|
||||||
},
|
},
|
||||||
submitText: t`Print`,
|
submitText: t`Print`,
|
||||||
successMessage: null,
|
successMessage: null,
|
||||||
onFormSuccess: (response: any) => {
|
onFormSuccess: (response: any, form: any) => {
|
||||||
setPluginKey('');
|
if (modelType) {
|
||||||
|
const values = form?.getValues?.();
|
||||||
|
localState.setLastUsedPrinting(modelType, {
|
||||||
|
plugin: pluginKey || undefined,
|
||||||
|
template: values?.template ? Number(values.template) : undefined
|
||||||
|
});
|
||||||
|
}
|
||||||
setLabelId(response.pk);
|
setLabelId(response.pk);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -33,6 +33,12 @@ interface LocalStateProps {
|
|||||||
setLayouts: (layouts: any, noPatch?: boolean) => void;
|
setLayouts: (layouts: any, noPatch?: boolean) => void;
|
||||||
showSampleDashboard: boolean;
|
showSampleDashboard: boolean;
|
||||||
setShowSampleDashboard: (value: boolean) => void;
|
setShowSampleDashboard: (value: boolean) => void;
|
||||||
|
// printing
|
||||||
|
lastUsedPrinting: Record<string, { plugin?: string; template?: number }>;
|
||||||
|
setLastUsedPrinting: (
|
||||||
|
modelType: string,
|
||||||
|
values: { plugin?: string; template?: number }
|
||||||
|
) => void;
|
||||||
// panels
|
// panels
|
||||||
lastUsedPanels: Record<string, string>;
|
lastUsedPanels: Record<string, string>;
|
||||||
setLastUsedPanel: (panelKey: string) => (value: string) => void;
|
setLastUsedPanel: (panelKey: string) => (value: string) => void;
|
||||||
@@ -122,6 +128,25 @@ export const useLocalState = create<LocalStateProps>()(
|
|||||||
setShowSampleDashboard: (value) => {
|
setShowSampleDashboard: (value) => {
|
||||||
set({ showSampleDashboard: value });
|
set({ showSampleDashboard: value });
|
||||||
},
|
},
|
||||||
|
// printing
|
||||||
|
lastUsedPrinting: {},
|
||||||
|
setLastUsedPrinting: (modelType, values) => {
|
||||||
|
const current = get().lastUsedPrinting[modelType] || {};
|
||||||
|
if (
|
||||||
|
current.plugin !== values.plugin ||
|
||||||
|
current.template !== values.template
|
||||||
|
) {
|
||||||
|
set({
|
||||||
|
lastUsedPrinting: {
|
||||||
|
...get().lastUsedPrinting,
|
||||||
|
[modelType]: {
|
||||||
|
...current,
|
||||||
|
...values
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
// panels
|
// panels
|
||||||
lastUsedPanels: {},
|
lastUsedPanels: {},
|
||||||
setLastUsedPanel: (panelKey) => (value) => {
|
setLastUsedPanel: (panelKey) => (value) => {
|
||||||
|
|||||||
@@ -65,7 +65,42 @@ test('Printing - Label Printing', async ({ browser }) => {
|
|||||||
await page.getByRole('button', { name: 'Print', exact: true }).isEnabled();
|
await page.getByRole('button', { name: 'Print', exact: true }).isEnabled();
|
||||||
await page.getByRole('button', { name: 'Print', exact: true }).click();
|
await page.getByRole('button', { name: 'Print', exact: true }).click();
|
||||||
|
|
||||||
await page.getByText('Process completed successfully').first().waitFor();
|
const successMessage = page
|
||||||
|
.getByText('Process completed successfully')
|
||||||
|
.first();
|
||||||
|
await successMessage.waitFor();
|
||||||
|
await successMessage.waitFor({ state: 'hidden' });
|
||||||
|
|
||||||
|
// Re-open print dialog to verify persistence (issue #12129)
|
||||||
|
await page
|
||||||
|
.getByLabel('Stock Items')
|
||||||
|
.getByLabel('action-menu-printing-actions')
|
||||||
|
.click();
|
||||||
|
await page.getByLabel('action-menu-printing-actions-print-labels').click();
|
||||||
|
|
||||||
|
const labelDialog = page.getByRole('dialog', { name: 'Print Label' });
|
||||||
|
|
||||||
|
// Wait for the dialog to fully load
|
||||||
|
await labelDialog.getByLabel('related-field-template').waitFor();
|
||||||
|
await labelDialog.getByLabel('related-field-plugin').waitFor();
|
||||||
|
|
||||||
|
// Verify the last-used template is preselected
|
||||||
|
await expect(labelDialog).toContainText('InvenTree Stock Item Label');
|
||||||
|
|
||||||
|
// Verify the last-used plugin is preselected
|
||||||
|
await expect(labelDialog).toContainText('InvenTreeLabel');
|
||||||
|
|
||||||
|
// Submit again without re-selecting template or plugin
|
||||||
|
const printResponse = page.waitForResponse(
|
||||||
|
(response) =>
|
||||||
|
response.url().includes('/api/label/print/') &&
|
||||||
|
response.request().method() === 'POST' &&
|
||||||
|
response.ok()
|
||||||
|
);
|
||||||
|
await labelDialog.getByRole('button', { name: 'Print', exact: true }).click();
|
||||||
|
|
||||||
|
await printResponse;
|
||||||
|
|
||||||
await page.context().close();
|
await page.context().close();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user