Attaching Files to NetSuite Records
Learn how to attach files to purchase orders, invoices, and other NetSuite records using the SOAP attach action.
Prerequisites
- NetSuite connector configured with
soapandfileCabinetresources enabled - File uploaded to File Cabinet (see uploading files)
Basic File Attachment
Attach a file to a purchase order:
from abstra.connectors import run_connection_action
# Assuming you have:
# - file_id: ID of uploaded file
# - po_id: ID of purchase order
result = run_connection_action(
"netsuite",
"attach",
{
"attachTo": {
"type": "purchaseOrder",
"internalId": po_id
},
"attachedRecord": {
"type": "file",
"internalId": file_id
}
}
)
if result['data']['success']:
print(f"✓ Attached file {file_id} to PO {po_id}")
Supported Record Types
Transactions
# Invoice
run_connection_action("netsuite", "attach", {
"attachTo": {"type": "invoice", "internalId": "12345"},
"attachedRecord": {"type": "file", "internalId": file_id}
})
# Vendor Bill
run_connection_action("netsuite", "attach", {
"attachTo": {"type": "vendorBill", "internalId": "67890"},
"attachedRecord": {"type": "file", "internalId": file_id}
})
# Sales Order
run_connection_action("netsuite", "attach", {
"attachTo": {"type": "salesOrder", "internalId": "11111"},
"attachedRecord": {"type": "file", "internalId": file_id}
})
# Cash Sale
run_connection_action("netsuite", "attach", {
"attachTo": {"type": "cashSale", "internalId": "22222"},
"attachedRecord": {"type": "file", "internalId": file_id}
})
Entities
# Customer
run_connection_action("netsuite", "attach", {
"attachTo": {"type": "customer", "internalId": "33333"},
"attachedRecord": {"type": "file", "internalId": file_id}
})
# Vendor
run_connection_action("netsuite", "attach", {
"attachTo": {"type": "vendor", "internalId": "44444"},
"attachedRecord": {"type": "file", "internalId": file_id}
})
# Employee
run_connection_action("netsuite", "attach", {
"attachTo": {"type": "employee", "internalId": "55555"},
"attachedRecord": {"type": "file", "internalId": file_id}
})
Complete Workflow: Upload and Attach
Upload a file and immediately attach it:
from abstra.connectors import run_connection_action
import base64
# Step 1: Upload file
with open("contract.pdf", "rb") as f:
file_content = base64.b64encode(f.read()).decode('utf-8')
file_result = run_connection_action(
"netsuite",
"add_file",
{
"name": "contract.pdf",
"content": file_content,
"fileType": "PDF"
}
)
file_id = file_result['data']['id']
print(f"✓ Uploaded file: {file_id}")
# Step 2: Attach to purchase order
attach_result = run_connection_action(
"netsuite",
"attach",
{
"attachTo": {
"type": "purchaseOrder",
"internalId": "501628"
},
"attachedRecord": {
"type": "file",
"internalId": file_id
}
}
)
if attach_result['data']['success']:
print(f"✓ File attached to PO")
Attach Multiple Files
Attach several files to one record:
def attach_files_to_record(record_type, record_id, file_ids):
"""Attach multiple files to a single record"""
results = []
for file_id in file_ids:
result = run_connection_action(
"netsuite",
"attach",
{
"attachTo": {
"type": record_type,
"internalId": record_id
},
"attachedRecord": {
"type": "file",
"internalId": file_id
}
}
)
results.append({
"file_id": file_id,
"success": result['data']['success']
})
if result['data']['success']:
print(f"✓ Attached file {file_id}")
else:
print(f"❌ Failed to attach file {file_id}")
return results
# Usage
file_ids = ["654321", "654322", "654323"]
attach_files_to_record("purchaseOrder", "501628", file_ids)
Attach Same File to Multiple Records
Share one file across multiple records:
def attach_file_to_records(file_id, record_type, record_ids):
"""Attach one file to multiple records"""
results = []
for record_id in record_ids:
result = run_connection_action(
"netsuite",
"attach",
{
"attachTo": {
"type": record_type,
"internalId": record_id
},
"attachedRecord": {
"type": "file",
"internalId": file_id
}
}
)
results.append({
"record_id": record_id,
"success": result['data']['success']
})
if result['data']['success']:
print(f"✓ Attached to {record_type} {record_id}")
return results
# Usage - attach policy document to all vendors
vendor_ids = ["100", "101", "102"]
attach_file_to_records("654321", "vendor", vendor_ids)
Practical Examples
Attach Invoice to Purchase Order
from abstra.connectors import run_connection_action
from datetime import datetime
import base64
# Create PO
po_result = run_connection_action("netsuite", "post_purchase_order", {
"data": {
"entity": {"id": "123"},
"subsidiary": {"id": "1"},
"tranDate": datetime.now().strftime("%Y-%m-%d"),
# ... other PO fields
}
})
po_id = po_result['data']['id']
# Upload invoice
with open("vendor_invoice.pdf", "rb") as f:
content = base64.b64encode(f.read()).decode('utf-8')
file_result = run_connection_action("netsuite", "add_file", {
"name": f"invoice_po_{po_id}.pdf",
"content": content,
"fileType": "PDF"
})
file_id = file_result['data']['id']
# Attach invoice to PO
run_connection_action("netsuite", "attach", {
"attachTo": {"type": "purchaseOrder", "internalId": po_id},
"attachedRecord": {"type": "file", "internalId": file_id}
})
print(f"✓ Created PO {po_id} with attached invoice")
Attach Supporting Documents to Customer
import base64
customer_id = "12345"
documents = [
{"file": "contract.pdf", "type": "PDF"},
{"file": "id_document.jpg", "type": "JPGIMAGE"},
{"file": "bank_statement.pdf", "type": "PDF"}
]
for doc in documents:
# Upload
with open(doc['file'], "rb") as f:
content = base64.b64encode(f.read()).decode('utf-8')
file_result = run_connection_action("netsuite", "add_file", {
"name": doc['file'],
"content": content,
"fileType": doc['type']
})
# Attach
run_connection_action("netsuite", "attach", {
"attachTo": {"type": "customer", "internalId": customer_id},
"attachedRecord": {"type": "file", "internalId": file_result['data']['id']}
})
print(f"✓ Attached {doc['file']} to customer {customer_id}")
Error Handling
Always include error handling:
def safe_attach(record_type, record_id, file_id):
"""Safely attach file with error handling"""
try:
result = run_connection_action(
"netsuite",
"attach",
{
"attachTo": {
"type": record_type,
"internalId": str(record_id)
},
"attachedRecord": {
"type": "file",
"internalId": str(file_id)
}
}
)
if result.get('status') == 'error':
return {
"success": False,
"error": result.get('message', 'Unknown error')
}
return {
"success": result['data']['success'],
"response": result['data']
}
except Exception as e:
return {
"success": False,
"error": str(e)
}
# Usage
result = safe_attach("purchaseOrder", "123456", "654321")
if result['success']:
print("✓ File attached successfully")
else:
print(f"❌ Attachment failed: {result['error']}")
Best Practices
Naming Convention
Name files to reflect their purpose:
# Good: Descriptive names
f"po_{po_id}_contract_signed.pdf"
f"invoice_{vendor_id}_{date}.pdf"
f"customer_{customer_id}_id_verification.jpg"
# Bad: Generic names
"document.pdf"
"file1.pdf"
Verify Before Attaching
Check that records exist:
# Verify PO exists before attaching
po_check = run_connection_action("netsuite", "query", {
"query": f"SELECT id FROM Transaction WHERE id = {po_id} AND type = 'PurchOrd'"
})
if po_check['data']['items']:
# PO exists, proceed with attachment
run_connection_action("netsuite", "attach", {
"attachTo": {"type": "purchaseOrder", "internalId": po_id},
"attachedRecord": {"type": "file", "internalId": file_id}
})
else:
print(f"❌ PO {po_id} not found")
Batch Processing
Process attachments in batches:
import time
def batch_attach(attachments, batch_size=10, delay=1):
"""
Process attachments in batches
attachments: list of {"record_type", "record_id", "file_id"}
"""
results = []
for i in range(0, len(attachments), batch_size):
batch = attachments[i:i + batch_size]
print(f"Processing batch {i//batch_size + 1}...")
for attachment in batch:
result = run_connection_action("netsuite", "attach", {
"attachTo": {
"type": attachment['record_type'],
"internalId": attachment['record_id']
},
"attachedRecord": {
"type": "file",
"internalId": attachment['file_id']
}
})
results.append(result)
# Delay between batches
if i + batch_size < len(attachments):
time.sleep(delay)
return results
Troubleshooting
"Action attach not found"
Enable the soap resource:
- Go to NetSuite connection settings
- Check
soapcheckbox - Save connection
"Invalid record reference"
Verify record exists and type is correct:
# Check record exists
result = run_connection_action("netsuite", "query", {
"query": f"SELECT id, type FROM Transaction WHERE id = {record_id}"
})
if result['data']['items']:
record = result['data']['items'][0]
print(f"Record {record_id} is type: {record['type']}")
else:
print(f"Record {record_id} not found")
"Permission denied"
Ensure your NetSuite role has:
- File Cabinet > View permission
- Attach Files to Records permission
Record Type Reference
Common NetSuite record types for attach action:
| Record Type | Parameter Value |
|---|---|
| Purchase Order | purchaseOrder |
| Sales Order | salesOrder |
| Invoice | invoice |
| Vendor Bill | vendorBill |
| Cash Sale | cashSale |
| Customer | customer |
| Vendor | vendor |
| Employee | employee |
| Expense Report | expenseReport |
| Journal Entry | journalEntry |