Whatever message this page gives is out now! Go check it out!

cfspreadsheet

Last update:
May 18, 2026

Description

Manages Excel spreadsheet files:
  • Reads a sheet from a spreadsheet file and stores it in a ColdFusion spreadsheet object, query, CSV string, or HTML string.
  • Writes single sheet to a new XLS file from a query, ColdFusion spreadsheet object, or CSV string variable.
  • Add a sheet an existing XLS file.

Category

Syntax

The tag syntax depends on the action attribute value:
Read  
<cfspreadsheet   
    action="read" 
   src = "filepath" 
    columns = "range" 
    columnnames = "comma-delimited list" 
    excludeHeaderRow = "true | false" 
    format = "CSV|HTML" 
    headerrow = "row number" 
    name = "text" 
    query = "query name" 
    rows = "range" 
    sheet = "number" 
    sheetname = "text">  
   
Update  
<cfspreadsheet  
    action="update" 
    filename = "filepath" 
    format = "csv" 
    name = "text" 
    password = "password" 
    query = "query name" 
    sheetname = "text" >  
   
Write  
<cfspreadsheet   
    action="write" 
    autosize="true|false" 
    filename = "filepath" 
    format = "csv" 
    name = "text" 
    overwrite = "true | false" 
    password = "password" 
    query = "queryname" 
    sheetname = "text" 
    autosize = "true | false" >

See also

Sreadsheet functions.

History

ColdFusion (2025 release): You can no longer read password-protected Excel files without specifying the password. If you skip the password attribute in the read action, you will get an exception. As a result, you’ll also be unable to update the file as the read action will produce the exception. View the examples for more information.
ColdFusion 11: Added the attribute autosize.
ColdFusion 9.0.1: Added the attribute {{excludeHeaderRow}}
ColdFusion 9: Added this tag.

Attributes

Attribute
Action
Req/Opt
Default
Description
action
All
Required
One of the following:
  • read-Reads the contents of an XLS format file.
  • update-Adds a new sheet to an existing XLS file. You cannot use the uppdate action to change an existing sheet in a file. For more information, see Usage.
  • write-Writes a new XLS format file or overwrites an existing file.
autosizewriteOptionaltrue
By default the value of this attribute is true. The columns in a sheet resize to accommodate the contents. To avoid resizing the columns, set it to false.
Note: Auto sizing can be relatively slow on large sheets.
filename
{{update, write}}r
Required
The pathname of the file that is written.
excludeHeaderRow
read
Optional
false
If set to true, excludes the headerrow from being included in the query results.

The attribute helps when you read Excel as a query. When you specify the headerrow attribute, the column names are retrieved from the header row. But they are also included in the first row of the query. To not include the header row, set true as the attribute value.
name
All
name or query is required.
  • read action: The variable in which to store the spreadsheet file data. Specify name or query.
  • write and update actions: A variable containing CSV-format data or an ColdFusion spreadsheet object containing the data to write. Specify the name or query.
query
All
name or query is required.
  • read action: The query in which to store the converted spreadsheet file. Specify format, name, or query.
  • write and update actions: A query variable containing the data to write. Specify name or query.
src
read
Required
The pathname of the file to read.
columns
read
Optional
Column number or range of columns. Specify a single number, a hypen-separated column range, a comma-separated list, or any combination of these; for example: 1,3-6,9.
Note: Setting a range higher than the actual number of columns in sheet results in an exception when accessing columns which are yet to be added.
columnnames
read
Optional
Comma-separated column names.
format
All
Optional
For read, save as a spreadsheet object.

For update and write: Save a spreadsheet object.
Format of the data represented by the name variable.
  • All: csv - On read, converts an XLS file to a CSV variable.
  • On update or write, Saves a CSV variable as an XLS file.
  • Read only: html-Converts an XLS file to an HTML variable. The cfspreadsheet tag always writes spreadsheet data as an XLS file. To write HTML variables or CSV variables as HTML or CSV files, use the cffile tag.
headerrow
read
Optional
Row number that contains column names.
overwrite
write
Optional
false
A Boolean value specifying whether to overwrite an existing file.
password
updatewrite
Optional
Set a password for modifying the sheet.

Note: Setting a password of the empty string does no unset password protection entirely; you are still prompted for a password if you try to modify the sheet.
rows
read
Optional
The range of rows to read. Specify a single number, a hypen-separated row range, a comma-separated list, or any combination of these; for example: 1,3-6,9.
sheet
read
Optional
Number of the sheet. For the read action, you can specify sheet or sheetname.
sheetname
All
Optional
Name of the sheet For the read action, you can specify sheet or sheetname. For write and update actions, the specified sheet is renamed according to the value you specify for sheetname.

Usage

Each ColdFusion spreadsheet object represents Excel sheet:
  • To read an Excel file with multiple sheets, use multiple cfspreadsheet tags with the read option and specify different name and sheet or sheetname attributes for each sheet.
  • To write multiple sheets to a single file, use the write action to create the file and save the first sheet and use the update action to add each additional sheet.
  • To update an existing file, read all sheets in the file, modify one or more sheets, and use the contents, and use the write action and Update actions (for multiple sheet files) to rewrite the entire file. The cfspreadsheet tag writes only XLS format files. To write a CSV file, put your data in a CSV formatted string variable and use the cffile tag to write the variable contents in a file.Use the ColdFusion Spreadsheet* functions, such as SpreadsheetNew and SpreadsheetAddColumn to create a new ColdFusion Spreadsheet object and modify the spreadsheet contents.

Example

The following example uses the cfspreadsheet tag to read and write Excel spreadsheets using various formats. It also shows a simple use of ColdFusion Spreadsheet functions to modify a sheet.
<!--- Read data from two datasource tables. ---> 
<cfquery 
       name="courses" datasource="cfdocexamples"
       cachedwithin="#CreateTimeSpan(0, 6, 0, 0)#"> 
       SELECT CORNUMBER,DEPT_ID,COURSE_ID,CORNAME 
       FROM COURSELIST 
</cfquery> 
  
<cfquery 
       name="centers" datasource="cfdocexamples"
       cachedwithin="#CreateTimeSpan(0, 6, 0, 0)#"> 
       SELECT * 
       FROM CENTERS 
</cfquery> 
      
<cfscript> 
    //Use an absolute path for the files. ---> 
       theDir=GetDirectoryFromPath(GetCurrentTemplatePath()); 
    theFile=theDir & "courses.xls"; 
    //Create two empty ColdFusion spreadsheet objects. ---> 
    theSheet = SpreadsheetNew("CourseData"); 
    theSecondSheet = SpreadsheetNew("CentersData"); 
    //Populate each object with a query. ---> 
    SpreadsheetAddRows(theSheet,courses); 
    SpreadsheetAddRows(theSecondSheet,centers); 
</cfscript> 
  
<!--- Write the two sheets to a single file ---> 
<cfspreadsheet action="write" filename="#theFile#" name="theSheet" 
    sheetname="courses" overwrite=true> 
<cfspreadsheet action="update" filename="#theFile#" name="theSecondSheet"
    sheetname="centers"> 
  
<!--- Read all or part of the file into a spreadsheet object, CSV string, 
      HTML string, and query. ---> 
<cfspreadsheet action="read" src="#theFile#" sheetname="courses" name="spreadsheetData"> 
<cfspreadsheet action="read" src="#theFile#" sheet=1 rows="3,4" format="csv" name="csvData"> 
<cfspreadsheet action="read" src="#theFile#" format="html" rows="5-10" name="htmlData"> 
<cfspreadsheet action="read" src="#theFile#" sheetname="centers" query="queryData"> 
  
<h3>First sheet row 3 read as a CSV variable</h3> 
<cfdump var="#csvData#"> 
  
<h3>Second sheet rows 5-10 read as an HTML variable</h3> 
<cfdump var="#htmlData#"> 
  
<h3>Second sheet read as a query variable</h3> 
<cfdump var="#queryData#"> 
  
<!--- Modify the courses sheet. ---> 
<cfscript> 
    SpreadsheetAddRow(spreadsheetData,"03,ENGL,230,Poetry 1",8,1); 
    SpreadsheetAddColumn(spreadsheetData, 
    "Basic,Intermediate,Advanced,Basic,Intermediate,Advanced,Basic,Intermediate,Advanced", 
    3,2,true); 
</cfscript> 
  
<!--- Write the updated Courses sheet to a new XLS file ---> 
<cfspreadsheet action="write" filename="#theDir#updatedFile.xls" name="spreadsheetData" 
    sheetname="courses" overwrite=true> 
<!--- Write an XLS file containing the data in the CSV variable. --->     
<cfspreadsheet action="write" filename="#theDir#dataFromCSV.xls" name="CSVData" 
    format="csv" sheetname="courses" overwrite=true>
Let's take chunks of the above code and see each chunk in action. For example, consider a csv file is uploaded on the web, which you want to retrieve and perform some actions.
You can also jump to ths fiddle and try out the code chunks. Sign in with your Google or Facebook credentials and launch the file cfspreadsheet.cfm.
Step 1
Read the csv file and store the response in a variable.
<cfscript>
 cfhttp( name="mydata", url="https://raw.githubusercontent.com/sauravg94/test-repo/master/MOCK_DATA.csv", firstrowasheaders="true" ,method="GET");
 writedump(mydata);
</cfscript>
Step 2
  1. Set the destination for the xlsx or xls file.
  2. Create an empty spreadsheet object.
  3. Populate the object with data fetched with cfhttp.
Note:
SpreadsheetNew(true|false)
  1. True or Yes: Creates an .xlsx file that is supported by Microsoft Office Excel 2007.
  2. False or No: Creates an .xls file.
<cfscript> 
    //Use an absolute path for the files. 
    theDir=GetDirectoryFromPath(GetCurrentTemplatePath()); 
    theFile=theDir & "mock_data.xlsx"; 
    //Create an empty ColdFusion spreadsheet objects. 
    theSheet = SpreadsheetNew(true); 
    //Populate the object with data fetched with cfhttp
    SpreadsheetAddRows(theSheet,mydata,1);
</cfscript>
Step 3
Write the sheet into a file.
<cfspreadsheet action="write" filename="#theFile#" name="theSheet" sheetname="mock_data" overwrite=true>
Step 4
Read all or part of the file into a spreadsheet object, CSV string, HTML string, and query.
<cfspreadsheet action="read" src="#theFile#" sheetname="mock_data" name="spreadsheetData">

<cfspreadsheet action="read" src="#theFile#" sheet=1 rows="100-200" format="csv" name="csvData">

<cfspreadsheet action="read" src="#theFile#" format="html" rows="5-10" name="htmlData">

<cfspreadsheet action="read" src="#theFile#" sheetname="mock_data" query="queryData">
Step 5
<cfdump var="#spreadsheetData#" >

<cfoutput >
 #csvData#
</cfoutput>

<cfdump var="#htmlData#" >

<cfdump var="#queryData#" >

Password protection

In the following examples, the script will attempt to read employee.xlsx, which is a password-protected file.
Example 1
Read the file with attribute password
<cfspreadsheet action="read" src="#expandPath('employee.xlsx')#" name="employee" password="abc123">
<cfdump var="#employee#" label="Result of cfspreadsheet read action (with password attribute)">
Example 2
Read the file without specifying the password attribute. The script will produce an exception.
cfspreadsheet action="read" src="#expandPath('employee.xlsx')#" name="employee">
<cfdump var="#employee#" label="Result of cfspreadsheet read action (without password attribute)">
Example 3
Read the file with an incorrect password, which will produce an exception.
<cfspreadsheet action="read" src="#expandPath('employee.xls')#" name="employee" password="1234">
<cfdump var="#employee#" label="Result of cfspreadsheet read action (with incorrect password attribute)">
Example 4
Now, attempt updating the file without specifying the password attribute. The read action will produce an exception.
<cfspreadsheet action="read" src="#expandPath('employee.xlsx')#" name="employee" >
<cfspreadsheet
        action="update"
        filename = "#expandPath('employee.xlsx')#"
        name="employee"
        sheetname = "sheet1" 
        password="abc123" > 
<cfdump var="#var#" >

Real-world uses of the cfspreadsheet tag

Database report export to Excel

Your sales department needs weekly reports showing all customer orders for management review. The reports must be in Excel format so managers can sort, filter, and analyze the data. The reports should include proper formatting, headers, and be easy to distribute via email.
Problem statement
  • Management requires reports in Excel format, not HTML or PDF
  • Database queries return data that needs to be exported
  • Reports must be well-formatted and professional
  • Need to automate weekly report generation
  • Multiple departments need different subsets of data
  • Manual export from database tools is time-consuming
Solution
Use cfspreadsheet to convert database query results directly into formatted Excel files.
<cfscript>
    // Create sample order data (in production, this would be a database query)
    ordersQuery = queryNew(
        "OrderID,OrderDate,CustomerName,ProductName,Quantity,UnitPrice,Total,Status",
        "integer,date,varchar,varchar,integer,decimal,decimal,varchar"
    );
    
    queryAddRow(ordersQuery, [
        {
            OrderID: 1001,
            OrderDate: dateAdd("d", -7, now()),
            CustomerName: "Acme Corporation",
            ProductName: "Laptop Computer",
            Quantity: 5,
            UnitPrice: 1299.99,
            Total: 6499.95,
            Status: "Shipped"
        },
        {
            OrderID: 1002,
            OrderDate: dateAdd("d", -6, now()),
            CustomerName: "Tech Solutions Inc",
            ProductName: "Wireless Mouse",
            Quantity: 25,
            UnitPrice: 29.99,
            Total: 749.75,
            Status: "Delivered"
        },
        {
            OrderID: 1003,
            OrderDate: dateAdd("d", -5, now()),
            CustomerName: "Digital Dynamics",
            ProductName: "USB-C Hub",
            Quantity: 10,
            UnitPrice: 49.99,
            Total: 499.90,
            Status: "Processing"
        },
        {
            OrderID: 1004,
            OrderDate: dateAdd("d", -4, now()),
            CustomerName: "Global Enterprises",
            ProductName: "Wireless Keyboard",
            Quantity: 15,
            UnitPrice: 79.99,
            Total: 1199.85,
            Status: "Shipped"
        },
        {
            OrderID: 1005,
            OrderDate: dateAdd("d", -3, now()),
            CustomerName: "Innovative Systems",
            ProductName: "Monitor 27-inch",
            Quantity: 8,
            UnitPrice: 349.99,
            Total: 2799.92,
            Status: "Delivered"
        }
    ]);
    
    // Set file location (use expandPath to get web-accessible directory)
    // In production, configure a specific reports directory with proper permissions
    reportDir = expandPath("./reports/");
    
    // Create directory if it doesn't exist (with error handling)
    try {
        if (!directoryExists(reportDir)) {
            directoryCreate(reportDir);
        }
        canWriteFiles = true;
    } catch (any e) {
        // If we can't create directory, use memory-only mode
        canWriteFiles = false;
        reportDir = "Unable to create directory - using memory mode";
    }
    
    reportFileName = "Weekly_Sales_Report_" & dateFormat(now(), "yyyy-mm-dd") & ".xlsx";
    fullReportPath = canWriteFiles ? reportDir & reportFileName : "";
</cfscript>

<h2>Database Report Export to Excel</h2>

<!--- Check if we can write files --->
<cfif NOT canWriteFiles>
    <div style="background: ##fff3e0; border: 2px solid ##ff9800; padding: 15px; margin: 15px 0; border-radius: 5px;">
        <h4 style="color: orange; margin: 0;">⚠ File System Access Limited</h4>
        <p>Unable to write files to disk. This demo will show in-memory spreadsheet operations instead.</p>
        <p><strong>For production:</strong> Configure a directory with proper write permissions in your application.</p>
    </div>
</cfif>

<!--- Display the query data --->
<h3>Order Data (Query Results)</h3>
<cfdump 
    var="#ordersQuery#" 
    label="Orders to Export"
    expand="yes">

<hr>

<!--- Method 1: Simple export - Query directly to Excel --->
<h3>Method 1: Simple Query Export</h3>

<cfif canWriteFiles>
    <cftry>
        <cfspreadsheet 
            action="write" 
            query="#ordersQuery#"
            filename="#fullReportPath#"
            sheetname="Weekly Orders"
            overwrite="true">
        
        <div style="background: ##e8f5e9; border: 2px solid ##4caf50; padding: 15px; margin: 15px 0; border-radius: 5px;">
            <h4 style="color: green; margin: 0;">✓ Excel File Created Successfully</h4>
            <p><strong>Location:</strong> #fullReportPath#</p>
            <p><strong>Records Exported:</strong> #ordersQuery.recordCount#</p>
        </div>
        
        <cfcatch type="any">
            <div style="background: ##ffebee; border: 2px solid ##f44336; padding: 15px; margin: 15px 0; border-radius: 5px;">
                <h4 style="color: red; margin: 0;">✗ Error Writing File</h4>
                <p><strong>Error:</strong> #cfcatch.message#</p>
                <p>The application may not have permission to write to this directory.</p>
            </div>
            <cfset canWriteFiles = false>
        </cfcatch>
    </cftry>
<cfelse>
    <div style="background: ##e3f2fd; border: 2px solid ##2196f3; padding: 15px; margin: 15px 0; border-radius: 5px;">
        <h4>📝 Code Example (File Write Disabled)</h4>
        <pre style="background: ##f5f5f5; padding: 10px; border-radius: 5px;">
&lt;cfspreadsheet 
    action="write" 
    query="##ordersQuery##"
    filename="##fullReportPath##"
    sheetname="Weekly Orders"
    overwrite="true"&gt;</pre>
        <p>This would create an Excel file with all query data when file permissions are available.</p>
    </div>
</cfif>

<!--- Method 2: Advanced export with formatting --->
<h3>Method 2: Formatted Excel Export (In-Memory)</h3>

<cfscript>
    // Create a new spreadsheet object (true = .xlsx format)
    // This works in memory even without file system access
    spreadsheetObj = SpreadsheetNew("Weekly Sales Report", true);
    
    // Add the query data starting at row 2 (leaving room for title)
    SpreadsheetAddRow(spreadsheetObj, "WEEKLY SALES REPORT - " & dateFormat(now(), "mmmm dd, yyyy"));
    SpreadsheetAddRow(spreadsheetObj, ""); // Blank row
    
    // Add query data
    SpreadsheetAddRows(spreadsheetObj, ordersQuery);
    
    // Format the title row
    SpreadsheetFormatRow(spreadsheetObj, 
        {
            bold: true,
            fontsize: 14,
            alignment: "center"
        }, 
        1
    );
    
    // Format the header row (row 3, after title and blank row)
    SpreadsheetFormatRow(spreadsheetObj, 
        {
            bold: true,
            fgcolor: "light_blue",
            alignment: "center"
        }, 
        3
    );
    
    // Set column widths for better readability
    SpreadsheetSetColumnWidth(spreadsheetObj, 1, 12);  // OrderID
    SpreadsheetSetColumnWidth(spreadsheetObj, 2, 15);  // OrderDate
    SpreadsheetSetColumnWidth(spreadsheetObj, 3, 25);  // CustomerName
    SpreadsheetSetColumnWidth(spreadsheetObj, 4, 25);  // ProductName
    SpreadsheetSetColumnWidth(spreadsheetObj, 5, 12);  // Quantity
    SpreadsheetSetColumnWidth(spreadsheetObj, 6, 12);  // UnitPrice
    SpreadsheetSetColumnWidth(spreadsheetObj, 7, 12);  // Total
    SpreadsheetSetColumnWidth(spreadsheetObj, 8, 15);  // Status
    
    // Try to save formatted spreadsheet
    formattedFileName = "Weekly_Sales_Report_Formatted_" & dateFormat(now(), "yyyy-mm-dd") & ".xlsx";
    formattedPath = canWriteFiles ? reportDir & formattedFileName : "";
    
    if (canWriteFiles) {
        try {
            SpreadsheetWrite(spreadsheetObj, formattedPath, true);
            formattedSaved = true;
        } catch (any e) {
            formattedSaved = false;
        }
    } else {
        formattedSaved = false;
    }
</cfscript>

<cfif formattedSaved>
    <div style="background: ##e8f5e9; border: 2px solid ##4caf50; padding: 15px; margin: 15px 0; border-radius: 5px;">
        <h4 style="color: green; margin: 0;">✓ Formatted Excel File Created</h4>
        <p><strong>Location:</strong> #formattedPath#</p>
        <p><strong>Features:</strong> Title row, formatted headers, adjusted column widths</p>
    </div>
<cfelse>
    <div style="background: ##e8f5e9; border: 2px solid ##4caf50; padding: 15px; margin: 15px 0; border-radius: 5px;">
        <h4 style="color: green; margin: 0;">✓ Spreadsheet Object Created in Memory</h4>
        <p><strong>Features Applied:</strong> Title row, formatted headers, adjusted column widths</p>
        <p><em>In production with file permissions, this would be saved to disk.</em></p>
        <p><strong>Alternative:</strong> Use <code>cfheader/cfcontent</code> to stream directly to browser for download.</p>
    </div>
</cfif>

<!--- Method 3: Multiple sheets in one workbook --->
<h3>Method 3: Multi-Sheet Workbook (In-Memory)</h3>

<cfscript>
    // Create summary data
    summaryQuery = queryNew(
        "Status,OrderCount,TotalAmount",
        "varchar,integer,decimal"
    );
    
    queryAddRow(summaryQuery, [
        {Status: "Shipped", OrderCount: 2, TotalAmount: 7699.80},
        {Status: "Delivered", OrderCount: 2, TotalAmount: 3549.67},
        {Status: "Processing", OrderCount: 1, TotalAmount: 499.90}
    ]);
    
    // Create main workbook
    workbook = SpreadsheetNew("Orders", true);
    SpreadsheetAddRows(workbook, ordersQuery);
    
    // Format headers
    SpreadsheetFormatRow(workbook, {bold: true, fgcolor: "light_blue"}, 1);
    
    // Add summary sheet
    SpreadsheetCreateSheet(workbook, "Summary");
    SpreadsheetSetActiveSheet(workbook, "Summary");
    SpreadsheetAddRows(workbook, summaryQuery);
    SpreadsheetFormatRow(workbook, {bold: true, fgcolor: "light_green"}, 1);
    
    // Try to save multi-sheet workbook
    multiSheetFileName = "Weekly_Sales_Report_MultiSheet_" & dateFormat(now(), "yyyy-mm-dd") & ".xlsx";
    multiSheetPath = canWriteFiles ? reportDir & multiSheetFileName : "";
    
    if (canWriteFiles) {
        try {
            SpreadsheetWrite(workbook, multiSheetPath, true);
            multiSheetSaved = true;
        } catch (any e) {
            multiSheetSaved = false;
        }
    } else {
        multiSheetSaved = false;
    }
</cfscript>

<cfif multiSheetSaved>
    <div style="background: ##e8f5e9; border: 2px solid ##4caf50; padding: 15px; margin: 15px 0; border-radius: 5px;">
        <h4 style="color: green; margin: 0;">✓ Multi-Sheet Workbook Created</h4>
        <p><strong>Location:</strong> #multiSheetPath#</p>
        <p><strong>Sheets:</strong> Orders (detailed data) + Summary (aggregated data)</p>
    </div>
<cfelse>
    <div style="background: ##e8f5e9; border: 2px solid ##4caf50; padding: 15px; margin: 15px 0; border-radius: 5px;">
        <h4 style="color: green; margin: 0;">✓ Multi-Sheet Workbook Created in Memory</h4>
        <p><strong>Sheets:</strong> Orders (detailed data) + Summary (aggregated data)</p>
        <p><em>In production with file permissions, this would be saved to: #multiSheetFileName#</em></p>
    </div>
</cfif>

<!--- Display summary data --->
<h3>Summary Data</h3>
<cfdump 
    var="#summaryQuery#" 
    label="Order Summary by Status"
    expand="yes">

<!--- Calculate totals --->
<cfscript>
    reportStats = {
        "totalOrders": ordersQuery.recordCount,
        "totalRevenue": dollarFormat(arraySum(ordersQuery["Total"])),
        "reportDate": dateTimeFormat(now(), "yyyy-mm-dd HH:nn:ss"),
        "filesGenerated": 3,
        "reportDirectory": reportDir
    };
</cfscript>

<h3>Report Generation Summary</h3>
<cfdump 
    var="#reportStats#" 
    label="Export Statistics"
    expand="yes">

<!--- Example: Download link --->
<div style="background: ##e3f2fd; border-left: 4px solid ##2196f3; padding: 15px; margin: 20px 0;">
    <h4>📥 Generated Reports:</h4>
    <ul>
        <li><strong>Simple Export:</strong> #reportFileName#</li>
        <li><strong>Formatted Export:</strong> #formattedFileName#</li>
        <li><strong>Multi-Sheet Export:</strong> #multiSheetFileName#</li>
    </ul>
    <cfif canWriteFiles>
        <p><strong>Location:</strong> #reportDir#</p>
    <cfelse>
        <p><strong>Status:</strong> Created in memory (file system access not available)</p>
        <p><strong>Alternative:</strong> Stream directly to browser using cfheader/cfcontent</p>
    </cfif>
</div>

<!--- Show download example --->
<div style="background: ##f5f5f5; border: 1px solid ##ccc; padding: 15px; margin: 20px 0; border-radius: 5px;">
    <h4>💡 Alternative: Stream Excel to Browser (No File System Required)</h4>
    <p>When file system access is limited, stream the spreadsheet directly to the browser:</p>
    <pre style="background: ##fff; padding: 10px; border: 1px solid ##ddd; border-radius: 3px; overflow-x: auto;">
&lt;cfscript&gt;
    // Create spreadsheet in memory
    sheet = SpreadsheetNew("Report", true);
    SpreadsheetAddRows(sheet, myQuery);
    
    // Get binary data
    binaryData = SpreadsheetReadBinary(sheet);
&lt;/cfscript&gt;

&lt;cfheader name="Content-Disposition" value="attachment; filename=report.xlsx"&gt;
&lt;cfcontent type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" 
           variable="##binaryData##" reset="true"&gt;</pre>
</div>

Employee data import from Excel

Your HR department maintains employee records in Excel spreadsheets. When onboarding new employees or updating existing records, HR staff upload Excel files that need to be imported into your employee management system. The system must validate the data and insert it into the database.
Problem statement
  • HR staff work with Excel for employee data entry
  • Need to import Excel data into ColdFusion application
  • Must validate data before database insertion
  • Handle errors gracefully (missing fields, invalid formats)
  • Support both .xls and .xlsx formats
  • Prevent duplicate employee records
  • Provide clear feedback on import success/failure
Solution
Use cfspreadsheet to read Excel files and convert them to query objects for processing.
<cfscript>
    // Set upload directory (use expandPath for web-accessible directory)
    uploadDir = expandPath("./employee-uploads/");
    
    // Create directory if it doesn't exist (with error handling)
    try {
        if (!directoryExists(uploadDir)) {
            directoryCreate(uploadDir);
        }
        canUploadFiles = true;
    } catch (any e) {
        // If we can't create directory, disable file upload
        canUploadFiles = false;
        uploadDir = "File system access not available";
    }
</cfscript>

<h2>Employee Data Import from Excel</h2>

<!--- Check if file upload is available --->
<cfif NOT canUploadFiles>
    <div style="background: ##fff3e0; border: 2px solid ##ff9800; padding: 20px; margin: 20px 0; border-radius: 5px;">
        <h3 style="color: orange; margin: 0;">⚠ File Upload Not Available</h3>
        <p>The application doesn't have permission to create upload directories.</p>
        <p><strong>For production:</strong> Configure a directory with proper write permissions.</p>
        <p><strong>Alternative:</strong> This demo will show how the import process works with sample data instead.</p>
    </div>
    
    <!--- Set flag to use sample data --->
    <cfset useSampleData = true>
<cfelse>
    <cfset useSampleData = false>
</cfif>

<!--- File upload form --->
<cfif NOT isDefined("form.employeeFile") AND canUploadFiles>
    
    <div style="background: ##e3f2fd; border: 2px solid ##2196f3; padding: 20px; margin: 20px 0; border-radius: 5px;">
        <h3>Upload Employee Data</h3>
        <p>Please upload an Excel file (.xlsx or .xls) containing employee information.</p>
        <p><strong>Required columns:</strong> EmployeeID, FirstName, LastName, Email, Department, Position, HireDate, Salary</p>
    </div>
    
    <form method="post" enctype="multipart/form-data">
        <div style="margin: 20px 0;">
            <label for="employeeFile"><strong>Select Excel File:</strong></label><br>
            <input type="file" name="employeeFile" id="employeeFile" accept=".xlsx,.xls" required>
        </div>
        <div>
            <button type="submit" style="padding: 10px 20px; background: ##2196f3; color: white; border: none; border-radius: 5px; cursor: pointer;">
                Upload and Import
            </button>
        </div>
    </form>
    
    <!--- Show sample data format --->
    <h3>Sample Data Format</h3>
    <cfscript>
        sampleData = queryNew(
            "EmployeeID,FirstName,LastName,Email,Department,Position,HireDate,Salary",
            "varchar,varchar,varchar,varchar,varchar,varchar,date,decimal"
        );
        
        queryAddRow(sampleData, [
            {
                EmployeeID: "EMP001",
                FirstName: "John",
                LastName: "Smith",
                Email: "john.smith@company.com",
                Department: "Engineering",
                Position: "Software Developer",
                HireDate: now(),
                Salary: 85000
            },
            {
                EmployeeID: "EMP002",
                FirstName: "Jane",
                LastName: "Doe",
                Email: "jane.doe@company.com",
                Department: "Marketing",
                Position: "Marketing Manager",
                HireDate: now(),
                Salary: 75000
            }
        ]);
    </cfscript>
    
    <cfdump 
        var="#sampleData#" 
        label="Expected Excel Format"
        expand="yes">

<cfelseif useSampleData>
    
    <!--- Process with sample data when file upload is not available --->
    <h3>Processing Sample Data (File Upload Not Available)...</h3>
    
    <cfscript>
        // Use the sample data we created earlier
        employeeData = sampleData;
    </cfscript>
    
    <div style="background: ##e3f2fd; border: 2px solid ##2196f3; padding: 15px; margin: 15px 0; border-radius: 5px;">
        <h4 style="color: blue; margin: 0;">📝 Using Sample Data</h4>
        <p>Since file upload is not available, demonstrating with sample employee data.</p>
        <p><strong>Records:</strong> #employeeData.recordCount#</p>
    </div>
    
    <!--- Display imported data --->
    <h3>Sample Employee Data</h3>
    <cfdump 
        var="#employeeData#" 
        label="Data for Processing (#employeeData.recordCount# records)"
        expand="yes"
        top="10">
    
    <!--- Continue with validation process (skip to line after file read) --->
    <cfset continueWithValidation = true>

<cfelse>
    
    <!--- Process file upload --->
    <h3>Processing Upload...</h3>
    
    <cftry>
        <!--- Upload the file --->
        <cffile 
            action="upload" 
            fileField="employeeFile" 
            destination="#uploadDir#" 
            nameConflict="makeunique"
            result="uploadResult">
        
        <cfscript>
            uploadedFile = uploadResult.serverDirectory & "/" & uploadResult.serverFile;
        </cfscript>
        
        <div style="background: ##e8f5e9; border: 2px solid ##4caf50; padding: 15px; margin: 15px 0; border-radius: 5px;">
            <h4 style="color: green; margin: 0;">✓ File Uploaded Successfully</h4>
            <p><strong>Filename:</strong> #uploadResult.clientFile#</p>
            <p><strong>Size:</strong> #numberFormat(uploadResult.fileSize / 1024, "0.00")# KB</p>
        </div>
        
        <!--- Read Excel file into query --->
        <cfspreadsheet 
            action="read" 
            src="#uploadedFile#"
            query="employeeData"
            headerrow="1">
        
        <!--- Display imported data --->
        <h3>Imported Employee Data</h3>
        <cfdump 
            var="#employeeData#" 
            label="Data from Excel (#employeeData.recordCount# records)"
            expand="yes"
            top="10">
        
        <cfset continueWithValidation = true>
        
        <cfcatch type="any">
            <div style="background: ##ffebee; border: 2px solid ##f44336; padding: 20px; margin: 20px 0; border-radius: 5px;">
                <h3 style="color: red; margin: 0;">✗ Upload Failed</h3>
                <p><strong>Error:</strong> #cfcatch.message#</p>
                <p><strong>Detail:</strong> #cfcatch.detail#</p>
                <p>Please ensure the file is a valid Excel format with the correct column structure.</p>
            </div>
            
            <p><a href="?">Try Again</a></p>
            <cfset continueWithValidation = false>
        </cfcatch>
    </cftry>
    
</cfif>

<!--- Continue with validation if we have data --->
<cfif (isDefined("continueWithValidation") AND continueWithValidation) OR useSampleData>
    
    <!--- Validate and process data --->
        <cfscript>
            validRecords = [];
            invalidRecords = [];
            duplicateRecords = [];
            processedCount = 0;
            
            // Simulate existing employee IDs (in production, query database)
            existingEmployeeIDs = ["EMP001", "EMP005"];
            
            // Process each row
            for (row = 1; row <= employeeData.recordCount; row++) {
                employee = {
                    "employeeID": employeeData.EmployeeID[row],
                    "firstName": employeeData.FirstName[row],
                    "lastName": employeeData.LastName[row],
                    "email": employeeData.Email[row],
                    "department": employeeData.Department[row],
                    "position": employeeData.Position[row],
                    "hireDate": employeeData.HireDate[row],
                    "salary": employeeData.Salary[row],
                    "rowNumber": row
                };
                
                // Validation
                isValid = true;
                validationErrors = [];
                
                // Check required fields
                if (len(trim(employee.employeeID)) == 0) {
                    arrayAppend(validationErrors, "Employee ID is required");
                    isValid = false;
                }
                
                if (len(trim(employee.firstName)) == 0) {
                    arrayAppend(validationErrors, "First Name is required");
                    isValid = false;
                }
                
                if (len(trim(employee.email)) == 0) {
                    arrayAppend(validationErrors, "Email is required");
                    isValid = false;
                }
                
                // Check for duplicates
                if (arrayFind(existingEmployeeIDs, employee.employeeID) > 0) {
                    arrayAppend(validationErrors, "Employee ID already exists");
                    employee.errors = validationErrors;
                    arrayAppend(duplicateRecords, employee);
                    continue;
                }
                
                // Categorize record
                if (isValid) {
                    arrayAppend(validRecords, employee);
                    processedCount++;
                } else {
                    employee.errors = validationErrors;
                    arrayAppend(invalidRecords, employee);
                }
            }
            
            // Import summary
            importSummary = {
                "totalRecords": employeeData.recordCount,
                "validRecords": arrayLen(validRecords),
                "invalidRecords": arrayLen(invalidRecords),
                "duplicateRecords": arrayLen(duplicateRecords),
                "importDate": dateTimeFormat(now(), "yyyy-mm-dd HH:nn:ss"),
                "filename": uploadResult.clientFile
            };
        </cfscript>
        
        <!--- Display import summary --->
        <h3>Import Summary</h3>
        <cfdump 
            var="#importSummary#" 
            label="Import Results"
            expand="yes">
        
        <!--- Show valid records --->
        <cfif arrayLen(validRecords) GT 0>
            <h3 style="color: green;">✓ Valid Records (Ready to Import)</h3>
            <cfdump 
                var="#validRecords#" 
                label="Records that passed validation"
                expand="yes">
            
            <div style="background: ##e8f5e9; border: 2px solid ##4caf50; padding: 15px; margin: 15px 0; border-radius: 5px;">
                <h4 style="color: green; margin: 0;">Ready for Database Import</h4>
                <p>#arrayLen(validRecords)# employee record(s) validated successfully.</p>
                <p><em>In production, these would be inserted into the database.</em></p>
            </div>
        </cfif>
        
        <!--- Show invalid records --->
        <cfif arrayLen(invalidRecords) GT 0>
            <h3 style="color: red;">✗ Invalid Records (Errors Found)</h3>
            <cfdump 
                var="#invalidRecords#" 
                label="Records with validation errors"
                expand="yes">
        </cfif>
        
        <!--- Show duplicate records --->
        <cfif arrayLen(duplicateRecords) GT 0>
            <h3 style="color: orange;">⚠ Duplicate Records (Already Exist)</h3>
            <cfdump 
                var="#duplicateRecords#" 
                label="Records with duplicate Employee IDs"
                expand="yes">
        </cfif>
        
        <!--- Success/Error summary --->
        <cfif arrayLen(invalidRecords) EQ 0 AND arrayLen(duplicateRecords) EQ 0>
            <div style="background: ##e8f5e9; border: 2px solid ##4caf50; padding: 20px; margin: 20px 0; border-radius: 5px;">
                <h3 style="color: green; margin: 0;">🎉 Import Successful!</h3>
                <p>All #importSummary.totalRecords# employee record(s) were validated successfully and are ready for database import.</p>
            </div>
        <cfelse>
            <div style="background: ##fff3e0; border: 2px solid ##ff9800; padding: 20px; margin: 20px 0; border-radius: 5px;">
                <h3 style="color: orange; margin: 0;">⚠ Import Completed with Issues</h3>
                <p><strong>Valid:</strong> #arrayLen(validRecords)# records</p>
                <p><strong>Invalid:</strong> #arrayLen(invalidRecords)# records</p>
                <p><strong>Duplicates:</strong> #arrayLen(duplicateRecords)# records</p>
                <p>Please correct the errors and re-upload the file.</p>
            </div>
        </cfif>
        
        <!--- Clean up uploaded file --->
        <cfif NOT useSampleData AND isDefined("uploadedFile") AND fileExists(uploadedFile)>
            <cftry>
                <cffile action="delete" file="#uploadedFile#">
                <cfcatch type="any">
                    <!--- Ignore cleanup errors --->
                </cfcatch>
            </cftry>
        </cfif>
        
        <cfif canUploadFiles>
            <p><a href="?" style="padding: 10px 20px; background: ##2196f3; color: white; text-decoration: none; border-radius: 5px; display: inline-block; margin-top: 20px;">
                Upload Another File
            </a></p>
        <cfelse>
            <p><a href="?" style="padding: 10px 20px; background: ##2196f3; color: white; text-decoration: none; border-radius: 5px; display: inline-block; margin-top: 20px;">
                View Demo Again
            </a></p>
        </cfif>
    
</cfif>

<!--- Show alternative when file system is not available --->
<cfif NOT canUploadFiles AND NOT isDefined("form.employeeFile") AND NOT useSampleData>
    <div style="background: ##f5f5f5; border: 1px solid ##ccc; padding: 15px; margin: 20px 0; border-radius: 5px;">
        <h4>💡 Alternative: Process Excel Data Without File Upload</h4>
        <p>When file system access is limited, you can still process Excel data by:</p>
        <ol>
            <li><strong>Reading from URL:</strong> Use cfhttp to fetch Excel files from external URLs</li>
            <li><strong>Binary Upload:</strong> Read uploaded file directly to memory without saving to disk</li>
            <li><strong>Database Storage:</strong> Store uploaded files in database BLOB fields</li>
        </ol>
        <pre style="background: ##fff; padding: 10px; border: 1px solid ##ddd; border-radius: 3px; overflow-x: auto;">
&lt;!--- Example: Read Excel from form without saving to disk ---&gt;
&lt;cffile action="readBinary" filefield="employeeFile" variable="excelBinary"&gt;
&lt;cfset tempFile = getTempFile(getTempDirectory(), "upload")&gt;
&lt;cffile action="write" file="##tempFile##" output="##excelBinary##"&gt;
&lt;cfspreadsheet action="read" src="##tempFile##" query="employeeData"&gt;
&lt;cffile action="delete" file="##tempFile##"&gt;</pre>
    </div>
</cfif>

Monthly sales report generation

Your finance department requires monthly sales reports with detailed formatting, charts-ready data, and multiple worksheets showing different views of sales performance. The reports must be professional, include company branding, and be distributed to executive leadership.
Problem statement
  • Need automated monthly sales report generation
  • Reports must be professionally formatted
  • Require multiple data views in separate sheets
  • Must include summary statistics and totals
  • Headers and formatting must be consistent
  • Reports distributed on first business day of month
  • Manual report creation is time-consuming and error-prone
Solution
Use cfspreadsheet with advanced formatting to create multi-sheet workbooks with professional styling.
<cfscript>
    // Generate sample sales data for the month
    salesData = queryNew(
        "SaleDate,Region,SalesRep,ProductCategory,ProductName,Quantity,UnitPrice,TotalSale",
        "date,varchar,varchar,varchar,varchar,integer,decimal,decimal"
    );
    
    regions = ["North", "South", "East", "West"];
    salesReps = ["Alice Johnson", "Bob Martinez", "Carol White", "David Lee", "Emma Davis"];
    categories = ["Electronics", "Software", "Hardware", "Accessories"];
    
    // Generate 20 sales records
    for (i = 1; i <= 20; i++) {
        saleDate = dateAdd("d", -randRange(1, 30), now());
        quantity = randRange(1, 10);
        unitPrice = randRange(50, 500);
        
        queryAddRow(salesData, {
            SaleDate: saleDate,
            Region: regions[randRange(1, arrayLen(regions))],
            SalesRep: salesReps[randRange(1, arrayLen(salesReps))],
            ProductCategory: categories[randRange(1, arrayLen(categories))],
            ProductName: "Product " & i,
            Quantity: quantity,
            UnitPrice: unitPrice,
            TotalSale: quantity * unitPrice
        });
    }
    
    // Calculate summary statistics
    totalSales = 0;
    for (i = 1; i <= salesData.recordCount; i++) {
        totalSales += salesData.TotalSale[i];
    }
    totalOrders = salesData.recordCount;
    averageOrderValue = totalSales / totalOrders;
    
    // Create summary by region
    regionSummary = queryNew(
        "Region,OrderCount,TotalSales,AvgOrderValue",
        "varchar,integer,decimal,decimal"
    );
    
    for (region in regions) {
        regionOrders = 0;
        regionTotal = 0;
        
        for (row = 1; row <= salesData.recordCount; row++) {
            if (salesData.Region[row] == region) {
                regionOrders++;
                regionTotal += salesData.TotalSale[row];
            }
        }
        
        queryAddRow(regionSummary, {
            Region: region,
            OrderCount: regionOrders,
            TotalSales: regionTotal,
            AvgOrderValue: regionOrders > 0 ? regionTotal / regionOrders : 0
        });
    }
    
    // Set report directory (use expandPath for web-accessible directory)
    reportDir = expandPath("./monthly-reports/");
    
    // Create directory if it doesn't exist (with error handling)
    try {
        if (!directoryExists(reportDir)) {
            directoryCreate(reportDir);
        }
        canWriteFiles = true;
    } catch (any e) {
        // If we can't create directory, work in memory only
        canWriteFiles = false;
        reportDir = "File system access not available";
    }
    
    reportMonth = dateFormat(now(), "mmmm yyyy");
    reportFileName = "Monthly_Sales_Report_" & dateFormat(now(), "yyyy-mm") & ".xlsx";
    reportPath = canWriteFiles ? reportDir & reportFileName : "";
</cfscript>

<h2>Monthly Sales Report Generation</h2>

<cfif NOT canWriteFiles>
    <div style="background: ##fff3e0; border: 2px solid ##ff9800; padding: 20px; margin: 20px 0; border-radius: 5px;">
        <h3 style="color: orange; margin: 0;">⚠ File System Access Limited</h3>
        <p>Unable to write files to disk. Report will be created in memory.</p>
        <p><strong>For production:</strong> Configure a directory with proper write permissions.</p>
        <p><strong>Alternative:</strong> Use cfheader/cfcontent to stream directly to browser.</p>
    </div>
</cfif>

<div style="background: ##e8f5e9; border: 2px solid ##4caf50; padding: 20px; margin: 20px 0; border-radius: 5px;">
    <h3 style="margin: 0;">📊 Generating Report for #reportMonth#</h3>
</div>

<!--- Display source data --->
<h3>Sales Data</h3>
<cfdump 
    var="#salesData#" 
    label="Detailed Sales Transactions (#salesData.recordCount# records)"
    expand="no">

<h3>Regional Summary</h3>
<cfdump 
    var="#regionSummary#" 
    label="Sales by Region"
    expand="yes">

<hr>

<!--- Create formatted Excel workbook --->
<cfscript>
    // Create new workbook
    workbook = SpreadsheetNew("Detailed Sales", true);
    
    // ===== SHEET 1: Company Header and Sales Details =====
    
    // Add company header (row 1)
    SpreadsheetAddRow(workbook, "COMPANY NAME - MONTHLY SALES REPORT");
    SpreadsheetMergeCells(workbook, 1, 1, 1, 8);
    headerFormat = {};
    headerFormat.bold = true;
    headerFormat.fontsize = 16;
    headerFormat.alignment = "center";
    headerFormat.fgcolor = "dark_blue";
    headerFormat.color = "white";
    SpreadsheetFormatRow(workbook, headerFormat, 1);
    
    // Add report period (row 2)
    SpreadsheetAddRow(workbook, "Report Period: " & reportMonth);
    SpreadsheetMergeCells(workbook, 2, 2, 1, 8);
    periodFormat = {};
    periodFormat.bold = true;
    periodFormat.fontsize = 12;
    periodFormat.alignment = "center";
    periodFormat.fgcolor = "light_blue";
    SpreadsheetFormatRow(workbook, periodFormat, 2);
    
    // Add blank row
    SpreadsheetAddRow(workbook, "");
    
    // Add sales data starting at row 4
    SpreadsheetAddRows(workbook, salesData, 4, 1);
    
    // Format header row (row 4)
    headerRowFormat = {};
    headerRowFormat.bold = true;
    headerRowFormat.fgcolor = "light_green";
    headerRowFormat.alignment = "center";
    headerRowFormat.border = "thin";
    SpreadsheetFormatRow(workbook, headerRowFormat, 4);
    
    // Format data rows with borders
    borderFormat = {};
    borderFormat.border = "thin";
    for (row = 5; row <= salesData.recordCount + 4; row++) {
        SpreadsheetFormatRow(workbook, borderFormat, row);
    }
    
    // Note: Currency formatting removed to simplify code
    // Excel will display numbers, you can format them manually in Excel if needed
    
    // Set column widths
    SpreadsheetSetColumnWidth(workbook, 1, 12);  // SaleDate
    SpreadsheetSetColumnWidth(workbook, 2, 12);  // Region
    SpreadsheetSetColumnWidth(workbook, 3, 18);  // SalesRep
    SpreadsheetSetColumnWidth(workbook, 4, 15);  // ProductCategory
    SpreadsheetSetColumnWidth(workbook, 5, 20);  // ProductName
    SpreadsheetSetColumnWidth(workbook, 6, 10);  // Quantity
    SpreadsheetSetColumnWidth(workbook, 7, 12);  // UnitPrice
    SpreadsheetSetColumnWidth(workbook, 8, 12);  // TotalSale
    
    // Add totals row
    totalRow = salesData.recordCount + 5;
    SpreadsheetSetCellValue(workbook, "TOTALS", totalRow, 5);
    SpreadsheetSetCellValue(workbook, totalOrders, totalRow, 6);
    SpreadsheetSetCellValue(workbook, totalSales, totalRow, 8);
    
    totalRowFormat = {};
    totalRowFormat.bold = true;
    totalRowFormat.fgcolor = "gold";
    totalRowFormat.border = "medium";
    totalRowFormat.alignment = "right";
    SpreadsheetFormatRow(workbook, totalRowFormat, totalRow);
    
    // ===== SHEET 2: Regional Summary =====
    SpreadsheetCreateSheet(workbook, "Regional Summary");
    SpreadsheetSetActiveSheet(workbook, "Regional Summary");
    
    // Add header
    SpreadsheetAddRow(workbook, "REGIONAL SALES SUMMARY - " & reportMonth);
    SpreadsheetMergeCells(workbook, 1, 1, 1, 4);
    regionHeaderFormat = {};
    regionHeaderFormat.bold = true;
    regionHeaderFormat.fontsize = 14;
    regionHeaderFormat.alignment = "center";
    regionHeaderFormat.fgcolor = "dark_blue";
    regionHeaderFormat.color = "white";
    SpreadsheetFormatRow(workbook, regionHeaderFormat, 1);
    
    SpreadsheetAddRow(workbook, "");
    
    // Add summary data
    SpreadsheetAddRows(workbook, regionSummary, 3, 1);
    
    // Format header
    regionDataHeaderFormat = {};
    regionDataHeaderFormat.bold = true;
    regionDataHeaderFormat.fgcolor = "light_green";
    regionDataHeaderFormat.alignment = "center";
    regionDataHeaderFormat.border = "thin";
    SpreadsheetFormatRow(workbook, regionDataHeaderFormat, 3);
    
    // Format data rows with borders
    regionBorderFormat = {};
    regionBorderFormat.border = "thin";
    for (row = 4; row <= regionSummary.recordCount + 3; row++) {
        SpreadsheetFormatRow(workbook, regionBorderFormat, row);
    }
    
    // Note: Currency formatting removed to simplify code
    // Numbers will display in Excel and can be formatted there
    
    // Set column widths
    SpreadsheetSetColumnWidth(workbook, 1, 15);
    SpreadsheetSetColumnWidth(workbook, 2, 12);
    SpreadsheetSetColumnWidth(workbook, 3, 15);
    SpreadsheetSetColumnWidth(workbook, 4, 15);
    
    // ===== SHEET 3: Executive Summary =====
    SpreadsheetCreateSheet(workbook, "Executive Summary");
    SpreadsheetSetActiveSheet(workbook, "Executive Summary");
    
    // Add title
    SpreadsheetAddRow(workbook, "EXECUTIVE SUMMARY");
    SpreadsheetMergeCells(workbook, 1, 1, 1, 2);
    summaryTitleFormat = {};
    summaryTitleFormat.bold = true;
    summaryTitleFormat.fontsize = 16;
    summaryTitleFormat.alignment = "center";
    summaryTitleFormat.fgcolor = "dark_blue";
    summaryTitleFormat.color = "white";
    SpreadsheetFormatRow(workbook, summaryTitleFormat, 1);
    
    // Add metrics
    SpreadsheetAddRow(workbook, "");
    SpreadsheetAddRow(workbook, ["Metric", "Value"]);
    summaryHeaderFormat = {};
    summaryHeaderFormat.bold = true;
    summaryHeaderFormat.fgcolor = "light_blue";
    SpreadsheetFormatRow(workbook, summaryHeaderFormat, 3);
    
    SpreadsheetAddRow(workbook, ["Total Sales", dollarFormat(totalSales)]);
    SpreadsheetAddRow(workbook, ["Total Orders", totalOrders]);
    SpreadsheetAddRow(workbook, ["Average Order Value", dollarFormat(averageOrderValue)]);
    SpreadsheetAddRow(workbook, ["Report Month", reportMonth]);
    SpreadsheetAddRow(workbook, ["Generated On", dateTimeFormat(now(), "yyyy-mm-dd HH:nn")]);
    
    // Set column widths
    SpreadsheetSetColumnWidth(workbook, 1, 25);
    SpreadsheetSetColumnWidth(workbook, 2, 20);
    
    // Try to save the workbook
    if (canWriteFiles) {
        try {
            SpreadsheetWrite(workbook, reportPath, true);
            reportSaved = true;
        } catch (any e) {
            reportSaved = false;
        }
    } else {
        reportSaved = false;
    }
</cfscript>

<cfif reportSaved>
    <div style="background: ##e8f5e9; border: 2px solid ##4caf50; padding: 20px; margin: 20px 0; border-radius: 5px;">
        <h3 style="color: green; margin: 0;">✓ Monthly Sales Report Generated Successfully</h3>
        <p><strong>Filename:</strong> #reportFileName#</p>
        <p><strong>Location:</strong> #reportPath#</p>
        <p><strong>Sheets:</strong> Detailed Sales, Regional Summary, Executive Summary</p>
        <p><strong>Total Records:</strong> #salesData.recordCount# sales transactions</p>
    </div>
<cfelse>
    <div style="background: ##e8f5e9; border: 2px solid ##4caf50; padding: 20px; margin: 20px 0; border-radius: 5px;">
        <h3 style="color: green; margin: 0;">✓ Monthly Sales Report Created in Memory</h3>
        <p><strong>Filename:</strong> #reportFileName#</p>
        <p><strong>Sheets:</strong> Detailed Sales, Regional Summary, Executive Summary</p>
        <p><strong>Total Records:</strong> #salesData.recordCount# sales transactions</p>
        <p><em>In production with file permissions, this would be saved to disk.</em></p>
    </div>
</cfif>

<!--- Display report statistics --->
<cfscript>
    reportStats = {};
    reportStats.reportMonth = reportMonth;
    reportStats.totalSales = dollarFormat(totalSales);
    reportStats.totalOrders = totalOrders;
    reportStats.averageOrderValue = dollarFormat(averageOrderValue);
    reportStats.generatedOn = dateTimeFormat(now(), "yyyy-mm-dd HH:nn:ss");
    if (reportSaved) {
        reportStats.fileSize = numberFormat(getFileInfo(reportPath).size / 1024, "0.00") & " KB";
    } else {
        reportStats.fileSize = "N/A (in memory)";
    }
    reportStats.sheets = "Detailed Sales, Regional Summary, Executive Summary";
    reportStats.saved = reportSaved ? "Yes" : "No (memory only)";
</cfscript>

<h3>Report Statistics</h3>
<cfdump 
    var="#reportStats#" 
    label="Generated Report Details"
    expand="yes">

<!--- Show alternative download method --->
<cfif NOT canWriteFiles>
    <div style="background: ##f5f5f5; border: 1px solid ##ccc; padding: 15px; margin: 20px 0; border-radius: 5px;">
        <h4>💡 Alternative: Stream Report to Browser</h4>
        <p>When file system access is limited, stream the Excel report directly:</p>
        <pre style="background: ##fff; padding: 10px; border: 1px solid ##ddd; border-radius: 3px; overflow-x: auto;">
&lt;cfscript&gt;
    // Get binary data from workbook
    binaryData = SpreadsheetReadBinary(invoice);
&lt;/cfscript&gt;

&lt;cfheader name="Content-Disposition" 
          value="attachment; filename=#reportFileName#"&gt;
&lt;cfcontent type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" 
           variable="##binaryData##" reset="true"&gt;</pre>
    </div>
</cfif>

Share this page

Was this page helpful?
We're glad. Tell us how this page helped.
We're sorry. Can you tell us what didn't work for you?
Thank you for your feedback. Your response will help improve this page.

On this page