Exporting Invoices to XML

Documentation •
In this article

This page explains how to create a Banana Accounting Javascript Extension, that creates the invoice in a specific format. The explanation takes as example the extension Fattura Elettronica , which exports electronic invoices according to Italian B2B standards in XML format.

The Extension has the following steps:

  • Use the Banana API, to retrieve the list invoicesCustomers in Json format.
  • InvoiceCustomers includes the Invoice objects.
  • For the Italian export it is necessary to have also information regarding the Tax code. This supplementary information is calculated by the VAT Extension. That is why the Extensions includes different files.
  • The user is given the ability to enter data. So a dialog is displayed with the information required.
    See Extension documentation in Italian.
  • The Extension can also have parameters that can be set with the settings dialog.
  • Once you have all the information, you need to convert the data in the format that you need. In the Italian case, the invoice is in the XML format that follows the specific requirements.
  • At the end, the Extension calls a function that prompts the user to specify the file where he wants to save the XML data.
  • The Extension for Italy also includes a possibility to have a preview of the invoice and export it in PDF.

References:

 

Attributes list

Each extension must declare some attributes in order to run in Banana environment. For more info see Extension's Attributes page

  • @id: this is the script ID. It will be used by getScriptSettings() and setScriptSettings() to save params values for this script
  • @includejs: ch.banana.it.invoice.it05.js this is the invoice template used to print out the invoice in preview and PDF format. 
  • @includejs: ch.banana.script.italy_vat_2017.journal.js this file contains the class Journal, used to retrieve transactions and customer info such as adresses. Other includes are dependencies of ch.banana.script.italy_vat_2017.journal.js

// @id = ch.banana.it.efattura.b2b
// @api = 1.0
// @pubdate = 2019-04-25
// @publisher = Banana.ch SA
// @description = [BETA] Fattura elettronica (XML, PDF)...
// @description.it = [BETA] Fattura elettronica (XML, PDF)...
// @doctype = *
// @task = app.command
// @inputdatasource = none
// @timeout = -1
// @includejs = ch.banana.it.invoice.it05.js
// @includejs = ch.banana.script.italy_vat_2017.errors.js
// @includejs = ch.banana.script.italy_vat_2017.journal.js
// @includejs = ch.banana.script.italy_vat.daticontribuente.js
// @includejs = ch.banana.script.italy_vat_2017.xml.js

Function exec()

The exec() function is the main function, which is called when the extension is executed. This function does the following:

  • loads user parameters
  • creates an EFattura-class object
  • the EFattura object loads data according to user parameters
  • the EFattura object  issues invoices in xml format or in print preview


function exec(inData, options) {
...
   // creates the EFattura object that contains the logic to load the invoices 
   // and transform them into the required formats
   var eFattura = new EFattura(Banana.document);
   if (!eFattura.verifyBananaVersion())
      return "@Cancel";

   // user params can be retrieved using the method Banana.document.getScriptSettings()
   // they are saved in the system table syskey using the method Banana.document.setScriptSettings()
   var param = {};
...
   param = JSON.parse(Banana.document.getScriptSettings());
...   
   eFattura.setParam(param);
   //loadData() retrieves data from table invoicesCustomers() and returns an array of json invoices grouped by customer
   var jsonCustomerList = eFattura.loadData();
...      
   //output the data according to user param (xml or pdf)
   if (eFattura.param.output == 0) {
      var docs = [];
      var styles = [];
      for (var i in jsonCustomerList) {
         var jsonInvoices = jsonCustomerList[i];
         for (var j = 0; j < jsonInvoices.length; j++) {
            var jsonInvoice = jsonInvoices[j];
            if (jsonInvoice.customer_info) {
               var repDocObj = Banana.Report.newReport('');
               var repStyleObj = Banana.Report.newStyleSheet();
               eFattura.createReport(jsonInvoice, repDocObj, repStyleObj);
               docs.push(repDocObj);
               styles.push(repStyleObj);
            }
         }
      }
      if (docs.length) {
         Banana.Report.preview("", docs, styles);
      }
   }
   else {
      //output xml
      for (var i in jsonCustomerList) {
         var jsonInvoices = jsonCustomerList[i];
         var xmlDocument = Banana.Xml.newDocument("root");
         var output = eFattura.createXml(jsonInvoices, xmlDocument, true);
         if (output != "@Cancel") {
            var xslt = "";
            var outputStyled = output.slice(0, 39) + xslt + output.slice(39);
            eFattura.saveFile(outputStyled);
         }
      }
   }
}

 

Class EFattura()

This class contains all the logic of the application in order to print out the invoices. The costructor initializes some variables.



function EFattura(banDocument) {
   this.banDocument = banDocument;
   ...
   this.name = "Banana Accounting EFattura";
   this.version = "V1.0";
   this.helpId = "ch.banana.it.efattura.b2b.js";
   this.errorList = [];

   /* errors id*/
   this.ID_ERR_ACCOUNTING_TYPE_NOTVALID = "ID_ERR_ACCOUNTING_TYPE_NOTVALID";
   ...

   this.initParam();
   this.initNamespaces();
   this.initSchemarefs();
}

EFattura.initParam()

This method initializes the class parameters, which will resume user-set values using the setParam() method and the settingsDialog() function.



EFattura.prototype.initParam = function () {
   this.param = {};
   /*output format 0=pdf, 1=xml*/
   this.param.output = 0;
   /*selection 0=single invoice, 1=single customer 2=all*/
   this.param.selection = 0;
   /*invoice number*/
   this.param.selection_invoice = '';
   /*customer number*/
   this.param.selection_customer = '';
   
   /* periodSelected 0=none, 1=1.Q, 2=2.Q, 3=3Q, 4=4Q, 10=1.S, 12=2.S, 30=Year */
   this.param.periodAll = true;
   this.param.periodSelected = 1;
   this.param.periodStartDate = '';
   this.param.periodEndDate = '';
   
   /*params for xml format*/
   this.param.xml = {};
   this.param.xml.progressive = '1';
   this.param.xml.open_file = false;
   this.param.xml.destination_folder = '';

   /*params for pdf format*/
   this.param.report = {};
   this.param.report.print_header = true;
   this.param.report.print_logo = true;
   this.param.report.print_quantity = false;
   this.param.report.font_family = '';
   this.param.report.color_1 = '#337ab7';
   this.param.report.color_2 = '#ffffff';
   this.param.report.header_row_1 = '';
   this.param.report.header_row_2 = '';
   this.param.report.header_row_3 = '';
   this.param.report.header_row_4 = '';
   this.param.report.header_row_5 = '';
   this.param.report.footer = '';
}

EFattura.loadData()

The loadData() declares the following objects:

  • this.journal: this is an object available from the script Iva Italia. The journal contains all the accounting transactions and the list of customers, including their addresses.
    In order to use this object you need to include the file ch.banana.script.italy_vat_2017.journal.js using the statement @includejs = ch.banana.script.italy_vat_2017.journal.js in the attribute list of the script.
  • this.journalInvoices: this can be loaded directly from Banana.document.invoicesCustomers and contains all invoices.


EFattura.prototype.loadData = function () {

   //loads transactions, the journal is declared in script /iva/2017/ch.banana.script.italy_vat_2017.journal.js
   //the journal is used for retrieving the following data: "IT_TipoDoc" (type of document), "IT_Natura" (nature of the transaction)
   //see @includes at the beginning of this script
   if (!this.journal) {
      this.journal = new Journal(this.banDocument);
      this.journal.excludeVatTransactions = true;
      this.journal.load();
   }
   //loads list of invoices using the Banana.document object
   if (!this.journalInvoices) {
      this.journalInvoices = this.banDocument.invoicesCustomers();
   }

   var jsonInvoiceList = [];
   //if invoice filter is defined and invoice number is empty, no invoice is returned
   if (this.param.selection == 0 && this.param.selection_invoice.length <= 0)
      return jsonInvoiceList;
   //if customer filter is defined and customer number is empty, no invoice is returned
   if (this.param.selection == 1 && this.param.selection_customer.length <= 0)
      return jsonInvoiceList;
   //if tax payer data is not defined, no invoiced is returned
   if (!this.initDatiContribuente())
      return jsonInvoiceList;

   //set period of the transactions to be loaded
   var periodAll = this.param.periodAll;
   var startDate = this.param.periodStartDate;
   var endDate = this.param.periodEndDate;
  
   for (var i = 0; i < this.journalInvoices.rowCount; i++) {
      var tRow = this.journalInvoices.row(i);
      //the column 'ObjectJSonData', in the table journalInvoices, contains the invoice object
      if (tRow.value('ObjectJSonData') && tRow.value('ObjectType') === 'InvoiceDocument') {
         var jsonData = {};
         jsonData = JSON.parse(tRow.value('ObjectJSonData'));
         var addInvoice = true;
         if (parseInt(this.param.selection) === 0 && jsonData.InvoiceDocument.document_info.number !== this.param.selection_invoice) {
            addInvoice = false;
         }
         if (parseInt(this.param.selection) === 1 && jsonData.InvoiceDocument.customer_info.number !== this.param.selection_customer) {
            addInvoice = false;
         }
         if (addInvoice && !periodAll) {
            if (jsonData.InvoiceDocument.document_info.date < startDate || jsonData.InvoiceDocument.document_info.date > endDate) {
               addInvoice = false;
            }
         }
         if (addInvoice) {
            jsonInvoiceList.push(jsonData.InvoiceDocument);
         }
      }
   }

   if (jsonInvoiceList.length<=0) {
      var msg = this.getErrorMessage(this.ID_ERR_NOINVOICE);
      this.addMessage(msg, this.ID_ERR_NOINVOICE);
   }

   //data is grouped by customer because the xml file can contain only one customer per file
   //if many customers are printed, these will be splitted into many files
   var jsonCustomerList = {};
   for (var i = 0; i < jsonInvoiceList.length; i++) {
      var jsonInvoice = jsonInvoiceList[i];
      if (jsonInvoice.customer_info) {
         var accountId = jsonInvoice.customer_info.number;
         if (!jsonCustomerList[accountId])
            jsonCustomerList[accountId] = [];
         jsonCustomerList[accountId].push(jsonInvoice);
      }
   }
   
   return jsonCustomerList;
}

EFattura.createReport(jsonInvoice, report, stylesheet)

The createReport() generates the preview of the invoice. If you wish to change the layout of the invoice you can modify or replace the script ch.banana.it.invoice.it05.js
@jsonInvoice: this is the json invoice object which contains all data to print out
@report: this is the Banana.Report.ReportElement object which permits you to preview and print out the data
@stylesheet: this is the Banana.Report.ReportStyleSheet object which contains all css information for printing out the report.



   //print the single invoice to the object report, 
   //the methods printInvoice and setInvoiceStyle are declared in the file ch.banana.it.invoice.it05.js
   if (jsonInvoice && jsonInvoice.customer_info) {
      printInvoice(jsonInvoice, report, stylesheet, this.param.report);
...
      setInvoiceStyle(report, stylesheet, this.param.report);
      stylesheet.addStyle("@page").setAttribute("margin", "0");
   }

EFattura.createXml(jsonInvoiceList, xmlDocument, indent)

The createXml() generates the xml code of the invoice. The XML file will contains one or more invoices of a single customer.
@jsonInvoiceList: this is an array with the list of invoices which belong to a single customer
@xmlDocument: this is a Banana.Xml.XmlElement which contains all XML data
@indent: if true the string xml will be indented and formatted with spaces



if (!xmlDocument || jsonInvoiceList.length<=0)
      return "@Cancel";
      
   var nodeRoot = this.createXmlHeader(jsonInvoiceList[0], xmlDocument);
   if (!nodeRoot || this.isEmpty(nodeRoot))
      return "@Cancel";
   for (var i = 0; i < jsonInvoiceList.length; i++) {
      this.createXmlBody(jsonInvoiceList[i], nodeRoot);
   }
   return Banana.Xml.save(xmlDocument, indent);

 

Tell us how we can help you better
If the information on this page is not what you're looking for, is not clear enough, or is not up-to-date, let us know.

Share this article: Twitter | Facebook | LinkedIn | Email