2
0
mirror of https://github.com/inventree/InvenTree.git synced 2025-07-02 19:50:59 +00:00

[plugin] Auto issue orders (#9565)

* Add builtin plugin for auto-issuing orders

* Add plugin to auto-issue orders

* Add placeholder documentation

* Fix typo

* Adds image macro

- To replace img.html
- includes checking if file exists

* Fix tooltips

* More docs

* Adjust plugin settings filters

* docs

* More docs

* More docs

* Updates

* Less restrictive URL checking

* Refactor build order page

* Fix typo

* Allow 429

* Debug output

* More debug

* Construct assets dir

* Cleanup

* Update docs README

* Refactoring more pages

* Fix image link

* Fix SSO settings

* Add hook to check for missing settings

- Ensure that all settings are documented!

* Add missing user settings

* Update docstring

* Tweak SSO.md

* Image updates

* More updates

* Tweaks

* Exclude orders without a target_date

* Fix for issuing build orders

* Further refactoring

* Fixes

* Image refactoring

* More refactoring

* More refactoring

* Refactor app images

* Fix pathing issues

* Suppress some openapidocs warnings in logs

(much easier to debug docs build issues)

* Fix image reference

* Reduce error messages

* Fix image links

* Fix image links

* Reduce docs log output

* Ensure settings are loaded before displaying them

* Fix for UI test

* Fix unit test

* Test tweaks
This commit is contained in:
Oliver
2025-06-03 17:07:12 +10:00
committed by GitHub
parent 89f8f132e1
commit 11ab0203b1
124 changed files with 1178 additions and 957 deletions

View File

@ -48,6 +48,7 @@ export interface SettingsStateProps {
settings: Setting[];
lookup: SettingsLookup;
fetchSettings: () => Promise<boolean>;
loaded: boolean;
endpoint: ApiEndpoints;
pathParams?: PathParams;
getSetting: (key: string, default_value?: string) => string; // Return a raw setting value

View File

@ -1,6 +1,6 @@
import { t } from '@lingui/core/macro';
import { Trans } from '@lingui/react/macro';
import { Stack, Text } from '@mantine/core';
import { Skeleton, Stack, Text } from '@mantine/core';
import { notifications } from '@mantine/notifications';
import React, {
useCallback,
@ -133,6 +133,10 @@ export function SettingList({
[settingsState]
);
if (!settingsState?.loaded) {
return <Skeleton animate />;
}
return (
<>
{editSettingModal.modal}

View File

@ -21,6 +21,7 @@ import { useUserState } from './UserState';
export const useGlobalSettingsState = create<SettingsStateProps>(
(set, get) => ({
settings: [],
loaded: false,
lookup: {},
endpoint: ApiEndpoints.settings_global_list,
fetchSettings: async () => {
@ -28,6 +29,9 @@ export const useGlobalSettingsState = create<SettingsStateProps>(
const { isLoggedIn } = useUserState.getState();
if (!isLoggedIn()) {
set({
loaded: false
});
return success;
}
@ -36,12 +40,17 @@ export const useGlobalSettingsState = create<SettingsStateProps>(
.then((response) => {
set({
settings: response.data,
loaded: true,
lookup: generate_lookup(response.data)
});
})
.catch((_error) => {
console.error('ERR: Error fetching global settings');
success = false;
set({
loaded: false
});
});
return success;
@ -62,12 +71,16 @@ export const useGlobalSettingsState = create<SettingsStateProps>(
export const useUserSettingsState = create<SettingsStateProps>((set, get) => ({
settings: [],
lookup: {},
loaded: false,
endpoint: ApiEndpoints.settings_user_list,
fetchSettings: async () => {
let success = true;
const { isLoggedIn } = useUserState.getState();
if (!isLoggedIn()) {
set({
loaded: false
});
return success;
}
@ -76,12 +89,16 @@ export const useUserSettingsState = create<SettingsStateProps>((set, get) => ({
.then((response) => {
set({
settings: response.data,
lookup: generate_lookup(response.data)
lookup: generate_lookup(response.data),
loaded: true
});
})
.catch((_error) => {
console.error('ERR: Error fetching user settings');
success = false;
set({
loaded: false
});
});
return success;
@ -110,6 +127,7 @@ export const createPluginSettingsState = ({
return createStore<SettingsStateProps>()((set, get) => ({
settings: [],
lookup: {},
loaded: false,
endpoint: ApiEndpoints.plugin_setting_list,
pathParams,
fetchSettings: async () => {
@ -121,12 +139,16 @@ export const createPluginSettingsState = ({
const settings = response.data;
set({
settings,
lookup: generate_lookup(settings)
lookup: generate_lookup(settings),
loaded: true
});
})
.catch((_error) => {
console.error(`Error fetching plugin settings for plugin ${plugin}`);
success = false;
set({
loaded: false
});
});
return success;
@ -158,6 +180,7 @@ export const createMachineSettingsState = ({
return createStore<SettingsStateProps>()((set, get) => ({
settings: [],
lookup: {},
loaded: false,
endpoint: ApiEndpoints.machine_setting_detail,
pathParams,
fetchSettings: async () => {
@ -171,7 +194,8 @@ export const createMachineSettingsState = ({
);
set({
settings,
lookup: generate_lookup(settings)
lookup: generate_lookup(settings),
loaded: true
});
})
.catch((error) => {
@ -180,6 +204,9 @@ export const createMachineSettingsState = ({
error
);
success = false;
set({
loaded: false
});
});
return success;

View File

@ -13,7 +13,7 @@ export function TableColumnSelect({
<Menu shadow='xs' closeOnItemClick={false}>
<Menu.Target>
<ActionIcon variant='transparent' aria-label='table-select-columns'>
<Tooltip label={t`Select Columns`}>
<Tooltip label={t`Select Columns`} position='top-end'>
<IconAdjustments />
</Tooltip>
</ActionIcon>

View File

@ -208,7 +208,7 @@ export default function InvenTreeTableHeader({
)}
{tableProps.enableRefresh && (
<ActionIcon variant='transparent' aria-label='table-refresh'>
<Tooltip label={t`Refresh data`}>
<Tooltip label={t`Refresh data`} position='top-end'>
<IconRefresh
onClick={() => {
tableState.refreshTable();
@ -235,7 +235,7 @@ export default function InvenTreeTableHeader({
variant='transparent'
aria-label='table-select-filters'
>
<Tooltip label={t`Table Filters`}>
<Tooltip label={t`Table Filters`} position='top-end'>
<IconFilter
onClick={() => setFiltersVisible(!filtersVisible)}
/>
@ -245,7 +245,7 @@ export default function InvenTreeTableHeader({
)}
{tableUrl && tableProps.enableDownload && (
<ActionIcon variant='transparent' aria-label='table-export-data'>
<Tooltip label={t`Download data`} position='bottom'>
<Tooltip label={t`Download data`} position='top-end'>
<IconDownload onClick={exportModal.open} />
</Tooltip>
</ActionIcon>

View File

@ -15,10 +15,13 @@ export default function ScheduledTasksTable() {
const columns: TableColumn[] = useMemo(() => {
return [
{
accessor: 'func',
accessor: 'name',
title: t`Task`,
sortable: true,
switchable: false
switchable: false,
render: (record: any) => {
return record.name || record.task;
}
},
{
accessor: 'last_run',

View File

@ -128,11 +128,13 @@ test('Part - Editing', async ({ browser }) => {
// Test URL validation
await page.getByLabel('text-field-link').fill('htxp-??QQQ++');
await page.waitForTimeout(200);
await page.getByRole('button', { name: 'Submit' }).click();
await page.getByText('Enter a valid URL.').waitFor();
// Fill with an empty URL
await page.getByLabel('text-field-link').fill('');
await page.waitForTimeout(200);
await page.getByRole('button', { name: 'Submit' }).click();
await page.getByText('Item Updated').waitFor();
});

View File

@ -41,7 +41,10 @@ test('Plugins - Settings', async ({ browser, request }) => {
// Edit numerical value
await page.getByLabel('edit-setting-NUMERICAL_SETTING').click();
const originalValue = await page.getByLabel('number-field-value').innerText();
const originalValue = await page
.getByLabel('number-field-value')
.inputValue();
await page
.getByLabel('number-field-value')
.fill(originalValue == '999' ? '1000' : '999');

View File

@ -28,7 +28,7 @@ test('Label Printing', async ({ browser }) => {
// Select plugin
await page.getByLabel('related-field-plugin').click();
await page.getByText('InvenTreeLabelSheet').last().click();
await page.getByText('InvenTreeLabelMachine').last().click();
// Select label template
await page.getByLabel('related-field-template').click();
@ -37,7 +37,8 @@ test('Label Printing', async ({ browser }) => {
await page.waitForTimeout(100);
await page.getByLabel('related-field-plugin').click();
await page.getByText('InvenTreeLabelSheet').last().click();
await page.getByText('InvenTreeLabel', { exact: true }).click();
// Submit the print form (second time should result in success)
await page.getByRole('button', { name: 'Print', exact: true }).isEnabled();