The Report object provides high level reporting functions for database ResultSet processing. It also provides high quality PDF generation.

1 Getting started

A report is a special class of ResultSet. It's bakced by a memory mapped file as a database report may contain an undetermined number of rows. This way, report can be treated as a scrollable ResultSet.

By nature, report contains rows from database and rows generated by report processor. These "new" rows are group totals or grand totals and both contain special cells for generated break groups or total values.

1.1 Conversion

As any ResultSet can be converted to TEXT, FOP, PDF or HTML format. It uses special renders appropiate to each output. This is not a Report. It is a simple ResultSet rendering.

1.2 ResultSet vs Report

You only need a Report class when you have groups, totals or computations. If you only need text decoration by expression (colors, fonts, etc) you can use a plain ResultSet. As a general approach, you can see the Report like an extended ResultSet that may do operations on it (groups, totals, column calculations).

  • Text decoration belongs to ResultSet using ResultSetMetaData.
  • Grouping, total and column computation belong to Report.
  • Page decoration using a consumer configurator belogns to report.

You will see all options combined together in the examples.

2 Report Class Reference

2.1 Report Class

You can use Ax.rs.Report methods to create a new report. We group the allowed methods related to its usability bu:

  • Groups: These methods allow us to define grouping headers and totals when data in ResultSet of some columns change.
  • Grand Totals: Grand Totals at the end of the report are defined independently from those of the groups.
    To calculate grand total of an Accumulated row, you should use a standard SUM aggregator.
  • Expressions: Allows to define calculated results as the value of the columns. The column should be provided in resultset, but can be void or 0.
  • Accumulated: Allows to define a special type of virtual column accumulating values in each row received. Aggregator functions for group or grand totals are calculated over the original value of the resultset, so you can use SUM aggregator to properly calculate the total value.

Return Method Description
JSResultSetReport Ax.rs.Report(Resultset rs) The constructor receives a resultset which is the source of data to generate report.
JSResultSetReport setCollapseTotals(boolean collapse) If collapse is true, try to merge in a single total line aggregators of different categories: SUM, AVG, etc. This only works if not single column has two or more aggregators.
JSResultSetReport setAvoidSingleRowTotals(boolean avoid) If avoid is true, avoid to show total lines if only one data line is in the group. This simplifies report output by avoiding reports with superfluous total separators.
JSResultSetReport setHeader(int columnIndex, String headerLabel) Set title for column.
PDF Data toPDF() Convert report to PDF and return PDF stream
String toHTML() Convert report to HTML table and return HTML String
Groups
integer getGroupCount() Returns the number of groups defined in the report.
ColumnGroup setGroupBy(colname,...) Defines a report group when content of columns in parameter changes.
ColumnGroup getGroup(int groupid) Returns the Column Group Object Referenced by groupid number: 1 to getGroupCount()
int getFirstTotalColumnOfGroup(int groupIndex) for given group, return the first column (zero based index) with totals.
Grand Totals
JSResultSetReport setAvoidSingleRowTotals(boolean) Do not display grant total row if only one single row is emitted by report.
JSResultSetReport setTotalBy(String columnName) Defines a SUM aggregator to show in the grand total row.
JSResultSetReport setTotalBy(String columnName, String aggregator) Defines an aggregator of type "aggregator" for the column "columnName". This aggregator shows in the grand total row. Allowed types of aggregators are: SUM, AVG, MAX, MIN, COUNT, DISTINCT, EXPRESSION
void setTextFormat(String type, String format) Defines Grand total message format for the aggregation row.
Expressions
JSResultSetReport setExpression(String columnName, String columnExpr) Defines an UEL Expression to be executed to get data value for the row. This allows to create virtual rows, coming voided or zeroed from recorsed and being calculated on the fly by the report generator
Accumulated columns
JSResultSetReport setAccumulated(String... colnames) This method defines colnames as accumulated. Accumulated is an special aggregator function. For each rows with columns defined as accumulated, it shows the current value received from the ResultSet plus the accumulated value calculated in previous row.

2.2 Column Group Class

Grouping lets you organize and arrange records by group. Report can groups rows depending of grouped columns. Each time values of the group columns changes, the new values of the columns are shown. In addition, you can place totals (such as sums or averages) at the end of each group in your report.

Return Method Description
ColumnGroup setTotalBy(String columnName) .
ColumnGroup setTotalBy(String columnName, String... types) Defines a summarization function for the column. If called at the group level, this summarization is performed at the end of the group.
ColumnGroup setTotalByExpression(String name, String expression) Defines a total aggregator calculated with expression provided. In this context, column name provided gets values from the same row in which expression calculation is provided. We encourage the use of collapsed totals when using totals by expression.
ColumnGroup setTotalFormat(String type, String format) Define the group label for each summarization type: SUM, MIN, MAX, etc.
ColumnGroup setTotalFunction(BiFunction<String, Object[], String> function) Setup a function to return the total text for a given type and group[]
ColumnGroup setDisplayTotalsOnColumn(String columnName) columnName the column name in the group where we want to display totals when ON TOP flag is on

3 Groups and totals

This section shows some specialized examples, about how to create group breaks in columns and create summarizing rows at the bottom of each group of rows.

3.1 Groups and totals example

The following examples expose the use for groups, totals and the accumulated function.

Copy
var rows = [
        ["ACMBCN", "SPORTS SHOP BARCELONA", "S0", "OFICINAS CENTRALES", "40", "PROVEEDORES",      0, 15.5, 0, 0  ],
        ["ACMBCN", "SPORTS SHOP BARCELONA", "S0", "OFICINAS CENTRALES", "43", "CLIENTES",      20.5,    0, 0, 0  ],
        ["ACMBCN", "SPORTS SHOP BARCELONA", "S0", "OFICINAS CENTRALES", "44", "DEUDORES",       5.0,    0, 0, 0  ],
        ["ACMBCN", "SPORTS SHOP BARCELONA", "S0", "OFICINAS CENTRALES", "57", "BANCOS Y CAJA", 15.0,    0, 0, 0  ],
        ["ACMBCN", "SPORTS SHOP BARCELONA", "UM", "ULTRAMAR",           "40", "PROVEEDORES",      0, 15.5, 0, 0  ],
        ["ACMBCN", "SPORTS SHOP BARCELONA", "UM", "ULTRAMAR",           "43", "CLIENTES",      20.5,    0, 0, 0  ],
        ["ACMBCN", "SPORTS SHOP BARCELONA", "UM", "ULTRAMAR",           "44", "DEUDORES",       5.0,    0, 0, 0  ],
        ["ACMMAD", "SPORTS MADRID"        , "S0", "OFICINAS CENTRALES", "40", "PROVEEDORES",      0, 11.5, 0, 0  ],
        ["ACMMAD", "SPORTS MADRID"        , "S0", "OFICINAS CENTRALES", "43", "CLIENTES",      14.5,    0, 0, 0  ],
        ["ACMMAD", "SPORTS MADRID"        , "S0", "OFICINAS CENTRALES", "44", "DEUDORES",       3.7,    0, 0, 0  ],
        ["ACMMAD", "SPORTS MADRID"        , "S0", "OFICINAS CENTRALES", "57", "BANCOS Y CAJA",  5.3,    0, 0, 0  ]
    ];
    
    var rs = new Ax.rs.Reader().build(rows, options => {
        options.setColumnNames(["proyec", "nompro", "seccio", "nomsec", "cuenta", "nomcta", "debe", "haber", "saldo", "acumulado"]);
        options.setColumnTypes([ Ax.sql.Types.CHAR, Ax.sql.Types.CHAR, Ax.sql.Types.CHAR, Ax.sql.Types.CHAR, Ax.sql.Types.CHAR, Ax.sql.Types.CHAR, Ax.sql.Types.DECIMAL, Ax.sql.Types.DECIMAL, Ax.sql.Types.DECIMAL, Ax.sql.Types.DECIMAL ]);
        options.setColumnScales([ 0, 0, 0, 0, 0, 0, 4, 4, 4, 4 ]);
    });
    
var report = new Ax.rs.Report(rs);
report.setCollapseTotals(true);

report.setGroupBy("proyec", "nompro")
		.setTotalFormat("SUM", "Total %s")
		.setTotalBy("debe",  "SUM")
		.setTotalBy("haber",  "SUM")
		.setTotalBy("saldo",  "SUM")
		.setTotalBy("acumulado",  "SUM")
		
report.setGroupBy("seccio", "nomsec")
		.setTotalFormat("SUM", "Parcial %s")
		.setTotalBy("saldo",  "SUM")
		.setTotalBy("acumulado",  "SUM")
		;

report.setExpression("saldo", "(debe -haber)");
report.setExpression("acumulado", "debe-haber");
report.setAccumulated("acumulado");

// Grand totalizer is SUM
report.setTotalBy("debe");
report.setTotalBy("haber");
report.setTotalBy("saldo");
report.setTotalBy("acumulado", "SUM");
console.log(report)
+------------+---------------------+--------+------------------+-------+-------------+----------+----------+----------+----------+
|proyec      |nompro               |seccio  |nomsec            |cuenta |nomcta       |debe      |haber     |saldo     |acumulado |
+------------+---------------------+--------+------------------+-------+-------------+----------+----------+----------+----------+
|ACMBCN      |SPORTS SHOP BARCELONA|S0      |OFICINAS CENTRALES|40     |PROVEEDORES  |    0.0000|   15.5000|  -15.5000|  -15.5000|
|            |                     |        |                  |43     |CLIENTES     |   20.5000|    0.0000|   20.5000|    5.0000|
|            |                     |        |                  |44     |DEUDORES     |    5.0000|    0.0000|    5.0000|   10.0000|
|            |                     |        |                  |57     |BANCOS Y CAJA|   15.0000|    0.0000|   15.0000|   25.0000|
|            |                     |Total S0|                  |       |             |          |          |   25.0000|   25.0000|
|            |                     |UM      |ULTRAMAR          |40     |PROVEEDORES  |    0.0000|   15.5000|  -15.5000|  -15.5000|
|            |                     |        |                  |43     |CLIENTES     |   20.5000|    0.0000|   20.5000|    5.0000|
|            |                     |        |                  |44     |DEUDORES     |    5.0000|    0.0000|    5.0000|   10.0000|
|            |                     |Total UM|                  |       |             |          |          |   10.0000|   10.0000|
|Total ACMBCN|                     |        |                  |       |             |   66.0000|   31.0000|   35.0000|   35.0000|
|ACMMAD      |SPORTS MADRID        |S0      |OFICINAS CENTRALES|40     |PROVEEDORES  |    0.0000|   11.5000|  -11.5000|  -11.5000|
|            |                     |        |                  |43     |CLIENTES     |   14.5000|    0.0000|   14.5000|    3.0000|
|            |                     |        |                  |44     |DEUDORES     |    3.7000|    0.0000|    3.7000|    6.7000|
|            |                     |        |                  |57     |BANCOS Y CAJA|    5.3000|    0.0000|    5.3000|   12.0000|
|            |                     |Total S0|                  |       |             |          |          |   12.0000|   12.0000|
|Total ACMMAD|                     |        |                  |       |             |   23.5000|   11.5000|   12.0000|   12.0000|
|Total       |                     |        |                  |       |             |   89.5000|   42.5000|   47.0000|   47.0000|
+------------+---------------------+--------+------------------+-------+-------------+----------+----------+----------+----------+

4 Column expressions

TO DO

This section is incomplete and will be concluded as soon as possible.

VSC: Even it's not a good method for accumulated columns, there is a fn_previous expression function showing previous row value.

VSC: TODO: In example %sales should be a calculated column with the percent sales row value represents compared with inner summarized total. How to do this? This is demanded in lots of reports. Usually this is done manually and now this can be done sometimes with SQL OLAP Functions.

This can be done in Informix using RATIO_TO_REPORT OLAP Function. Should we use OLAP or try to implement similar feature at Report, Cursor or Reader Level????

Copy
var rows = [
        ["Outlets Ltd.", "STORE SMITH (AL)", "C0001", "LINK SYSTEN",      2500, 0, 0, 1100, 0, 0  ],
        ["Outlets Ltd.", "STORE SMITH (AL)", "C0004", "SPCA LTD.",        4300, 0, 0, 4200, 0, 0  ],
        ["Outlets Ltd.", "STORE SMITH (AL)", "C0010", "VIA FOODS",        3200, 0, 0, 3200, 0, 0  ],
        ["Outlets Ltd.", "NY APPARELS",      "C0038", "MAXWELL SONS",     8100, 0, 0, 8000, 0, 0  ],
        ["Outlets Ltd.", "NY APPARELS",      "C0056", "ANARPEED HARD",    7400, 0, 0, 6900, 0, 0  ],
        ["21 Apparel",   "Big Food",         "C0078", "DROPPED ELEMENTS", 5600, 0, 0, 5600, 0, 0  ],
        ["21 Apparel",   "Big Food",         "C0036", "Soumilier Tenth",  9300, 0, 0, 6400, 0, 0  ],
        ["21 Apparel",   "Outbreak AL",      "C0090", "Tramit brothers",  2100, 0, 0, 1000, 0, 0  ],
    ];
    
    var rs = new Ax.rs.Reader().build(rows, options => {
        options.setColumnNames(["Company", "StoreName", "Customer", "CustomerName", "sales", "%sales_store", "%sales_company", "goods", "services", "%goods"]);
        options.setColumnTypes([ Ax.sql.Types.CHAR, Ax.sql.Types.CHAR, Ax.sql.Types.CHAR, Ax.sql.Types.CHAR, Ax.sql.Types.DECIMAL, Ax.sql.Types.DECIMAL, Ax.sql.Types.DECIMAL, Ax.sql.Types.DECIMAL, Ax.sql.Types.DECIMAL, Ax.sql.Types.DECIMAL ]);
        options.setColumnScales([ 0, 0, 0, 0, 4, 4, 4, 4, 4, 4 ]);
    });
    
var report = new Ax.rs.Report(rs);
report.setCollapseTotals(true);
report.setGroupBy("Company");

report.setGroupBy("StoreName")
		.setTotalBy("sales",  "SUM")
		.setTotalBy("goods",  "SUM")
		.setTotalBy("services",  "SUM")
		.setTotalByExpression("%goods", "goods * 100 / sales");

report.setExpression("services", "(sales -goods)");
report.setExpression("%goods", "goods * 100 / sales");
console.log(report)
+------------+----------------------+--------+----------------+-----------+------------+--------------+-----------+----------+----------+
|Company     |StoreName             |Customer|CustomerName    |sales      |%sales_store|%sales_company|goods      |services  |%goods    |
|char(0)     |char(0)               |char(0) |char(0)         |decimal(0, |decimal(0, 4|decimal(0, 4) |decimal(0, |decimal(0,|decimal(0,|
|            |                      |        |                |4)         |)           |              |4)         |4)        |4)        |
+------------+----------------------+--------+----------------+-----------+------------+--------------+-----------+----------+----------+
|Outlets Ltd.|STORE SMITH (AL)      |C0001   |LINK SYSTEN     | 2,500.0000|      0.0000|        0.0000| 1,100.0000|1,400.0000|   44.0000|
|            |                      |C0004   |SPCA LTD.       | 4,300.0000|      0.0000|        0.0000| 4,200.0000|  100.0000|   97.6744|
|            |                      |C0010   |VIA FOODS       | 3,200.0000|      0.0000|        0.0000| 3,200.0000|    0.0000|  100.0000|
|            |Total STORE SMITH (AL)|        |                |10,000.0000|            |              | 8,500.0000|1,500.0000|   85.0000|
|            |NY APPAREALS          |C0038   |MAXWELL SONS    | 8,100.0000|      0.0000|        0.0000| 8,000.0000|  100.0000|   98.7654|
|            |                      |C0056   |ANARPEED HARD   | 7,400.0000|      0.0000|        0.0000| 6,900.0000|  500.0000|   93.2432|
|            |Total NY APPAREALS    |        |                |15,500.0000|            |              |14,900.0000|  600.0000|   96.1290|
|            |                      |        |                |           |            |              |           |          |          |
|21 Appareal |Big Food              |C0078   |DROPPED ELEMENTS| 5,600.0000|      0.0000|        0.0000| 5,600.0000|    0.0000|  100.0000|
|            |                      |C0036   |Soumilier Tenth | 9,300.0000|      0.0000|        0.0000| 6,400.0000|2,900.0000|   68.8172|
|            |Total Big Food        |        |                |14,900.0000|            |              |12,000.0000|2,900.0000|   80.5369|
|            |Outbreak AL           |C0090   |Tramit brothers | 2,100.0000|      0.0000|        0.0000| 1,000.0000|1,100.0000|   47.6190|
|            |Total Outbreak AL     |        |                | 2,100.0000|            |              | 1,000.0000|1,100.0000|   47.6190|
|            |                      |        |                |           |            |              |           |          |          |
+------------+----------------------+--------+----------------+-----------+------------+--------------+-----------+----------+----------+

5 PDF Report Definition

Exporting report to PDF using method toPDF(options), allows to extend report features allowing to create complex a feature full PDF documents based on ResultSets.

You can build a report at three levels.

5.1 Report builder

The report object provides extensions on resulset metadata to allow more complex operations. Those include transforming data from ResultSet using computation:

  • Setting total by group including calculations for SUM, AVG, MAX, MIN, COUNT, COUNT-DISTINCT
  • Setting calculated columns using aritmetic expressions on row columns.

5.2 Metadata columns

The ResultSet Metadata object provides basic configuration for columns like:

  • Setting column labels
  • Setting mappings for column values (setColumnCombo)
  • Setting a specific (non automatic) align on a cell
  • Setting a specific number or date format for a cell
  • Setting headers for a group of cells
  • Setting joins between cells (to display them in a primary cell)
  • Conditional setup the font of a cell
  • Conditional setup the color of a cell
  • Conditional setup the trend style of a cell
  • Conditional setup a hyperlink on a cell
  • Add prefix or suffix to a cell
  • Replace NULL values by constants

5.3 Report print configurator

Finally can provide a configurtor consumer to setup page rendering options like:

  • Setting page title, font, cover.
  • Setting different group display options.
  • Adding pre and post report content.

6 Examples

The following examples will show several types of reports and report engine capabilities.

6.1 Primary Domain vs Competitors

The following example shows a report for internet providers ranking. It uses static data to generate a memory ResultSet. The ResultSet is decorated using it's ResultSetMetaData. Then, it's transformed to a Report object to perform grouping and PDF rendering.

Sample output (no grouping)
Sample output (with group compression and repeating header)

A special render feature called group compression is used in previous example. It forces groups to be rendered on a single line instead of as part of the row they belong. This reduces width of report and makes it more legible compared with standard version.

Copy
<script>
   // Sample data
   var rows = [
        ["L", "www.dell.com",    "/us/p/laptops",          "laptop",              7,   3,    6,    1,     1,   823000,    "Low",  168,    141,    2252,    5,   202,   50],
        ["L", "www.apple.com",   "/mac-book-pro/",         "laptop",              3,   3,   42,  -12,   -39,   823000,   "High",   14,   7604,   54096,  578,  2212,  138],
        ["L", "www.samsung.com", "/us/computer/laptops",   "laptop",             22,  17,   52,  -31,   -30,   823000,    "Low",  200,    389,     211,    3,    19,    6],
        ["L", "www.asus.com",    "/us/computer/laptops",   "laptop",             59,  18,   61,    9,    -2,   823000,    "Low",    0,      8,     122,    0,     0,    0],

        ["D", "www.dell.com",    "/us/p/desktops",         "desktop_computer",    8,   3,    5,    1,     3,   18100,    "High",   76,     15,     846,    2,     0,    0],
        ["D", "www.apple.com",   "/us/buy-mac/imac",       "desktop_computer",   19,   4,   19,    9,  null,   18100,  "Medium",    0,      3,       0,    0,     1,    0],
        ["D", "www.asus.com",    "/us/Desktops",           "desktop_computer",   46,  17,   42,   -5,     4,   18100,        "",    0,      3,       0,    0,     1,    0],
        ["D", "www.samsung.com", "",                       "desktop_computer",   64,  30,  500, null,     1,   18100,        "",  168,    141,   2252,     5,   202,   50],

        ["P", "www.samsung.com", "/us/p/laptops",          "personal_computer",  16,   7,   38,     9,   -22,  2400,      "Low",     4,     0,      0,     0,     0,     0],
        ["P", "www.dell.com",    "/in/p",                  "personal_computer",  45,  23,   90,     4,   -45,  2400,      "Low",    35,     4,    202,     0,     0,     0],
        ["P", "www.apple.com",   "",                       "personal_computer",  54,  30,  500,  null,  null,  2400,   "Medium",  null,  null,  null,   null,  null,  null],
        ["P", "www.ausu.com",    "",                       "personal_computer", 331, 236,  500,  null,  null,  2400,     "High",  null,  null,  null,   null,  null,  null],
    ];

    // Build ResultSet with previously created rows
    // and add some column configurations
    var rs = new Ax.rs.Reader().build(rows, options => {
        options.setColumnNames([
        "type",
        "URL",
        "Landing Page",
        "Keyboard",
        "Baseline",
        "Best Rank",
        "Rank",
        "Monthly",
        "Overall Change",
        "Vol.",
        "PPC",
        "Google",
        "Twitter",
        "Facebook",
        "Linkedin",
        "Pinterest",
        "Reddit"]);
    });

    // ========================================================================
    // Decorate ResultSet
    // ========================================================================
    var md = rs.getMetaData();

    // Create a combo for type
    md.setColumnCombo("type",
       {
    		"L" : "Google USA(google.com) | laptop",
			"D" : "Google USA(google.com) | desktop computer",
			"P" : "Google USA(google.com) | personal computers"
       }
    );

    // Add some font style
    md.setColumnFont("Keyboard").setFontStyle("italic");
    md.setColumnFont("Rank").setFontWeight("bold");
    md.setColumnFont("Monthly").setFontWeight("bold");

    // Avoid null on Monthly values
    md.setColumnNullValue("Monthly", "N/A");

	// Set center column alignment on some numeric data
    md.setColumnAlign("Google", "center");
    md.setColumnAlign("Facebook", "center");
    md.setColumnAlign("Twitter", "center");
    md.setColumnAlign("Pinterest", "center");
    md.setColumnAlign("Linkedin", "center");
    md.setColumnAlign("Reddit", "center");

	// Set column trends to columns
    md.setColumnTrend("Monthly");
    md.setColumnTrend("Overall Change");

    // Colorize columns
    md.setColumnColor("Google",    "'#FFFFFF:#D32F2F'");
    md.setColumnColor("Twitter",   "'#FFFFFF:#03A9F4'");
    md.setColumnColor("Facebook",  "'#FFFFFF:#303F9F'");
    md.setColumnColor("Linkedin",  "'#FFFFFF:#1976D2'");
    md.setColumnColor("Pinterest", "'#FFFFFF:#F44336'");
    md.setColumnColor("Reddit",    "'#FFFFFF:#E64A19'");

    // Colorize column ppc based on expression
    md.setColumnColor("ppc", "'#D32F2F:#FFFFFF'").setExpression("ppc == 'High'");
    md.setColumnColor("ppc", "'#388E3C:#FFFFFF'").setExpression("ppc == 'Low'");
    md.setColumnColor("ppc", "'#FF5722:#FFFFFF'").setExpression("ppc == 'Medium'");

    // ========================================================================
    // Generate report
    // ========================================================================
    var report = new Ax.rs.Report(rs);

    // Group report by type
    report.setGroupBy("type");

    // Render to PDF
    var pdf0 = report.toPDF(config => {
        config.setTitle("Primary Domain vs Competitor Rankings");
        config.setGroupCompression(false);
    });

    // Render to PDF
    var pdf1 = report.toPDF(config => {
        config.setTitle("Primary Domain vs Competitor Rankings");
        config.setGroupCompression(true);
    });

    // Render to PDF
    var pdf2 = report.toPDF(config => {
        config.setTitle("Primary Domain vs Competitor Rankings");
        config.setGroupCompression(true);
        config.setGroupHeadingRepeat(true);
        config.setGroupColor(1, 0x3F51B5, 0xFFFFFF);
        config.setTableHeadBackgroundColor(0xF44336, 0xFFFFFF);
    });

    // Write to temp file
    new Ax.io.File("/tmp/dashboard0.pdf").write(pdf0);
    new Ax.io.File("/tmp/dashboard1.pdf").write(pdf1);
    new Ax.io.File("/tmp/dashboard2.pdf").write(pdf2);

    // return the PDF to caller
    return rs;
</script>

6.2 Financial Statements

The following example generates a report of a company financial statements condition and activities during a given period of time. Financial statements show the financial performance and strength of a company. The three core financial statements are the income statement, balance sheet, and cash flow statement. These three statements are linked together to create the three statement financial model.

Sample output

A special render feature called group compression is used in this report example. It forces groups to be rendered on a single line instead of as part of the row they belong. This reduces the width of report and makes it more legible compared with standard version.

Sample output with group compression
Copy
<script>    
    // Sample data
    var rows = [
       	// Income Statement
        ["I", "P", "Revenue",                             102007, 118086,  142341, 150772, 158311, 158311],
        ["I", "P", "Cost of Goods Sold(COGS)",            -39023, -48004,  -49123, -52654, -58575, -58575],
        ["I", "E", "Salaries and Benefits",               26427,  22658,   23872,  23002,  25245,   26913],
        ["I", "E", "Rent and Overhead",                   10963,  10125,   10087,  11020,  11412,   10000],
        ["I", "E", "Depreciation & Amortization",         19500,  18150,   17205,  16544,  16080,   15008],
        ["I", "E", "Interest",                            2500,   2500,    1500,   1500,   1500,     1500],
    
        // Balance Sheet
        ["B", "A", "Cash",                                167971, 181210,  183715, 211063, 239550, 272530],
        ["B", "A", "Accounts Receivable",                   5100,   5904,    6567,   7117,   7539,   7807],
        ["B", "A", "Inventory",                             7805,   9601,    9825,  10531,  11342,  11715],
        ["B", "A", "Property & Equipment",                 45500,  42350,   40145,  38602,  37521,  37513],
        ["B", "L", "Accounts Payable",                      3902,   4800,    4912,   5265,   5671,   5938],
        ["B", "L", "Debt",                                 50000,  50000,   30000,  30000,  30000,  30000],
        ["B", "S", "Equity Capital",                      170000, 170000,  170000, 170000, 170000, 170000],
        ["B", "S", "Retained Earnings",                     2474,  14265,   35340,  62053,  30280, 123627],
    
        // Cash Flow Statement
        ["C", "O", "Not Earnings",                          2474,  11791,   21075,   26713,  28227,  33346],
        ["C", "O", "Plus: Deprecation & Amortization",     19500,  18150,   17205,   16544,  16080,  15008],
        ["C", "O", "Less: Changes in Working Capital",     -9003,  -1702,    -775,    -903,   -827,	 -375],
        ["C", "I", "Investments in Property & Equipment",  15000,  15000,   15000,   15000,  15000,  15000],
        ["C", "F", "Issuance(repayment) of debt",           null,   null,   20000,    null,   null,   null],
        ["C", "F", "Issuance(repayment) of equity",       170000,   null,    null,    null,   null,   null],
        ["C", "B", "Net Increase(decrease) in Cash",      167971,  13239,    2505,   27354,  28480,  32980],
        ["C", "B", "Opening Cash Balance",      		    null,  167971, 181210,  183715, 211069, 239500],
    ];
    
    // Build ResultSet with previously created rows
    // and add some column configurations
    var rs = new Ax.rs.Reader().build(rows, options => {
        options.setColumnNames([
        "type",
        "group",
        "item",
        "2012",
        "2013",
        "2014",
        "2015",
        "2016",
        "2017"]);
    });
    
    // ========================================================================
    // Decorate ResultSet
    // ========================================================================
    
    var md = rs.getMetaData();
    
    md.setColumnLabel("item", "");
    // Create a combo for type
    md.setColumnCombo("type",
       {
            // Types of statements
            "I" : "Income Statement",
            "B" : "Balance Sheet",
            "C" : "Cash Flow Statement",
       }
    );
    
    md.setColumnCombo("group",
       {
            "P" : "",
            "E" : "Expenses",
            "A" : "Assets",
            "L" : "Liabilities",
            "S" : "Shareholder's Equity",
            "O" : "Operating Cash Flow",
            "I" : "Investing Cash Flow",
            "F" : "Financing Cash Flow",
            "B" : "",
       }
    );
    
    // Avoid null values on item columns
    md.setColumnNullValue("2012", "-");
    md.setColumnNullValue("2013", "-");
    md.setColumnNullValue("2014", "-");
    md.setColumnNullValue("2015", "-");
    md.setColumnNullValue("2016", "-");
    md.setColumnNullValue("2017", "-");
    
    // ========================================================================
    // Generate report
    // ========================================================================
    var report = new Ax.rs.Report(rs);
    
    // Group report by type
    report.setGroupBy("type", "group")
        // Use a function to map total names
        .setTotalFunction((type, group) => {
            switch (group[0]) {
                case 'I':
                    switch (group[1]) {
                        case 'P': return 'Gross Profit';
                        case 'E': return 'Total Expenses';
                    }
                    break;
                case 'B':
                    switch (group[1]) {
                        case 'A': return 'Total Assets';
                        case 'L': return 'Total Liabilities';
                        case 'S': return 'Shareholders Equity';
                    }
                    break;
                case 'C':
                    switch (group[1]) {
                        case 'O': return 'Cash from Operations';
                        case 'I': return 'Cash from Investing';
                        case 'F': return 'Cash from Financing';
                        case 'B': return 'Closing Cash Balance';
                    }
                    break;
            }
            return "N/A";
        })
        .setTotalBy("2012", "SUM")
        .setTotalBy("2013", "SUM")
        .setTotalBy("2014", "SUM")
        .setTotalBy("2015", "SUM")
        .setTotalBy("2016", "SUM")
        .setTotalBy("2017", "SUM");
    
    // Render to PDF
    var pdf0 = report.toPDF(config => {
        config.setTitle("FINANCIAL STATEMENTS (Double Sided)");
        config.setFootText("Acme Inc");
        config.setGroupCompression(true);
        config.setGroupHeadingRepeat(false);
        config.setGroupCompressionIndentWidth(0.5);
        config.setPrintNewLineAfterGroup(true);
        config.setPrintNewLineAfterTotal(true);
        config.setPrintCompressedGroupColumnsInNewLine(true);
    
        config.setGroupColor(1, 0x192e54, 0xFFFFFF);
        config.setTableHeadBackgroundColor(0x192e54, 0xFFFFFF);
        config.setDoubleSided(true);
    });
    
    var pdf1 = report.toPDF(config => {
        config.setTitle("FINANCIAL STATEMENTS");
        config.setFootText("Acme Inc");
        config.setGroupCompression(true);
        config.setGroupHeadingRepeat(false);
        config.setGroupCompressionIndentWidth(0.5);
        config.setPrintNewLineAfterGroup(true);
        config.setPrintNewLineAfterTotal(true);
        config.setPrintCompressedGroupColumnsInNewLine(true);
    
        config.setGroupColor(1, 0x192e54, 0xFFFFFF);
        config.setTableHeadBackgroundColor(0x192e54, 0xFFFFFF);
    });
    
    var pdf2 = report.toPDF(config => {
        config.setTitle("FINANCIAL STATEMENTS");
        config.setFootText("Acme Inc");
        config.setGroupCompression(false);
        config.setGroupHeadingRepeat(false);
        config.setGroupCompressionIndentWidth(0.5);
        config.setPrintNewLineAfterGroup(true);
        config.setPrintNewLineAfterTotal(true);
        config.setPrintCompressedGroupColumnsInNewLine(true);
    
        //config.setGroupColor(1, 0x192e54, 0xFFFFFF);
        config.setTableHeadBackgroundColor(0x192e54, 0xFFFFFF);
    });
    
    // Write to temp file
    new Ax.io.File("/tmp/financial_statements0.pdf").write(pdf0);
    new Ax.io.File("/tmp/financial_statements1.pdf").write(pdf1);
    new Ax.io.File("/tmp/financial_statements2.pdf").write(pdf2);
    
    // Return the PDF to caller
    return report;
</script>

6.3 Stocks

The following example generates a report of a series of stock assets. Some special features of this report are:

  • The first column of this report is acheived by adding three consecutive new lines. Then an RGB color is applied by following an especified expression.
  • Another render feature applied on this report is the setCompact() configuration flag, which avoids proportional column expansion to make the report fill the whole page.
Sample output
Copy
<script>
    // Sample data
    var nl = "\n\n\n";
    var rows = [
    	[ nl, "AMD",    "PURCH",    27.82,    2,   "PRICE",    29.92,    59.84,          4.19,    7.53],
    	[ nl, "ANET",   "PURCH",   251.64,    7,   "PRICE",   243.14,  1701.98,        -59.49,    3.38],
    	[ nl, "HQY",    "PURCH",    68.71,   11,   "PRICE",    57.67,   634.37,       -121.48,   16.07],
    	[ nl, "ILMN",   "PURCH",   362.99,    5,   "PRICE",   285.12,  1425.62,       -389.32,   21.45],
    	[ nl, "JKHY",   "PURCH",   146.48,    2,   "PRICE",   144.17,   288.34,         -4.61,    1.57],
    	[ nl, "MA",     "PURCH",   252.14,   10,   "PRICE",   270.87,  2708.10,        187.26,    7.43],
    	[ nl, "MELI",   "PURCH",   367.86,    1,   "PRICE",   556.57,   556.57,        188.71,   51.30],
    	[ nl, "SNPS",   "PURCH",   121.06,   10,   "PRICE",   135.58,  1355.57,        145.10,   11.99],
    ];
    
    // Build ResultSet with previously created rows
    // and add some column configurations
    var rs = new Ax.rs.Reader().build(rows, options => {
    	options.setColumnNames([
    		"label",
    		"stock",
    		"lpurch",
    		"units",
    		"quantity",
    		"lprice",
    		"tprice",
    		"tgain",
    		"tgain_relative",
    		"tgain_percent"]);
    });
    
    
    var md = rs.getMetaData();
    
    md.setColumnLabel("label", "");
    md.setColumnLabel("stock", "STOCKS");
    md.setColumnLabel("quantity", "QUANTITY");
    md.setColumnLabel("tgain", "TOTAL GAIN");
    
    
    md.setColumnColor("label", "tgain_relative < 0 ? '#FFFFFF:#FF0000' : '#FFFFFF:#00AA00'");
    
    // Ensure column align (by default will be taken from main column, but better setup)
    md.setColumnAlign("stock", "left");
    md.setColumnAlign("quantity", "right");
    md.setColumnAlign("tgain", "right");
    
    md.setColumnFont("lpurch").setFontSize("8");
    md.setColumnFont("lprice").setFontSize("8");
    md.setColumnColor("lpurch", "'#808080'");
    md.setColumnColor("lprice", "'#808080'");
    
    md.setColumnFont("stock").setFontWeight("bold");
    md.setColumnFont("units").setFontWeight("bold");
    md.setColumnFont("tprice").setFontWeight("bold");
    md.setColumnFont("tgain").setFontWeight("bold");
    md.setColumnFont("tgain_relative").setFontWeight("bold");
    md.setColumnColor("tgain_relative", "tgain_relative > 0 ? '#006600' : '#FF0000'");
    md.setColumnColor("tgain_percent", "tgain_relative > 0 ? '#006600' : '#FF0000'");
    md.setColumnSuffix("tgain_percent", "%");
    md.setColumnJoin("stock", "lpurch", "units");
    md.setColumnJoin("quantity", "lprice", "tprice");
    md.setColumnJoin("tgain", "tgain_relative", "tgain_percent");
    
    // ========================================================================
    // Generate report
    // ========================================================================
    var report = new Ax.rs.Report(rs);
    
    // Render to PDF
    var pdf = report.toPDF(config => {
    	// Avoid proportional column expansion to make report fill page
    	config.setCompact(true);
    	// Align header as column
    	config.setTableHeadAlign("column");
    	// Debug flag
    	config.setDebug(false);
    	// Report title
    	config.setTitle("STOCKS");
    });
    
    // Write to temp file
    new Ax.io.File("/tmp/stocks.pdf").write(pdf);
    
    // Return the PDF to caller
    return report;
</script>

6.4 Foodmart stores

You can use pre-built financial SVG chart types specifically designed for reporting. This can be done by using special SQL ROW type objects.

The report is executed on foodmart database using tables store and sales_fact_month_store. For simplification you can use the following SQL statements to create the minimum scheme to run the report.

Copy

Data tables

<script>
CREATE TABLE store (
                         store_id integer NOT NULL,
                       store_name varchar (30),
                       store_city varchar (30)
);
INSERT INTO store (store_id,store_name,store_city) VALUES (0,'HQ','Alameda');
INSERT INTO store (store_id,store_name,store_city) VALUES (1,'Store 1','Acapulco');
INSERT INTO store (store_id,store_name,store_city) VALUES (2,'Store 2','Bellingham');
INSERT INTO store (store_id,store_name,store_city) VALUES (3,'Store 3','Bremerton');
INSERT INTO store (store_id,store_name,store_city) VALUES (4,'Store 4','Camacho');
INSERT INTO store (store_id,store_name,store_city) VALUES (5,'Store 5','Guadalajara');
INSERT INTO store (store_id,store_name,store_city) VALUES (6,'Store 6','Beverly Hills');
INSERT INTO store (store_id,store_name,store_city) VALUES (7,'Store 7','Los Angeles');
INSERT INTO store (store_id,store_name,store_city) VALUES (8,'Store 8','Merida');
INSERT INTO store (store_id,store_name,store_city) VALUES (9,'Store 9','Mexico City');
INSERT INTO store (store_id,store_name,store_city) VALUES (10,'Store 10','Orizaba');
INSERT INTO store (store_id,store_name,store_city) VALUES (11,'Store 11','Portland');
INSERT INTO store (store_id,store_name,store_city) VALUES (12,'Store 12','Hidalgo');
INSERT INTO store (store_id,store_name,store_city) VALUES (13,'Store 13','Salem');
INSERT INTO store (store_id,store_name,store_city) VALUES (14,'Store 14','San Francisco');
INSERT INTO store (store_id,store_name,store_city) VALUES (15,'Store 15','Seattle');
INSERT INTO store (store_id,store_name,store_city) VALUES (16,'Store 16','Spokane');
INSERT INTO store (store_id,store_name,store_city) VALUES (17,'Store 17','Tacoma');
INSERT INTO store (store_id,store_name,store_city) VALUES (18,'Store 18','Hidalgo');
INSERT INTO store (store_id,store_name,store_city) VALUES (19,'Store 19','Vancouver');
INSERT INTO store (store_id,store_name,store_city) VALUES (20,'Store 20','Victoria');
INSERT INTO store (store_id,store_name,store_city) VALUES (21,'Store 21','San Andres');
INSERT INTO store (store_id,store_name,store_city) VALUES (22,'Store 22','Walla Walla');
INSERT INTO store (store_id,store_name,store_city) VALUES (23,'Store 23','Yakima');
INSERT INTO store (store_id,store_name,store_city) VALUES (24,'Store 24','San Diego');


CREATE TABLE sales_fact_month_store (
                         store_id integer NOT NULL,
                         the_year integer NOT NULL,
                       unit_sales integer,
                  unit_sales_prev integer,
                            sales decimal (10,4),
                       sales_prev decimal (10,4),
                        sales_pct decimal (10,4),
                        sales_avg decimal (10,4),
                        sales_jan decimal (10,4),
                        sales_feb decimal (10,4),
                        sales_mar decimal (10,4),
                        sales_apr decimal (10,4),
                        sales_may decimal (10,4),
                        sales_jun decimal (10,4),
                        sales_jul decimal (10,4),
                        sales_ago decimal (10,4),
                        sales_sep decimal (10,4),
                        sales_oct decimal (10,4),
                        sales_nov decimal (10,4),
                        sales_dec decimal (10,4),
                             cost decimal (10,4),
                         cost_jan decimal (10,4),
                         cost_feb decimal (10,4),
                         cost_mar decimal (10,4),
                         cost_apr decimal (10,4),
                         cost_may decimal (10,4),
                         cost_jun decimal (10,4),
                         cost_jul decimal (10,4),
                         cost_ago decimal (10,4),
                         cost_sep decimal (10,4),
                         cost_oct decimal (10,4),
                         cost_nov decimal (10,4),
                         cost_dec decimal (10,4),
                        cost_prev decimal (10,4),
                         cost_pct decimal (10,4),
                         cost_avg decimal (10,4),
                           profit decimal (10,4),
                  promotion_sales decimal (10,4),
             promotion_sales_prev decimal (10,4),
                      sales_count integer,
                 sales_count_prev integer
);
INSERT INTO sales_fact_month_store (store_id,the_year,unit_sales,unit_sales_prev,sales,sales_prev,sales_pct,sales_avg,sales_jan,sales_feb,sales_mar,sales_apr,sales_may,sales_jun,sales_jul,sales_ago,sales_sep,sales_oct,sales_nov,sales_dec,cost,cost_jan,cost_feb,cost_mar,cost_apr,cost_may,cost_jun,cost_jul,cost_ago,cost_sep,cost_oct,cost_nov,cost_dec,cost_prev,cost_pct,cost_avg,profit,promotion_sales,promotion_sales_prev,sales_count,sales_count_prev) VALUES (16,1997,23591,0,49634.4600,0.0000,100.0000,1649.6776,3244.8800,4606.8400,3778.3900,4109.2200,3809.0100,3717.1900,4562.7000,4511.6800,3731.8700,3629.5400,5540.1100,4393.0300,19795.4910,1282.6955,1827.3348,1520.9016,1639.9913,1515.0720,1487.9631,1829.3190,1812.7779,1469.4601,1429.7410,2222.8340,1757.4007,0.0000,100.0000,1649.6776,29838.9690,14996.8500,0.0000,7397,0);
INSERT INTO sales_fact_month_store (store_id,the_year,unit_sales,unit_sales_prev,sales,sales_prev,sales_pct,sales_avg,sales_jan,sales_feb,sales_mar,sales_apr,sales_may,sales_jun,sales_jul,sales_ago,sales_sep,sales_oct,sales_nov,sales_dec,cost,cost_jan,cost_feb,cost_mar,cost_apr,cost_may,cost_jun,cost_jul,cost_ago,cost_sep,cost_oct,cost_nov,cost_dec,cost_prev,cost_pct,cost_avg,profit,promotion_sales,promotion_sales_prev,sales_count,sales_count_prev) VALUES (17,1997,35257,0,74843.9600,0.0000,100.0000,2496.6929,6083.2500,5760.4800,6022.0200,5376.2200,5054.0300,6630.9100,6646.5000,6479.8200,5536.4100,6231.9200,6693.9200,8328.4800,29959.2813,2431.5611,2308.0997,2420.8056,2134.2894,2039.8646,2630.9710,2660.5136,2576.9768,2217.5348,2506.7982,2692.0899,3339.7766,0.0000,100.0000,2496.6929,44884.6787,18024.8900,0.0000,11184,0);
INSERT INTO sales_fact_month_store (store_id,the_year,unit_sales,unit_sales_prev,sales,sales_prev,sales_pct,sales_avg,sales_jan,sales_feb,sales_mar,sales_apr,sales_may,sales_jun,sales_jul,sales_ago,sales_sep,sales_oct,sales_nov,sales_dec,cost,cost_jan,cost_feb,cost_mar,cost_apr,cost_may,cost_jun,cost_jul,cost_ago,cost_sep,cost_oct,cost_nov,cost_dec,cost_prev,cost_pct,cost_avg,profit,promotion_sales,promotion_sales_prev,sales_count,sales_count_prev) VALUES (22,1997,2203,0,4705.9700,0.0000,100.0000,156.7028,285.0400,328.3100,392.4600,358.3800,375.9000,419.5100,429.7400,315.7900,377.6800,314.0400,441.7100,667.4100,1880.3396,112.7973,131.7375,157.6817,143.3161,152.7442,166.7105,170.7446,129.4157,148.4774,123.4886,181.5098,261.7162,0.0000,100.0000,156.7028,2825.6304,1067.5800,0.0000,1339,0);
INSERT INTO sales_fact_month_store (store_id,the_year,unit_sales,unit_sales_prev,sales,sales_prev,sales_pct,sales_avg,sales_jan,sales_feb,sales_mar,sales_apr,sales_may,sales_jun,sales_jul,sales_ago,sales_sep,sales_oct,sales_nov,sales_dec,cost,cost_jan,cost_feb,cost_mar,cost_apr,cost_may,cost_jun,cost_jul,cost_ago,cost_sep,cost_oct,cost_nov,cost_dec,cost_prev,cost_pct,cost_avg,profit,promotion_sales,promotion_sales_prev,sales_count,sales_count_prev) VALUES (23,1997,11491,0,24329.2300,0.0000,100.0000,809.5085,2036.5600,1990.1000,2412.6600,2004.1100,2031.3200,2016.1400,1707.5000,1376.5200,2169.4800,1848.1600,2284.1300,2452.5500,9713.8130,812.1701,792.7257,956.8270,787.7103,818.2792,805.1419,681.4063,556.8724,878.5405,731.0557,915.1098,977.9741,0.0000,100.0000,809.5085,14615.4170,7531.7700,0.0000,3652,0);
INSERT INTO sales_fact_month_store (store_id,the_year,unit_sales,unit_sales_prev,sales,sales_prev,sales_pct,sales_avg,sales_jan,sales_feb,sales_mar,sales_apr,sales_may,sales_jun,sales_jul,sales_ago,sales_sep,sales_oct,sales_nov,sales_dec,cost,cost_jan,cost_feb,cost_mar,cost_apr,cost_may,cost_jun,cost_jul,cost_ago,cost_sep,cost_oct,cost_nov,cost_dec,cost_prev,cost_pct,cost_avg,profit,promotion_sales,promotion_sales_prev,sales_count,sales_count_prev) VALUES (24,1997,25635,0,54431.1400,0.0000,100.0000,1809.5226,4722.8700,3854.6900,4720.2700,4296.0700,4345.2900,4335.9200,4336.5900,5525.7000,4380.3000,3446.5800,4782.5100,5684.3500,21713.5328,1879.0787,1545.5640,1881.1057,1713.1067,1742.0720,1726.2151,1723.6417,2180.0348,1753.3429,1370.6290,1896.1725,2302.5697,0.0000,100.0000,1809.5226,32717.6072,16100.9400,0.0000,8095,0);
INSERT INTO sales_fact_month_store (store_id,the_year,unit_sales,unit_sales_prev,sales,sales_prev,sales_pct,sales_avg,sales_jan,sales_feb,sales_mar,sales_apr,sales_may,sales_jun,sales_jul,sales_ago,sales_sep,sales_oct,sales_nov,sales_dec,cost,cost_jan,cost_feb,cost_mar,cost_apr,cost_may,cost_jun,cost_jul,cost_ago,cost_sep,cost_oct,cost_nov,cost_dec,cost_prev,cost_pct,cost_avg,profit,promotion_sales,promotion_sales_prev,sales_count,sales_count_prev) VALUES (9,1998,10880,0,23142.7900,0.0000,100.0000,771.5980,2136.2600,2215.3100,2506.2700,2396.3400,1947.1300,2598.5800,1805.3300,2381.7700,1776.7800,1519.6600,1859.3600,0.0000,9258.8558,858.9268,892.0128,995.6766,966.3638,779.2064,1038.3386,724.0723,942.0794,712.3742,606.7789,743.0260,0.0000,0.0000,100.0000,771.5980,13883.9342,9307.0900,0.0000,3487,0);
INSERT INTO sales_fact_month_store (store_id,the_year,unit_sales,unit_sales_prev,sales,sales_prev,sales_pct,sales_avg,sales_jan,sales_feb,sales_mar,sales_apr,sales_may,sales_jun,sales_jul,sales_ago,sales_sep,sales_oct,sales_nov,sales_dec,cost,cost_jan,cost_feb,cost_mar,cost_apr,cost_may,cost_jun,cost_jul,cost_ago,cost_sep,cost_oct,cost_nov,cost_dec,cost_prev,cost_pct,cost_avg,profit,promotion_sales,promotion_sales_prev,sales_count,sales_count_prev) VALUES (10,1998,24696,0,52142.0700,0.0000,100.0000,1745.8031,5364.4000,4795.6700,3810.7500,3471.7300,4853.7200,4789.0200,4535.0300,5284.4700,4908.0500,6240.9300,4088.3000,0.0000,20948.8956,2129.3007,1914.8524,1524.9854,1398.1592,1975.9851,1941.4396,1830.2841,2122.9400,1961.8319,2505.7181,1643.3991,0.0000,0.0000,100.0000,1745.8031,31193.1744,16107.0300,0.0000,7898,0);
INSERT INTO sales_fact_month_store (store_id,the_year,unit_sales,unit_sales_prev,sales,sales_prev,sales_pct,sales_avg,sales_jan,sales_feb,sales_mar,sales_apr,sales_may,sales_jun,sales_jul,sales_ago,sales_sep,sales_oct,sales_nov,sales_dec,cost,cost_jan,cost_feb,cost_mar,cost_apr,cost_may,cost_jun,cost_jul,cost_ago,cost_sep,cost_oct,cost_nov,cost_dec,cost_prev,cost_pct,cost_avg,profit,promotion_sales,promotion_sales_prev,sales_count,sales_count_prev) VALUES (11,1998,25266,26079,53633.2600,55058.7900,-2.5900,1789.9675,4336.2400,5916.1100,4972.2100,4728.2700,3765.0100,4477.3700,4057.4800,5408.1000,5170.3700,4531.2500,6270.8500,0.0000,21478.9026,1744.6776,2372.7185,1982.6805,1887.9829,1506.2793,1795.4301,1626.2727,2169.6255,2078.8058,1797.2659,2517.1638,0.0000,21948.9440,-2.1400,1789.9675,32154.3574,12966.3600,17845.9200,8022,8264);
INSERT INTO sales_fact_month_store (store_id,the_year,unit_sales,unit_sales_prev,sales,sales_prev,sales_pct,sales_avg,sales_jan,sales_feb,sales_mar,sales_apr,sales_may,sales_jun,sales_jul,sales_ago,sales_sep,sales_oct,sales_nov,sales_dec,cost,cost_jan,cost_feb,cost_mar,cost_apr,cost_may,cost_jun,cost_jul,cost_ago,cost_sep,cost_oct,cost_nov,cost_dec,cost_prev,cost_pct,cost_avg,profit,promotion_sales,promotion_sales_prev,sales_count,sales_count_prev) VALUES (15,1997,25011,0,52644.0700,0.0000,100.0000,1746.4613,4037.0000,4154.5100,4569.1300,4444.0600,4557.5800,3531.2200,4627.2800,4376.5800,4257.5600,4387.5000,4806.4500,4895.2000,20956.8025,1621.2704,1644.6051,1799.6706,1783.4852,1812.0376,1407.7635,1848.1923,1746.7337,1687.3558,1749.9959,1908.5792,1947.1132,0.0000,100.0000,1746.4613,31687.2675,13787.4900,0.0000,7956,0);
INSERT INTO sales_fact_month_store (store_id,the_year,unit_sales,unit_sales_prev,sales,sales_prev,sales_pct,sales_avg,sales_jan,sales_feb,sales_mar,sales_apr,sales_may,sales_jun,sales_jul,sales_ago,sales_sep,sales_oct,sales_nov,sales_dec,cost,cost_jan,cost_feb,cost_mar,cost_apr,cost_may,cost_jun,cost_jul,cost_ago,cost_sep,cost_oct,cost_nov,cost_dec,cost_prev,cost_pct,cost_avg,profit,promotion_sales,promotion_sales_prev,sales_count,sales_count_prev) VALUES (2,1997,2237,0,4739.2300,0.0000,100.0000,158.0591,375.0600,358.2300,362.1700,398.7800,356.6300,290.1200,356.0400,310.0100,410.2200,463.7600,599.3400,458.8700,1896.6174,146.2278,140.8153,144.7635,162.4681,142.2610,120.7102,142.5179,121.3718,168.1243,183.9463,241.4736,181.9376,0.0000,100.0000,158.0591,2842.6126,1125.7700,0.0000,1380,0);
INSERT INTO sales_fact_month_store (store_id,the_year,unit_sales,unit_sales_prev,sales,sales_prev,sales_pct,sales_avg,sales_jan,sales_feb,sales_mar,sales_apr,sales_may,sales_jun,sales_jul,sales_ago,sales_sep,sales_oct,sales_nov,sales_dec,cost,cost_jan,cost_feb,cost_mar,cost_apr,cost_may,cost_jun,cost_jul,cost_ago,cost_sep,cost_oct,cost_nov,cost_dec,cost_prev,cost_pct,cost_avg,profit,promotion_sales,promotion_sales_prev,sales_count,sales_count_prev) VALUES (3,1997,24576,0,52896.3000,0.0000,100.0000,1760.2235,3437.9100,4530.3300,4517.5300,4235.6000,3700.0400,5080.6700,4096.3200,4924.0400,3793.6400,3410.8100,5787.1000,5382.3100,21121.9631,1383.6258,1802.6154,1801.1504,1689.0315,1479.4612,2033.9754,1625.4829,1974.2374,1512.2048,1370.1274,2301.5247,2148.5262,0.0000,100.0000,1760.2235,31774.3369,13050.7700,0.0000,7876,0);
INSERT INTO sales_fact_month_store (store_id,the_year,unit_sales,unit_sales_prev,sales,sales_prev,sales_pct,sales_avg,sales_jan,sales_feb,sales_mar,sales_apr,sales_may,sales_jun,sales_jul,sales_ago,sales_sep,sales_oct,sales_nov,sales_dec,cost,cost_jan,cost_feb,cost_mar,cost_apr,cost_may,cost_jun,cost_jul,cost_ago,cost_sep,cost_oct,cost_nov,cost_dec,cost_prev,cost_pct,cost_avg,profit,promotion_sales,promotion_sales_prev,sales_count,sales_count_prev) VALUES (6,1997,21333,0,45750.2400,0.0000,100.0000,1522.2545,2629.0900,2836.3700,2738.4300,5065.0000,3667.8000,3864.3500,2778.6300,3253.5700,4076.5200,4781.4900,4577.3500,5481.6400,18266.4404,1041.6798,1130.8253,1087.2982,2023.5164,1465.8565,1542.0282,1108.1492,1298.7415,1623.9997,1911.0702,1841.5582,2191.7172,0.0000,100.0000,1522.2545,27483.7996,15595.3100,0.0000,6815,0);
INSERT INTO sales_fact_month_store (store_id,the_year,unit_sales,unit_sales_prev,sales,sales_prev,sales_pct,sales_avg,sales_jan,sales_feb,sales_mar,sales_apr,sales_may,sales_jun,sales_jul,sales_ago,sales_sep,sales_oct,sales_nov,sales_dec,cost,cost_jan,cost_feb,cost_mar,cost_apr,cost_may,cost_jun,cost_jul,cost_ago,cost_sep,cost_oct,cost_nov,cost_dec,cost_prev,cost_pct,cost_avg,profit,promotion_sales,promotion_sales_prev,sales_count,sales_count_prev) VALUES (7,1997,25663,0,54545.2800,0.0000,100.0000,1814.3556,4167.7200,5570.1700,3999.0800,3917.4900,3396.0000,4370.9500,4155.8700,5754.6800,3957.8600,4468.7800,5575.3000,5211.3800,21771.5360,1666.5780,2229.9979,1596.7152,1575.7027,1355.0990,1730.5070,1638.5773,2303.5425,1575.3754,1792.6840,2235.6256,2071.1314,0.0000,100.0000,1814.3556,32773.7440,16662.9500,0.0000,8207,0);
INSERT INTO sales_fact_month_store (store_id,the_year,unit_sales,unit_sales_prev,sales,sales_prev,sales_pct,sales_avg,sales_jan,sales_feb,sales_mar,sales_apr,sales_may,sales_jun,sales_jul,sales_ago,sales_sep,sales_oct,sales_nov,sales_dec,cost,cost_jan,cost_feb,cost_mar,cost_apr,cost_may,cost_jun,cost_jul,cost_ago,cost_sep,cost_oct,cost_nov,cost_dec,cost_prev,cost_pct,cost_avg,profit,promotion_sales,promotion_sales_prev,sales_count,sales_count_prev) VALUES (11,1997,26079,0,55058.7900,0.0000,100.0000,1829.1366,4419.2000,4712.5000,5039.5000,3810.8300,5458.2600,5078.8900,3903.0700,3990.2000,3820.5700,4077.2700,4551.3200,6197.1800,21948.9440,1770.1870,1888.1646,2003.5843,1524.9092,2169.6922,2023.8172,1554.4568,1590.5317,1512.3358,1625.7884,1823.5930,2461.8838,0.0000,100.0000,1829.1366,33109.8460,17845.9200,0.0000,8264,0);
INSERT INTO sales_fact_month_store (store_id,the_year,unit_sales,unit_sales_prev,sales,sales_prev,sales_pct,sales_avg,sales_jan,sales_feb,sales_mar,sales_apr,sales_may,sales_jun,sales_jul,sales_ago,sales_sep,sales_oct,sales_nov,sales_dec,cost,cost_jan,cost_feb,cost_mar,cost_apr,cost_may,cost_jun,cost_jul,cost_ago,cost_sep,cost_oct,cost_nov,cost_dec,cost_prev,cost_pct,cost_avg,profit,promotion_sales,promotion_sales_prev,sales_count,sales_count_prev) VALUES (13,1997,41580,0,87218.2800,0.0000,100.0000,2902.0655,9883.5000,4962.8400,11152.7500,4535.1600,7326.0900,5563.6500,12318.3200,4982.2100,6866.0900,4901.4800,7374.5900,7351.6000,34823.5566,3946.9878,1998.1653,4473.9845,1801.1636,2938.8411,2220.5378,4892.2197,1987.6416,2736.5982,1952.9298,2955.3157,2919.1715,0.0000,100.0000,2902.0655,52394.7234,14103.2900,0.0000,13347,0);
INSERT INTO sales_fact_month_store (store_id,the_year,unit_sales,unit_sales_prev,sales,sales_prev,sales_pct,sales_avg,sales_jan,sales_feb,sales_mar,sales_apr,sales_may,sales_jun,sales_jul,sales_ago,sales_sep,sales_oct,sales_nov,sales_dec,cost,cost_jan,cost_feb,cost_mar,cost_apr,cost_may,cost_jun,cost_jul,cost_ago,cost_sep,cost_oct,cost_nov,cost_dec,cost_prev,cost_pct,cost_avg,profit,promotion_sales,promotion_sales_prev,sales_count,sales_count_prev) VALUES (14,1997,2117,0,4441.1800,0.0000,100.0000,148.2523,217.6100,393.4200,325.4800,327.3300,378.3400,432.2100,328.3200,398.2400,447.7700,380.9400,349.8800,461.6400,1778.9159,83.6333,159.0182,129.5908,133.0088,151.2751,173.6289,130.5697,157.0830,179.7679,153.7511,142.3726,185.2165,0.0000,100.0000,148.2523,2662.2641,1317.6800,0.0000,1325,0);
INSERT INTO sales_fact_month_store (store_id,the_year,unit_sales,unit_sales_prev,sales,sales_prev,sales_pct,sales_avg,sales_jan,sales_feb,sales_mar,sales_apr,sales_may,sales_jun,sales_jul,sales_ago,sales_sep,sales_oct,sales_nov,sales_dec,cost,cost_jan,cost_feb,cost_mar,cost_apr,cost_may,cost_jun,cost_jul,cost_ago,cost_sep,cost_oct,cost_nov,cost_dec,cost_prev,cost_pct,cost_avg,profit,promotion_sales,promotion_sales_prev,sales_count,sales_count_prev) VALUES (8,1998,37143,0,79063.1300,0.0000,100.0000,2638.3958,7494.1500,6453.9200,6644.5800,6134.8100,6623.9300,7150.9500,8498.0400,8403.9300,7346.0000,8534.5100,5778.3100,0.0000,31659.7140,2990.4867,2583.8629,2665.1922,2474.3939,2654.6150,2860.6438,3389.9120,3364.3749,2950.3013,3418.8431,2307.0882,0.0000,0.0000,100.0000,2638.3958,47403.4160,26583.6700,0.0000,11784,0);
INSERT INTO sales_fact_month_store (store_id,the_year,unit_sales,unit_sales_prev,sales,sales_prev,sales_pct,sales_avg,sales_jan,sales_feb,sales_mar,sales_apr,sales_may,sales_jun,sales_jul,sales_ago,sales_sep,sales_oct,sales_nov,sales_dec,cost,cost_jan,cost_feb,cost_mar,cost_apr,cost_may,cost_jun,cost_jul,cost_ago,cost_sep,cost_oct,cost_nov,cost_dec,cost_prev,cost_pct,cost_avg,profit,promotion_sales,promotion_sales_prev,sales_count,sales_count_prev) VALUES (1,1998,23226,0,49090.0300,0.0000,100.0000,1639.1628,5668.2700,4212.2500,5182.6200,4604.3000,4312.8300,3384.4000,4396.1200,3686.9900,4672.6500,4301.5700,4668.0300,0.0000,19669.3270,2257.2385,1683.8136,2079.5331,1827.9842,1731.7976,1358.2468,1772.4353,1492.3861,1870.5296,1724.7814,1870.5808,0.0000,0.0000,100.0000,1639.1628,29420.7030,8567.9500,0.0000,7376,0);
INSERT INTO sales_fact_month_store (store_id,the_year,unit_sales,unit_sales_prev,sales,sales_prev,sales_pct,sales_avg,sales_jan,sales_feb,sales_mar,sales_apr,sales_may,sales_jun,sales_jul,sales_ago,sales_sep,sales_oct,sales_nov,sales_dec,cost,cost_jan,cost_feb,cost_mar,cost_apr,cost_may,cost_jun,cost_jul,cost_ago,cost_sep,cost_oct,cost_nov,cost_dec,cost_prev,cost_pct,cost_avg,profit,promotion_sales,promotion_sales_prev,sales_count,sales_count_prev) VALUES (2,1998,1984,2237,4164.8100,4739.2300,-12.1200,138.0733,338.3000,286.4600,435.9200,306.4500,349.7100,462.2700,425.8000,433.4000,292.2200,284.0500,550.2300,0.0000,1656.7944,132.7956,113.4016,176.2846,126.6381,137.9922,183.6514,163.9996,168.7444,119.7285,113.9112,219.6472,0.0000,1896.6174,-12.6400,138.0733,2508.0156,1252.6500,1125.7700,1212,1380);
INSERT INTO sales_fact_month_store (store_id,the_year,unit_sales,unit_sales_prev,sales,sales_prev,sales_pct,sales_avg,sales_jan,sales_feb,sales_mar,sales_apr,sales_may,sales_jun,sales_jul,sales_ago,sales_sep,sales_oct,sales_nov,sales_dec,cost,cost_jan,cost_feb,cost_mar,cost_apr,cost_may,cost_jun,cost_jul,cost_ago,cost_sep,cost_oct,cost_nov,cost_dec,cost_prev,cost_pct,cost_avg,profit,promotion_sales,promotion_sales_prev,sales_count,sales_count_prev) VALUES (3,1998,24069,24576,51135.1900,52896.3000,-3.3300,1706.8044,4737.2000,4288.0000,4456.9400,4567.8100,5254.7100,4241.6900,4027.1600,4979.8100,4151.4800,4175.2500,6255.1400,0.0000,20480.9627,1878.6491,1711.3271,1775.2624,1842.4115,2103.8778,1702.2985,1616.7543,1997.4268,1674.8141,1677.8385,2500.3026,0.0000,21121.9631,-3.0300,1706.8044,30654.2273,8726.0600,13050.7700,7671,7876);
INSERT INTO sales_fact_month_store (store_id,the_year,unit_sales,unit_sales_prev,sales,sales_prev,sales_pct,sales_avg,sales_jan,sales_feb,sales_mar,sales_apr,sales_may,sales_jun,sales_jul,sales_ago,sales_sep,sales_oct,sales_nov,sales_dec,cost,cost_jan,cost_feb,cost_mar,cost_apr,cost_may,cost_jun,cost_jul,cost_ago,cost_sep,cost_oct,cost_nov,cost_dec,cost_prev,cost_pct,cost_avg,profit,promotion_sales,promotion_sales_prev,sales_count,sales_count_prev) VALUES (4,1998,23752,0,50047.5100,0.0000,100.0000,1675.5284,5896.6300,4469.6400,5011.5200,5297.5300,4738.4500,3505.8400,5713.1600,2674.2500,3719.5600,3426.5900,5594.3400,0.0000,20105.6690,2383.1526,1794.1842,2007.9024,2113.3781,1891.4288,1412.1987,2304.7414,1073.9307,1497.7460,1368.1466,2258.8595,0.0000,0.0000,100.0000,1675.5284,29941.8410,15489.1900,0.0000,7546,0);
INSERT INTO sales_fact_month_store (store_id,the_year,unit_sales,unit_sales_prev,sales,sales_prev,sales_pct,sales_avg,sales_jan,sales_feb,sales_mar,sales_apr,sales_may,sales_jun,sales_jul,sales_ago,sales_sep,sales_oct,sales_nov,sales_dec,cost,cost_jan,cost_feb,cost_mar,cost_apr,cost_may,cost_jun,cost_jul,cost_ago,cost_sep,cost_oct,cost_nov,cost_dec,cost_prev,cost_pct,cost_avg,profit,promotion_sales,promotion_sales_prev,sales_count,sales_count_prev) VALUES (5,1998,2124,0,4328.8700,0.0000,100.0000,143.6140,411.6600,473.9200,471.2300,270.4300,481.9500,494.3900,299.1100,424.6100,311.7000,343.0100,346.8600,0.0000,1723.2344,164.2113,190.0998,186.8755,108.2924,191.1038,194.9717,119.1573,169.1985,123.8933,135.3974,140.0334,0.0000,0.0000,100.0000,143.6140,2605.6356,1274.2900,0.0000,1298,0);
INSERT INTO sales_fact_month_store (store_id,the_year,unit_sales,unit_sales_prev,sales,sales_prev,sales_pct,sales_avg,sales_jan,sales_feb,sales_mar,sales_apr,sales_may,sales_jun,sales_jul,sales_ago,sales_sep,sales_oct,sales_nov,sales_dec,cost,cost_jan,cost_feb,cost_mar,cost_apr,cost_may,cost_jun,cost_jul,cost_ago,cost_sep,cost_oct,cost_nov,cost_dec,cost_prev,cost_pct,cost_avg,profit,promotion_sales,promotion_sales_prev,sales_count,sales_count_prev) VALUES (6,1998,22707,21333,47843.9200,45750.2400,4.5800,1597.3045,4032.2700,4302.4800,4135.2900,3812.4200,3434.5500,3311.3400,4797.9000,4274.9100,5283.7800,4738.5300,5720.4500,0.0000,19166.9998,1603.8143,1756.0441,1653.7262,1530.6682,1366.8888,1328.9139,1933.8068,1711.5997,2105.9221,1891.9067,2283.7090,0.0000,18266.4404,4.9300,1597.3045,28676.9202,11890.3000,15595.3100,7288,6815);
INSERT INTO sales_fact_month_store (store_id,the_year,unit_sales,unit_sales_prev,sales,sales_prev,sales_pct,sales_avg,sales_jan,sales_feb,sales_mar,sales_apr,sales_may,sales_jun,sales_jul,sales_ago,sales_sep,sales_oct,sales_nov,sales_dec,cost,cost_jan,cost_feb,cost_mar,cost_apr,cost_may,cost_jun,cost_jul,cost_ago,cost_sep,cost_oct,cost_nov,cost_dec,cost_prev,cost_pct,cost_avg,profit,promotion_sales,promotion_sales_prev,sales_count,sales_count_prev) VALUES (7,1998,24061,25663,50819.1500,54545.2800,-6.8300,1705.3503,5143.2100,4672.4800,5023.8700,3822.9600,3999.5800,4018.0500,4230.3900,4492.9200,3723.7600,3997.8200,7694.1100,0.0000,20463.5459,2065.8543,1887.3015,2016.6160,1542.0351,1613.3200,1634.7144,1701.5910,1804.4904,1497.6744,1606.7201,3093.2287,0.0000,21771.5360,-6.0100,1705.3503,30355.6041,9759.9200,16662.9500,7644,8207);
INSERT INTO sales_fact_month_store (store_id,the_year,unit_sales,unit_sales_prev,sales,sales_prev,sales_pct,sales_avg,sales_jan,sales_feb,sales_mar,sales_apr,sales_may,sales_jun,sales_jul,sales_ago,sales_sep,sales_oct,sales_nov,sales_dec,cost,cost_jan,cost_feb,cost_mar,cost_apr,cost_may,cost_jun,cost_jul,cost_ago,cost_sep,cost_oct,cost_nov,cost_dec,cost_prev,cost_pct,cost_avg,profit,promotion_sales,promotion_sales_prev,sales_count,sales_count_prev) VALUES (12,1998,37680,0,79045.6700,0.0000,100.0000,2647.5295,6065.0100,7308.9000,7346.1200,7224.7200,5860.0700,7480.7200,8482.9600,6560.2900,9181.4300,5759.0100,7776.4400,0.0000,31769.2633,2433.5073,2960.4632,2940.7619,2907.6927,2352.6870,2998.8867,3420.6302,2635.1575,3696.1044,2305.3259,3118.0465,0.0000,0.0000,100.0000,2647.5295,47276.4067,24030.9300,0.0000,11924,0);
INSERT INTO sales_fact_month_store (store_id,the_year,unit_sales,unit_sales_prev,sales,sales_prev,sales_pct,sales_avg,sales_jan,sales_feb,sales_mar,sales_apr,sales_may,sales_jun,sales_jul,sales_ago,sales_sep,sales_oct,sales_nov,sales_dec,cost,cost_jan,cost_feb,cost_mar,cost_apr,cost_may,cost_jun,cost_jul,cost_ago,cost_sep,cost_oct,cost_nov,cost_dec,cost_prev,cost_pct,cost_avg,profit,promotion_sales,promotion_sales_prev,sales_count,sales_count_prev) VALUES (13,1998,35346,41580,74965.2400,87218.2800,-14.0500,2502.9050,6844.7500,5644.2100,7431.5900,6703.9300,7484.1400,5680.5000,6891.1400,5573.9900,8408.0500,5887.1200,8415.8200,0.0000,30033.8774,2743.3876,2244.4893,2975.7971,2661.1968,3013.2762,2288.7663,2775.1327,2218.9191,3370.4502,2345.3752,3397.0869,0.0000,34823.5566,-13.7500,2502.9050,44931.3626,21661.2200,14103.2900,11293,13347);
INSERT INTO sales_fact_month_store (store_id,the_year,unit_sales,unit_sales_prev,sales,sales_prev,sales_pct,sales_avg,sales_jan,sales_feb,sales_mar,sales_apr,sales_may,sales_jun,sales_jul,sales_ago,sales_sep,sales_oct,sales_nov,sales_dec,cost,cost_jan,cost_feb,cost_mar,cost_apr,cost_may,cost_jun,cost_jul,cost_ago,cost_sep,cost_oct,cost_nov,cost_dec,cost_prev,cost_pct,cost_avg,profit,promotion_sales,promotion_sales_prev,sales_count,sales_count_prev) VALUES (14,1998,2027,2117,4230.0200,4441.1800,-4.7500,139.8201,324.8400,533.1900,300.5700,325.8700,425.1100,306.7700,361.2300,364.8100,362.6100,472.6500,452.3700,0.0000,1677.7254,129.5154,208.6644,120.4850,128.1480,168.1321,123.8681,145.0448,143.3821,143.9921,188.0495,178.4439,0.0000,1778.9159,-5.6900,139.8201,2552.2946,1181.5900,1317.6800,1268,1325);
INSERT INTO sales_fact_month_store (store_id,the_year,unit_sales,unit_sales_prev,sales,sales_prev,sales_pct,sales_avg,sales_jan,sales_feb,sales_mar,sales_apr,sales_may,sales_jun,sales_jul,sales_ago,sales_sep,sales_oct,sales_nov,sales_dec,cost,cost_jan,cost_feb,cost_mar,cost_apr,cost_may,cost_jun,cost_jul,cost_ago,cost_sep,cost_oct,cost_nov,cost_dec,cost_prev,cost_pct,cost_avg,profit,promotion_sales,promotion_sales_prev,sales_count,sales_count_prev) VALUES (15,1998,26672,25011,56579.4600,52644.0700,7.4800,1888.5807,4723.2000,4179.6200,4215.5900,4468.1500,5243.9600,6786.5900,4316.9100,5526.0000,4008.1200,5259.9400,7851.3800,0.0000,22662.2654,1889.0759,1684.8818,1713.6498,1782.9946,2097.0451,2723.1382,1738.1463,2210.4240,1584.8842,2078.4016,3159.6239,0.0000,20956.8025,8.1400,1888.5807,33917.1946,17187.1000,13787.4900,8481,7956);
INSERT INTO sales_fact_month_store (store_id,the_year,unit_sales,unit_sales_prev,sales,sales_prev,sales_pct,sales_avg,sales_jan,sales_feb,sales_mar,sales_apr,sales_may,sales_jun,sales_jul,sales_ago,sales_sep,sales_oct,sales_nov,sales_dec,cost,cost_jan,cost_feb,cost_mar,cost_apr,cost_may,cost_jun,cost_jul,cost_ago,cost_sep,cost_oct,cost_nov,cost_dec,cost_prev,cost_pct,cost_avg,profit,promotion_sales,promotion_sales_prev,sales_count,sales_count_prev) VALUES (16,1998,26164,23591,54984.9400,49634.4600,10.7800,1837.8885,4450.9900,5200.8100,5161.2100,5714.3000,5874.5700,4243.8200,3902.3000,5613.5500,4542.7600,4227.7900,6052.8400,0.0000,22053.9739,1778.8282,2080.4103,2076.8821,2301.3524,2345.3918,1695.8955,1558.5299,2245.9400,1831.8038,1715.1129,2423.8270,0.0000,19795.4910,11.4100,1837.8885,32930.9661,14486.2600,14996.8500,8264,7397);
INSERT INTO sales_fact_month_store (store_id,the_year,unit_sales,unit_sales_prev,sales,sales_prev,sales_pct,sales_avg,sales_jan,sales_feb,sales_mar,sales_apr,sales_may,sales_jun,sales_jul,sales_ago,sales_sep,sales_oct,sales_nov,sales_dec,cost,cost_jan,cost_feb,cost_mar,cost_apr,cost_may,cost_jun,cost_jul,cost_ago,cost_sep,cost_oct,cost_nov,cost_dec,cost_prev,cost_pct,cost_avg,profit,promotion_sales,promotion_sales_prev,sales_count,sales_count_prev) VALUES (17,1998,35444,35257,75219.1300,74843.9600,0.5000,2508.8140,6234.2300,6332.0300,7670.2200,7306.2800,5275.2200,8623.8700,7447.4900,5529.0200,7401.7900,5835.6500,7563.3300,0.0000,30104.7334,2499.0381,2532.9933,3071.5702,2916.5880,2096.4625,3459.5890,3009.9126,2211.4111,2962.0720,2325.4329,3019.6637,0.0000,29959.2813,0.4900,2508.8140,45114.3966,19861.1900,18024.8900,11257,11184);
INSERT INTO sales_fact_month_store (store_id,the_year,unit_sales,unit_sales_prev,sales,sales_prev,sales_pct,sales_avg,sales_jan,sales_feb,sales_mar,sales_apr,sales_may,sales_jun,sales_jul,sales_ago,sales_sep,sales_oct,sales_nov,sales_dec,cost,cost_jan,cost_feb,cost_mar,cost_apr,cost_may,cost_jun,cost_jul,cost_ago,cost_sep,cost_oct,cost_nov,cost_dec,cost_prev,cost_pct,cost_avg,profit,promotion_sales,promotion_sales_prev,sales_count,sales_count_prev) VALUES (18,1998,10070,0,21049.9100,0.0000,100.0000,701.6268,2123.3100,2037.3800,1757.0900,2092.2500,2036.8800,1925.3000,1542.8700,2394.9200,2319.0500,1251.9700,1568.8900,0.0000,8419.2509,850.9833,810.5517,701.3193,835.5714,814.0182,769.8415,615.5195,969.1523,930.3948,489.4547,632.4442,0.0000,0.0000,100.0000,701.6268,12630.6591,5806.1800,0.0000,3153,0);
INSERT INTO sales_fact_month_store (store_id,the_year,unit_sales,unit_sales_prev,sales,sales_prev,sales_pct,sales_avg,sales_jan,sales_feb,sales_mar,sales_apr,sales_may,sales_jun,sales_jul,sales_ago,sales_sep,sales_oct,sales_nov,sales_dec,cost,cost_jan,cost_feb,cost_mar,cost_apr,cost_may,cost_jun,cost_jul,cost_ago,cost_sep,cost_oct,cost_nov,cost_dec,cost_prev,cost_pct,cost_avg,profit,promotion_sales,promotion_sales_prev,sales_count,sales_count_prev) VALUES (19,1998,36643,0,77931.1700,0.0000,100.0000,2605.4531,5290.3000,6344.3100,6305.2400,7010.9300,7360.2900,8031.9800,8113.9700,6160.3600,7726.8100,7172.7600,8414.2200,0.0000,31264.3752,2121.5261,2540.5661,2539.9895,2806.5083,2929.8191,3209.4898,3259.1116,2479.6542,3095.9525,2871.9261,3409.8319,0.0000,0.0000,100.0000,2605.4531,46666.7948,21019.0900,0.0000,11647,0);
INSERT INTO sales_fact_month_store (store_id,the_year,unit_sales,unit_sales_prev,sales,sales_prev,sales_pct,sales_avg,sales_jan,sales_feb,sales_mar,sales_apr,sales_may,sales_jun,sales_jul,sales_ago,sales_sep,sales_oct,sales_nov,sales_dec,cost,cost_jan,cost_feb,cost_mar,cost_apr,cost_may,cost_jun,cost_jul,cost_ago,cost_sep,cost_oct,cost_nov,cost_dec,cost_prev,cost_pct,cost_avg,profit,promotion_sales,promotion_sales_prev,sales_count,sales_count_prev) VALUES (20,1998,9514,0,20114.2900,0.0000,100.0000,672.3741,1854.5000,1988.3500,2098.4300,1473.0900,1982.0300,1826.6800,1898.0700,1746.6600,1530.4300,1864.9000,1851.1500,0.0000,8068.1953,739.4691,795.0933,840.0005,591.8894,800.0288,734.4454,762.5716,703.1349,615.1618,749.0738,737.3267,0.0000,0.0000,100.0000,672.3741,12046.0947,3850.0800,0.0000,2985,0);
INSERT INTO sales_fact_month_store (store_id,the_year,unit_sales,unit_sales_prev,sales,sales_prev,sales_pct,sales_avg,sales_jan,sales_feb,sales_mar,sales_apr,sales_may,sales_jun,sales_jul,sales_ago,sales_sep,sales_oct,sales_nov,sales_dec,cost,cost_jan,cost_feb,cost_mar,cost_apr,cost_may,cost_jun,cost_jul,cost_ago,cost_sep,cost_oct,cost_nov,cost_dec,cost_prev,cost_pct,cost_avg,profit,promotion_sales,promotion_sales_prev,sales_count,sales_count_prev) VALUES (21,1998,34343,0,72383.6100,0.0000,100.0000,2419.5717,6459.6700,6698.6100,5574.2700,6144.4700,7038.7800,6971.0700,7161.0800,6754.6700,7471.2300,6328.1100,5781.6500,0.0000,29033.8348,2575.8007,2673.2178,2253.3137,2444.8684,2813.2062,2828.6286,2869.6300,2735.5188,2973.6538,2549.7588,2316.2380,0.0000,0.0000,100.0000,2419.5717,43349.7752,18553.0600,0.0000,10990,0);
INSERT INTO sales_fact_month_store (store_id,the_year,unit_sales,unit_sales_prev,sales,sales_prev,sales_pct,sales_avg,sales_jan,sales_feb,sales_mar,sales_apr,sales_may,sales_jun,sales_jul,sales_ago,sales_sep,sales_oct,sales_nov,sales_dec,cost,cost_jan,cost_feb,cost_mar,cost_apr,cost_may,cost_jun,cost_jul,cost_ago,cost_sep,cost_oct,cost_nov,cost_dec,cost_prev,cost_pct,cost_avg,profit,promotion_sales,promotion_sales_prev,sales_count,sales_count_prev) VALUES (22,1998,2244,2203,4847.9500,4705.9700,3.0200,159.9275,422.1000,560.9500,330.5800,503.1400,359.2000,386.7400,394.5600,411.5700,477.8500,407.0000,594.2600,0.0000,1919.0123,163.2202,225.8915,131.1221,201.6516,140.0716,155.8875,158.2407,158.8788,186.7720,159.8038,237.4725,0.0000,1880.3396,2.0600,159.9275,2928.9377,1098.9100,1067.5800,1351,1339);
INSERT INTO sales_fact_month_store (store_id,the_year,unit_sales,unit_sales_prev,sales,sales_prev,sales_pct,sales_avg,sales_jan,sales_feb,sales_mar,sales_apr,sales_may,sales_jun,sales_jul,sales_ago,sales_sep,sales_oct,sales_nov,sales_dec,cost,cost_jan,cost_feb,cost_mar,cost_apr,cost_may,cost_jun,cost_jul,cost_ago,cost_sep,cost_oct,cost_nov,cost_dec,cost_prev,cost_pct,cost_avg,profit,promotion_sales,promotion_sales_prev,sales_count,sales_count_prev) VALUES (23,1998,9710,11491,20764.9500,24329.2300,-14.6500,693.2092,3074.0800,1642.9300,2119.2000,2226.5200,1945.8600,1485.9100,1250.5200,1699.2600,1783.2200,1623.7300,1913.7200,0.0000,8318.2589,1215.2052,666.4275,845.8800,895.3982,782.4720,601.1904,498.3846,672.5664,712.5250,657.8662,770.3434,0.0000,9713.8130,-14.3700,693.2092,12446.6911,5130.3900,7531.7700,3082,3652);
INSERT INTO sales_fact_month_store (store_id,the_year,unit_sales,unit_sales_prev,sales,sales_prev,sales_pct,sales_avg,sales_jan,sales_feb,sales_mar,sales_apr,sales_may,sales_jun,sales_jul,sales_ago,sales_sep,sales_oct,sales_nov,sales_dec,cost,cost_jan,cost_feb,cost_mar,cost_apr,cost_may,cost_jun,cost_jul,cost_ago,cost_sep,cost_oct,cost_nov,cost_dec,cost_prev,cost_pct,cost_avg,profit,promotion_sales,promotion_sales_prev,sales_count,sales_count_prev) VALUES (24,1998,24222,25635,51620.4000,54431.1400,-5.1600,1719.0584,4729.7100,3940.4700,5258.5900,4169.4400,4536.1300,4866.1900,4394.8200,3938.2500,4808.9000,4252.1100,6725.7900,0.0000,20628.0615,1890.5108,1563.4149,2111.3254,1667.5400,1819.6984,1955.3463,1760.7891,1554.3983,1914.7479,1697.8056,2692.4848,0.0000,21713.5328,-5.0000,1719.0584,30992.3385,14412.1900,16100.9400,7637,8095);

</script>
Copy

Report example

<script>
    var rs = Ax.db.executeQuery(`
    SELECT  FIRST 15
            store.store_name || ' (' || store.store_city || ')' store_name,
           row('ball', 'Units', sfms.unit_sales, (select Min(abs(a.unit_sales)) from sales_fact_month_store a),(select Max(abs(a.unit_sales)) from sales_fact_month_store a)) Bunit_sales,
           sfms.sales,
           row('vbar colored', 'Jan', sales_jan, 'Feb', sales_feb, 'Mar', sales_mar, 'Apr', sales_apr, 'May', sales_may, 'Jun', sales_jun, 'Jul', sales_jul, 'Aug', sales_ago, 'Sep', sales_sep, 'Oct', sales_oct, 'Nov', sales_nov, 'Dec', sales_dec) sales_m,
           row('percent', '%', sfms.sales_pct, 0,(select Max(abs(a.sales_pct)) from sales_fact_month_store a)) sales_perc,
           row('line', 'Jan', cost_jan, 'Feb', cost_feb, 'Mar', cost_mar, 'Apr', cost_apr, 'May', cost_may, 'Jun', cost_jun, 'Jul', cost_jul , 'Aug', cost_ago, 'Sep', cost_sep, 'Oct', cost_oct, 'Nov', cost_nov, 'Dec', cost_dec) cost_m,
           row('mm', 'cost', (sfms.cost_prev - sfms.cost), (select Min(a.cost_prev - a.cost) from sales_fact_month_store a),(select Max(a.cost_prev - a.cost) from sales_fact_month_store a)) cost_dif
      FROM sales_fact_month_store sfms,
           store
      WHERE sfms.store_id = store.store_id
        AND the_year =1998
    ORDER BY 1
    `);
    
    // ========================================================================
    // Setup metadata
    // ========================================================================
    
    var md = rs.getMetaData();
    
    md.setColumnHeader("store_name", "Store");
    md.setColumnHeader("bunit_sales", "Unit sales");
    md.setColumnHeader("store_name", "Store");
    md.setColumnHeader("sales", "Sales");
    md.setColumnHeader("cost_m", "Cost");
    md.setScale("sales", 0);
    md.setColumnSuffix("sales", " $");
    
    // ========================================================================
    // Generate report
    // ========================================================================
    var report = new Ax.rs.Report(rs);
    
    // Render to PDF
    var pdf1 = report.toPDF(config => {
    	// Avoid proportional column expansion to make report fill page
    	config.setCompact(false);
    	
    	// Hide column names so we see only group headers
    	config.setTableHideColumnNames(true);
    	// Spacing between cells
    	config.setTableBorderSpacing(5);
    	config.setHeadFontSize(9);
    
    	// Font for page (title, numbering)
    	config.setPageFontSize(8);
    	config.setPageFontFamily("Helvetica");
    
    	
    	// Font for body (font size max ...)
    	config.setBodyFontSize(8);
    	config.setBodyFontFamily("Helvetica");
    
    	// Align header as column
    	config.setTableHeadAlign("auto");
    	config.setTableHeadColor("0x555555");
    	
    	// Debug flag
    	config.setDebug(false);
    	
    	// Report title
    	config.setTitle("Sales & Cost by store");
    	config.setSubtitle("Stores Analytics of each store during the year. Analyzing sales and cost, using totals and %.");
    });
    
    
    // Write to temp file
    new Ax.io.File("/tmp/foodmart1.pdf").write(pdf1);
    
    // Return the PDF to caller
    return report;
</script>

6.5 Sysmaster database tables

The following example produces a report on sysmaster database.

Copy
var rs = Ax.db.of("sysmaster").executeQuery(`
    SELECT FIRST 500 substr(DBINFO("DBSPACE",systabnames.partnum),1,10) dbspace,
           dbsname[1,15] database,
           tabname[1,20] tabname,
           sysptnhdr.rowsize,
           sysptnhdr.nrows,
           sysptnhdr.ncols,
           sysptnhdr.nkeys no_of_indexes,
           sysptnhdr.nextns no_of_exts,
           nptotal tot_space,
           npused  tot_used,
           (nptotal - npused) * 100 / nptotal per_realused,
           ROW('pie', 'total', nptotal, 'used', npused, 'data', npdata) npdistrib,
           ROW( 'pie', 
           		'read',   sysptntab.pf_isread, 
           		'write',  sysptntab.pf_iswrite,
                'bread',  sysptntab.pf_bfcread, 
                'bwrite', sysptntab.pf_bfcwrite, 
                'dread',  sysptntab.pf_dskreads, 
                'dwrite', sysptntab.pf_dskwrites, 
                'scans',  sysptntab.pf_seqscans) io_stats
      FROM systabnames, sysptnhdr, sysptntab
     WHERE systabnames.partnum  = sysptnhdr.partnum
       AND systabnames.partnum  = sysptntab.partnum
       AND systabnames.tabname != "TBLSpace"
       AND systabnames.tabname not matches '_temp*'
       AND systabnames.tabname not matches 'HASH*'
       AND systabnames.tabname not matches 'sys*'
       AND sysptnhdr.nptotal > 0
       AND sysptnhdr.ncols != 0 -- Only tables. Discard indexes
       AND nrows > 100
     ORDER BY dbspace, database, nrows desc`);

var report = new Ax.rs.Report(rs);

report.getMetaData().setColumnLabel("per_realused",   "% Used");
report.getMetaData().setColumnColor("per_realused", "per_realused > 50 ? '#FFFFFF:#0000FF' : null");
report.getMetaData().setColumnColor("no_of_exts", "no_of_exts > 5 ? '#FFFFFF:#0000FF' : null");
report.getMetaData().setColumnSuffix("per_realused", "%");
report.getMetaData().setScale("per_realused", 2);
//report.getMetaData().setColumnTrend("tot_used").setUpIsBad();

// Collapse is required to compute percent on calculated columns of will get infinity
report.setCollapseTotals(false);

report.setHeader( 1, "Dbspace")
      .setHeader( 2, "Database")
      .setHeader( 3, "Content");

report.setGroupBy("dbspace")
      .setTotalBy("no_of_indexes",  "SUM")
      .setTotalBy("no_of_exts",  "SUM")
      .setTotalBy("tot_space",  "SUM")
      .setTotalBy("tot_used",   "SUM")
      .setTotalByExpression("per_realused",  "((tot_space - tot_used )/tot_space ) * 100");

report.setGroupBy("database")
      .setTotalBy("no_of_indexes",  "SUM")
      .setTotalBy("no_of_exts",    "SUM")
      .setTotalBy("tot_space",     "SUM")
      .setTotalBy("tot_used",      "SUM")
      .setTotalByExpression("per_realused",  "((tot_space - tot_used )/tot_space ) * 100");

report.setTotalBy("no_of_indexes", "SUM");
report.setTotalBy("no_of_exts",    "SUM");
report.setTotalBy("tot_space",     "SUM");
report.setTotalBy("tot_used",      "SUM");
report.setTotalByExpression("per_realused",  "((tot_space - tot_used )/tot_space ) * 100");

console.log(report);

return report.toPDF(config => {
	config.setTitle("Tables with more than 100 rows")
});

6.6 Amazon billing

The following example shows an Amazon style billing report. It contains several features including:

  • Cover page.
  • Add a block content before report body.
  • Map function for total group names.
  • Show totals before group instead of after group (default).

Totals are not consistent (numbers are random).
Copy
<script>

// ========================================================================
// Sample AMAZON BILLING with cover page
// Group compression with totals on top plus multiple total columns
// ========================================================================

// Sample data
var rows = [
	// T: AWS Service Charges
	// 		E: Elastic Compute Cloud
	// 		   US East (Ohio)
	// 		       EBS
	// 		K: Key Management Service
	// 		S: Simple Storage Service
	
	["T", "E",   	"US East (Ohio)", "EBS",                             "$0.10 per GB-month of General Purpose SSD (gp2) provisioned storage - US East (Ohio)" ,   "10.161 GB-Mo", 1.00, "", 2.10],
	["T", "K", 	 	"EU (Ireland)",   "AWS Key Management Service eu-west-1-KMS-Keys",           "$1 per customer managed KMS key version in EU (Ireland)", 	    "1.274 Keys",   1.10, "", 1.05],
	["T", "K", 	 	"EU (Ireland)",   "AWS Key Management Service eu-west-1-KMS-Requests",       "$0.00 per request - Monthly Global Free Tier for KMS requests",   "6 Requests",   0.10, "", 0.20],
	["T", "K", 	 	"US West (NV)",   "AWS Key Management Service eu-west-1-KMS-Requests",       "$0.01 per request - Monthly Global Free Tier for KMS requests",   "6 Requests",   0.30, "", 0.12],
	["T", "K", 	 	"US West (NV)",   "AWS Key Management Service eu-west-1-KMS-Requests",       "$0.05 per request - Extra KMS requests",                          "12 Requests",  0.40, "", 0.75],
	["T", "S", 	 	"US East (Ohio)", "Amazon Simple Storage Service USE2-TimedStorage-ByteHrs", "$0.023 per GB - first 50 TB / month of storage used",			    "0.222 GB-Mo",  0.50, "", 0.02],
	["T", "V", 	 	null,             null,				                                         "VAT to be collected",                                             "",             0.60, "", 0.53],
];

// =================================================================
// Build ResultSet with previously created rows
// =================================================================
var rs = new Ax.rs.Reader().build(rows, options => {
	options.setColumnNames([
		"type",
		"group",
		"site",
		"product",
		"item",
		"quantity",
		"charge",
		"separator",
		"lastmonth"
		]);
});

// ========================================================================
// Decorate ResultSet
// ========================================================================
var md = rs.getMetaData();

md.setScale("charge", 2);
md.setScale("lastmonth", 2);
md.setColumnLabel("item",      "");
md.setColumnLabel("quantity",  "");
md.setColumnLabel("charge",    "");
md.setColumnLabel("separator", "");
md.setColumnLabel("lastmonth", "");

md.setColumnFont("type").setFontWeight(700);
md.setColumnFont("group").setFontWeight(700);
md.setColumnFont("site").setFontWeight(700);
md.setColumnFont("group").setFontSize(12);

// Create a combo for type
md.setColumnCombo("type", {
	"T":  "AWS Service Charges",
});

md.setColumnCombo("group", {
	// Types of charged service
	"E" : "Elastic Compute Cloud",
	"K" : "Key Management Service",
	"S" : "Simple Storage Service",
	"V" : "Taxes",
});

// ========================================================================
// Generate report
// ========================================================================
var report = new Ax.rs.Report(rs);

// Group report by type
report.setGroupBy("type")
	// Use a function to map total names
	.setTotalFunction((type, group) => {
		switch (group[0]) {
			case 'T':
				switch (group[1]) {
					case 'E':
						return 'Elastic Compute Cloud';
						break;
				}
				switch (group[1]) {
					case 'K':
						return 'Key Management Service';
						break;
				}
				switch (group[1]) {
					case 'S':
						return 'Simple Storage Service';
						break;
				}
		}
		return "N/A";
	}).setTotalBy("charge", "SUM").setTotalBy("lastmonth", "SUM");


report.setGroupBy("group").setTotalBy("charge", "SUM");
report.setGroupBy("site", "product").setTotalBy("charge", "SUM").setTotalBy("lastmonth", "SUM");


// Grupo sin totales falla!!!
//report.setGroupBy("product").setTotalBy("charge", "SUM").setTotalBy("lastmonth", "SUM");
//report.setGroupBy("product");

// =================================================================
// Render to FOP
// =================================================================
var debug = false;

var fop = report.toFOP(config => {
	// Image will take all top extent height and scale horizonal
	config.setDebug(debug);
	config.getDocumentExtents().setBefore(2.0);
	config.setHeadFontSize(25);
	config.setIcon("base64:,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");

	config.setTitle("AWS Service Charges V2");
	config.setTableCaption("Details");
	config.setGroupCompression(true);
	config.setGroupTotalsOnTop(true);
	config.setGroupHeadingRepeat(false);
	config.setGroupCompressionIndentWidth(0.5);
	config.setPrintNewLineAfterGroup(true);
	config.setPrintNewLineAfterTotal(true);
	config.setPrintCompressedGroupColumnsInNewLine(true);
	
	config.setCover(cover => {
		cover.setForegroundColor("#FFFFFF");
		cover.setBackgroundColor("#303F9F");
		// By default, it will take background color if not specified
		cover.addBodyText("Billing Report").setFontSize(40);
		cover.addBodyText("November 2019").setFontSize(20).setColor("yellow");
		cover.setHeadText("Amazon Inc").setFontSize(20);
	});

	// ==============================================================================
	// Billing statement ...                        Name: Alex Morgan
	// Date printed ..                              Account Number: 8032294375040403
	// ==============================================================================
	var t1 = new Ax.fop.Table();
	t1.setSpaceBefore(20, "pt");			// has no effect on top
	t1.setBorderBottomWidth(1);
	t1.setBorderBottomColor("blue");
	t1.setBorderBottomStyle("solid");
	t1.addColumn("").setWidthPercent(60);
	t1.addColumn("");

	// ADD ROWS TO TABLE BODY
	t1.getBody().addRow(["Billing statement: 2019-10-01 to 2019-10-31", "Name: Alex Morgan" ]);
	t1.getBody().addRow(["Date printed: 2019-10-21",                    "Account Number: 8032294375040403" ]);
	
	// ==============================================================================
	// Estimated Total                                                          $4.77
	// ==============================================================================
	var t2 = new Ax.fop.Table();
	t2.setSpaceBefore(20, "pt");
	t2.setSpaceAfter(20, "pt");
	t2.setBorderBottomWidth(1);
	t2.setBorderBottomColor("gray");
	t2.setBorderBottomStyle("solid");
	t2.addColumn("");
	t2.addColumn("");

	// ADD ROWS TO TABLE BODY
	t2.getBody().addRow([
		new Ax.fop.Text("Estimated Total").setFontSize(16).setFontWeight("bold").setColor("blue"), 
		new Ax.fop.Text("$4.77").setTextAlign("end")
	]);
	
	// ==============================================================================
	// Taxes
	// Vat to be collected                                                      $0.44
	// ==============================================================================
	var t3 = new Ax.fop.Table();
	t3.setFontSize(30);
	t3.setSpaceBefore(20, "pt");
	t3.addColumn("").setWidthPercent(60);
	t3.addColumn("").setAlign("right");

	// ADD ROWS TO TABLE BODY
	t3.getBody().addRow([new Ax.fop.Text("Taxes").setFontSize(16).setFontWeight("bold").setColor("gray").setProperty("padding-bottom", 3, "pt"), "" ])
		.setProperty("border-bottom-style", "solid")
		.setProperty("border-bottom-color", "gray")
	;
	t3.getBody().addRow([
		new Ax.fop.Text("Vat to be collected").setFontSize(16).setFontWeight("bold"), 
		new Ax.fop.Text("<b>$0.44</b>").setTextAlign("end") 
	]);

	// ==============================================================================
	// Inject blocks to report
	// ==============================================================================
	config.addBeforeObject(t1);
	config.addBeforeObject(t2);
	config.addBeforeObject(new Ax.fop.Text("Your invoiced total will be displayed once is issued.").setProperty("space-after", 20, "pt"));
	config.addAfterObject(t3);

});

new Ax.io.File("/tmp/amazon_billing2.fop").write(fop);
new Ax.io.File("/tmp/amazon_billing2.pdf").write(report.toPDF(fop));

// =================================================================
// Return the PDF to caller
// =================================================================
return report;
</script>