πΌ Business Requirement
The client needs a script that automatically fills in Department and Class fields on each line item based on the Cost Center selected either at the header level or at the line level.
This ensures that accounting data stays consistent across transactions and removes the need for users to manually select departments and classes for every line item.
π― Key Requirements
- When a user selects a Cost Center on the Header, all line items should automatically get the corresponding Department and Class.
- If the Cost Center on a line is modified manually, that lineβs Department and Class should be updated accordingly.
- If the Cost Center on the Header changes during Edit, all line-level Department and Class values should update to match the new Cost Center.
- Script should work for Sales Orders, Purchase Orders, and Journal Entries.
- On Edit mode, if a user manually changes a line-level cost center, the header cost center should not override it.
π§© Case Scenarios
| # | Scenario | Expected Behavior |
|---|---|---|
| 1 | Cost Center selected on Header | Department and Class auto-populate on all lines |
| 2 | Cost Center changed on Header after lines exist | All line Departments and Classes update automatically |
| 3 | Cost Center manually changed on one Line | Only that line updates (header values remain) |
| 4 | Record opened for Edit | Header values no longer override existing lines |
π§ Solution Approach
To handle both real-time and backend updates, weβll use a combination of:
- Client Script β Runs in real time in the browser as users interact.
- Triggers on
fieldChangedevent whencostcenteris updated on either header or line. - Updates
departmentandclassfields instantly.
- Triggers on
- User Event Script β Runs during record creation or edit in the backend (server-side).
- Ensures that Department and Class are correct even for records created by CSV import, scripts, or integrations.
- Re-applies mapping logic in
beforeSubmitorafterSubmit.
π§© Data Source: Cost Center Mapping
Create a Custom Record (Cost Center Mapping) to store relationships between Cost Center β Department β Class.
Example fields:
| Field | ID | Description |
|---|---|---|
| Cost Center | custrecord_cost_center | Reference to Cost Center record |
| Department | custrecord_cost_center_dept | Department linked to that Cost Center |
| Class | custrecord_cost_center_class | Class linked to that Cost Center |
The scripts will lookup this mapping record to find the appropriate department and class values.
π§© SuiteScript 2.1 β Client Script (for Real-Time UI Updates)
/**
* @NApiVersion 2.1
* @NScriptType ClientScript
* @Author The NetSuite Pro
* @Description Auto-populates Department and Class based on Cost Center selection
*/
define(['N/search', 'N/currentRecord'], (search, currentRecord) => {
function fieldChanged(context) {
const rec = context.currentRecord;
const fieldId = context.fieldId;
// Header-level change
if (fieldId === 'custbody_cost_center') {
const costCenter = rec.getValue('custbody_cost_center');
if (!costCenter) return;
// Loop through each line and update
const lineCount = rec.getLineCount({ sublistId: 'item' });
for (let i = 0; i < lineCount; i++) {
const mapping = getCostCenterMapping(costCenter);
if (mapping) {
rec.selectLine({ sublistId: 'item', line: i });
rec.setCurrentSublistValue({ sublistId: 'item', fieldId: 'department', value: mapping.department });
rec.setCurrentSublistValue({ sublistId: 'item', fieldId: 'class', value: mapping.class });
rec.commitLine({ sublistId: 'item' });
}
}
}
// Line-level change
if (fieldId === 'custcol_cost_center') {
const line = context.line;
const costCenter = rec.getCurrentSublistValue({ sublistId: 'item', fieldId: 'custcol_cost_center' });
if (!costCenter) return;
const mapping = getCostCenterMapping(costCenter);
if (mapping) {
rec.setCurrentSublistValue({ sublistId: 'item', fieldId: 'department', value: mapping.department });
rec.setCurrentSublistValue({ sublistId: 'item', fieldId: 'class', value: mapping.class });
}
}
}
function getCostCenterMapping(costCenterId) {
const results = search.create({
type: 'customrecord_cost_center_mapping',
filters: [['custrecord_cost_center', 'is', costCenterId]],
columns: ['custrecord_cost_center_dept', 'custrecord_cost_center_class']
}).run().getRange({ start: 0, end: 1 });
if (results && results.length > 0) {
return {
department: results[0].getValue('custrecord_cost_center_dept'),
class: results[0].getValue('custrecord_cost_center_class')
};
}
return null;
}
return { fieldChanged };
});
π§© SuiteScript 2.1 β User Event Script (for Server-Side Accuracy)
/**
* @NApiVersion 2.1
* @NScriptType UserEventScript
* @Description Ensures Department and Class are updated per Cost Center in backend processes
*/
define(['N/record', 'N/search', 'N/log'], (record, search, log) => {
function beforeSubmit(context) {
const rec = context.newRecord;
const headerCostCenter = rec.getValue('custbody_cost_center');
const lineCount = rec.getLineCount({ sublistId: 'item' });
for (let i = 0; i < lineCount; i++) {
let lineCostCenter = rec.getSublistValue({ sublistId: 'item', fieldId: 'custcol_cost_center', line: i });
const effectiveCostCenter = lineCostCenter || headerCostCenter;
if (!effectiveCostCenter) continue;
const mapping = getMapping(effectiveCostCenter);
if (!mapping) continue;
rec.setSublistValue({ sublistId: 'item', fieldId: 'department', line: i, value: mapping.department });
rec.setSublistValue({ sublistId: 'item', fieldId: 'class', line: i, value: mapping.class });
}
}
function getMapping(costCenterId) {
const results = search.create({
type: 'customrecord_cost_center_mapping',
filters: [['custrecord_cost_center', 'is', costCenterId]],
columns: ['custrecord_cost_center_dept', 'custrecord_cost_center_class']
}).run().getRange({ start: 0, end: 1 });
if (results && results.length) {
return {
department: results[0].getValue('custrecord_cost_center_dept'),
class: results[0].getValue('custrecord_cost_center_class')
};
}
return null;
}
return { beforeSubmit };
});
β Deployment Plan
| Record Type | Script Type | Trigger | Purpose |
|---|---|---|---|
| Sales Order | Client + User Event | fieldChanged / beforeSubmit | Real-time + backend |
| Purchase Order | Client + User Event | fieldChanged / beforeSubmit | Real-time + backend |
| Journal Entry | User Event only | beforeSubmit | Backend-only automation |
π Benefits
| Benefit | Description |
|---|---|
| Accuracy | Consistent Department and Class per Cost Center |
| Speed | Instant update for users and integrations |
| Flexibility | Works across SO, PO, and JE |
| Maintenance | Mapping easily managed through a custom record |
Leave a Reply