AnnualReport_DemoFurnitures
Back to ListDescription: Full package with report generation to Excel, adding 3 different chart types to the report. Stacked column and line-chart on sheet 1, 2 and 3. Multiple doughnuts on sheet 4.
FileMaker Prototype:
Set Variable [$res; ACF_Run("AnnualReport_DemoFurnitures";)]
Category: EXCEL
The AnnualReport_DemoFurnitures Function is located near the bottom of the programlisting listing, with all its support function defined above.
- function _BuildPivotSheet - are used to generate sheet 1, 2 and 3 in the excel spreadsheet.
- function _PlaceLineChart - are used to place a line-chart on sheet 1, 2 and 3. (Below the stacked column chart)
- function _PlaceStackedColumnChart are used to place a stacked colum chart on sheet 1, 2 and 3.
- function _BuildSellerDetailSheet are used to build the seller detail sheet 4.
- function _PlaceDonut - are used for all the doughnut charts on sheet 4.
- function lookup_color: A helper function to lookup the color for a product or category in the doughnut, so the same product or category uses the same colors on all doughnuts.
- function getColor: Get a color from a 20x color map by index.
- find_in_array, min3, min2 and max helper functions used in the calculations.
The full demo application (FileMaker application), along with the ACF-Plugin 1.7.8.2 (Mac/Win) can be downloaded here:
Function source:
Package AnnualReportDemo "Annual report Excel Spreadsheet demo";
// ============================================================
// Annual Report workbook for "Demo Furnitures Inc"
// Sheets:
// 1) By Category x Period
// 2) By Seller x Period
// 3) By Product x Period
// ============================================================
function find_in_array ( array string arr, string key)
int i;
for ( i = 1, sizeof(arr))
if ( arr[i] == key) then
return i;
end if
end for
return -1;
end
function min3 (int a, int b, int c)
int res;
res = (a<b?a:b);
res = (res<c?res:c);
return res;
end
function min2 (int a, int b)
int res;
res = (a<b?a:b);
return res;
end
function max (int a, int b)
int res;
res = (a>b?a:b);
return res;
end
function getColor (int idx)
array string colormap = {
"1F77B4","FF7F0E","2CA02C","D62728","9467BD",
"8C564B","E377C2","7F7F7F","BCBD22","17BECF",
"AEC7E8","FFBB78","98DF8A","FF9896","C5B0D5",
"C49C94","F7B6D2","C7C7C7","DBDB8D","9EDAE5"
};
idx = mod(idx, 20);
idx = (idx==0?20:idx);
return colormap [idx];
end
// Return up to topK sheet row numbers ranked by totals (desc)
// NOTE: arrays are passed by reference in ACF, so copy before sorting.
function _SelectTopKRows ( array int dataRows, array float totalsForRows, int topK ) // -> array int
array int dr = {dataRows}; // local copies
array float tr = {totalsForRows};
if (sizeof (dr) != sizeof (tr)) then
throw format ("SelectTopKRows, arrays are not equal size, dr=%d, tr=%d", sizeof (dr), sizeof (tr));
end if
// Sort by totals DESC, carrying dataRows along
Sort_array ( > tr, dr );
// Trim to requested K (also safe-guard 20 for colormap)
int K = Min3(topK, 20, sizeof(dr));
array int out;
int i;
for (i=1, K)
out[] = dr[i];
end for
return out;
end
// Returns chart height (points) so caller can compare with table height
function _PlaceDonut ( int sheetID,
string sheetName,
int anchorRow, int anchorCol,
float chartW, float chartH,
string chartTitle,
string catRange, string valRange, array string colors ) // -> float
print format("PlaceDonut: %s, colors: %s\n", chartTitle, implode (",",colors));
JSON j = JSON(
"type", "doughnut",
"name", chartTitle,
"anchor", JSON(
"col", anchorCol,
"row", anchorRow,
"width", chartW,
"height", chartH,
"offsetX", 0.0
),
"legend", JSON("show", true, "position", "bottom"),
"plotArea", JSON("fill", JSON("alpha", 0.0))
);
j["series[]"] = JSON(
"name", chartTitle,
"categories", catRange,
"values", valRange,
"colors", colors
);
Print "-"*40+" Place chart: " + chartTitle + " "+ "-"*40+"\n";
print string(j) + "\n";
EXCEL_PlaceChart(sheetID, j);
return chartH;
end
// Helper to lookup colors per product or category.
function lookup_color ( array string arr, array string colors, string key)
int x = find_in_array ( arr, key);
if (x>0) then
return colors[x];
end if
return "white";
end
function _BuildSellerDetailSheet ( int wb, int sheetID, string sheetName, string tableName ) // -> string
// Ratio to translate chart height (points) -> approx. row count (our empirical ratio)
float kRowsPerAnchorHeight = 33.0 / 72.0; // ≈ 0.458333 rows/pt-height unit
int i;
// ---- Column widths: table (A..D) + spacer (E) + charts area (F..J)
array float cw;
cw[] = 26; // A: Category
cw[] = 14; // B: Value
cw[] = 26; // C: Product
cw[] = 14; // D: Value
cw[] = 3; // E: spacer
cw[] = 14; cw[] = 14; cw[] = 14; // F..H (left doughnut width base)
cw[] = 2; // I: spacer between pies
cw[] = 14; cw[] = 14; cw[] = 14; // J..L (right doughnut width base)
Excel_SetColumnWidth(sheetID, cw);
// ---- Sellers
array string sellers;
string res = ExecuteSQL(
"SELECT DISTINCT Seller FROM " + tableName + " ORDER BY Seller
INTO :sellers"
);
// Assign colors to Products. To ensure all doughnut's have the same product colors.
array string products;
res = ExecuteSQL(
"SELECT DISTINCT Product FROM " + tableName + " ORDER BY Product
INTO :products"
);
array string prodcolors;
for (i=1, sizeof(products))
prodcolors[] = getColor(i);
end for
// Assign colors to categories. To ensure all doughnut's have the same category colors.
array string Categories;
res = ExecuteSQL(
"SELECT DISTINCT Category FROM " + tableName + " ORDER BY Category
INTO :Categories"
);
array string catColors;
int j;
for (j=1, sizeof(Categories))
catColors[] = getColor(i+j); // contine with indexes where prod left off.
end for
// Sheet data
print "CatColors: " + implode(",", catcolors)+"\n";
if (sizeof(sellers) == 0) then
Excel_setCell(sheetID, 1, 1, "No data", "cText");
return "OK";
end if
int rTop = 1;
int s;
for (s=1, sizeof(sellers))
string seller = sellers[s];
// Headline
Excel_setCell(sheetID, rTop, 1, "Seller: " + seller, "H2");
// ---- Data: Category totals for this seller
array string catName; array float catSum;
res = ExecuteSQL(
"SELECT Category, SUM(InvoiceAmount)
FROM "+ tableName + "
WHERE Seller = :seller
GROUP BY Category
ORDER BY SUM(InvoiceAmount) DESC
INTO :catName, :catSum"
);
// ---- Data: Product totals for this seller
array string prodName; array float prodSum;
res = ExecuteSQL(
"SELECT Product, SUM(InvoiceAmount)
FROM "+tableName+"
WHERE Seller = :seller
GROUP BY Product
ORDER BY Product DESC
INTO :prodName, :prodSum"
);
// ---- Table headers
int headerRow = rTop + 1;
Excel_setCell(sheetID, headerRow, 1, "Category", "H2");
Excel_setCell(sheetID, headerRow, 2, "Value", "H2");
Excel_setCell(sheetID, headerRow, 3, "Product", "H2");
Excel_setCell(sheetID, headerRow, 4, "Value", "H2");
// ---- Write rows (side-by-side; independent lengths OK)
int rowsAB = sizeof(catName);
int rowsCD = sizeof(prodName);
int rowsMax = Max(rowsAB, rowsCD);
array string pColor, cColor;
for (i=1, rowsMax)
int row = headerRow + i;
if (i <= rowsAB) then
Excel_setCell(sheetID, row, 1, catName[i], "cText");
Excel_setCell(sheetID, row, 2, catSum[i], "cNum");
cColor[] = lookup_color ( categories, catColors, catName[i]);
end if
if (i <= rowsCD) then
Excel_setCell(sheetID, row, 3, prodName[i], "cText");
Excel_setCell(sheetID, row, 4, prodSum[i], "cNum");
pColor[] = lookup_color ( products, prodcolors, prodName[i]);
end if
end for
// ---- Totals row (sum each side if present)
int sumRow = headerRow + rowsMax + 1;
if (rowsAB > 0) then
string rAB = Excel_SheetAddress(headerRow+1, 2, headerRow+rowsAB, 2);
Excel_setCell(sheetID, sumRow, 1, "Total", "cSum");
Excel_setCellFormula(sheetID, sumRow, 2, "SUM(" + rAB + ")", "cSum");
end if
if (rowsCD > 0) then
string rCD = Excel_SheetAddress(headerRow+1, 4, headerRow+rowsCD, 4);
Excel_setCell(sheetID, sumRow, 3, "Total", "cSum");
Excel_setCellFormula(sheetID, sumRow, 4, "SUM(" + rCD + ")", "cSum");
end if
// ---- Charts to the right (two doughnuts)
// Compute square sizes from column widths (F..H and J..L)
float wLeft = (sizeof(cw)>=8 ? cw[6]+cw[7]+cw[8] : 42.0);
float wRight = (sizeof(cw)>=12 ? cw[10]+cw[11]+cw[12] : 42.0);
float hLeft = wLeft; // square doughnut
float hRight = wRight;
int chartRowAnchor = headerRow; // top aligned with the table header
int leftCol = 6; // F
int rightCol = 10; // J
// Category donut ranges (A/B columns)
string catCats = Excel_SheetAddress(sheetName, -(headerRow+1), -1, -(headerRow+rowsAB), -1);
string catVals = Excel_SheetAddress(sheetName, -(headerRow+1), -2, -(headerRow+rowsAB), -2);
// Product donut ranges (C/D columns)
string prodCats = Excel_SheetAddress(sheetName, -(headerRow+1), -3, -(headerRow+rowsCD), -3);
string prodVals = Excel_SheetAddress(sheetName, -(headerRow+1), -4, -(headerRow+rowsCD), -4);
float h1 = 0.0, h2 = 0.0;
if (rowsAB > 0) then
h1 = _PlaceDonut(sheetID, sheetName, chartRowAnchor, leftCol, wLeft, hLeft,
"Category share (" + seller + ")", catCats, catVals, cColor);
end if
if (rowsCD > 0) then
h2 = _PlaceDonut(sheetID, sheetName, chartRowAnchor, rightCol, wRight, hRight,
"Product share (" + seller + ")", prodCats, prodVals, pColor);
end if
// ---- Advance rTop to next free row:
// translate chart height (points) into rows using your ratio
int chartRows = int(Max(h1, h2) * kRowsPerAnchorHeight);
int tableRows = (sumRow - rTop) + 1;
int advance = Max(tableRows, chartRows) + 2; // extra gap
rTop = rTop + advance;
end for
return "OK";
end
// ------------------------------------------------------------
// Place a stacked-column chart below the pivot table
// sheetID : target sheet
// chartName : label for the chart (e.g., "Periods – Category")
// headerRow : header row (the one with "Period" captions). In your code: 4
// rowBase : first data row. In your code: headerRow + 1 (i.e., 5)
// noD : number of data rows (unique dims)
// noP : number of period columns
// dataRows : array of sheet row numbers to chart (subset of rowBase..rowBase+noD-1)
// cw : column-widths array you already set (index 1=first column)
// ------------------------------------------------------------
function _PlaceStackedColumnChart ( int sheetID,
string chartName,
int headerRow,
int rowBase,
int noD,
int noP,
array int dataRows,
array float cw ) // -> void
// ---- Colors (max 20 series)
array string colormap = {
"1F77B4","FF7F0E","2CA02C","D62728","9467BD",
"8C564B","E377C2","7F7F7F","BCBD22","17BECF",
"AEC7E8","FFBB78","98DF8A","FF9896","C5B0D5",
"C49C94","F7B6D2","C7C7C7","DBDB8D","9EDAE5"
};
// ---- Compute chart geometry
// Period columns are 2..(noP+1). Sum their widths for chart width.
float chartW = 0.0;
int c;
for (c=2, 1+noP)
chartW += cw[c];
end for
if (chartW < 50.0) then chartW = 50.0; end if
// A small X offset so the chart aligns nicely after the dimension column
float xoff = cw[2] / 2.0;
// Place chart 2 rows below the last data row
int lastDataRow = rowBase + noD - 1;
int anchorRow = lastDataRow + 3; // leave a blank row
int anchorCol = 2; // start in column B, right after labels
// ---- Build base chart JSON
JSON chartOptions = JSON(
"type", "column",
"name", chartName,
"anchor", JSON(
"col", anchorCol,
"row", anchorRow,
"width", chartW,
"height", chartW / 2.0,
"offsetX", xoff
),
"legend", JSON(
"show", true,
"overlay", false,
"position","bottom"
),
"plotArea", JSON(
"fill", JSON( "color","grey", "tint",0.85, "alpha",0.5 ),
"line", JSON( "color","blue" )
)
);
// ---- Series (stacked)
int cntRows = sizeof(dataRows);
if (cntRows > 20) then cntRows = 20; end if
int r;
int endCol = noP + 1; // last period column (Total is at noP+2 and not charted)
for (r=1, cntRows)
int srow = dataRows[r]; // absolute sheet row of this series
// Series name is the row label in column 1
string sname = Excel_GetCell(sheetID, srow, 1);
// Ranges: categories = headerRow across periods; values = srow across periods
string catRange = Excel_SheetAddress(chartName, -headerRow, -2, -headerRow, -endCol);
string valRange = Excel_SheetAddress(chartName, -srow, -2, -srow, -endCol);
chartOptions["series[]"] = JSON(
"name", sname,
"categories", catRange,
"values", valRange,
"stack", "stacked",
"style", JSON(
"fill", JSON(
"color", colormap[r],
"tint", 0.25,
"alpha", 1.0
),
"line", JSON(
"color", "black",
"widthPt", 1
)
)
);
end for
print string(chartOptions);
// ---- Place chart
EXCEL_PlaceChart(sheetID, chartOptions);
return chartW / 2.0; // For next chart placement offsetY
end
// ------------------------------------------------------------
// Place a multi-series LINE chart below the first chart
// Returns the chart height (so you can add to offsetY for more charts)
//
// Params:
// sheetID, chartName, headerRow, rowBase, noD, noP, dataRows, cw, topK, offsetY
// - offsetY: vertical offset in points from the table anchor (stacked chart height)
// ------------------------------------------------------------
float MaxY, MinY;
function _PlaceLineChart ( int sheetID,
string chartName,
int headerRow,
int rowBase,
int noD,
int noP,
array int dataRows,
array float totalsForRows,
array float cw,
int topK,
float offsetY ) // -> float
// Colors (max 20 series)
array string colormap = {
"1F77B4","FF7F0E","2CA02C","D62728","9467BD",
"8C564B","E377C2","7F7F7F","BCBD22","17BECF",
"AEC7E8","FFBB78","98DF8A","FF9896","C5B0D5",
"C49C94","F7B6D2","C7C7C7","DBDB8D","9EDAE5"
};
// --- Geometry from period columns (2..noP+1)
float chartW = 0.0;
int c;
for (c=2, 1+noP)
if (c <= sizeof(cw)) then chartW += cw[c]; end if
end for
if (chartW < 50.0) then chartW = 50.0; end if
float chartH = chartW / 2.0; // keep same aspect as first chart
float xoff = (sizeof(cw) >= 2 ? cw[2] : 12) / 2.0;
// Anchor row/col same as first chart, but shifted down by offsetY (+ small gap)
int lastDataRow = rowBase + noD - 1;
int anchorRow = lastDataRow + 2; // leave a blank row after table
int anchorCol = 2;
int gapRows = 1; // small spacing between charts
float kRowsPerAnchorHeight = 33.0 / 72.0;
int extraRows = int(offsetY * kRowsPerAnchorHeight) + gapRows;
anchorRow += extraRows;
float pad = (MaxY - MinY) * 0.10;
MinY = MinY - pad;
MaxY = MaxY + pad;
JSON chartOptions = JSON(
"type", "line",
"name", chartName + " – Trend",
"anchor", JSON(
"col", anchorCol,
"row", anchorRow,
"width", chartW,
"height", chartH,
"offsetX", xoff
),
"legend", JSON("show",true,"overlay",false,"position","bottom"),
"plotArea", JSON(
"fill", JSON("color","white","alpha",0.0),
"line", JSON("color","blue")
),
"axes", JSON(
"value", JSON("min", MinY, "max", MaxY, "showMajorGrid",true,
"majorGridlines", JSON(
"line", JSON(
"color", "grey",
"alpha", 0.25,
"widthPt", 0.25
))),
"category", JSON("showMajorGrid", false)
),
"markers", JSON("show", true, "size", 3)
);
// --- Choose Top-K by grand total (column noP+2)
int totalCol = noP + 2;
// --- Series
int r, cntRows = sizeof(dataRows);
int endCol = noP + 1; // last period column (Total is noP+2)
string catRange = Excel_SheetAddress(chartName, -headerRow, -2, -headerRow, -endCol);
for (r=1, cntRows)
int srow = dataRows[r];
string name = Excel_GetCell(sheetID, srow, 1);
string vals = Excel_SheetAddress(chartName, -srow, -2, -srow, -endCol);
chartOptions["series[]"] = JSON(
"name", name,
"categories", catRange,
"values", vals,
"style", JSON(
"line", JSON(
"color", colormap[r],
"widthPt", 2
),
"fill", JSON( // keep lines unfilled (transparent under/over)
"alpha", 0.0
)
)
);
end for
Print "Linechart:\n" + string(chartOptions);
EXCEL_PlaceChart(sheetID, chartOptions);
return chartH;
end
// ------------------------------------------------------------
// Helper: Build one pivot sheet (Dimension x Period)
// dimensionField must be one of: "Category", "Seller", "Product"
// ------------------------------------------------------------
function _BuildPivotSheet ( int wb, int sheetID, string title, string tableName, string dimensionField ) // -> string
// 1) Get all distinct Periods in sorted order (columns)
array string periods;
string res = ExecuteSQL(
"SELECT DISTINCT Period FROM " + tableName + " ORDER BY Period
INTO :periods"
);
int noP = sizeof(periods);
if (noP == 0) then
// No data: write a simple header and return
Excel_setCell(sheetID, 1, 1, title, "H1");
Excel_setCell(sheetID, 3, 1, "No data", "cText");
return "OK";
end if
// 2) Get the aggregated data in one pass: Dimension, Period, SUM
array string dimArr, perArr;
array float sumArr;
res = ExecuteSQL(
"SELECT " + dimensionField + ", Period, SUM(InvoiceAmount) " +
"FROM " + tableName + " " +
"GROUP BY " + dimensionField + ", Period " +
"ORDER BY " + dimensionField + ", Period \n" +
"INTO :dimArr, :perArr, :sumArr"
);
int n = sizeof(dimArr);
// 3) Build list of unique dimension values (rows), in order encountered
array string dimsUnique;
int i;
for (i=1, n)
if ( find_in_array(dimsUnique, dimArr[i]) < 0 ) then
dimsUnique[] = dimArr[i];
end if
end for
int noD = sizeof(dimsUnique);
// 4) Make a 2D pivot (dimsUnique x periods) – store as a flat array row-major
// Initialize all to 0
array float data;
int r, c;
for (r=1, noD)
for (c=1, noP)
data[] = 0.0;
end for
end for
// map indexes
int rowIx, colIx, posx;
for (i=1, n)
rowIx = find_in_array(dimsUnique, dimArr[i]);
colIx = find_in_array(periods, perArr[i]);
if (rowIx>0 && colIx>0) then
posx = (rowIx-1)*noP + colIx;
data[posx] = sumArr[i];
end if
end for
// 5) Write heading + header row
Excel_setCell(sheetID, 1, 1, "Annual report – Demo Furnitures Inc", "H1");
Excel_setCell(sheetID, 2, 1, title, "H2");
// Column headers: A = dimension title, then one column per Period, then a Total
int headerRow = 4;
Excel_setCell(sheetID, headerRow, 1, dimensionField, "H2");
for (c=1, noP)
Excel_setCell(sheetID, headerRow, c+1, periods[c], "H2");
end for
Excel_setCell(sheetID, headerRow, noP+2, "Total", "H2");
// Set some column widths (first wider, then medium, end for total)
array float cw;
cw[] = 28; // dimension column
for (c=1, noP) cw[] = 12; end for
cw[] = 14; // total column
Excel_SetColumnWidth(sheetID, cw); // ref
// 6) Write rows
int rowBase = headerRow + 1;
float rowSum;
array int dataRows;
array float totalsForRows;
MaxY = -1.0E10;
MinY = 1.0E10;
for (r=1, noD)
int outRow = rowBase + (r-1);
// Dimension label
dataRows[] = outrow;
Excel_setCell(sheetID, outRow, 1, dimsUnique[r], "cText");
// Values across periods
rowSum = 0.0;
for (c=1, noP)
posx = (r-1)*noP + c;
float v = data[posx];
Excel_setCell(sheetID, outRow, c+1, data[posx], "cNum");
rowSum += data[posx];
if (v < MinY) then MinY = v; end if
if (v > MaxY) then MaxY = v; end if
end for
totalsForRows[] = rowSum;
string formula = format("SUM(%s)", Excel_SheetAddress(outRow, 2, outRow, noP+1));
Excel_setCellFormula(sheetID, outRow, noP+2, formula, "cNum");
end for
// 7) Sum row at bottom (Excel formulas)
int sumRow = rowBase + noD;
Excel_setCell(sheetID, sumRow, 1, "Total", "cSum");
// Period sums
for (c=1, noP)
// =SUM( R[rowBase]:R[rowBase+noD-1] in column c+1 )
string f = format("SUM(%s)", Excel_SheetAddress(rowBase, c+1, rowBase + noD - 1, c+1));
Excel_setCellFormula(sheetID, sumRow, c+1, f, "cSum");
end for
// Grand total
string fTot = format("SUM(%s)", Excel_SheetAddress(rowBase, noP+2,rowBase + noD - 1, noP+2));
Excel_setCellFormula(sheetID, sumRow, noP+2, fTot, "cSum");
double h = _PlaceStackedColumnChart(
sheetID,
title,
headerRow,
rowBase,
noD,
noP,
dataRows,
cw // the same widths array we already computed/set
);
// Second chart (lines) — pass h so it stacks right below
h += _PlaceLineChart(sheetID, title, headerRow, rowBase, noD, noP, dataRows, totalsForRows, cw, 12, h);
return "OK";
end
function AnnualReport_DemoFurnitures () // -> string (path)
string res;
// ---- Choose/save path
string path = desktop_directory() + format("AnnualReport-DemoFurnitures_%s.xlsx", string(now(), "%Y-%m-%d-%H%I%S"));
if (file_exists(path)) then
res = delete_file(path);
end if
// ---- Create workbook and a base sheet (sheet 1 exists by default)
string bycat = "By Category", bySel = "By Seller", byPro = "By Product";
string bySelDet = "By Seller details";
int wb = Excel_Create(path, byCat); // Creates workbook + first sheet
int s1 = EXCEL_GetSheetID(wb, 1); // Get SheetID of the first sheet (ref)
int s2 = Excel_Add_Worksheet(wb, bySel); // Add 2nd sheet (ref)
int s3 = Excel_Add_Worksheet(wb, byPro); // Add 3rd sheet
int s4 = Excel_Add_Worksheet(wb, bySelDet);
// ---- Minimal styles
excel_addstyle (wb, "H1", json("Font", "Arial","FontSize", 18, "Decoration", "Bold"));
excel_addstyle (wb, "H2", json("Font", "Arial", "FontSize", 12, "Decoration", "Bold", "FillColor", "DDDDDD"));
excel_addstyle (wb, "cNum", json("NumFormatCode", 37)); // Excel built-in: #,##0.00_);(#,##0.00)
excel_addstyle (wb, "cText", "Default"); // default text
excel_addstyle (wb, "cSum", "cNum");
Excel_SetStyleOptions(wb, "cSum", json("Decoration", "Bold", "FillColor", "EEEEEE", "Borders", json("Top", linetype_thin)));
// ---- Build the four sheets
res = _BuildPivotSheet(wb, s1, byCat, "DemoInvoices", "Category");
res = _BuildPivotSheet(wb, s2, bySel, "DemoInvoices", "Seller");
res = _BuildPivotSheet(wb, s3, byPro, "DemoInvoices", "Product");
res = _BuildSellerDetailSheet(wb, s4, bySelDet, "DemoInvoices");
excel_close(wb); // write file
$$ReportPath = path;
return "OK";
end
function OpenFileInExcel ( string path )
// Location of the Excel App
string excel, launch;
if (isWindows) then
path = substitute (path, "\\", "/");
end if
string basepath = regex_replace("^(.+)/(.+\..+)$", path, "\1");
string filename = regex_replace("^(.+)/(.+\..+)$", path, "\2");
if (isWindows) then
excel = "start excel";
launch = format ("%s %s", excel,filename);
else
excel = "/Applications/Microsoft Excel.app";
if ( ! directory_exists ( excel ) ) then
return "Excel not installed";
end if
launch = format ("open -a \"%s\" %s", excel,filename);
end if
print launch;
return SystemCommand ( launch, basepath);
end
Example
Set Variable [$res; ACF_Run("AnnualReport_DemoFurnitures";)] 