Suite Utils
Back to Blog
NetSuite TipsSep 19, 2026 • 6 min read

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.

Ethan James MarshalEthan James MarshalSenior SuiteScript Architect & Lead NetSuite Engineer
Fix NetSuite Customer to Vendor Transform Error
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.

The reliable pattern is a two-step process:

  1. Create (or load) the Vendor record, copying relevant fields from the Customer.
  2. 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 FieldVendor FieldNotes
companynamecompanynameRequired on Vendor
emailemail
phonephone
addressbook (sublist)addressbook (sublist)Must iterate subrecords; no direct copy
subsidiarysubsidiaryOnly in OneWorld; copy if applicable
currencycurrencyVendor uses currency; Customer uses currency, same ID
termstermsPayment 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 (or entityid if not using company name). If the Customer is an individual (isperson = T), map firstname/lastname to companyname or set entityid manually.
  • Subsidiary restrictions: In OneWorld, the Vendor must be assigned to at least one subsidiary the script's runtime subsidiary can access. Set subsidiary before saving.
  • Duplicate detection: NetSuite's duplicate vendor check runs on companyname + subsidiary. Run a search.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 yield or 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.

About the author

Put these ideas to work.

Suite Utils builds small NetSuite tools that fix the specific thing breaking your day. Each one runs as a native SuiteScript SuiteApp inside your account. No sales call, no onboarding.

Browse the Tools

Enjoyed this one?

Get NetSuite tips like this in your inbox. No spam. Practical guides only.

Keep reading