2
0
mirror of https://github.com/inventree/InvenTree.git synced 2026-04-04 10:31:03 +00:00

[Enhancement] Import by natural key fields (#11288)

* Data import flexibility

- Allow specification of alternative lookup fields for data import

* Observe field filters during data import

* Add alternative import fields for Part models

* More options for IMPORT_ID_FIELDS

* Update src/backend/InvenTree/importer/models.py

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Bump CHANGELOG

* Handle empty input values

* Add IMPORT_ID_FIELDS for more models

* PK field takes highest priority

* Update import docs

* Tweak return type

* Handle multiple date formats

* Add playwright testing

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
Oliver
2026-02-20 15:30:00 +11:00
committed by GitHub
parent 1ac3f5e479
commit 14d6d2354f
11 changed files with 220 additions and 3 deletions

View File

@@ -0,0 +1,3 @@
Project Code,Quantity,Reference,Target Date,Project Code Label,Build Order,Overdue,Received,Purchase price,Currency,Auto Pricing,Destination,Total price,SKU,MPN,Internal Part Number,Internal Part,Internal Part Name
5,123,,30/01/2026,PRO-ZEN,,TRUE,0,0.48,USD,TRUE,,59.04,FUT-43861-DDU,,,55,C_100nF_0603
,9999,my-custom-reference,,,,FALSE,0,0.1179,USD,TRUE,9,1178.8821,FUT-82092-CQB,,,60,C_10uF_0805
1 Project Code Quantity Reference Target Date Project Code Label Build Order Overdue Received Purchase price Currency Auto Pricing Destination Total price SKU MPN Internal Part Number Internal Part Internal Part Name
2 5 123 30/01/2026 PRO-ZEN TRUE 0 0.48 USD TRUE 59.04 FUT-43861-DDU 55 C_100nF_0603
3 9999 my-custom-reference FALSE 0 0.1179 USD TRUE 9 1178.8821 FUT-82092-CQB 60 C_10uF_0805

View File

@@ -172,3 +172,53 @@ test('Importing - Purchase Order', async ({ browser }) => {
await page.getByRole('cell', { name: 'Database Field' }).waitFor();
await page.getByRole('cell', { name: 'Field Description' }).waitFor();
});
test('Importing - Natural Keys', async ({ browser }) => {
const page = await doCachedLogin(browser, {
username: 'steven',
password: 'wizardstaff',
url: 'purchasing/purchase-order/15/line-items'
});
// Import line item data, but use natural keys as the import fields
await page
.getByRole('button', { name: 'action-button-import-line-' })
.click();
const fileInput = await page.locator('input[type="file"]');
await fileInput.setInputFiles('./tests/fixtures/po_data_natural_keys.csv');
await page.getByRole('button', { name: 'Submit' }).click();
// Attempt import with missing required fields
await page.getByRole('button', { name: 'Accept Column Mapping' }).click();
await page.getByText('Some required fields have not been mapped').waitFor();
// Select different columns for data import
// We will use the "SKU" field to map to the supplier part
await page.getByRole('textbox', { name: 'import-column-map-part' }).click();
await page.getByRole('option', { name: 'SKU' }).click();
// Other import fields will be left as default
await page.getByRole('button', { name: 'Accept Column Mapping' }).click();
// Check for expected values to be displayed
await page.getByText('PRO-ZEN').first().waitFor();
await page.getByText('Project Zenith').first().waitFor();
await page.getByText('my-custom-reference').first().waitFor();
await page.getByText('Factory/Mechanical Lab').first().waitFor();
await page.getByText('FUT-43861-DDU').first().waitFor();
await page.getByText('FUT-82092-CQB').first().waitFor();
await page.getByText('2026-01-30').first().waitFor();
// Let's import all the data
await page
.getByRole('row', { name: 'Select all records Row Not' })
.getByLabel('Select all records')
.click();
await page
.getByRole('button', { name: 'action-button-import-selected' })
.click();
await page.getByText('Data has been imported successfully').waitFor();
await page.getByRole('button', { name: 'Close' }).click();
});