Common Code Patterns
Frequently used code patterns for NetSuite report development.
1. Loading and Processing NetSuite Data
Basic Data Loading
import json
import pandas as pd
# Load from JSON file
with open('test_data/aging_data.json', 'r', encoding='utf-8') as f:
aging_data = json.load(f)
# Convert to DataFrame
df = pd.DataFrame(aging_data)
# Clean up
if 'rownumber' in df.columns:
df = df.drop(columns=['rownumber'])
df = df.fillna(0)
Loading from Pyodide
from js import array_aging
import json
import pandas as pd
aging_data = json.loads(array_aging)
df = pd.DataFrame(aging_data).fillna(0)
2. Separating Data by Type
AR/AP Split
# Split by account type
ar_df = df[df['accttype'] == 'AcctRec'].drop(columns=['accttype'])
ap_df = df[df['accttype'] == 'AcctPay'].drop(columns=['accttype'])
print(f"AR records: {len(ar_df)}")
print(f"AP records: {len(ap_df)}")
Currency Split
currencies = df['currency'].unique().tolist()
print(f"Currencies: {currencies}")
for curr in currencies:
curr_df = df[df['currency'] == curr]
print(f"{curr}: {len(curr_df)} records")
3. Calculating Totals and Overdue
Overdue Calculation
# Calculate overdue (sum of all aging buckets except current)
df['FC Overdue'] = (df['foreignamount_h30'] +
df['foreignamount_h60'] +
df['foreignamount_h60_above'])
df['Rp Overdue'] = (df['amount_h30'] +
df['amount_h60'] +
df['amount_h60_above'])
Total Calculation
# Calculate total (current + all overdue)
df['FC Total'] = (df['foreignamount_current'] +
df['foreignamount_h30'] +
df['foreignamount_h60'] +
df['foreignamount_h60_above'])
df['Rp Total'] = (df['amount_current'] +
df['amount_h30'] +
df['amount_h60'] +
df['amount_h60_above'])
4. Building Report with Subtotals
Per-Currency Subtotals
final_df = pd.DataFrame()
empty_row = pd.DataFrame("", index=range(1), columns=df.columns)
for curr in currencies:
# Get data for this currency
temp_curr_df = df[df['currency'] == curr].sort_values(by=['Rp Total'])
# Create subtotal row
temp_total_df = pd.DataFrame(columns=temp_curr_df.columns)
temp_total_df.loc[f"Subtotal {curr}"] = temp_curr_df.sum(numeric_only=True)
# Combine: empty row + data + empty row + subtotal
final_df = pd.concat([final_df, empty_row, temp_curr_df, empty_row, temp_total_df])
# Reset index for clean output
final_df = final_df.reset_index(drop=True)
Grand Total
# Add grand total at the end
grand_total = pd.DataFrame(columns=df.columns)
grand_total.loc['Grand Total'] = df.sum(numeric_only=True)
grand_total['name'] = 'GRAND TOTAL'
final_df = pd.concat([final_df, empty_row, grand_total])
5. Excel Report Structure
Standard Report Layout
import io
import pandas as pd
bio = io.BytesIO()
with pd.ExcelWriter(bio, engine='xlsxwriter') as writer:
workbook = writer.book
worksheet = workbook.add_worksheet('Report')
# Row layout:
# 0-2: Title area (company name, report name, date)
# 3: Empty
# 4-5: Column headers
# 6+: Data
# Define formats
title_fmt = workbook.add_format({
'bold': True, 'font_size': 14, 'align': 'center'
})
header_fmt = workbook.add_format({
'bold': True, 'bg_color': '#4472C4', 'font_color': 'white',
'align': 'center', 'border': 1
})
data_fmt = workbook.add_format({'border': 1})
number_fmt = workbook.add_format({'border': 1, 'num_format': '#,##0'})
# Write title (rows 0-2)
worksheet.merge_range(0, 0, 0, 10, 'PT. COMPANY NAME', title_fmt)
worksheet.merge_range(1, 0, 1, 10, 'Aging Report', title_fmt)
worksheet.merge_range(2, 0, 2, 10, f'As of {asofdate}', title_fmt)
# Write headers (rows 4-5)
# ... header writing code ...
# Write data starting at row 6
data_start_row = 6
for idx, row in final_df.iterrows():
excel_row = data_start_row + idx
# Write each cell...
Column Width Configuration
# Set column widths for common report layout
worksheet.set_column('A:A', 30) # Customer/Vendor name
worksheet.set_column('B:B', 10) # Currency
worksheet.set_column('C:N', 15) # Numeric columns
6. Date Formatting
Parsing Date Strings
from datetime import datetime
# Parse date from string
asof_date = datetime.strptime(asofdate, '%d/%m/%Y')
# Format for display
formatted_date = asof_date.strftime('%d %B %Y') # "15 December 2024"
Date in Report Title
# Common date formats for reports
date_short = asof_date.strftime('%d/%m/%Y') # "15/12/2024"
date_long = asof_date.strftime('%d %B %Y') # "15 December 2024"
date_month = asof_date.strftime('%B %Y') # "December 2024"
7. Currency Formatting
Indonesian Rupiah Display
# Format number as Indonesian Rupiah
def format_idr(value):
return f"Rp {value:,.0f}".replace(",", ".")
# Example
amount = 1500000
print(format_idr(amount)) # "Rp 1.500.000"
Multiple Currency Display
def format_currency(value, currency):
if currency == 'IDR':
return f"Rp {value:,.0f}"
elif currency == 'USD':
return f"$ {value:,.2f}"
elif currency == 'SGD':
return f"S$ {value:,.2f}"
else:
return f"{currency} {value:,.2f}"
8. Row Position Tracking
Dynamic Row Management
# Track current row position
current_row = 6 # Start after headers
# Write data and track position
for idx, row in df.iterrows():
worksheet.write(current_row, 0, row['name'], data_fmt)
worksheet.write(current_row, 1, row['amount'], number_fmt)
current_row += 1
# Add subtotal after data
worksheet.write(current_row, 0, 'Subtotal', subtotal_fmt)
worksheet.write(current_row, 1, df['amount'].sum(), subtotal_fmt)
current_row += 1
# Record row ranges for later reference
ar_row_start = 6
ar_row_end = current_row - 1
9. Conditional Cell Formatting
Format Based on Value
# Apply different format based on value
for idx, row in df.iterrows():
excel_row = data_start + idx
value = row['amount']
if value > 1000000:
fmt = high_value_fmt
elif value < 0:
fmt = negative_fmt
else:
fmt = normal_fmt
worksheet.write(excel_row, col, value, fmt)
Subtotal Row Detection
# Check if row is a subtotal row
for idx, row in final_df.iterrows():
is_subtotal = 'Subtotal' in str(row.get('name', ''))
is_grandtotal = 'Grand Total' in str(row.get('name', ''))
if is_grandtotal:
row_fmt = grandtotal_fmt
elif is_subtotal:
row_fmt = subtotal_fmt
else:
row_fmt = data_fmt
10. Memory-Efficient Processing
Process Large Datasets
def process_in_batches(data, batch_size=1000):
"""Process large datasets in batches"""
results = []
for i in range(0, len(data), batch_size):
batch = data[i:i + batch_size]
df_batch = pd.DataFrame(batch)
# Process batch...
results.append(df_batch)
return pd.concat(results, ignore_index=True)
Cleanup After Processing
import gc
# After creating Excel file
del df, final_df, ar_df, ap_df
gc.collect()
11. Error Handling
Safe Column Access
# Check column exists before using
if 'rownumber' in df.columns:
df = df.drop(columns=['rownumber'])
# Get column with default
def safe_get(df, column, default=0):
return df[column] if column in df.columns else default
Try-Catch Pattern
try:
df = pd.DataFrame(data)
result = process_data(df)
except KeyError as e:
print(f"Missing column: {e}")
raise
except Exception as e:
print(f"Processing error: {e}")
raise
12. Quick Reference
Data Loading
df = pd.DataFrame(json.loads(data_str)).fillna(0)
Split by Type
ar_df = df[df['type'] == 'AR']
ap_df = df[df['type'] == 'AP']
Calculate Totals
df['total'] = df['col1'] + df['col2'] + df['col3']
Group and Subtotal
grouped = df.groupby('currency').sum(numeric_only=True)
Write to Excel
worksheet.write(row, col, value, format)
Track Position
current_row += 1