Why that column prints blank on your PDF
It shows in the UI and it is empty on the document. The template is fine — a rendered PDF sees stored data, not derived data.
The column that printed nothing
A pick ticket with a Bin column, so the warehouse knows where to walk. The column is populated on the transaction lines. It shows correctly in the UI. On the printed PDF it is blank on every line.
Not wrong. Not stale. Empty.
The wrong theory
A blank column in a rendered template looks like a template problem, so that is where the time goes. Check the field reference. Check the loop. Check for a typo in the column id. Print the whole line object to see what the template can actually see.
That last one is the step that ends it, because what comes back shows the field present and empty — which rules out the template and points at the data being handed to it.
Sourced fields are not stored fields
The Bin column was a sourced transaction column field: its value comes from a field on the item record, pulled in automatically.
Sourcing runs in the client and during save. It populates the field on the form as you work, and if the value is stored it persists. What it does not do is recompute when the record is loaded as a data source for a render:
renderer.addRecord({
templateName: 'record',
record: record.load({ type: record.Type.SALES_ORDER, id: soId })
});
record.load() gives you what is stored. A sourced column that derives its value at form time, and was never written to the database, is simply not there to load. The template asks for it, gets nothing, prints nothing.
Resolve it yourself and inject it
The fix is to read the source values directly and hand them to the template as an extra data source:
const buildBinMap = (soRec) => {
const out = {}, itemIds = [];
const lc = soRec.getLineCount({ sublistId: 'item' });
for (let i = 0; i < lc; i++) {
const id = soRec.getSublistValue({ sublistId: 'item', fieldId: 'item', line: i });
if (id && itemIds.indexOf(id) === -1) itemIds.push(id);
}
if (!itemIds.length) return out;
query.runSuiteQL({
query: 'SELECT itemid, custitem_bin AS bin, custitem_wh_bin AS whbin ' +
'FROM item WHERE id IN (' + itemIds.map(Number).join(',') + ')'
}).asMappedResults().forEach(r => {
if (r.itemid) out[String(r.itemid)] = { bin: r.bin || '', whbin: r.whbin || '' };
});
return out;
};
renderer.addCustomDataSource({
format: render.DataSource.OBJECT,
alias: 'binmap',
data: buildBinMap(soRec)
});
One query for every item on the document rather than a lookup per line, and the template reads it as binmap["ITEM-CODE"].bin.
The detail that costs the second hour
Key the map by the same thing the template prints.
In an Advanced PDF template, ${item.item} renders the item name, not its internal id. So a map keyed by internal id looks perfectly correct in the script, is correct as data, and returns nothing at all in the template — because the template is looking up binmap["PKG-BOX-A"] and your keys are binmap["4471"].
Same symptom as the original bug: a blank column and no error anywhere. Worth logging the map once during development and confirming the keys look like what the template is asking for.
Another one from the same family
The render context is not the UI context, and that catches more than sourced fields. A pick ticket footer printed the operator's name using the template's built-in user reference:
Packed by: ${user}
In an interactive print that is the person clicking Print. In a Map/Reduce render it is the execution context user, which is shared and indeterminate — and under two batches running at once it can resolve to somebody else's operator entirely.
The fix is the same shape: stop asking the render engine for context it does not have, and pass the value in explicitly.
renderer.addCustomDataSource({
format: render.DataSource.OBJECT,
alias: 'meta',
data: { createdBy: createdBy || '' }
});
Where createdBy was captured at the moment the batch was created, by the person who created it, and carried through to the render.
How to spot this class of bug quickly
- If a field prints blank rather than wrong, suspect the data source before the template.
- Ask whether the value is stored on the record or derived. Sourced columns, formula fields and client-script defaults are all derived.
- Ask whether the value depends on who or what is running. If it does, the render engine is the wrong place to ask.
- When you inject a data source, confirm its keys match what the template is looking up — names, not ids.
Everything a rendered document prints is either stored, or something you handed it. Anything else is a coincidence that works in the UI.
Related: MISSING_PDF_PARAMETERS when the template was never set.