Vendor item codes in NetSuite live on the Item record's Vendors subtab as rows of the itemvendor sublist. The vendorCode field on that sublist, a 15-character string, is the cross-reference key that ties a vendor's printed SKU to your internal NetSuite item ID. Everything else downstream, from purchase order defaulting to vendor bill line matching, depends on that one field being populated and correct.
Storing more than one vendor against an item requires the Multiple Vendors feature. Enable it once, per account, at Setup > Company > Enable Features > Items & Inventory > Multiple Vendors. Without it, each item carries exactly one vendor in the header Preferred Vendor field and cannot hold alternate-source SKUs.
There are four practical ways to populate and maintain vendor codes at scale:
- Edit the Vendors subtab directly on the item record.
- CSV Import to Items with the Vendors sublist mapped.
- SuiteScript 2.x against the
itemvendorrecord type. - Vendor Price List Import for vendor-provided price catalogs.
To match vendor item codes to inventory NetSuite uses as its unit of record is to build the data plumbing that purchase orders, vendor bill creation, and three-way match all read from. If the mapping layer is wrong, every downstream step is wrong too.
Why the mapping matters: vendor item codes as the data plumbing for three-way match
The NetSuite vendor cross reference is not a convenience field. It is the hinge on which purchasing automation, bill creation, and variance reporting all turn.
On a Purchase Order, NetSuite uses the itemvendor sublist as an index. When a buyer types the vendor's part number or vendor name into the PO line, NetSuite resolves it to the correct item only if there is an itemvendor row tying that vendor to that item. The header Preferred Vendor, a single-select on the item record, then auto-populates the PO Vendor field when the item is added to a new PO. The header drives the initial PO Vendor default when an item is added; the sublist governs line-level vendor-code resolution on transactions. When the two drift, the row-level preferredVendor flag on the sublist is authoritative: saving a row-flag change updates the header, but header edits alone do not always propagate back to the sublist. Both need to be right for the PO to behave.
Once goods are received and a vendor bill arrives, the "Match Bill to Receipt" flag on the PO enforces line-level matching: each bill line has to tie back to an item receipt line for the same item. That enforcement is cheap and useful when the mapping is clean, and actively misleading when it is not. If the bill arrives with the vendor's SKU and no itemvendor row connects it to your item, the line cannot be resolved automatically, and a user is either left to pick the item by hand or accept a workflow exception.
Three variance accounts receive postings when bill values diverge from receipt or PO values: Bill Quantity Variance, Bill Price Variance, and Bill Exchange Rate Variance. These are diagnostic accounts, read every month end by the controller or AP lead. They are only as meaningful as the mapping underneath them. Mismatched or missing vendor SKUs produce false variances, because the system matched against the wrong item or failed to match at all; the report then measures bookkeeping noise instead of real price or quantity drift. The native 3-Way Match Vendor Bill Approval Workflow SuiteApp reads these variances and routes bills for approval against configured tolerances. Feed it bad mapping data and you get bad approvals.
Treating vendor item code mapping as data plumbing rather than data entry is the right mental model. The mapping sits upstream of every other accounts payable automation step in NetSuite, and the downstream logic is only as good as the itemvendor layer it reads from. For the approval side of the story, see NetSuite 3-way match vendor bill approval; the variance accounts and tolerances covered there assume the mapping layer is already doing its job.
Data quality in procurement is getting more operational attention generally, not less. In the Amazon Business 2025 State of Procurement Data Report, 74% of senior procurement leaders and 64% of procurement decision-makers agreed that generating stronger data, insight and analysis over the next two years will be essential in supporting operational enhancements. Vendor-item mapping is the most concrete example of that data layer inside an AP stack: the cross-reference that decides whether every downstream match report, exception queue, and approval decision is built on firm ground or on guesses.
Setting vendor codes on the item record: the itemvendor sublist
The default entry point for a single item is the UI. From Lists > Accounting > Items, open the item, switch to the Purchasing/Inventory tab, and scroll to the Vendors subtab. That subtab is the UI view of the itemvendor sublist, and every row you see there is one vendor-to-item mapping the rest of NetSuite will read from.
Before going further, confirm that Multiple Vendors is enabled for the account. If it is not, the Vendors subtab will not accept more than one row, and the item's vendor relationship is held in the header Preferred Vendor field only. Turning the feature on later does not break existing records, but the UI behavior changes: the header field stays, and the sublist becomes the authoritative multi-vendor store.
Each row on the NetSuite itemvendor sublist carries the same set of fields:
preferredVendor(boolean): marks this vendor as the default for the item. Only one row can be preferred at a time; the flag is what drives the header Preferred Vendor on save.vendor(RecordRef, required): the vendor record this mapping associates. Blank is not valid.vendorCode(string, 15 characters): the vendor's SKU for this item. This is the NetSuite vendor part number field and the cross-reference key that lets an incoming invoice line or PO line resolve back to the NetSuite item. Populate it even when the vendor's code matches your own, because downstream lookups query the field directly rather than inferring fromitemid.purchasePrice: the per-unit price NetSuite should default on a PO for this vendor. Leave blank to force manual entry.schedule: a cost schedule reference, used where tiered or volume pricing applies.subsidiary: the subsidiary scope for OneWorld accounts; rows are filtered by the active subsidiary context when editing.vendorCurrencyName: the currency for this vendor's pricing on this item. The currency must already exist on the vendor record's Currencies subtab, or saves and imports will fail with a currency-not-available error.
The header Preferred Vendor field works in tandem with the row-level flag. When an item is added to a new Purchase Order, NetSuite reads the header Preferred Vendor to set the PO Vendor, then reads the matching itemvendor row to default the purchase price and terms. Changing the row-level preferredVendor from F to T on save updates the header; changing the header directly does not always update the sublist rows, so treat the row flag as the source of truth when you write integrations against this data.
One behavioral nuance often catches new admins. Typing a vendor's code or vendor name in an item field on a transaction line only resolves to the item for purchase and resale item types. For service items, inventory parts flagged as non-purchase, assemblies, and other types, the auto-resolve does not fire, and the item must be selected from the Item dropdown directly. If the buyer reports that typing the vendor's SKU does nothing, the item type is the first thing to check before suspecting the mapping.
Bulk loading vendor codes: CSV, Saved Search, SuiteScript, and Vendor Price List Import
Once you are past a handful of items, the UI stops being realistic. NetSuite offers four bulk paths to the itemvendor sublist, each suited to a different operational situation: CSV Import to Items with the Vendors sublist mapped, a Saved Search export-edit-re-import pattern, a SuiteScript 2.x N/record routine, and the Vendor Price List Import for vendor-provided catalogs.
CSV Import to Items with the Vendors sublist mapped
The standard bulk import for vendor codes is at Setup > Import/Export > Import CSV Records > Items, choosing the item record type the import targets and mapping the Vendors sublist columns during step three of the wizard. The minimum useful column set is:
- Item identifier (Name, Internal ID, or a unique key on the item master)
- Vendor (Name or Internal ID)
- Vendor Code
- Preferred Vendor flag (
TorF)
When you do a bulk import of vendor item codes NetSuite applies a default sublist handling behavior that catches many admins the first time. The default is "Replace", which on a sublist column means the import file's rows for an item replace every existing row on that item's Vendors sublist. If your file carries one line per item, the import will wipe any multi-vendor entries that were not in the file. Under Advanced Options on step two, change the sublist handling to Add when you want to append or update without disturbing existing rows. Use Replace intentionally, not by default.
A related CSV failure is the "Base Price missing" error. NetSuite requires Base Price for several item types, and an item import that does not include the field, or includes it empty for an item whose type requires it, fails validation before any Vendors-sublist rows are applied. Populate Base Price on the item file (zero is acceptable when the item has no standalone sell price), or segment the import into items that already have a Base Price set.
For the broader catalog of CSV pitfalls worth knowing before a large import, see common NetSuite CSV import errors and how to fix them; the vendor-item sublist rules sit inside that larger set.
Saved Search export-edit-re-import
A Saved Search on the Item Vendor record type is the simplest way to pull the current state of the itemvendor sublist into a spreadsheet for review or editing. Build a search with Item, Vendor, Vendor Code, Purchase Price, and Preferred Vendor as result columns, run it, export to CSV, edit the fields you need in Excel or Google Sheets, then import the edited file through the same CSV Import wizard described above. The pattern works well for periodic catalog hygiene, bulk price updates, or reconciling vendor codes against a supplier-provided master list. Keep the exported Item internal ID in the file so the re-import matches back to the exact items the search returned.
SuiteScript 2.x against the itemvendor record
When the update logic is conditional, or when the volume justifies programmatic handling, a SuiteScript 2.x scheduled script against the itemvendor sublist is the right tool. The N/record module loads the parent item, modifies the sublist in memory, and saves. A working skeleton:
/**
* @NApiVersion 2.1
* @NScriptType ScheduledScript
*/
define(['N/record', 'N/search'], (record, search) => {
const upsertVendorCode = (itemId, vendorId, vendorCode, purchasePrice, preferred) => {
const item = record.load({ type: record.Type.INVENTORY_ITEM, id: itemId, isDynamic: true });
// Find an existing itemvendor row for this vendor
const count = item.getLineCount({ sublistId: 'itemvendor' });
let line = -1;
for (let i = 0; i < count; i++) {
if (item.getSublistValue({ sublistId: 'itemvendor', fieldId: 'vendor', line: i }) === vendorId) {
line = i;
break;
}
}
if (line === -1) {
item.selectNewLine({ sublistId: 'itemvendor' });
} else {
item.selectLine({ sublistId: 'itemvendor', line: line });
}
item.setCurrentSublistValue({ sublistId: 'itemvendor', fieldId: 'vendor', value: vendorId });
item.setCurrentSublistValue({ sublistId: 'itemvendor', fieldId: 'vendorcode', value: vendorCode });
item.setCurrentSublistValue({ sublistId: 'itemvendor', fieldId: 'purchaseprice', value: purchasePrice });
item.setCurrentSublistValue({ sublistId: 'itemvendor', fieldId: 'preferredvendor', value: preferred });
item.commitLine({ sublistId: 'itemvendor' });
item.save({ ignoreMandatoryFields: false });
};
return { execute: (ctx) => { /* iterate a saved search of pending updates and call upsertVendorCode */ } };
});
Use the lowercase field IDs shown (vendor, vendorcode, purchaseprice, preferredvendor); the SuiteScript record API is case-sensitive and does not accept the camelCase form from the schema docs. Every record.save consumes governance units, so for large batches chunk the work across multiple scheduled script runs, or move to a Map/Reduce script that distributes the save cost across the Map stage.
Vendor Price List Import
The Vendor Price List Import, reached at Lists > Relationships > Vendors > Vendor Price Lists, is the right tool when a vendor sends you a complete price list to apply rather than a per-item change. It creates or updates itemvendor rows for that vendor across many items in one pass, respecting the vendor record's currency and subsidiary scope.
The most common failure here is "Currency not available to the vendor". NetSuite refuses to set a vendorCurrencyName on the itemvendor row for a currency the vendor record does not list on its own Currencies subtab. Open the vendor record, add the missing currency to the Currencies subtab, then retry the import.
All four paths produce the same result in the itemvendor table. The choice is about operational fit: CSV for one-off bulk changes, Saved Search export-edit-re-import for periodic hygiene, SuiteScript for conditional or scheduled updates, and Vendor Price List Import when a vendor hands you a price list and you want the currency-aware load.
Invoice-driven lookup: from PDF line items to NetSuite vendor bill lines
The UI and bulk paths assume you already know which NetSuite item a given vendor SKU maps to. In a real AP pipeline, that knowledge arrives mid-flow: a vendor sends a PDF invoice, the invoice carries line items printed in the vendor's own SKU namespace, and the system has to resolve each SKU to the correct internal item ID before a vendor bill can be written. Three steps do that work.
Step one: extract the line items from the PDF. Each invoice line yields, at minimum, the vendor's SKU, a description, a quantity, and a unit price. At the invoice header, the vendor's name or internal ID is captured so the later query can scope its match to the right vendor. Extraction tools pull vendor SKUs straight from PDF invoices into a structured file, typically JSON or CSV, with a shape along the lines of:
{
"vendor_name": "Acme Supplies Ltd",
"line_items": [
{ "code": "ACME-123", "description": "Widget, 10mm, steel", "qty": 25, "price": 100 },
{ "code": "ACME-456", "description": "Widget, 20mm, steel", "qty": 10, "price": 140 }
]
}
The code field is the key input to the NetSuite lookup; the vendor_name (or its internal ID) is the filter that prevents cross-vendor false positives. For a deeper walkthrough of the extraction layer connecting to NetSuite, see invoice capture for NetSuite AP automation.
Step two: run a SuiteQL cross-reference query. A SuiteQL item vendor cross-reference query joins item, itemvendor, and vendor to resolve the incoming SKU to an internal item ID, filtered by vendor so an identical code on two different vendors does not collide:
SELECT i.id, i.itemid AS netsuite_item, iv.vendorcode, v.entityid AS vendor_name
FROM item i
INNER JOIN itemvendor iv ON iv.item = i.id
INNER JOIN vendor v ON v.id = iv.vendor
WHERE iv.vendorcode = :vendor_sku_from_invoice
AND v.entityid = :vendor_name_from_invoice
The item join supplies the internal item ID (i.id) your bill line will need and the human-readable item identifier (i.itemid) for audit output. The itemvendor join restricts the match to rows where the vendor has actually been set up against that item; without it, every item in the account would be a candidate. The vendor join, filtered by v.entityid, scopes the lookup to the vendor on the invoice in hand. Skipping the vendor filter is the single most common way this query returns the wrong item: identical vendor codes across vendors are not rare, especially for commodity parts, and the vendor-name filter is what keeps the match honest. Run it through the SuiteQL REST endpoint or the Suite Analytics Connect driver, passing the vendor SKU and vendor name as bound parameters.
Step three: write the resolved item ID to a vendor bill line. The output of the query is the input to bill creation. Feed i.id to whichever path creates the vendor bill: the NetSuite REST API (vendorBill record), a SuiteScript that builds the bill from a script, or the native Bill Capture feature that ingests supplier emails and attachments. All three write to the same underlying record; the lookup step is reusable across them. For the detail on those bill-creation paths, see importing vendor bills into NetSuite.
This is the product-native angle that most competing guides skip. From the NetSuite side, step two is the SuiteQL query running against the customer's own account; step three is the bill creation in whichever pattern fits the integration. The extraction tool does not touch NetSuite; NetSuite does not know about the extraction tool. The vendorCode value is what they share.
Decision framework: map inside NetSuite or map in the extraction layer
The vendor-SKU-to-item mapping has to live somewhere. There are three defensible places to put it: inside NetSuite's itemvendor sublist, inside the extraction tool's own mapping layer, or in both with a designated source of truth. The choice is not theoretical. It determines how new vendors are onboarded, how three-way match behaves on day one of a relationship, how much NetSuite governance the pipeline consumes, and who owns the data when the extraction tool is eventually swapped out.
Option A: map inside NetSuite
The mapping lives on the itemvendor sublist. The extraction tool emits raw vendor SKUs; NetSuite resolves them at bill-creation time through the SuiteQL query or native Bill Capture.
Strengths:
- The mapping participates in native three-way match, PO auto-resolution, and variance posting without any extra work. The system of record and the system of truth are the same.
- The data is visible to standard NetSuite reports, Saved Searches, and any SuiteScript running against the account.
- When the extraction tool is replaced, the mapping stays. The next tool gets a working lookup layer for free.
Weaknesses:
- A new vendor has no
itemvendorrows until someone creates them. The first invoice from a new vendor cannot be line-matched automatically; it goes to an exception queue or a manual data-entry step. - Bulk maintenance for thousands of items requires CSV imports or SuiteScript. Both work, but both need admin time and governance units, and both are slower iteration loops than editing a mapping file.
Option B: map in the extraction layer
The extraction tool maintains its own vendor-SKU-to-NetSuite-item-ID table and emits the resolved item ID directly in its output. NetSuite never runs a lookup; it accepts the ID as given.
Strengths:
- New vendors can be onboarded without touching NetSuite. The mapping is added to the extraction layer's table, and the next invoice from that vendor flows through without an exception.
- Iteration is faster. Mapping edits are file edits or config changes, not item record saves. No NetSuite governance units are consumed by the mapping step.
- The same mapping table can feed multiple downstream targets, not just NetSuite; useful for a shared services group running multiple ERPs.
Weaknesses:
- The mapping is invisible to NetSuite. Reports, saved searches, and native matching logic cannot see it. If the extraction tool's mapping diverges from the
itemvendorsublist, native matching silently uses theitemvendorview and the extraction layer's view disagrees. - The extraction tool becomes a correctness dependency for data in your ERP. Swapping it out means rebuilding the mapping layer somewhere else.
Hybrid: NetSuite is the source of truth, extraction layer is a cache
The pragmatic pattern in production AP stacks treats NetSuite as the authoritative store and the extraction tool as a cache. A scheduled SuiteQL export, typically nightly, pulls the current itemvendor rows out of NetSuite and writes them into the extraction tool's mapping store. The extraction tool then emits both the raw vendor SKU and the pre-resolved internal item ID on lines where the cache holds a match; when the cache misses, it emits only the raw SKU and lets the SuiteQL lookup at bill-creation time handle the resolution. When a new vendor is onboarded, the mapping is created in NetSuite (through whichever bulk path fits the situation), and the next nightly sync propagates it to the cache.
The hybrid preserves the strengths of both options. NetSuite remains the system of record, so native matching, reporting, and variance posting all read correct data. The extraction layer accelerates day-one matching on recurring vendors without owning the data long term. If the extraction tool is replaced, the cache is rebuilt from the next sync; no data is lost.
The practical rule is short. If native three-way match is the primary goal, map inside NetSuite. If the speed of onboarding new vendors and the independence of the extraction tool matter more, map in the extraction layer. In most production AP stacks processing meaningful invoice volume, the hybrid wins; it gives you NetSuite correctness with extraction-layer iteration speed, and the cost is a single scheduled export.
Troubleshooting: common vendor item mapping failures and their fixes
The ten failures below are the recurring ones that show up on NetSuite Professionals threads, Oracle Community posts, and in AP-team incident queues. Consolidated into one reference with root cause and concrete fix:
| Symptom | Cause | Fix |
|---|---|---|
| Duplicate rows when searching items by vendor code. | A Saved Search joining from Item to Item Vendor returns one row per itemvendor entry for each matching item, so items with multiple vendors appear multiple times. | Group the Saved Search by Item internal ID, or replace the search with a SuiteQL query using SELECT DISTINCT to collapse the join. |
| Vendor part number does not appear on the PO print template. | vendorCode lives on the itemvendor sublist, not on the transaction line. PO print pulls data from the transaction, not from the item master. | Add a Transaction Column Field sourced from Item, Vendors, Vendor Code, and include it on the PO Advanced PDF/HTML print layout. |
| Typing a vendor code on a transaction line does not find the item. | Vendor-code auto-resolution only fires on purchase and resale item types. Other item types require explicit selection. | Confirm the item type, or select the item directly from the Item dropdown. |
| CSV import of vendor items wipes existing vendors on the Vendors sublist. | The default sublist handling is Replace, which substitutes all existing rows on the Vendors sublist with the rows in the import file. | In the CSV Import wizard Advanced Options, change sublist handling to Add, or include every existing vendor row in the import file before running. |
| "Currency not available to the vendor" error on Vendor Price List Import. | The vendor record does not have the target currency on its Currencies subtab, so vendorCurrencyName cannot be set on the itemvendor row. | Add the currency to the vendor record's Currencies subtab first, then retry the import. |
| EDI 810 inbound fails matching because the incoming SKU differs from the one on the item record. | The vendor's EDI SKU and the value in vendorCode are out of sync, often because the vendor updated its SKU without notice. | Update vendorCode to match the EDI 810 line item, or add an alternate-code mapping at the EDI integration layer so multiple vendor SKUs resolve to the same item. |
| Supplier silently changes a part number with no audit trail. | vendorCode is a plain field with no change history by default. | Run a scheduled SuiteQL audit that snapshots itemvendor daily, diff against the prior snapshot, and alert on unexpected changes. For a durable record, write diffs to a custom change-log record. |
| Bulk UI maintenance is slow for thousands of vendor items. | The item record UI is designed for single-record editing, not catalog-scale updates. | Use the SuiteScript 2.x itemvendor update pattern covered earlier, or the Saved Search export-edit-re-import flow, depending on volume and iteration speed. |
| Base Price missing causes CSV imports to fail. | NetSuite rejects item imports where Base Price is empty and the item type requires the field. | Populate Base Price (zero is acceptable) before import, or segment the import so only items that already carry Base Price are included. |
preferredVendor flag inconsistent with the header Preferred Vendor field. | The header field and the itemvendor row flag can drift when updates target only one of them. | Run a SuiteQL audit joining the item header Preferred Vendor to itemvendor rows where preferredvendor = 'T', and flag rows where the two disagree. Reconcile by treating the row-level flag as authoritative. |
Related Articles
Explore adjacent guides and reference articles on this topic.
NetSuite 3-Way Match Vendor Bill Approval Guide
NetSuite 3-way match vendor bill approval guide covering setup, tolerances, Pending Approval triggers, bill-before-receipt rules, and false variances.
Best NetSuite AP Automation SuiteApps in 2026
Compare native and integrated NetSuite AP SuiteApps in 2026, including Tipalti, Stampli, AvidXchange, and Bill Capture. See when an API-led build fits better.
NetSuite CSV Import Errors: How to Fix the Most Common Issues
Fix the most common NetSuite CSV import errors: invalid reference keys, date mismatches, duplicates, and more. Error-by-error diagnosis with vendor bill focus.
Extract invoice data to Excel with natural language prompts
Upload your invoices, describe what you need in plain language, and download clean, structured spreadsheets. No templates, no complex configuration.