2
0
mirror of https://github.com/inventree/InvenTree.git synced 2025-10-30 04:35:42 +00:00

Delete locations fix (#10672)

* Cleaner handling of inputs

* Fix for frontend form:

- Fix typo in field
- Better option defaults

* Tweak part category delete form

* Add frontend tests
This commit is contained in:
Oliver
2025-10-26 11:40:22 +11:00
committed by GitHub
parent b579ccdaa2
commit 23d580c4a9
5 changed files with 81 additions and 14 deletions

View File

@@ -283,13 +283,11 @@ class CategoryDetail(CategoryMixin, OutputOptionsMixin, CustomRetrieveUpdateDest
def destroy(self, request, *args, **kwargs): def destroy(self, request, *args, **kwargs):
"""Delete a Part category instance via the API.""" """Delete a Part category instance via the API."""
delete_parts = ( delete_parts = str2bool(request.data.get('delete_parts', False))
'delete_parts' in request.data and request.data['delete_parts'] == '1' delete_child_categories = str2bool(
) request.data.get('delete_child_categories', False)
delete_child_categories = (
'delete_child_categories' in request.data
and request.data['delete_child_categories'] == '1'
) )
return super().destroy( return super().destroy(
request, request,
*args, *args,

View File

@@ -431,8 +431,12 @@ class StockLocationDetail(
def destroy(self, request, *args, **kwargs): def destroy(self, request, *args, **kwargs):
"""Delete a Stock location instance via the API.""" """Delete a Stock location instance via the API."""
delete_stock_items = str(request.data.get('delete_stock_items', 0)) == '1' delete_stock_items = InvenTree.helpers.str2bool(
delete_sub_locations = str(request.data.get('delete_sub_locations', 0)) == '1' request.data.get('delete_stock_items', False)
)
delete_sub_locations = InvenTree.helpers.str2bool(
request.data.get('delete_sub_locations', False)
)
return super().destroy( return super().destroy(
request, request,

View File

@@ -185,11 +185,11 @@ export default function CategoryDetail() {
const deleteOptions = useMemo(() => { const deleteOptions = useMemo(() => {
return [ return [
{ {
value: 0, value: 'false',
display_name: t`Move items to parent category` display_name: t`Move items to parent category`
}, },
{ {
value: 1, value: 'true',
display_name: t`Delete items` display_name: t`Delete items`
} }
]; ];
@@ -204,12 +204,14 @@ export default function CategoryDetail() {
label: t`Parts Action`, label: t`Parts Action`,
description: t`Action for parts in this category`, description: t`Action for parts in this category`,
choices: deleteOptions, choices: deleteOptions,
required: true,
field_type: 'choice' field_type: 'choice'
}, },
delete_child_categories: { delete_child_categories: {
label: t`Child Categories Action`, label: t`Child Categories Action`,
description: t`Action for child categories in this category`, description: t`Action for child categories in this category`,
choices: deleteOptions, choices: deleteOptions,
required: true,
field_type: 'choice' field_type: 'choice'
} }
}, },

View File

@@ -218,11 +218,11 @@ export default function Stock() {
const deleteOptions = useMemo(() => { const deleteOptions = useMemo(() => {
return [ return [
{ {
value: 0, value: 'false',
display_name: t`Move items to parent location` display_name: t`Move items to parent location`
}, },
{ {
value: 1, value: 'true',
display_name: t`Delete items` display_name: t`Delete items`
} }
]; ];
@@ -235,12 +235,14 @@ export default function Stock() {
fields: { fields: {
delete_stock_items: { delete_stock_items: {
label: t`Items Action`, label: t`Items Action`,
required: true,
description: t`Action for stock items in this location`, description: t`Action for stock items in this location`,
field_type: 'choice', field_type: 'choice',
choices: deleteOptions choices: deleteOptions
}, },
delete_sub_location: { delete_sub_locations: {
label: t`Child Locations Action`, label: t`Locations Action`,
required: true,
description: t`Action for child locations in this location`, description: t`Action for child locations in this location`,
field_type: 'choice', field_type: 'choice',
choices: deleteOptions choices: deleteOptions

View File

@@ -54,6 +54,67 @@ test('Stock - Location Tree', async ({ browser }) => {
await page.getByRole('cell', { name: 'Factory' }).first().waitFor(); await page.getByRole('cell', { name: 'Factory' }).first().waitFor();
}); });
test('Stock - Location Delete', async ({ browser }) => {
const page = await doCachedLogin(browser, {
url: 'stock/location/38/sublocations'
});
// Create a sub-location
await page
.getByRole('button', { name: 'action-button-add-stock-location' })
.click();
await page
.getByRole('textbox', { name: 'text-field-name' })
.fill('my-location-1');
await page.getByRole('button', { name: 'Submit' }).click();
// Create a secondary sub-location
await loadTab(page, 'Sublocations');
await page
.getByRole('button', { name: 'action-button-add-stock-location' })
.click();
await page
.getByRole('textbox', { name: 'text-field-name' })
.fill('my-location-2');
await page.getByRole('button', { name: 'Submit' }).click();
// Navigate up to parent
await page.getByRole('link', { name: 'breadcrumb-2-my-location-1' }).click();
await loadTab(page, 'Sublocations');
await page
.getByRole('cell', { name: 'my-location-2', exact: true })
.waitFor();
// Delete this location, and all child locations
await page
.locator('div')
.filter({ hasText: /^Stock>PCB Assembler>my-location-1Stock Location$/ })
.getByLabel('action-menu-location-actions')
.click();
await page
.getByRole('menuitem', { name: 'action-menu-location-actions-delete' })
.click();
await page
.getByRole('textbox', { name: 'choice-field-delete_stock_items' })
.click();
await page
.getByRole('option', { name: 'Move items to parent location' })
.click();
await page
.getByRole('textbox', { name: 'choice-field-delete_sub_locations' })
.click();
await page.getByRole('option', { name: 'Delete items' }).click();
await page.getByRole('button', { name: 'Delete' }).click();
// Confirm we are on the right page
await page.getByText('External PCB assembler').waitFor();
await loadTab(page, 'Sublocations');
await page.getByText('No records found').first().waitFor();
});
test('Stock - Filters', async ({ browser }) => { test('Stock - Filters', async ({ browser }) => {
const page = await doCachedLogin(browser, { const page = await doCachedLogin(browser, {
username: 'steven', username: 'steven',