Fix NetSuite Customer to Vendor Transform Error
record.transform() is designed for transaction workflows where a source transaction carries forward line items, dates, and addressing into a target transaction.

On this page
The record.transform() method works for transaction-to-transaction conversions, Sales Order to Invoice, Purchase Order to Vendor Bill, but it does not support entity-to-entity transforms. When you call record.transform({ fromType: record.Type.CUSTOMER, toType: record.Type.VENDOR }), NetSuite throws "That record does not exist" because no transform definition exists between those two record types. The documentation table you saw lists supported source records for various targets, not bidirectional entity transforms. To link a Customer and Vendor programmatically, you must create the Vendor record explicitly and populate the Other Relationships sublist on one or both records.
Why the Transform Fails
record.transform() is designed for transaction workflows where a source transaction carries forward line items, dates, and addressing into a target transaction. Entity records (Customer, Vendor, Employee, Lead) have no such transform mappings. The SuiteScript Records Guide confirms: transform operations are defined per transaction type, not per entity type. When you pass record.Type.CUSTOMER as fromType, the transform loader looks for a transform definition keyed to the Customer record, none exists, so the API returns the generic "record does not exist" error instead of a helpful validation message.
The transform table in the help center lists "Customer → Vendor" under supported source records for Vendor Bill creation, not as a standalone entity transform. That row means you can transform a Customer Deposit or Customer Payment into a Vendor Bill, not a Customer entity into a Vendor entity.
Correct Approach: Create Vendor + Link via Other Relationships
The reliable pattern is a two-step process:
- Create (or load) the Vendor record, copying relevant fields from the Customer.
- Add a line to the Other Relationships sublist on the Vendor (or Customer) pointing to the counterpart.
Step 1: Create the Vendor Record
/**
* @NApiVersion 2.1
* @NScriptType ScheduledScript
*/
define(['N/record', 'N/search', 'N/log'], (record, search, log) => {
const execute = (context) => {
const CUSTOMER_ID = 1234; // replace with actual internal ID
const custRec = record.load({ type: record.Type.CUSTOMER, id: CUSTOMER_ID });
// Pull fields you want to carry over
const vendorData = {
companyname: custRec.getValue({ fieldId: 'companyname' }),
email: custRec.getValue({ fieldId: 'email' }),
phone: custRec.getValue({ fieldId: 'phone' }),
addressbook: [] // see note below
};
// Create new Vendor
const vendorRec = record.create({ type: record.Type.VENDOR, isDynamic: true });
Object.entries(vendorData).forEach(([fieldId, value]) => {
if (value) vendorRec.setValue({ fieldId, value });
});
// Address sublist requires special handling — iterate addressbook lines
const addrCount = custRec.getLineCount({ sublistId: 'addressbook' });
for (let i = 0; i < addrCount; i++) {
const addrSubrec = custRec.getSublistSubrecord({ sublistId: 'addressbook', fieldId: 'addressbookaddress', line: i });
if (addrSubrec) {
vendorRec.selectNewLine({ sublistId: 'addressbook' });
const vAddr = vendorRec.getCurrentSublistSubrecord({ sublistId: 'addressbook', fieldId: 'addressbookaddress' });
['addr1', 'addr2', 'city', 'state', 'zip', 'country', 'addressee', 'attention', 'phone'].forEach(f => {
const val = addrSubrec.getValue({ fieldId: f });
if (val) vAddr.setValue({ fieldId: f, value: val });
});
vendorRec.commitLine({ sublistId: 'addressbook' });
}
}
const vendorId = vendorRec.save({ enableSourcing: true, ignoreMandatoryFields: false });
log.audit('Vendor Created', `Customer ${CUSTOMER_ID} → Vendor ${vendorId}`);
// Step 2: Link via Other Relationships (on Vendor side)
linkOtherRelationship(vendorId, CUSTOMER_ID);
};
const linkOtherRelationship = (vendorId, customerId) => {
const vendorRec = record.load({ type: record.Type.VENDOR, id: vendorId });
const relCount = vendorRec.getLineCount({ sublistId: 'relationships' });
let exists = false;
for (let i = 0; i < relCount; i++) {
const entityId = vendorRec.getSublistValue({ sublistId: 'relationships', fieldId: 'entityid', line: i });
if (entityId == customerId) { exists = true; break; }
}
if (!exists) {
vendorRec.selectNewLine({ sublistId: 'relationships' });
vendorRec.setCurrentSublistValue({ sublistId: 'relationships', fieldId: 'entityid', value: customerId });
vendorRec.setCurrentSublistValue({ sublistId: 'relationships', fieldId: 'relationshiptype', value: 'CUSTOMER' });
vendorRec.commitLine({ sublistId: 'relationships' });
vendorRec.save();
log.audit('Relationship Linked', `Vendor ${vendorId} ↔ Customer ${customerId}`);
}
};
return { execute };
});Field Mapping Notes
| Customer Field | Vendor Field | Notes |
|---|---|---|
companyname | companyname | Required on Vendor |
email | email | |
phone | phone | |
addressbook (sublist) | addressbook (sublist) | Must iterate subrecords; no direct copy |
subsidiary | subsidiary | Only in OneWorld; copy if applicable |
currency | currency | Vendor uses currency; Customer uses currency, same ID |
terms | terms | Payment terms; internal IDs must match |
The addressbook sublist uses subrecords (addressbookaddress). You cannot setSublistValue on it directly, you must getSublistSubrecord on the source, then selectNewLine/getCurrentSublistSubrecord on the target.
Linking an Existing Vendor Instead
If the Vendor already exists and you only need the relationship:
const linkExisting = (vendorId, customerId) => {
const vendorRec = record.load({ type: record.Type.VENDOR, id: vendorId });
const relCount = vendorRec.getLineCount({ sublistId: 'relationships' });
for (let i = 0; i < relCount; i++) {
if (vendorRec.getSublistValue({ sublistId: 'relationships', fieldId: 'entityid', line: i }) == customerId) {
log.audit('Link Exists', 'Relationship already present');
return;
}
}
vendorRec.selectNewLine({ sublistId: 'relationships' });
vendorRec.setCurrentSublistValue({ sublistId: 'relationships', fieldId: 'entityid', value: customerId });
vendorRec.setCurrentSublistValue({ sublistId: 'relationships', fieldId: 'relationshiptype', value: 'CUSTOMER' });
vendorRec.commitLine({ sublistId: 'relationships' });
vendorRec.save();
};The relationshiptype value CUSTOMER is a fixed list entry; other options include VENDOR, EMPLOYEE, PARTNER, CONTACT. On the Customer record, the same sublist ID is relationships and the type value for a Vendor link is VENDOR.
Common Pitfalls
- Mandatory fields: Vendor requires
companyname(orentityidif not using company name). If the Customer is an individual (isperson = T), mapfirstname/lastnametocompanynameor setentityidmanually. - Subsidiary restrictions: In OneWorld, the Vendor must be assigned to at least one subsidiary the script's runtime subsidiary can access. Set
subsidiarybefore saving. - Duplicate detection: NetSuite's duplicate vendor check runs on
companyname+subsidiary. Run asearch.create({ type: search.Type.VENDOR, filters: [...] })first to avoid creating duplicates. - Governance: Creating a Vendor with addresses consumes ~10–15 governance units. In a Scheduled Script processing hundreds, batch with
yieldor switch to Map/Reduce.
When to Use This Pattern
Use the create-and-link approach whenever you need a Vendor that mirrors a Customer, shared payee/payer scenarios, marketplace onboarding, or intercompany clearing. If you only need the relationship for reporting (e.g., "show me all Customers who are also Vendors"), a saved search joining the relationships sublist on both records is lighter weight and requires no script deployment.
The transform error you hit is a documentation trap: the help table shows transaction transforms where Customer payments become Vendor bills, not entity-to-entity. Build the Vendor record directly, link the relationship, and you'll have a clean, auditable link that survives upgrades.


