Sample output from the SuiteScript Review Agent prompt in the Sonar AI Prompt Library, run against a NetSuite test account. Every name and number here is test data. Back to the post · The library
SONAR AI
Code Review · Confidential
SuiteScript Review — Final Report

Margin Pulse
marginpulse.portlet.js

Independent code review of the month-to-date gross-margin dashboard portlet, with live verification of deployment state, schema references, and account configuration.

File91434 · /SuiteScripts
14,705 bytes · 263 lines
Script Recordcustomscript_
marginpulse_portlet (4006)
AccountTD3016323
Production · OneWorld
Review Date2026-08-26
File revision 2026-08-25
VerdictSOLID
Read-only. Injection-free. XSS-escaped. Governance-bounded. Fails gracefully. One configuration defect and one scalability gap stand between this script and clean.
0
Critical
3
Important
7
Nice-to-have
Section 01

Executive Summary

Margin Pulse is a read-only HTML portlet (@NApiVersion 2.1, @NScriptType Portlet) that renders a month-to-date gross-margin tile on the NetSuite dashboard. It executes three static SuiteQL queries against the general ledger — transactionaccountingline joined to transaction, transactionline, account, and item — and computes three views:

The measurement basis is correct and documented: GL basis (ties to the Income Statement), posted transactions only, consolidated across all subsidiaries excluding the elimination subsidiary (id 4), bounded by transaction date. Sign conventions are handled properly — -tal.amount for Income, +tal.amount for COGS. The script mutates nothing, calls no external services, and loads only N/query and N/runtime.

The single material defect is configuration, not code. The script's advertised tuning knob — script parameter custscript_marginpulse_floor — does not exist in this account. Every render silently falls back to the hardcoded 38% default. The documentation describes a control that is not connected. (Finding I-1.)

Section 02

Provenance & Deployment

All facts in this section were verified live against the account on 2026-08-26. Queries appear verbatim in Section 9.

AttributeValue
Script recordcustomscript_marginpulse_portlet — “Margin Pulse”, internal id 4006, type PORTLET, active.
Source file/SuiteScripts/marginpulse.portlet.js, file id 91434. Created and last modified 2026-08-25 — one day old at review time.
OwnerEmployee 3894, “Timothy Dietrich.” Distinct from the reviewing session's identity (employee 158, “Tim Dietrich”) — two employee records appear to exist for the same person. See Section 8.
Deploymentcustomdeploy_marginpulse_portlet — status RELEASED, deployed, log level DEBUG, not available without login (isonline = F).
Audienceallroles is null on the deployment row — the audience is role-scoped rather than “all roles.” The specific role list is not exposed through SuiteQL; confirm on the deployment record if the tile should be broadly visible.
Execution historyZero entries in scriptnote for this script. Consistent with the code, which logs only in the failure path (line 240): no errors have occurred since deployment. Portlet success-path executions are not individually logged, so “no logged errors” is the strongest available claim.
Script parameterMissing  custscript_marginpulse_floor — documented at lines 22–23, read at line 168 — is absent from the account's customfield table. Verified with two independent queries against fieldtype = 'SCRIPT' (exact and widened match). Finding I-1.

Section 03

Dependencies

None. The script is fully self-contained.

With no dependencies present, there are no secondary files to evaluate as part of this review.


Section 04

Security Review

All eight standard checks pass. No critical or important security findings.

CheckResultDetail
Hardcoded credentials / tokens / URLsPASSNone. The account id appears in a header comment (line 17) as validation provenance — informational, not secret. Strip before sharing the file externally.
Secrets or PII in logsPASSThe only log call (line 240) emits e.message.
SQL injectionPASSAll three queries are static template-literal constants (lines 38, 66, 85). The only external input — the floor parameter — passes through parseFloat plus range validation (lines 168–169) and is used numerically, never interpolated into SQL.
XSS in generated HTMLPASSA correct esc() helper (line 114) covers & < > " and is applied to every data-derived string: item names in cell text and title attributes (lines 214–216), sparkline labels (line 194), error text (line 243). Single quotes are unescaped, but all generated attributes use double quotes — no vector exists. Numerics pass through toFixed / toLocaleString.
Available Without Login exposurePASSisonline = F; portlets render only inside authenticated dashboards.
External HTTP callsPASSNone.
eval / dynamic requirePASSNone.
Role / permission assumptionsPASSQueries execute as the viewing user; a role without GL access lands in the catch block rather than leaking data. Soft note: raw e.message is echoed to the dashboard UI (line 243), exposing internal error text to any viewer — low severity, tracked as N-6.

Section 05

Best-Practices Scorecard

Checklist compliance by category · red = below standard
Pre-flight verification 90 Environment & safety 95 Code structure 70 Error handling & logging 85 Performance & governance 65 Record API usage n/a — clean SuiteQL correctness 90 Security & integration 100 0255075100
Ratings are the reviewer's judgment of checklist adherence per category, 0–100. “n/a — clean” denotes categories with no applicable items and no violations.

Pre-flight verification — strong

Environment, safety, deployment — clean for its type

Read-only; idempotency and concurrency are not applicable to a portlet. The one configuration-drift issue is covered under I-1. Deployment log level DEBUG is harmless — nothing logs at debug level.

Code structure — one systematic violation

// Current — lines 227–231 (abridged)
'<div style="margin:8px 0 10px;…">' +
'vs same days last month: <b>' + (gmLm == null ? '—' : gmLm.toFixed(1) + '%') + '</b>' +
' (' + fmtPts(gmLm == null ? null : gmMtd - gmLm) + ')' + …

// Standard — template literal
`<div style="margin:8px 0 10px;…">
   vs same days last month: <b>${gmLm == null ? '—' : gmLm.toFixed(1) + '%'}</b>
   (${fmtPts(gmLm == null ? null : gmMtd - gmLm)})
 …`

Error handling & logging — adequate

Performance & governance — fine today, structurally unbounded

SuiteQL correctness — strong, two subtleties undocumented

Record API · Integration · Debugging — clean

No record loads, saves, or sublist work. No external calls or public endpoints. Error-only logging with a user-facing fallback is the right strategy for a portlet, where the execution log is the only debugging surface.


Section 06

Documentation Quality — Adequate

AspectRatingAssessment
Header blockExcellentPlain-language purpose, full measurement basis (GL basis, consolidation, xElim exclusion, date bounds), validation provenance with reconciliation figures, parameter documentation (lines 1–25). Missing only @NModuleScope and an author tag.
Function JSDocMissingZero JSDoc on all eight functions/helpers. sparklineSvg has a one-line shape comment (line 136); render, wrap, statusOf, and the arrow helpers have none.
Inline commentsGoodSQL constants carry rationale (lines 34–36, 65, 83–84 — including why the ≥$500 filter exists). The HTML-building code has none, but is straightforward.
Stale contentOne itemThe header documents custscript_marginpulse_floor as the configuration mechanism (lines 22–23), but the parameter does not exist in the account. The documentation describes a disconnected knob — stale by omission rather than wrong code.

Section 07

Findings Register

Critical — none

No security, data-integrity, or governance-exception risks identified.

Important — 3 findings

IDFindingLocationRemediation
I-1Documented script parameter does not exist. custscript_marginpulse_floor is absent from the account (verified twice against customfield, fieldtype SCRIPT). Every render silently uses the hardcoded 38% default; the advertised configuration knob is dead.header 22–23; read at 168Configuration fix, not code. On script record 4006: Parameters tab → New Parameter → ID exactly custscript_marginpulse_floor, Type Decimal Number, default 38. Alternatively remove it from the header docs.
I-2Uncached GL scans on every render. Three aggregate queries over transactionaccountingline execute per dashboard load, per user. Linear degradation with data volume and audience size.171, 190, 197Wrap results in N/cache (scope PUBLIC, key per day + floor, TTL 600–900s). Staleness ≤ 15 min is acceptable for a pulse metric; disclose in the footer.
I-3Silent default fallback masks I-1. When the parameter is missing or invalid, the script defaults with no signal — the tile is indistinguishable from a configured one.168–169Track a usedDefault flag; render “(default)” beside the floor in the subtitle and emit one log.audit.

Nice-to-have — 7 findings

IDFindingLocationRemediation
N-1All HTML built via multi-line string concatenation (standard violation; SQL already uses template literals).150–161, 177–180, 199–218, 222–245, 252–258Convert to template literals with ${} interpolation.
N-2No JSDoc on any function.all functionsAdd JSDoc — inputs, outputs, null semantics (esp. gmPct → null on rev ≤ 0), governance note on render.
N-3Timezone implicit: SYSDATE (Pacific) drives the MTD window; footer timestamp unlabeled.queries; 249–251Document the Pacific-midnight boundary; label the timestamp or format via N/format.
N-4Sparkline omits zero-activity months — missing rows compress the x-axis.66–82, 191Generate the 12 expected YYYY-MM keys in JS; left-join query rows onto them (null point already renders correctly).
N-5log as implicit global; error log omits name and stack.240Add N/log to the define array; log e.name + guarded e.stack.
N-6Raw e.message echoed into the portlet UI — internal error text visible to any dashboard viewer.241–245Show a generic message + timestamp; keep detail in the execution log only.
N-7Elimination subsidiary id 4 hardcoded in all three queries — documented, but a per-tenant magic number.53, 77, 101Hoist to a named constant minimally; a script parameter or iselimination = 'T' subquery if portability matters.

Section 08

Observations & Patterns

Patterns worth preserving and replicating:
  • GL-basis margin via transactionaccountingline with correct sign handling and the composite-key join to transactionline — the right way to build a margin metric that reconciles to the Income Statement, and the join most scripts get wrong.
  • Account-quirk awareness — filtering on tl.subsidiary because transaction.subsidiary is not SuiteQL-exposed here.
  • Validation provenance in the header (lines 15–19): named report, exact reconciliation figures, date. The breadcrumb that makes a script trustworthy a year later.
  • Dependency-free inline SVG with an accessibility label (role="img", aria-label, line 151).
  • Layered null-safety — gmPct returns null on non-positive revenue; every consumer renders “—”. NaN never reaches the UI.

Section 09

Methodology & Evidence

The review followed a fixed procedure: full-source read (the 14.7 KB file fits comfortably in a single pass), live provenance verification via SuiteQL, schema verification of every referenced identifier, then section-by-section assessment against the embedded best-practices checklist. Platform judgments (query limits, logging behavior, portlet execution model) were grounded in the account's verified SuiteScript reference material, not assumptions.

Evidence trail — every query, verbatim

#PurposeQuery / operationResult
1Locate the filefileSearch(q: "marginpulse")1 hit — file 91434, 14,705 bytes, modified 2026-08-25
2Read full sourcefileGet(91434) — single pass, 263 linesComplete source in review scope
3Find the script recordSELECT s.id, s.scriptid, s.name, s.scripttype, s.scriptfile, s.owner, s.isinactive FROM script s WHERE s.scriptfile = 91434customscript_marginpulse_portlet, id 4006, PORTLET, active, owner 3894
4Deployment stateSELECT sd.id, sd.scriptid, sd.status, sd.isdeployed, sd.loglevel, sd.allroles, sd.alllocalizationcontexts, sd.isonline FROM scriptdeployment sd WHERE sd.script = 4006customdeploy_marginpulse_portlet — RELEASED, deployed, DEBUG, isonline F
5Resolve ownerSELECT id, entityid, firstname, lastname FROM employee WHERE id = 3894“Timothy Dietrich”
6Execution history (first attempt)SELECT type, COUNT(*), MAX(date) FROM scriptnote WHERE script = 4006 GROUP BY typeErrored — scriptnote.script not an exposed identifier in this account. Retried with the exposed column (row 7).
7Execution history (corrected)SELECT * FROM scriptnote WHERE scripttype = 4006 ORDER BY internalid DESC FETCH FIRST 5 ROWS ONLY0 rows — no logged entries
8Verify floor parameter (exact)SELECT scriptid, name, fieldtype, description FROM customfield WHERE fieldtype = 'SCRIPT' AND LOWER(scriptid) LIKE '%marginpulse%'0 rows
9Verify floor parameter (widened)SELECT scriptid, name FROM customfield WHERE fieldtype = 'SCRIPT' AND (LOWER(scriptid) LIKE '%margin%' OR LOWER(scriptid) LIKE '%floor%')0 rows → parameter confirmed absent (I-1)
10Pin line numbersfileGrep(91434) — two regex passes over declarations, SQL constants, log calls, HTML anchors23 + 9 matches; all citations in this report
Note on rows 6–7: the first execution-history query failed because this account exposes the script linkage on scriptnote as scripttype, not script. The corrected query returned zero rows. The error and correction are disclosed here rather than omitted — the failed attempt is part of the evidence trail.

Source documents


Section 10

Assumptions & Limitations

#Assumption / limitationBasis & risk
A1The header's reconciliation claim (Aug 2026: Income 1,061,652.94 / COGS 722,337.81, exact match to the Income Statement) was taken at face value and not re-reconciled during this review.Query construction is consistent with the claim (signs, filters, basis). Risk: low. Re-run the reconciliation if the numbers are ever challenged.
A2Zero rows in scriptnote for script 4006 is interpreted as “no errors logged,” using scripttype as the script linkage column.Column mapping confirmed by the account's exposed-identifier error message. Portlet success paths log nothing by design, so absence of rows is the expected healthy state.
A3Single-currency arithmetic on tal.amount is treated as safe.Multi-Currency is verified OFF in this account (USD only). The finding is recorded as a portability caveat, not a defect.
A4Deployment audience could not be fully resolved — allroles is null and the role list is not SuiteQL-exposed.Stated explicitly in Section 2 rather than guessed. Verify on the deployment record UI if audience matters.
A5Compliance ratings in Section 5 are reviewer judgment, not a mechanical score.Each rating is traceable to the itemized findings beneath it.
A6All line references bind to the 2026-08-25 revision of file 91434.Any subsequent edit invalidates line numbers; finding IDs (I-1…N-7) remain stable.

Section 11

Next Steps

#ActionOwnerEffort
1Resolve I-1 — create custscript_marginpulse_floor on script record 4006 (Decimal Number, default 38), or remove it from the header documentation.Administrator5 minutes, UI only
2Revised script — apply I-2, I-3, and N-1 through N-7 in a single rewrite: N/cache layer, default-floor disclosure, template literals, JSDoc, hardened logging, generic error UI, named constants. Business logic and rendered output preserved exactly.Sonar AI, on approvalOne artifact, side-by-side reviewable
3Optional — supplementary documents: UAT guide, deployment guide, dependencies reference, or future-enhancements roadmap, scoped to this script.Sonar AI, on requestPer document

The script is production-worthy today. The remediation above converts it from good to durable — configured rather than defaulted, cached rather than re-scanned, and documented at the function level.