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.

## 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>
// 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 sheet = wb.getSheet(0);

var lender = {
phone: "01-937-403-33002",
web: "http://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 (
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>
Ax.db.execute('DROP TABLE IF EXISTS plan');
Ax.db.execute(CREATE TABLE IF NOT EXISTS plan (
responsible VARCHAR(40),
start       DATE,
end         DATE,
days        INTEGER,
status      VARCHAR(20)
););

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

// 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());

rs.close();

// 9. Return the Excel as BLOB
Ax.db.execute('DROP TABLE plan');

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,
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
CONNECT BY PRIOR  id = parentID
ORDER BY id

## 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>
Ax.db.execute('DROP TABLE IF EXISTS gantt');
Ax.db.execute(CREATE TABLE gantt (
ID              INTEGER NOT NULL,
parentID        INTEGER,
orderID         INTEGER NOT NULL,
priority        CHAR(1) NOT NULL,
start           DATE NOT NULL,
end             DATE NOT NULL,
completed       DECIMAL(5,2) NOT NULL
););

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

// 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
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, 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
Ax.db.execute('DROP TABLE gantt');

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>

// 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",
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 projection 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>
// =================================================================================
// 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
// =================================================================================

// =================================================================================
// 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.
// =================================================================================

Ax.db.execute('DROP TABLE IF EXISTS @ctercero');
Ax.db.execute('DROP TABLE IF EXISTS @cterdire');

Ax.db.execute(CREATE TEMP TABLE @ctercero (
codigo CHAR(12),
nombre NCHAR(60)
););

Ax.db.execute(CREATE TEMP TABLE @cterdire (
codigo CHAR(12),
direcc NVARCHAR(60),
poblac NVARCHAR(60),
codprv CHAR(6),
codpos CHAR(10),
codnac CHAR(3),
telef1 VARCHAR(20),
fax1 VARCHAR(20),
email VARCHAR(255)
););

Ax.db.execute(INSERT INTO @ctercero VALUES (00010, 'MOZART ART SL'                     ););
Ax.db.execute(INSERT INTO @ctercero VALUES (00002, 'REPSOL BUTANO S.A.'                ););
Ax.db.execute(INSERT INTO @ctercero VALUES (00003, 'TELEFONICA DE ESPAÑA S.A.'         ););
Ax.db.execute(INSERT INTO @ctercero VALUES (00004, 'CEPSA'                             ););
Ax.db.execute(INSERT INTO @ctercero VALUES (00005, 'SISTEMAS DIGITALES CORPORATE S.L.' ););
Ax.db.execute(INSERT INTO @ctercero VALUES (00006, 'PROAUDIT CONSULTING, S.L.'         ););
Ax.db.execute(INSERT INTO @ctercero VALUES (00007, 'NOTARIA AGUSTIN - CASADO, C.B.'    ););
Ax.db.execute(INSERT INTO @ctercero VALUES (00008, 'COVASA'                            ););
Ax.db.execute(INSERT INTO @ctercero VALUES (00009, 'CAFES BATALLA 2000 S.L.'           ););
Ax.db.execute(INSERT INTO @ctercero VALUES (90001, 'TRIATLON, SA'                      ););

Ax.db.execute(INSERT INTO @cterdire VALUES (00010, 'Calle Bolivia, 195',                                       'Barcelona',                '08',   '08018',    'ESP',  '933089908',        '933081568',    'info@mozart.com'   ););
Ax.db.execute(INSERT INTO @cterdire VALUES (00002, 'Calle Mendez Alvaro, 44',                                  'Madrid',                   '28',   '28045',    'ESP',  '91 789 24 00',     NULL,           'deister@gmail.com' ););
Ax.db.execute(INSERT INTO @cterdire VALUES (00003, 'Gran Via, 28',                                             'Madrid',                   '28',   '28013',    'ESP',  '91 121 10 00',     NULL,           'deister@gmail.com' ););
Ax.db.execute(INSERT INTO @cterdire VALUES (00004, 'Av. del Partenon, 12 Campo De Las Naciones',               'Madrid',                   '28',   '28042',    'ESP',  '913376000',        '913376003',    'deister@gmail.com' ););
Ax.db.execute(INSERT INTO @cterdire VALUES (00005, 'Avda. Alcalde Porqueres, 6',                               'Lleida',                   '25',   '25008',    'ESP',  '973 477 22 44',    NULL,           'deister@gmail.com' ););
Ax.db.execute(INSERT INTO @cterdire VALUES (00006, 'VIA LAIETANA, 45 A, 4 2º',                                 'Barcelona',                '08',   '08003',    'ESP',  '934883131',        NULL,           'deister@gmail.com' ););
Ax.db.execute(INSERT INTO @cterdire VALUES (00007, 'AVDA. DIAGONAL, 538, 2º 2º',                               'Barcelona',                '08',   '08006',    'ESP',  '91 345 29 34',     NULL,           'deister@gmail.com' ););
Ax.db.execute(INSERT INTO @cterdire VALUES (00008, 'Carretera de Francia, 345',                                'Ametlla Del Valles, L',    '08',   '08480',    'ESP',  '93 890 34 55',     NULL,           'deister@gmail.com' ););
Ax.db.execute(INSERT INTO @cterdire VALUES (00009, 'Poligon Industrial Cami Dels Frares, Par.69 C/ C. Nave 1', 'Lleida',                   '25',   '25191',    'ESP',  '973 474 29 30',    NULL,           'deister@gmail.com' ););
Ax.db.execute(INSERT INTO @cterdire VALUES (90001, 'Avenida Europa Edificio Alcor Plaza 2-2',                  'Alcorcón',                 '28',   '28922',    'ESP',  '918386100',        '916 64 83 36',  NULL               ););

var rs_data = Ax.db.executeQuery(
SELECT  FIRST 10
ctc.codigo, ctc.nombre,
ctd.direcc, ctd.poblac,
ctd.codprv, ctd.codpos,
ctd.codnac, ctd.telef1 telef,
ctd.fax1 fax, ctd.email,
2508 debe, 9634 haber
FROM @ctercero ctc, @cterdire ctd
WHERE ctc.codigo = ctd.codigo
AND ctd.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();

Ax.db.execute('DROP TABLE @ctercero');
Ax.db.execute('DROP TABLE @cterdire');
</script>