When you first start cloning records in X++, or talking about x++ buf2buf where you are copying records without dragging system fields along it’s natural to think “I’ll just copy everything from one buffer into another and insert,” but there are two subtly different tools for that job and they don’t behave the same. The data() method on an xRecord copies all fields, including system fields like RecId, dataAreaId, and audit info, whereas buf2Buf() copies user (business) fields only and leaves system fields alone so the platform can generate fresh values. For beginners this difference matters a lot: it changes whether your new row is a clean, independent record or a suspicious twin carrying metadata it shouldn’t.
In the typical same-company, same-table scenario, a quick dest.data(source); dest.insert(); will work because the kernel assigns a new RecId on insert, but you may still carry over things like dataAreaId or stale audit values you didn’t intend to preserve. By contrast, buf2Buf(source, dest); dest.insert(); deliberately avoids system fields, which keeps your clone honest and lets the database and kernel fill in company, identity, and timestamps correctly. This makes buf2Buf() an easy default when you want a “business-only” copy that behaves like a brand-new row, especially helpful if you’re switching companies with changeCompany(...) or writing to a log table that should have its own identity.
You’ll also hear people talk about “shallow versus deep” copies; both data() and buf2Buf() copy only the scalar fields in the one buffer you’re holding, not related child rows, attachments, or references in other tables. If you need a true business duplicate—say, a sales line plus its associated reservation records—you’ll still have to query and clone those explicitly. Think of data() and buf2Buf() as buffer-population shortcuts, not full object graph cloners.
Here’s a tiny side-by-side to make it concrete. Imagine an electronics warehouse table WhsBinNote with a free-text note you sometimes duplicate to another record. The first fragment uses data(), the second uses buf2Buf(); both tweak a field before inserting.
// Same-company clone with data(): copies ALL fields (incl. system)
WhsBinNote src = WhsBinNote::find(binId);
WhsBinNote dst;
dst.clear();
dst.data(src); // includes RecId, dataAreaId, etc.
dst.Note = src.Note + " (copy)";
dst.insert(); // RecId re-generated, other system fields may carry semantics
// Cross-company or “clean clone” with buf2Buf(): copies NON-system fields only
changeCompany('USMF')
{
WhsBinNote src2 = WhsBinNote::find(binId);
WhsBinNote dst2;
dst2.clear();
buf2Buf(src2, dst2); // excludes system fields on purpose
dst2.Note = src2.Note + " (copy)";
dst2.insert(); // all system fields freshly assigned for this company
}
As you grow into multi-company and integration scenarios, you’ll appreciate why buf2Buf() is often the safer default: it reduces accidental leakage of tenant or identity metadata and makes your intent obvious—copy the business data, let the system mint the rest. Reserve data() for cases where you explicitly want a complete field-for-field transfer into a buffer before insert (still yielding a new RecId), and always keep in mind that neither approach brings along related rows. With that mental model, cloning records stops being mysterious, your inserts remain clean, and your warehouse logic for electronics—bins, labels, notes, and all—behaves predictably across companies and tables.
Have a Question ?
Fill out this short form, one of our Experts will contact you soon.
Talk to an Expert Today
Call Now