The follwing examples will teach how to use Excel as a template engine using Axional Excel library.

1 Projectile motion solver

We have a Excel sheet that solves the physics calculations of a projectile motion uder gravity conditions. We need initial speed and angle as input parameters.

Now let's setup the input variables with names using a prefix and suffix. We will name D7 for data.velocity and D8 for data.angle.

You can download the excel here

1.1 Feed the template with data

Now let's see how to:

  1. Load an excel as template
  2. Prepare the data
  3. Update template and evaluate results
  4. Return the evaluated excel to caller

Copy
<script>
    // 1. Load the template
    var wb = Ax.ms.Excel.load("https://bitbucket.org/deister/axional-docs-resources/raw/master/Excel/templates/ProjectileSolver.xlsx");
    // 2. Prepare the data
    var input = { velocity: 200, angle: 36 };
    // 3. Update template (table name perfix for named cells is data) and evaluate
    wb.update(input, options => {
            options.setTableName("data");
            options.setEvaluate(true);
        }
    );
  // 4. Return the evaluated excel
  return wb.toBlob();
</script>

We have succesfully processed our first excel template using input data to perform calculations and obtain a result.

2 Loan amortization calculator

The Loan Template serves as an example of how to use Form layout updates and row copy with formula rewriting. Let's see how.

The template contains two form areas, lender and loan. You can see in the javascript code the JSON objects to update both "form" data fields.

In the rows area we have two rows.

  • Excel row 24, with the initial calculus (first payment)
  • Excel row 25, with the consecutive calculus (next payment)

So we need to:

  • Get the number of payments (depending on years and payment period) calculated by Excel from cell named numpayments (I15).
  • Get the row 25 that we will use as template to copy rows down till fullfill the number of payments. The cell at A25 is named rowtemplate. So we can use it's name to get the cell and the the row object.

    When row it's copied, it's formulas are rewritten moving its references to new row position.

  • Once we have both, we can copy rows till number of payments minus the two initial rows.

Copy
<script>

    var wb = Ax.ms.Excel.load("https://bitbucket.org/deister/axional-docs-resources/raw/master/Excel/templates/Loan-Template.xlsx");
    var sheet = wb.getSheet(0);
    
     var lender = {
        name : "Adam Wilcot",
        phone: "01-937-403-33002",
        web: "http://wilcotlender.com",
        mail: "adam@wilcotlender.com"
    }

    var loan = {
        purchase_price : 10000,
        percent_down: 0.05,
        interest: 0.0475,
        years: 8,
        payment_freq: "Monthly",
        first_payment: new Date(2019, 9, 1)
    }
    
    // Update lender prefixed cells
    wb.update(lender, options => {
            options.setTableName("lender");
        }
    );
    
    // Update loan prefixed cells
    wb.update(loan, options => {
            options.setTableName("loan");
        }
    );
    
    // Get the cell of row template (I15)
    var cell_numpayments = wb.getCellByName("numpayments");
    // Get the cell of row template (A25)
    var cell_rowtemplate = wb.getCellByName("rowtemplate");
    // The row that is our template
    var row_template = cell_rowtemplate.getRow();
    
    console.log("sheet last row  :" + sheet.getLastRowNum());
    console.log("cell_rowtemplate:" + cell_rowtemplate + " " + cell_rowtemplate.getCellValue());
    console.log("cell_numpayments:" + cell_numpayments + " " + cell_numpayments.getCellValue());
    console.log("row_template    :" + row_template     + " " + row_template.getRowNum());
    
    // Copy from row 24+1 to the number of payment less 2 (first row and template row)
    sheet.copyRow(row_template, row_template.getRowNum() + 1, cell_numpayments.getCellValue() - 2);
    
    return wb.toBlob();
</script>

We have succesfully processed the loan template, filled it with JSON data and generated copy of rows updating it's formula references.

3 Gantt Chart (I)

In the following example we will see how to use database project information to fill a Excel Gantt template. Let's see first a database model to support the project information.

3.1 The database model

Copy
CREATE TABLE IF NOT EXISTS plan (
	task        VARCHAR(40),
	responsible VARCHAR(40),
	start       DATE,
	end         DATE,
	days        INTEGER,
	status      VARCHAR(20)
);

INSERT INTO plan VALUES("Set Kick-Off Meeting",	"Alex B.",      MDY(9,2,2022),	MDY(9,3,2022),	1, "Complete");
INSERT INTO plan VALUES("Agree on Objectives",	"Frank C.",     MDY(9,3,2022),	MDY(9,7,2022),	4, "Complete");
INSERT INTO plan VALUES("Detailed Reqs.",       "Jacob S.",     MDY(9,7,2022),	MDY(9,12,2022),	5, "Complete");
INSERT INTO plan VALUES("Hardware Reqs.",       "Jacob S.",     MDY(9,9,2022),	MDY(9,11,2022),	2, "Overdue" );
INSERT INTO plan VALUES("Final Resource Plan",  "Jacob S.",     MDY(9,11,2022),	MDY(9,15,2022),	4, "In Progress");
INSERT INTO plan VALUES("Staffing",             "Alex B.",      MDY(9,16,2022),	MDY(9,17,2022),	1, "In Progress");
INSERT INTO plan VALUES("Technical Reqs.",      "Frank C.",     MDY(9,17,2022),	MDY(9,21,2022),	4, "Not Started");
INSERT INTO plan VALUES("DB Development",       "Shari W.",     MDY(9,22,2022),	MDY(9,24,2022),	2, "Not Started");
INSERT INTO plan VALUES("API Development",      "Shari W.",     MDY(9,23,2022),	MDY(9,27,2022),	4, "Not Started");
INSERT INTO plan VALUES("UI Client",            "Alex B.",      MDY(9,25,2022),	MDY(9,29,2022),	4, "Not Started");
INSERT INTO plan VALUES("Testing",              "Kennedy K.",   MDY(9,24,2022),	MDY(10,2,2022),	8, "Not Started");
INSERT INTO plan VALUES("Dev. Complete",        "Jacob S.",     MDY(10,2,2022),	MDY(10,5,2022),	3, "Not Started");
INSERT INTO plan VALUES("Hardware Config.",     "Alex B.",      MDY(10,5,2022),	MDY(10,7,2022),	2, "Not Started");
INSERT INTO plan VALUES("System Testing",       "Kennedy K.",	MDY(10,6,2022),	MDY(10,9,2022),	3, "Not Started");

3.2 The Gantt template

The Gantt template contains named cells for project and plan data sets. You can download the templater here

3.3 Processing the template

Copy
<script>
    // 1. Load the template
    var wb = Ax.ms.Excel.load("https://bitbucket.org/deister/axional-docs-resources/raw/master/Excel/templates/Gantt-Template-1.xlsx");

    // 2. Create a project JSON description
    var project = {
        name : "The Project Name",
        manager: "The Manager Name",
        deliverable: "The Deliverable",
        scope: "The Scope",
    }
    
    // 3. Update "project" fields
    wb.update(project, options => {
            options.setTableName("project");
        }
    );
    
    // 4. Load data for project orderde by date
    var rs = Ax.db.executeQuery(
    	"SELECT * FROM plan ORDER BY start"
    );
    
    // 5. Update rows starting with "plan"
    wb.update(rs, options => {
            options.setTableName("plan");
            // Skip first row (the header)
            options.setStartRow(wb.getNamedRow("plan") + 1);
            // Evaluate formulas
            options.setEvaluate(true);
        }
    );
    
    // 6. Get date filled by formula on "project.start"
    var date = wb.getCellByName("project.start");
    console.log("Date cell =" + date);
    console.log("Date value=" + date.getCellValue());
    
    // 7. Get the sheet (0=first) and the chart from sheet (0=first chart from sheet)
    var chart = wb.getSheet(0).getCharts()[0];
    
    // 8. Adjust the minimum value for value axis with start date
    chart.getValueAxis().setMinimum(date.getCellValue());
    
    // 9. Return the Excel as BLOB
    return wb.toBlob();
</script>

We have succesfully generated a Gantt with it's chart filling data from JSON and database and adjusting chart axis with calculated date.

4 Gantt Chart (II)

The following example is a more complex Gantt template using a hierachical structure.

4.1 The database model

The gantt table in the following example is a simple data model to store hierachical information.

Copy
CREATE TABLE gantt (
   	ID              INTEGER NOT NULL,
  	parentID        INTEGER,
 	orderID         INTEGER NOT NULL,
 	priority        CHAR(1) NOT NULL,
 	taskname        VARCHAR(25),
 	leader          VARCHAR(20),
 	start           DATE NOT NULL,
 	end             DATE NOT NULL,
 	completed       DECIMAL(5,2) NOT NULL
);

INSERT INTO gantt VALUES (1, NULL, 1, "", "" ,                           "Root Node",  MDY(1,1,3000), MDY(1, 1, 3000), 0);
INSERT INTO gantt VALUES (2,    1, 1, "M", "Analisys" ,                  "",           MDY(9,1,2022), MDY(9,6,2022), 100);
INSERT INTO gantt VALUES (3,    2, 1, "M", "Set Kick-Off Meeting" ,      "Alex B.",    MDY(9,2,2022), MDY(9,3,2022), 80);
INSERT INTO gantt VALUES (4,    2, 1, "M", "Agree on Objectives" ,       "Frank C.",   MDY(9,4,2022), MDY(9,7,2022), 0);
INSERT INTO gantt VALUES (5,    2, 1, "M", "Requirements planning" ,     "Jacob S.",   MDY(9,7,2022), MDY(9,12,2022), 50);
INSERT INTO gantt VALUES (6,    5, 1, "M", "Detailed Reqs." ,            "Jacob S.",   MDY(9,10,2022), MDY(9,15,2022), 80);
INSERT INTO gantt VALUES (7,    5, 1, "M", "Hardware Reqs." ,            "Jacob S.",   MDY(9,11,2022), MDY(9,12,2022), 0);
INSERT INTO gantt VALUES (8,    5, 1, "M", "Technical Reqs." ,           "Frank C.",   MDY(9,17,2022), MDY(9,21,2022), 50);
INSERT INTO gantt VALUES (9,    2, 1, "M", "Staffing" ,                  "Alex B.",    MDY(9,16,2022), MDY(9,17,2022), 0);
INSERT INTO gantt VALUES (10,   1, 1, "M", "Development" ,               "",           MDY(9,22,2022), MDY(10,5, 2022), 0);
INSERT INTO gantt VALUES (11,  10, 1, "M", "DB Development" ,            "Shari W.",   MDY(9,22,2022), MDY(9,24,2022), 25);
INSERT INTO gantt VALUES (12,  10, 1, "M", "API Development" ,           "Shari W.",   MDY(9,23,2022), MDY(9,27,2022), 10);
INSERT INTO gantt VALUES (13,  10, 1, "M", "UI Client" ,                 "Alex B.",    MDY(9,25,2022), MDY(9,29,2022), 0);
INSERT INTO gantt VALUES (14,  10, 1, "M", "Testing" ,                   "Kennedy K.", MDY(9,24,2022), MDY(10,2,2022), 0);
INSERT INTO gantt VALUES (15,  10, 1, "M", "Dev. Complete" ,             "Jacob S.",   MDY(10,2,2022), MDY(10,5,2022), 0);
INSERT INTO gantt VALUES (16,   1, 1, "M", "Deployment" ,                "",           MDY(10,5,2022), MDY(10,17, 2022), 0);
INSERT INTO gantt VALUES (17,  16, 1, "M", "Hardware Config." ,          "Alex B.",    MDY(10,10,2022), MDY(10,12,2022), 0);
INSERT INTO gantt VALUES (18,  16, 1, "M", "System Testing" ,            "Kennedy K.", MDY(10,11,2022), MDY(10,14,2022), 0);
INSERT INTO gantt VALUES (19,   1, 1, "M", "Support" ,                   "",           MDY(10,14,2022),MDY(12,24,2022), 0);
INSERT INTO gantt VALUES (20,  19, 1, "M", "Acvanced formation" ,        "John M.",    MDY(10,14,2022), MDY(10,20,2022), 0);
INSERT INTO gantt VALUES (21,  19, 1, "M", "Phone Support" ,             "N/A",        MDY(12,1,2022), MDY(12,9,2022), 0);

Now, we can use the CONNECT BY clause for performing recursive operations in hierarchical queries.

Copy
SELECT id, parentid, orderid, level, LPAD(' ', 2 * LEVEL - 1) || TRIM(taskname) name, leader, start, end, end-start duration
   FROM gantt
START WITH id = 1
CONNECT BY PRIOR  id = parentID
ORDER BY id
+----------+----------+----------+-------+----------------------------------------+--------------------+----------+----------+----------+
|id        |parentid  |orderid   |level  |name                                    |leader              |start     |end       |duration  |
+----------+----------+----------+-------+----------------------------------------+--------------------+----------+----------+----------+
|         1|          |         1|      1|                                        |                    |2019-01-01|2019-01-20|        19|
|         2|         1|         1|      2|   Analisys                             |                    |2019-01-01|2019-01-20|        19|
|         3|         2|         1|      3|     Set Kick-Off Meeting               |Alex B.             |2022-09-02|2022-09-03|         1|
|         4|         2|         1|      3|     Agree on Objectives                |Frank C.            |2022-09-03|2022-09-07|         4|
|         5|         2|         1|      3|     Requirements plannin               |Jacob S.            |2022-09-07|2022-09-12|         5|
|         6|         5|         1|      4|       Detailed Reqs.                   |Jacob S.            |2022-09-07|2022-09-12|         5|
|         7|         5|         1|      4|       Hardware Reqs.                   |Jacob S.            |2022-09-09|2022-09-11|         2|
|         8|         5|         1|      4|       Technical Reqs.                  |Frank C.            |2022-09-17|2022-09-21|         4|
|         9|         2|         1|      3|     Staffing                           |Alex B.             |2022-09-16|2022-09-17|         1|
|        10|         1|         1|      2|   Development                          |                    |2019-01-01|2019-01-20|        19|
|        11|        10|         1|      3|     DB Development                     |Shari W.            |2022-09-22|2022-09-24|         2|
|        12|        10|         1|      3|     API Development                    |Shari W.            |2022-09-23|2022-09-27|         4|
|        13|        10|         1|      3|     UI Client                          |Alex B.             |2022-09-25|2022-09-29|         4|
|        14|        10|         1|      3|     Testing                            |Kennedy K.          |2022-09-24|2022-10-02|         8|
|        15|        10|         1|      3|     Dev. Complete                      |Jacob S.            |2022-10-02|2022-10-05|         3|
|        16|         1|         1|      2|   Deployment                           |                    |2019-01-01|2019-01-20|        19|
|        17|        16|         1|      3|     Hardware Config.                   |Alex B.             |2022-10-05|2022-10-07|         2|
|        18|        16|         1|      3|     System Testing                     |Kennedy K.          |2022-10-06|2022-10-09|         3|
+----------+----------+----------+-------+----------------------------------------+--------------------+----------+----------+----------+

4.2 The Gantt template 2

The Gantt template 2, contains named cells for project and a sheet with templates of each line corresponding to its indentation level. You can download the template here

4.3 Processing the template

Copy
<script>
    // 1. Load the template
    var wb = Ax.ms.Excel.load("https://bitbucket.org/deister/axional-docs-resources/raw/master/Excel/templates/Gantt-Template-2.xlsx");
    
    // 2. Create a project JSON description
    var sheet = wb.getSheet(0);
    var project = {
        name : "Development Software",
        company: "ACME Company Ltd.",
        manager: "The Manager Name",
        start_date: "1/9/2022",
        deliverable: "The Deliverable",
    }
   
    // 3. Update "project" fields
    wb.update(project, options => {
            options.setTableName("project");
        }
    );

    // 4. Load data of project items
    var rs = Ax.db.executeQuery(`
        SELECT id, parentid, orderid, level, LPAD(' ', 2 * LEVEL - 1) || TRIM(taskname) name, 
                leader, start, end, end-start duration, completed / 100.0 completed
          FROM gantt
         START WITH id = 1
        CONNECT BY PRIOR  id = parentID
        ORDER BY id
`
    );

    rowno = 8;
    for (var row of rs) {
        if (row.parentid == null)
            continue;

        // Get the cell from named row template
        var cell_rowtemplate = wb.getCellByName("rowtemplate" + row.level);
        // The whole row of cell
        var row_template = cell_rowtemplate.getRow();
        
        // Copy root from template to sheet in row rowno, just once.
        sheet.copyRow(row_template, rowno, 1);
        
        sheet.setCellValue(rowno, 1, row.name);
        sheet.setCellValue(rowno, 2, row.leader);
        sheet.setCellValue(rowno, 4, row.start);
        sheet.setCellValue(rowno, 6, row.duration);
        sheet.setCellValue(rowno, 7, row.completed);
        rowno++;
    }
    

/*
    console.log(sheet.getSheetConditionalFormatting().toResultSet());
	    for (var condFormat of sheet.getSheetConditionalFormatting()) {
		    console.log("Format1: " + condFormat + " range:" + condFormat.getFormattingRanges()); 
		    for (var condformat2 of condFormat) {
			    console.log("   Format2: " + condformat2); 
		    }
	    }
*/

    // 5. Return the Excel as BLOB
    return wb.toBlob();
</script>

We have succesfully generated a Gantt type 2, filling data from JSON and database by copying level template rows from sheet 1 and filling cells with data from database.

5 Deprecation Inventory Table

You can use Excel Spreadsheers to perform, complex calculations like deprecation of inventory. In this example we defined a template where we can inject inventory data and get deprecation table.

5.1 Processing the template

Execute this script to see how to implement templates for recursive table calculation:

Copy
<script>
    // 1. Load the template
    var wb = Ax.ms.Excel.load("https://bitbucket.org/deister/axional-docs-resources/raw/master/Excel/templates/Depreciation-Template.xlsx");

    // 2. Create a project JSON description
    var sheet = wb.getSheet(0);
    var asset = {
        code : "AXJ200-0352",
        name: "EMC2 DataStore 4500 E5",
        description: "",
        location: "Nevada Datacenter N1 - Rack 19",
        serial: "NMK345AZV",
        adquisition: "14/3/2019",
        cost: 9800.0,
        activation: "1/7/2019",
        salvage: 500,
        method: "SL",
    }
   
    // 3. Update "project" fields
    wb.update(asset, options => {
            options.setTableName("asset");
            options.setEvaluate(true);
        }
    );
    wb.evaluate();

    // 4. Return the Excel as BLOB
    return wb.toBlob();
</script>

6 Data transformation

This example show how to use templates to making a transformation from source table ctercero and cterdire to match some theorical specification required by third party.

First, we create a Template Excel with two sheets: sheet0 will store data retrieved from database, sheet1 will store a row with formulas to transform this data. You can get sample template excel from ctercero.xlsx

Be aware, we use named cells to simplify template JS program. To create a vertical proyection as rows of all data in recordset, you should name cells in a single column, and prefix the name with a single virtual table name followed by ".".

Example code is self explained, so follow it to understand how it works.

This example is based on tables present in Axional ERP Financials. You require to execute it in an Axional ERP database to work properly.

Copy
<script>
    // =================================================================================
    // Load an XSL template
    // Sheet 0 is where we store data in named cells.
    // Sheet 1 is where we have the formulas to process data from named cells on sheet 0
    // =================================================================================
    var wb = Ax.ms.Excel.load("https://bitbucket.org/deister/axional-docs-resources/raw/master/Excel/templates/ctercero.xlsx");
    
    // =================================================================================
    // Get database data and store into memory cause we need to know the number of rows.
    // We will use the number of rows to copy as many Excel ROWS as database rows selected.
    // =================================================================================
    var rs_data = Ax.db.executeQuery(`
SELECT  FIRST 10 
        ctercero.codigo, ctercero.nombre,
        cterdire.direcc, cterdire.poblac, 
        cterdire.codprv, cterdire.codpos, 
        cterdire.codnac, cterdire.telef1 telef, 
        cterdire.fax1 fax, cterdire.email, 
        2508 debe, 9634 haber
  FROM ctercero, cterdire
 WHERE ctercero.codigo = cterdire.codigo
   AND cterdire.tipdir = "0"
   AND cterdire.codpos is not null
`).toMemory();
  
    // =================================================================================
    // Update excel workbook using named columns. All cells prefixed by "ctercero."
    // that matches a column in select will be updated.
    // The projection must be vertical cause we have multiple rows. This implies all
    // all named cells must be in a single row.
    // =================================================================================
    wb.update(rs_data, options => {
        options.setTableName("ctercero");
        options.setStartRow(wb.getNamedRow("ctercero") + 1);
    });

    // =================================================================================
    // Point to sheet1 where we have the formulas.
    // In this sheet whe have
    //
    // [row 0] cell name1, cell name2, ...
    // [row 1] formula1, formula2, ...
    // [row 2] empty
    //
    // So we have only 1 row with formulas.
    //
    // NOTICE that sheet and row starts at index 0. So accessing first sheet is
    // wb.getSheet(0) and second is wb.getSheet(1)
    //
    // =================================================================================

    var sheet = wb.getSheet(1);
    var row_template = sheet.getRow(1);

    // =================================================================================
    // Copy from row 1 (row_template in target sheet) to row 2 in target sheet up to the 
    // number of ctercero rows selected.
    //
    // This is a magic trick. As we copy sample row with formula to increasing number
    // of row, copied formulas will be transformed for the new references. And will
    // be evauated.
    // =================================================================================
    sheet.copyRow(row_template, row_template.getRowNum() + 1, rs_data.getRowCount());
    
    // =================================================================================
    // To retrieve formula calculated rows, you can use select() method. This method,
    // operates in a similar way like update() searching a row with named cells starting by
    // tableName and creating a resultset with all rows bellow reference one.
    // =================================================================================
    var rs2 = wb.select(options => {
        options.setTableName("result");
        // Select data starting at row 2
        options.setStartRow(2);
        // Return calculated value instead of formula text
        options.setEvaluate(true);
    });

    console.log(rs2);

    // =================================================================================
    // Return the resulting Excel as BLOB
    // =================================================================================
    return wb.toBlob();
</script>