Skip to main content

NetSuite Python Report - Quick Debug Cheatsheet

Quick Start

# 1. Setup
pip install jupyter pandas xlsxwriter numpy

# 2. Start Jupyter
jupyter notebook

# 3. Open aging_report_dev.ipynb

Export Data from NetSuite

Method 1: Browser Console

// Run in browser console after Suitelet loads data
console.log(JSON.stringify(agingData, null, 2));
// Copy output and save to .json file

Method 2: Download Button (add to HTML)

function downloadTestData() {
const blob = new Blob([JSON.stringify(agingData, null, 2)], {type: 'application/json'});
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'test_data.json';
a.click();
}

Method 3: Suitelet Debug Files

// Uncomment in your Suitelet
var fileObj = file.create({
name: 'debug_data.json',
fileType: file.Type.PLAINTEXT,
contents: JSON.stringify(mainResult, null, 2),
folder: 4742 // Your folder ID
});
fileObj.save();

Common Debug Points

Check Data Structure

print(f"Records: {len(data)}")
print(f"Columns: {list(data[0].keys())}")
pd.DataFrame(data).info()

Check for Nulls

df = pd.DataFrame(data)
print(df.isnull().sum())
df = df.fillna(0) # Fix nulls

Verify Calculations

# Add debug column
df['_debug_total'] = df['col1'] + df['col2']
print(df[['col1', 'col2', '_debug_total']])

Check Excel Row Positions

print(f"AR: rows {ar_row_start} to {ar_row_end}")
print(f"AP: rows {ap_row_start} to {ap_row_end}")

Pyodide ↔ JavaScript Bridge

Pass Data to Python

// JavaScript
window.myData = JSON.stringify(dataArray).replaceAll(null, 'null');
# Python (Pyodide)
from js import myData
data = json.loads(myData)

Return File to JavaScript

# Python
import base64
bio = io.BytesIO()
# ... create Excel ...
bio.seek(0)
base64.b64encode(bio.getvalue()).decode() # Returns to JS
// JavaScript
const bytesData = pyodide.runPython(`...`);
const arrayBuffer = base64ToArrayBuffer(bytesData);
saveByteArray('report.xlsx', arrayBuffer);

xlsxwriter Quick Reference

Formats

bold_fmt = workbook.add_format({'bold': True})
currency_fmt = workbook.add_format({'num_format': '#,##0'})
border_fmt = workbook.add_format({'top': 1, 'left': 1, 'bottom': 1, 'right': 1})
center_fmt = workbook.add_format({'align': 'center', 'valign': 'vcenter'})

Common Operations

worksheet.write(row, col, value, format)
worksheet.merge_range(r1, c1, r2, c2, value, format)
worksheet.set_column(col, col, width)
worksheet.conditional_format(xl_range(r1, c1, r2, c2), {'type': 'no_errors', 'format': fmt})

Common Errors & Fixes

ErrorCauseFix
KeyError: 'rownumber'Column doesn't existCheck with 'rownumber' in df.columns first
TypeError: NaNNull valuesUse df.fillna(0)
Index out of rangeWrong row calculationVerify ar_row_start, ar_row_end
Merge errorOverlapping rangesCheck merge coordinates
Pyodide hangsLarge dataProcess in chunks

Project Structure

project/
├── suitelet/
│ ├── report_sl.js # Suitelet
│ └── report.html # HTML template
├── notebooks/
│ └── report_dev.ipynb # Jupyter development
├── test_data/
│ └── sample.json # Test data
└── test_output/
└── test_report.xlsx # Generated reports

Debug Workflow

1. Export real data → JSON file

2. Load in Jupyter → Develop logic

3. Test Excel output → Verify formatting

4. Copy to HTML → Port to Pyodide

5. Test in browser → Final verification

6. Deploy to NetSuite