Tutorial Extensions

Embedded Extensions are extensions written in JavaScript and saved directly in the Banana Accounting file. For more information visit:

Embedded extensions examples

The following pages contain code examples for most of the Javascript APIs:

Embedded Extensions examples Tutorial 1

'Hello world' report

Hello World


// Banana Accounting Extension Javascript
// @api = 1.0
// @id = ch.banana.uni.app.tutorialhelloworld
// @description = Tutorial: Hello world
// @task = app.command
// @doctype = nodocument
// @publisher = Banana.ch SA
// @pubdate = 2019-01-25
// @inputdatasource = none
// @timeout = -1

function exec() {
    //Create the report
    var report = Banana.Report.newReport('Report title');

    //Add a paragraph with some text
    report.addParagraph('Hello World!!!');

    //Print the report
    var stylesheet = Banana.Report.newStyleSheet();
    Banana.Report.preview(report, stylesheet);

}

Creating Reports

Add several paragraphs


// Banana Accounting Extension Javascript
// @api = 1.0
// @id = ch.banana.uni.app.tutorialaddseveralparagraphs
// @description = Tutorial: Add several paragraphs
// @task = app.command
// @doctype = nodocument
// @publisher = Banana.ch SA
// @pubdate = 2019-01-25
// @inputdatasource = none
// @timeout = -1

function exec() {
    //Create the report
    var report = Banana.Report.newReport('Report title');

    //Add several paragraphs with some text
    report.addParagraph('This');
    report.addParagraph('is');
    report.addParagraph('a');
    report.addParagraph('text');
    report.addParagraph(' '); //Empty paragraph
    report.addParagraph('printed');
    report.addParagraph('on');
    report.addParagraph('several');
    report.addParagraph('paragraphs.');

    //Print the report
    var stylesheet = Banana.Report.newStyleSheet();
    Banana.Report.preview(report, stylesheet);

}

Add the header


// Banana Accounting Extension Javascript
// @api = 1.0
// @id = ch.banana.uni.app.tutorialheaderstyles
// @description = Tutorial: Add the header with some styles
// @task = app.command
// @doctype = nodocument
// @publisher = Banana.ch SA
// @pubdate = 2019-01-25
// @inputdatasource = none
// @timeout = -1

function exec() {
    //Create the report
    var report = Banana.Report.newReport('Report title');

    //Add the header
    report.getHeader().addClass('header');
    report.getHeader().addText('This is the header text aligned to the right with a bottom border', 'header');

    //Add some style
    var stylesheet = Banana.Report.newStyleSheet();

    //Header style
    style = stylesheet.addStyle(".header");
    style.setAttribute("text-align", "right");
    style.setAttribute("border-bottom", "thin solid black");

    //Print the report
    Banana.Report.preview(report, stylesheet);

}

Add the footer


// Banana Accounting Extension Javascript
// @api = 1.0
// @id = ch.banana.uni.app.tutorialfooterstyles
// @description = Tutorial: Add the footer with some styles
// @task = app.command
// @doctype = nodocument
// @publisher = Banana.ch SA
// @pubdate = 2019-01-25
// @inputdatasource = none
// @timeout = -1

function exec() {
    //Create the report
    var report = Banana.Report.newReport('Report title');

    //Add the footer with page numbers
    report.getFooter().addClass('footer');
    report.getFooter().addText('Banana Accounting, v. ' + Banana.document.info('Base', 'ProgramVersion'));
    report.getFooter().addText(' - Page' + ' ');
    report.getFooter().addFieldPageNr();

    //Add some style
    var stylesheet = Banana.Report.newStyleSheet();

    //Footer style
    style = stylesheet.addStyle(".footer");
    style.setAttribute("text-align", "right");
    style.setAttribute("font-size", "8px");
    style.setAttribute("font-family", "Courier New");

    //Print the report
    Banana.Report.preview(report, stylesheet);

}

Add an image


// Banana Accounting Extension Javascript
// @api = 1.0
// @id = ch.banana.uni.app.tutorialimage
// @description = Tutorial: Add an image
// @task = app.command
// @doctype = nodocument
// @publisher = Banana.ch SA
// @pubdate = 2019-01-25
// @inputdatasource = none
// @timeout = -1

function exec() {
    //Create the report
    var report = Banana.Report.newReport('Report title');

    //Add an image stored in the document tabel
    report.addImage("documents:logo");

    //Add a png image passing the content of the image
    report.addImage("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAARqklEQVRogb2ZeVRT1/bHT9+TgiKTIiiCgAMig0wm997MzKKiOHR4WgdAUAhgRQWnIn1Pa21r+161Uu3wfG0d2tfqs1a7bJkHAyGQhJAACSQEUQZFxalWge/vj6AFoVZb+9trfde6f9yz92fvs885dyDkD1i87QTXLRMnLXnDbfLrh6d7f3TSa+aRr718P8+Z6rlv40SX9I3OzqIkm0l2fyTGMzURISPEjhNDd7tNffOIp09hgV9QmyyIgpJFQzVAilkUpIHs3ryZAReOePrk73Bx35vu5DLfnxDb3xPX3cHScXMc68cXhFMOEkKe+z0+nlvrMGHhN14z86RB7BtNNA8GhocGigs1m4GKRaOmXyoWjVoWDQ2bgZbiQk/zYKB5UASx757w9lVmT3LbHGpp6fg0wbdlLsmQ/i8ZcZEe3xFCnn8qcl9CJu+b5nVEw2Z6dRQXdRQHKhYNZb8egCsfIxWLRh2bg0aai0aaix/8ArTZLm5rRYRYPAnDgf0b/qEq3ILEeR650+2J1RPDz7a0jDwvCtdeilwAFZuGchb1EHig1P3VbqS5aKK50FJc1LGZYe9VsmhoKS50FBcHp3l+HWRuPuW3OJYtC32p7nwW1r/kK2FPtBr7RPDzRlotrIl56Xrr4mVQzKJQMwBe2Q+tp3lopLmQBrHvnZ0Z0Hp8hm/tMU8f1SkfP32J/6xb9RQHzQwfWor7cNYejK9lMbjA8HHa108VYWMT9DiWyZOdfasKttzLSmDVhgfYO/0mfAAZIahZvLTrwiuroQgIGgKv66/24ele8liH8Vm+FhZh5oR4EEKcCCHjCSHuzmZmlMDKNm67i9uxQv/AzhaGDw2bGdR2NSwaLQwf53wD6/2eH+39GKRRX3+WqHx/S4hR5D/e7bHwLoQ4nRGE1XQkp0MROBS+ieaiLIDVucjOIcPSBPubNs1sJLXdxe3LBjbTV8fmDFk7Fxg+jnh6l1oR8qvtsWNz5Edfvhd9zdyceDw22GbnSW91pWWiliOEMoiFGhYzCL7QL6jNz3zUvCcBf8TMl45z3K5hMz31FGfITFzk8JHuNOnNXxscM98n6btPF/WNtTZj/WoEJ0I8pXNi2o2Ll5laZ0AADZtB7SzqZ2qk9cu/A/6hxTlO2K2neVAPWOTK/l1KHsS+5W1m6T/cOH9/p3DJ6eVgeY195Vedrxo3PrNtyXLUMALUBLEHBWhheNjo7PIxIeQvfyQBQohNzrTpxQaGN2SHMjJ8ZE9yPTTcoJleE/g6aTLES733/5pjs8PefgXGsHmDtksli0YDxUV5AOuqi9ljpu8pjBo5ekkdi+lTPZKAjuLi+5l+Bu+RIyc9OkbAnbaorW49ju2bU0fIMGeBDSHuRYGsK3U0b0gCRoaHf03x+J4QMvJZJEAIsf3ay1eto7iD1kIdmwMNi7kXZmOzeODN0RFunvvfij6trUyCqiC+j/J1XDDEI9vKil89i+6pfaQqtSwGzQwPseMcdz4jeEIIITsmTf53C8MblEBtf7HiHJyy+28zz8pgZ6tLVrTpqxOhKotFY3ka/rlZcIY8Wsxo27ExahY9ZFo1bAZaNqcvwsp25bNMINZhfJZxmHXQwvCwzWXSvwkhZOlS1rLeG6/hWlMimhWJuKRJgL5yHerOxvUI/Ma8NMhhjM2YxWoW3adiD3ZYR3FQy2bu86xsFz7LBJY6OGwcdiFzeHjTbcqXhJC/fnUs/RRgQNeFAzBUx6NRKkaDJA2tFa8ia7X/UTJwQxFZW89Wsqi+GjYN5QBpKA7qKKY32MbmxWeZQMJ4p6xmDh8K1uB4Rg4Pu1wnHyOE2KgVnzQAt3DnaimaqlejUZqM+vOpuCDLwIdZYaVzfW1+eddwMzPzlwSxbteyGSgHSMVm0MTwsHiM/fpnmcAOV7dPjBw+FI/EM3L4SB/vtN/dw8Hvdnf+T333jbjZlYvGqtVokiWjoUwMoywT3+a8qHOfYOE60KfdCW8/tZbmDus023XyF+R3vkwMY6O+9vFV6hjeoFgqNoNmjgALLcds/Ft8yGr0yfHzrWp0d5yEVvZLAgbpJpQcjWsfa0U8B3nNdHH/2MgR9DvlPFQDzUOB/6wL4wiZ+izoeZbWs2vYnPs1A2Io2Ryo2Vxo2Jz7IgvLVZ9/ueNL9JbgTncRrl78HDp5KvSVSWgoE6OpYhOkJ5KuudqPChzkOGiUVYyGzelTU1wo2JxBMnD4SHNy3v0M+J87MNXztJ7hD4lRR/OQ7+PXGjXDLamp5bih59ZJ3L5yGh2GD9CkWAe9NAl1ZWI0VWSi6lRq9+Txo4YcrDaHPGYUGzlCyB9xXktxoWAxt/jW1pF/hH6Bnf0aLcPrqR2mSPU0D5+6T5FuS39pX1/Pib7blz9F96XP0Nb4NgyKZDRJk1BfJoa+YjMqT6Zec7U3CxwSgBlt80INi3tXQ/NNSVAmydkcNDAC5AeyjNzRtsG/B36ds+viilnUlbpHfD/wr5lF9+308DxdWv6mvPdODq61vo8u4z60N+5Cs3wtGqVi1Jcmw1C5HcXH1rQNWQP99tf1Tq6HWngiKCgOqtkm53K26VrLCFAaSF3Ndp2aHkGI5ZOAv2BnZyOeMHFr+Sz6Rj0jGOTzgZQUF2U+ATdz1sYU/nwnp+fGxSxcad6Fy4Z30K59DYaqNWisEENTnIKW6myczlmqtRtJhjwzPTCHfR5eeaYkuKimuJD3q5riQkPzUcfw8a1vYNWa8S4bY2zsgzImTnSOsbW1DSJkFJfYW71q7zohzmGi37qJLuu/nRlUreUIoGb4JvgB/h6ols3F2SBWp6Ziz5Wfr/0dl5vS0aHbgi7Du2hVr4e+ai105WJoilJxUb4TOVlRpbyB58CjZkOI+wfTZhQ284KhoHmoYnOHSMPw0cARopLN3DnpG6D5yNOn8AMPr7M5Hj4/Hvf2V0hYzI16jgAahj/s+Aeqprio8p+Fih1r7t/sfhtd+tXoaEhCh3Yruoz/hFGRCH1lMhokKVAXpaGtehe2rQ46Sgj562OnfhQhTttcJx+t5whRzxFCTvEgo3io6teD6xqaj3qOADqu6KHqOULU0PxB9w0rmofqIAoSYQQMVbvR3ZmMTm0cWmvjcfXCAXTotkFftRqNFWLUl6airiQdxrLtWCyatPlJ2pcQQswj7camnJwZ1NjIC4aOGwwFI4CM4j+xqujHiOKj0p9G+evxuHp1O7oMsbikiUW7dhu62w6jRRkPfZXY1D7FKWgsz4T8VOJtbxfLp9sN7QmZ/vK4ibuO+gbWKBkhGnnBqOeKUMMIUE0LUEUPBq6m+VAyAqgemywPVUE8lIWHQit7Bd2X4tDZEI+L6gTc6Pgv2rWZaJYnQl+ZgoayVKgLU2GUZuLIG+FqQshTfd17aCMJcQm0tF6W4ux28CNP38qzgezLEhanV0bzIWcEkPcnVM7m9p7xZ3UenxmglVK8niqaD9mjYvNREciD5MO56OqMR1fTalxUr8C11kO4fvEQjIo4GKpN1a8rSUV9aRqai5IhXuzxq6+VT2tj7QiZOdPScnaonf2yhfaOCYvsJ6yJsB27kra2i3I2M6N2T/U8V8MRDYWn+ZAF8FGQIERL8wpcNybiknoFLut34k7XWbTWrkVz9RroZWI0nDct3vrSNEiPLLo3ZbzF7zqHnspEhIx4Z5rXQQ03GJW0AJW08KGktBCyQCEKw3molbyI7rY1aK9fhQ7tJty9XoxO3WY0y+NgqBI/XLy1Ba+iMS8Fe5IDvieEmP+p8N6EPL9ryoycOl4oZIwIFbRwkCpZQpSweZAcj0ZXZwI6G1aio+FV3L1xHl3Ne2BUrIKhWmx6gSlLgbowFXWFG1D91d96/KdYxfyp8FGEmO+c5nVQxQuFjAlGOS38RYwQFZQQZYF8FO6JQPuleFzWLUenbgPudpfjWut+tChXolmejCZZMnQSMTRFaVDlvwpjcTr+vsbvNHl2HxaG2rIxY6zf8/Q9XMMNRQUjgoQWopwWmcSIUEGLIAkUIm9DCJr1K3ClcSWu6LNw72YVrl14Hy3KlTAqkqDvh68vTYUqPxW60q0o+njRdZdxI/jDxX0+lHIKf2sXd/2Bfy7J3LYxKlokmuqcnf10H7CW2kyy+8DL/ysVPxwVnGBIGNEgldMiSAJE+CFBBK3mZXQZYnHjUg7u3Vaiy7jHVPkH8OX98HliaIq3Qv9jKl4Mcc4eNrD7OAtB9alVt291bMLtLjHuXPtX76XG9zvPffvqt7v/sSDzhYX+4W9kz/HYtXXRhGVRlHVq1FTz7Gzv51OjpppHRDhaxsYGjIsMcPfbN93vnJIfDgk3BGWcYJzvVxknGOeZEJwPDMa5ZVzUyhaj+1IG7l7/EXe7S9GhzYRRsaofXgxdeTIaylJRWyCGumgLLkpewxtr/L4nw33MIoSQyY4WYdqyrXd/6vwPWlSJuNnxFnC/EOgrBHpOo+fGERgb97VJirOVZ05uyDt9OPZU7pH4kycOLf/f5x+/fO7w1nnSs8GhlxWiuShhglHCCUEJJ/ShyjihKA0S4OxSCmpZLH66/gXu36pCd9t/0KpOgFERb+r5SlPlG8rSoC4QQ120GZdku/HZ6wKNvRWZ/rjZN0+J5++53/YumipSoK9KwBXjFty+9jnu3c5H790yoCcP6DuDnzoO4IJsM4xVG9GqWQ/VwZdQvWABVC+vRSk/EiVMCEq4oQ9VyoSiOFCIH1Yvgl73IXruSXHr8gm0azP6+z0RhuoUNEqToS1P6e95MdTF29Au34tT70YZPV1Hcp6khR0/fndh8VXtdjTLE9CsiEVrbTwu6zfi9uW9uHfzY1wx7EWjJAN6+Qaoy1OQvz4SJaJ5UIq3oixyEYpoEUo4YabKc0NRQoeiMCgEuevXwWg8gTvXvkCnLgMtylg0K1ajuToJTTIxdBWmg0pTnAJVnhh15/+OdvlenHwnomWK44iQJ4EnhBBiY2Mxef9bL35/VZcNvTQZBnkimmXJ0FfFo/CbefjukzDkn4xG4fEY5C4NhmTOK1C/9ibK5r6AQkqIYl4EinkRKOJFoISJQD4VjLMZC1BfuwntDSloUayCUZGIZnky9LJkNEqToZOYqq4uSIYqfx0aZXvRLtuNT7fza2a4jaSfGP6XJGzs0pPn7rug3NXbpsqEMi8B/z0QhY9283DwHS4OpbHxrYAH2fI01GbtRknEAhRQIhTyI1HIj0ARPwJFdATO8cJwZvd8aGoS0KpeY3okqBKbwCvE0JanoKEsBZoisanqpdtxqeYA9AWbsCPO56S91W/8hfkN+0uoyH/l15/E6k7kROLT9wQ4uIePL1byURARA6V4G2RJ6cjnhyOPE4IC4WzkCyNRKJiNQjoc380Ox7mPlqBekYQWZQr0laYTVVeeAu15U8U1RWKo8sWoLcpAi/w9dCrexY8HF3fOZ8ZtIYSM/iPwD83amkybO9v17Q83cW+cig5BSfgSlC9PQlH0C8hlQpArjESuKAq5oigU8KOQx4nE/16ZjbxvlkNXnYImqQlYez4F9WVpqCtJhbpADFV+CjTFmdDL3ka7/B1UfbPmTsYyz/+6Ooxgngn4APvLionTXssVRPWeF0ajIGw+ckWz8QM3DLmiKPwoikK+cA4KuFE4I4zENxkLUPpDAhqk69BQmgZNSRo0RWlQF6ZBXbgO9SUb0FS+A63Vb6G1fCdKvlh567V4v69mTDSfS/6MhzMns1GBn7GFuh/4s2/mB8/plYRGQxo2H9Lw+SgPi4Y0dD4kYdHIXRGDwv/EoqFqA1qqMmEo3wR9+UYYJBthLM9AS/lmGCXboCvYgtLjq7tytgZX/i3c5Q2HMYQhhJg9c/ABZjbZ3Hqaj41NBGU7Lnaek3N2grvHZ1leAbmHAjmad3wp2eJprkfXLvM9dWBnhOTY3ui6E/sXNZ/+cOGFU/tjjEffnqM9mB1W+Y9U7vcJC70+DJk1LsV9wgg+ecyv1P8Pe86eECsLQlyJ6R+xmRshFlZWxN7CgriPHk1m2I0mPmNGE29zczKFEOLoRMioPxPo/wBFxyrdFyIbZQAAAABJRU5ErkJggg==");

    //Add a svg image passing the content of image
    report.addImage('data:image/svg;utf8,<svg version="1.2" baseProfile="tiny" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:a="http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/" x="0px" y="0px" width="337px" height="82px" viewBox="0 0 337 82" xml:space="preserve"><defs></defs><path fill="#234571" d="M16.737,0c5.755,0,9.592,3.309,9.592,8.151c0,3.453-2.973,6.38-5.803,6.906c3.405,0.191,8.105,3.405,8.105,8.632c0,5.947-4.172,9.881-10.839,9.881H4.651V0H16.737z M16.209,14.1c4.412,0,7.337-2.35,7.337-5.851s-2.925-5.755-7.337-5.755H7.48v11.604L16.209,14.1L16.209,14.1z M17.648,31.078c4.556,0,8.104-2.734,8.104-7.242c0-4.221-3.836-7.29-8.393-7.29H7.48v14.532H17.648z"/><path fill="#234571" d="M59.56,24.076H39.514l-4.844,9.495h-3.069L48.77,0.001h1.535l17.169,33.57h-3.069L59.56,24.076z M58.218,21.438L49.873,4.989l-0.336-0.863l-0.335,0.863l-8.345,16.448L58.218,21.438L58.218,21.438z"/><path fill="#234571" d="M100.178,0v33.568H97.54L76.678,5.801l-0.671-1.007l0.096,1.391v27.387h-2.83V0.001h2.638l20.91,27.672l0.624,1.055l-0.096-1.438V0H100.178z"/><path fill="#234571" d="M132.833,24.076h-20.047l-4.844,9.495h-3.069l17.169-33.57h1.535l17.168,33.57h-3.068L132.833,24.076z M131.492,21.438L123.145,4.99l-0.336-0.863l-0.334,0.863l-8.346,16.448H131.492z"/><path fill="#234571" d="M173.452,0v33.568h-2.638L149.954,5.8l-0.672-1.007l0.096,1.391v27.387h-2.83V0.001h2.64l20.91,27.672l0.623,1.055l-0.097-1.438V0H173.452z"/><path fill="#234571" d="M206.105,24.076h-20.047l-4.844,9.495h-3.068l17.168-33.57h1.535l17.17,33.57h-3.067L206.105,24.076z M204.762,21.438L196.416,4.99l-0.336-0.863l-0.336,0.863L187.4,21.438H204.762z"/><path fill="#234571" d="M26.376,75.723H10.934l-1.966,4.316H0l15.826-33.57h5.659l15.826,33.57h-8.968L26.376,75.723z M22.972,68.193l-3.837-8.486l-0.479-1.344l-0.479,1.344l-3.837,8.486H22.972z"/><path fill="#234571" d="M61.334,56.875c-1.535-1.918-4.412-3.117-7.05-3.117c-5.563,0-9.016,3.98-9.016,9.496c0,5.803,3.789,9.398,9.016,9.398c3.213,0,6.283-1.633,7.673-3.934l8.153,2.109c-2.781,5.945-8.776,9.88-15.826,9.88c-9.64,0-17.409-7.338-17.409-17.457s7.769-17.457,17.409-17.457c6.618,0,12.277,3.55,15.251,8.92L61.334,56.875z"/><path fill="#234571" d="M97.012,56.875c-1.535-1.918-4.412-3.117-7.05-3.117c-5.563,0-9.016,3.98-9.016,9.496c0,5.803,3.789,9.398,9.016,9.398c3.213,0,6.283-1.633,7.673-3.934l8.153,2.109c-2.781,5.945-8.776,9.88-15.826,9.88c-9.64,0-17.409-7.338-17.409-17.457s7.769-17.457,17.409-17.457c6.618,0,12.277,3.55,15.251,8.92L97.012,56.875z"/><path fill="#234571" d="M125.64,45.797c9.642,0,17.457,7.338,17.457,17.457s-7.815,17.457-17.457,17.457c-9.64,0-17.409-7.338-17.409-17.457S116,45.797,125.64,45.797z M125.64,72.749c5.467,0,9.064-3.884,9.064-9.495c0-5.658-3.598-9.496-9.064-9.496c-5.418,0-9.016,3.838-9.016,9.496C116.623,68.865,120.22,72.749,125.64,72.749z"/><path fill="#234571" d="M178.102,46.469v18.655c0,10.069-6.232,15.587-15.104,15.587c-8.922,0-15.106-5.518-15.106-15.587V46.469h8.057v18.943c0,4.796,2.637,7.77,7.051,7.77c4.412,0,7.049-2.974,7.049-7.77V46.469H178.102z"/><path fill="#234571" d="M214.645,46.469v33.57h-7.58l-13.905-17.938l-0.671-1.248l0.097,1.248v17.938h-8.06v-33.57h7.576l13.908,17.696l0.672,1.247l-0.098-1.247V46.469H214.645z"/><path fill="#234571" d="M247.204,46.469v7.529h-10.218v26.041h-8.059v-26.04h-10.068v-7.53H247.204L247.204,46.469z"/><path fill="#234571" d="M251.418,80.04V46.469h8.061v33.57L251.418,80.04L251.418,80.04z"/><path fill="#234571" d="M297.077,46.469v33.57h-7.578L275.59,62.102l-0.67-1.248l0.096,1.248v17.938h-8.057v-33.57h7.576l13.908,17.696l0.672,1.247l-0.098-1.247V46.469H297.077z"/><path fill="#234571" d="M329.008,77.307c-1.965,2.109-5.317,3.404-9.106,3.404c-9.644,0-17.408-7.338-17.408-17.457s7.77-17.457,17.408-17.457c6.52,0,12.229,3.453,15.25,8.92l-8.199,2.158c-1.584-1.918-4.412-3.117-7.051-3.117c-5.562,0-9.021,3.98-9.021,9.496c0,5.896,3.98,9.592,9.496,9.592c5.371,0,7.725-2.781,8.684-5.273v-0.051h-8.823v-6.474h15.682v18.991h-6.906v-2.732H329.008L329.008,77.307z"/></svg>');

    //Print the report
    var stylesheet = Banana.Report.newStyleSheet();
    stylesheet.addStyle("img", "margin-bottom:4em");
    Banana.Report.preview(report, stylesheet);

}

Add page break


// Banana Accounting Extension Javascript
// @api = 1.0
// @id = ch.banana.uni.app.tutorialpagebreak
// @description = Tutorial: Page break
// @task = app.command
// @doctype = nodocument
// @publisher = Banana.ch SA
// @pubdate = 2019-01-25
// @inputdatasource = none
// @timeout = -1

function exec() {
    //Create the report
    var report = Banana.Report.newReport('Report title');

    //Add a paragraph with some text
    report.addParagraph('Hello');

    //Add a page break
    report.addPageBreak();

    //Add an other paragraph 
    report.addParagraph('World!!!');

    //Print the report
    var stylesheet = Banana.Report.newStyleSheet();
    Banana.Report.preview(report, stylesheet);

}

Add attachments


// Banana Accounting Extension Javascript
// @api = 1.0
// @id = ch.banana.uni.app.tutorialattachments
// @description = Tutorial: Attachments
// @task = app.command
// @doctype = nodocument
// @publisher = Banana.ch SA
// @pubdate = 2019-01-25
// @inputdatasource = none
// @timeout = -1

function exec() {
    //Create the report
    var report = Banana.Report.newReport('Report attachments');

    //Add a paragraph with some text
    report.addParagraph('Report with attachments');

    //Attach text files created on the fly
    //We use the prefix 'data:...' to tell that the string is not an url but is itself the content of the file
    report.addAttachment('text file 1.txt', 'data:text/plain;utf8,This is the content of the text file 1.');
    report.addAttachment('text file 2.txt', 'data:text/plain;utf8,This is the content of the text file 2.');
    report.addAttachment('text file 3.txt', 'data:text/plain;utf8,This is the content of the text file 3.');

    //Attach an image stored in the document table
    //We use the prefix 'document:...'
    report.addAttachment('logo.jpg', 'documents:logo');

    //Add an xml element
    //We just add the new created Banana.Xml.newDocument
    var xmlDocument = Banana.Xml.newDocument("eCH-0217:VATDeclaration");
    var rootNode = xmlDocument.addElement("eCH-0217:VATDeclaration");
    rootNode.addElement("title").addTextNode("Vat Declaration 2018");
    report.addAttachment('vat_declaration.xml', xmlDocument);

    //Print the report
    var stylesheet = Banana.Report.newStyleSheet();
    Banana.Report.preview(report, stylesheet);
}

Print multiple reports


// Banana Accounting Extension Javascript
// @api = 1.0
// @id = ch.banana.uni.app.tutorialmultiplereports
// @description = Tutorial: Multiple reports
// @task = app.command
// @doctype = nodocument
// @publisher = Banana.ch SA
// @pubdate = 2019-01-25
// @inputdatasource = none
// @timeout = -1

function exec() {
	var docs = [];
	var styles = [];
	
	// Report
	for (var i = 0; i < 10; i++) {
		var report = Banana.Report.newReport("Report title");
		report.addParagraph("Hello World #" + i + " !!!", "styleHelloWorld");
		report.setTitle("Document " + i);
		report.getFooter().addFieldPageNr();
		docs.push(report);

		// Styles
		var stylesheet = Banana.Report.newStyleSheet();
		var style = stylesheet.addStyle(".styleHelloWorld");
		style.setAttribute("font-size", "24pt");
		style.setAttribute("text-align", "center");
		style.setAttribute("margin-top", "10mm");
		var style2 = stylesheet.addStyle("@page");
		style2.setAttribute("size", "landscape");
		styles.push(stylesheet);
	}

	// Print preview of 10 documents together
	Banana.Report.preview("Multi documents printing example", docs, styles);
}

Add structured text


// Banana Accounting Extension Javascript
// @api = 1.2.0
// @id = ch.banana.uni.app.tutorialstructuredtext
// @description = Hello world
// @task = app.command
// @doctype = nodocument
// @publisher = Publisher name
// @pubdate = 2022-05-31
// @inputdatasource = none
// @timeout = -1

function exec() {

    //Create the report and the stylesheet
    var report = Banana.Report.newReport('Tutorial Banana.ReportElement.addStructuredText');
    var stylesheet = Banana.Report.newStyleSheet();
    stylesheet.addStyle(".example", "margin-top: 2em; margin-bottom: 0.5em; font-style: italic;");

    report.addParagraph("--- Markdown example ---", "example");

    let mdText = "# Header 1\n";
    mdText += "## Header 2\n";
    mdText += "### Header 3\n";
    mdText += "Markdown text with **bold** and *italic*.  \n";
    mdText += "[Markdown link](https://www.banana.ch)  \n";
    mdText += "* List row 1\n";
    mdText += "* List row 2\n";
    mdText += "* List row 3\n";
    report.addStructuredText(mdText, "md");
    
    report.addParagraph("--- Simple html example ---", "example");

    let htmlText = "<h1 style=\"color: purple\">Header 1</h1>\n";
    htmlText += "<p style=\"color: blue\">Paragraph</p>\n";
    report.addStructuredText(htmlText, "html");

    report.addParagraph("--- Full html example ---", "example");

    htmlText = "<html>\n";
    htmlText += "<head>\n";
    htmlText += "<style>\n";
    htmlText += "p.green {color: green;}\n";
    htmlText += ".red {color: red;}\n";
    htmlText += "</style>\n";
    htmlText += "</head>\n";
    htmlText += "<body>\n";
    htmlText += "<h1 class=\"red\">Header 1</h1>\n";
    htmlText += "<p class=\"green\">Paragraph</p>\n";
    htmlText += "</body>\n";
    htmlText += "</html>\n";
    report.addStructuredText(htmlText, "html", stylesheet);

    report.addParagraph("--- Plain html example ---", "example");

    let plainText = "Testo riga 1\n";
    plainText += "Testo riga 2\n";
    plainText += "Testo riga 3\n";
    report.addStructuredText(plainText, "text");	

    //Print the report
    Banana.Report.preview(report, stylesheet);

}

Creating Reports with table object

Create a table with one row


// Banana Accounting Extension Javascript
// @api = 1.0
// @id = ch.banana.uni.app.tutorialtableonerow
// @description = Tutorial: Create a table with one row
// @task = app.command
// @doctype = nodocument
// @publisher = Banana.ch SA
// @pubdate = 2019-01-25
// @inputdatasource = none
// @timeout = -1

function exec() {
    //Create the report
    var report = Banana.Report.newReport('Report title');

    //Create a table
    var table = report.addTable('myTable');

    //Add a row
    tableRow = table.addRow();
    tableRow.addCell('Cash');
    tableRow.addCell('1200');

    //Create style
    var stylesheet = Banana.Report.newStyleSheet();
    Banana.Report.preview(report, stylesheet);

}

Create a table with multiple rows


// Banana Accounting Extension Javascript
// @api = 1.0
// @id = ch.banana.uni.app.tutorialtablemultiplerows
// @description = Tutorial: Create a table with multiple rows
// @task = app.command
// @doctype = nodocument
// @publisher = Banana.ch SA
// @pubdate = 2019-01-25
// @inputdatasource = none
// @timeout = -1

function exec() {
    //Create the report
    var report = Banana.Report.newReport('Report title');

    //Create a table
    var table = report.addTable('myTable');

    //Add row 1
    tableRow = table.addRow();
    tableRow.addCell('Cash');
    tableRow.addCell('1200');

    //Add row 2
    tableRow = table.addRow();
    tableRow.addCell('Bank 1');
    tableRow.addCell('500');

    //Add row 3
    tableRow = table.addRow();
    tableRow.addCell('Bank 2');
    tableRow.addCell('2600');

    //Print report
    var stylesheet = Banana.Report.newStyleSheet();
    Banana.Report.preview(report, stylesheet);

}

Create a table with multiple rows and a header


// Banana Accounting Extension Javascript
// @api = 1.0
// @id = ch.banana.uni.app.tutorialtablewithheader
// @description = Tutorial: Create a table with multiple rows and header
// @task = app.command
// @doctype = nodocument
// @publisher = Banana.ch SA
// @pubdate = 2019-01-25
// @inputdatasource = none
// @timeout = -1

function exec() {
    //Create the report
    var report = Banana.Report.newReport('Report title');

    //Create a table
    var table = report.addTable('myTable');

    //Create table header
    var tableHeader = table.getHeader();

    //Add the header of the table
    tableRow = tableHeader.addRow();
    tableRow.addCell('Description');
    tableRow.addCell('Amount');

    //Add row 1
    tableRow = table.addRow();
    tableRow.addCell('Cash');
    tableRow.addCell('1200');

    //Add row 2
    tableRow = table.addRow();
    tableRow.addCell('Bank 1');
    tableRow.addCell('500');

    //Add row 3
    tableRow = table.addRow();
    tableRow.addCell('Bank 2');
    tableRow.addCell('2600');

    //Print the report
    var stylesheet = Banana.Report.newStyleSheet();
    Banana.Report.preview(report, stylesheet);

}

Create a table with multiple rows, header and borders


// Banana Accounting Extension Javascript
// @api = 1.0
// @id = ch.banana.uni.app.tutorialtablewithheaderandborders
// @description = Tutorial: Create a table with multiple rows, header and borders
// @task = app.command
// @doctype = nodocument
// @publisher = Banana.ch SA
// @pubdate = 2019-01-25
// @inputdatasource = none
// @timeout = -1

function exec() {
    //Create the report
    var report = Banana.Report.newReport('Report title');

    //Create a table
    var table = report.addTable('myTable');

    //Create table header
    var tableHeader = table.getHeader();

    //Add the header of the table
    tableRow = tableHeader.addRow();
    tableRow.addCell('Description');
    tableRow.addCell('Amount');

    //Add row 1
    tableRow = table.addRow();
    tableRow.addCell('Cash');
    tableRow.addCell('1200');

    //Add row 2
    tableRow = table.addRow();
    tableRow.addCell('Bank 1');
    tableRow.addCell('500');

    //Add row 3
    tableRow = table.addRow();
    tableRow.addCell('Bank 2');
    tableRow.addCell('2600');

    //Stylesheet
    var stylesheet = Banana.Report.newStyleSheet();

    //Create style for the table adding borders
    var style = stylesheet.addStyle("table");
    stylesheet.addStyle("table.myTable td", "border: thin solid black");

    //Print report
    Banana.Report.preview(report, stylesheet);

}

Create a table with fixed columns widths


// Banana Accounting Extension Javascript
// @api = 1.0
// @id = ch.banana.uni.app.tutorialtablecolumnswidth
// @description = Tutorial: Create a table with fixed columns width
// @task = app.command
// @doctype = nodocument
// @publisher = Banana.ch SA
// @pubdate = 2019-01-25
// @inputdatasource = none
// @timeout = -1

function exec() {

   var report = Banana.Report.newReport("Javascrip Tutorial - Table with fixed column widths");
   
   /* Create the table */
   var table = report.addTable("table");
   
   /* Add columns */
   var column1 = table.addColumn("col1");
   var column2 = table.addColumn("col2");
   var column3 = table.addColumn("col3");
   var column4 = table.addColumn("col4");

   var row;
   
   /* Add a row with cells */
   row = table.addRow(); //Add a row
   row.addCell("A", "", 1); //add first cell
   row.addCell("B", "", 1); //add second cell
   row.addCell("C", "", 1); //add third cell
   row.addCell("D", "", 1); //add fourth cell

   //Add more rows...

   /* Print the report */
   var stylesheet = createStyleSheet();
   Banana.Report.preview(report, stylesheet);
}

function createStyleSheet() {
   var stylesheet = Banana.Report.newStyleSheet();

   var style = stylesheet.addStyle(".table");
	style.setAttribute("width","100%"); //table takes all the page width
	stylesheet.addStyle("table.table td", "border: thin solid black");

   /* Set column widths */
   stylesheet.addStyle(".col1", "width:10%");
   stylesheet.addStyle(".col2", "width:55%");
   stylesheet.addStyle(".col3", "width:30%");
   stylesheet.addStyle(".col4", "width:5%");
   
   return stylesheet;
}

Add multiple paragraphs in one cell


// Banana Accounting Extension Javascript
// @api = 1.0
// @id = ch.banana.uni.app.tutorialcellmultipleparagraphs
// @description = Tutorial: Add multiple paragraphs in one cell of the table
// @task = app.command
// @doctype = nodocument
// @publisher = Banana.ch SA
// @pubdate = 2019-01-25
// @inputdatasource = none
// @timeout = -1

function exec() {
   
   var report = Banana.Report.newReport("Javascrip Tutorial - Add multiple paragraphs in one cell");
   
   /* Create the table */
   var table = report.addTable("table");
   var row;
   
   /* Add a row */
   row = table.addRow(); 
   
   /* Add first cell with paragraphs*/
   var cell1 = row.addCell("", "", 1);
   cell1.addParagraph("First paragraph...", "");
   cell1.addParagraph("Second paragraph...", "");
   cell1.addParagraph(" "); //empty paragraph
   cell1.addParagraph("Fourth paragraph...", "");

   /* Add second cell without paragraphs */
   var cell2 = row.addCell("Cell2...", "", 1);

   /* Print the report */
   var stylesheet = createStyleSheet();
   Banana.Report.preview(report, stylesheet);

}

function createStyleSheet() {
   var stylesheet = Banana.Report.newStyleSheet();

   style = stylesheet.addStyle(".table");
   style.setAttribute("width","100%");
   stylesheet.addStyle("table.table td", "border: thin solid black");
   
   return stylesheet;
}

Create a table with fixed columns and cells span


// Banana Accounting Extension Javascript
// @api = 1.0
// @id = ch.banana.uni.app.tutorialtablefixedcolumnscellspan
// @description = Tutorial: Create a table with fixed columns and cells span
// @task = app.command
// @doctype = nodocument
// @publisher = Banana.ch SA
// @pubdate = 2019-01-25
// @inputdatasource = none
// @timeout = -1

function exec() {

   var report = Banana.Report.newReport("Javascrip Tutorial - Simple Table");
   
   /* Create the table */
   var table = report.addTable("table");
   
   /* Add columns */
   var column1 = table.addColumn("col1");
   var column2 = table.addColumn("col2");
   var column3 = table.addColumn("col3");
   var column4 = table.addColumn("col4");
   var row;
   
   /* 1st row */
   row = table.addRow();
   row.addCell("Row 1, Cell 1: span cell over 4 columns", "", 4); //span cell over 4 columns

   /* 2nd row */
   row = table.addRow();
   row.addCell("Row 2, Cell 1: span cell over 2 columns", "", 2); //span cell over 2 columns
   row.addCell("Row 2, Cell 3: span cell over 2 columns", "", 2); //span cell over 2 columns

   /* 3rd row */
   row = table.addRow();
   row.addCell("Row 3, Cell 1", "", 1);
   row.addCell("Row 3, Cell 2: span cell over 2 columns", "", 2); //span cell over 2 columns
   row.addCell("Row 3, Cell 4", "", 1);

   /* 4th row*/
   row = table.addRow();
   row.addCell("Row 4, Cell 1", "", 1);
   row.addCell("Row 4, Cell 2", "", 1);
   row.addCell("Row 4, Cell 3", "", 1);
   row.addCell("Row 4, Cell 4", "", 1);

   /* 5th row */
   row = table.addRow();
   row.addCell("Row 5, Cell 1: span cell over 3 columns", "", 3); //span cell over 3 columns
   row.addCell("Row 5, Cell 4", "", 1);

   /* Print the report */
   var stylesheet = createStyleSheet();
   Banana.Report.preview(report, stylesheet);

}

function createStyleSheet() {
   var stylesheet = Banana.Report.newStyleSheet();

   var style = stylesheet.addStyle(".table");
   style.setAttribute("width","100%"); //table takes all the page width
   stylesheet.addStyle("table.table td", "border: thin solid black; padding-top:10px; padding-bottom:10px;");

   /* Set columns' width */
   stylesheet.addStyle(".col1", "width:25%");
   stylesheet.addStyle(".col2", "width:25%");
   stylesheet.addStyle(".col3", "width:25%");
   stylesheet.addStyle(".col4", "width:25%");

   return stylesheet;
}

Add reporting styles

Set page margins


// Banana Accounting Extension Javascript
// @api = 1.0
// @id = ch.banana.uni.app.tutorialstylesheetsetmargins
// @description = Tutorial: Stylesheet set margins
// @task = app.command
// @doctype = nodocument
// @publisher = Banana.ch SA
// @pubdate = 2019-01-25
// @inputdatasource = none
// @timeout = -1

function exec() {
    //Create the report
    var report = Banana.Report.newReport('Report title');

    //Add a paragraph with some text using a style
    report.addParagraph('Hello world!');

    //Add some styles
    var stylesheet = Banana.Report.newStyleSheet();

    //Create the margin for the page: [top, right, bottom, left]
    var pageStyle = stylesheet.addStyle("@page");
    pageStyle.setAttribute("margin", "10mm 20mm 10mm 20mm");

    //Print the report
    Banana.Report.preview(report, stylesheet);

}

Set landscape page


// Banana Accounting Extension Javascript
// @api = 1.0
// @id = ch.banana.uni.app.tutorialstylesheetsetlandscape
// @description = Tutorial: Stylesheet set landscape
// @task = app.command
// @doctype = nodocument
// @publisher = Banana.ch SA
// @pubdate = 2019-01-25
// @inputdatasource = none
// @timeout = -1

function exec() {
    //Create the report
    var report = Banana.Report.newReport('Report title');

    //Add a paragraph with some text using a style
    report.addParagraph('Hello world!');

    //Add some styles
    var stylesheet = Banana.Report.newStyleSheet();

    //Create the margin for the page
    var pageStyle = stylesheet.addStyle("@page");
    pageStyle.setAttribute("size", "landscape");

    //Print the report
    Banana.Report.preview(report, stylesheet);

}

Add bold style to a text


// Banana Accounting Extension Javascript
// @api = 1.0
// @id = ch.banana.uni.app.tutorialstylesheetboldtext
// @description = Tutorial: Stylesheet add bold style to a text
// @task = app.command
// @doctype = nodocument
// @publisher = Banana.ch SA
// @pubdate = 2019-01-25
// @inputdatasource = none
// @timeout = -1

function exec() {
    //Create the report
    var report = Banana.Report.newReport('Report title');

    //Add a paragraph with some text using a style
    report.addParagraph('Hello world!', 'boldStyle');

    //Add some styles
    var stylesheet = Banana.Report.newStyleSheet();

    //Create the "boldStyle"
    style = stylesheet.addStyle(".boldStyle");
    style.setAttribute("font-weight", "bold");

    //Print the report
    Banana.Report.preview(report, stylesheet);

}

Add a font size to a text


// Banana Accounting Extension Javascript
// @api = 1.0
// @id = ch.banana.uni.app.tutorialstylesheetfontsizetext
// @description = Tutorial: Stylesheet add a specific font size to a text
// @task = app.command
// @doctype = nodocument
// @publisher = Banana.ch SA
// @pubdate = 2019-01-25
// @inputdatasource = none
// @timeout = -1

function exec() {
    //Create the report
    var report = Banana.Report.newReport('Report title');

    //Add a paragraph with some text using a style
    report.addParagraph('Hello world!', 'titleStyle');

    //Add some styles
    var stylesheet = Banana.Report.newStyleSheet();

    //Create the style for the text
    style = stylesheet.addStyle(".titleStyle");
    style.setAttribute("font-size", "20px");

    //Print the report
    Banana.Report.preview(report, stylesheet);

}

Add a color to a text


// Banana Accounting Extension Javascript
// @api = 1.0
// @id = ch.banana.uni.app.tutorialstylesheetcolortext
// @description = Tutorial: Stylesheet add a color to a text
// @task = app.command
// @doctype = nodocument
// @publisher = Banana.ch SA
// @pubdate = 2019-01-25
// @inputdatasource = none
// @timeout = -1

function exec() {
    //Create the report
    var report = Banana.Report.newReport('Report title');

    //Add a paragraph with some text using a style
    report.addParagraph('Hello world!', 'colorStyle');

    //Add some styles
    var stylesheet = Banana.Report.newStyleSheet();

    //Create the style for the text
    style = stylesheet.addStyle(".colorStyle");
    style.setAttribute("color", "red");

    //Print the report
    Banana.Report.preview(report, stylesheet);

}

First page/cover example


// Banana Accounting Extension Javascript
// @api = 1.0
// @id = ch.banana.uni.app.tutorialcoverpageexample
// @description = Tutorial: First page/cover example
// @task = app.command
// @doctype = nodocument
// @publisher = Banana.ch SA
// @pubdate = 2019-01-25
// @inputdatasource = none
// @timeout = -1

function exec() {
    //Create the report
    var report = Banana.Report.newReport("Cover example");

    var title = " 'This is the title' ";
    var companyName = "Banana.ch SA";
    var openingDate = "01.01.2015";
    var closufeDate = "31.12.2015";
    var year = "2015";

    report.addParagraph(title, "heading1 alignCenter");
    report.addParagraph(" ");
    report.addParagraph(companyName, "heading2 alignCenter");
    report.addParagraph(" ");
    report.addParagraph(year, "heading3 alignCenter");
    report.addParagraph(" ");
    report.addParagraph("(" + openingDate + " - " + closufeDate + ")", "heading4 alignCenter");

    //Add some styles
    var stylesheet = Banana.Report.newStyleSheet();
    var pageStyle = stylesheet.addStyle("@page");
    pageStyle.setAttribute("margin", "10mm 10mm 10mm 20mm");

    stylesheet.addStyle("body", "font-family : Helvetica");

    style = stylesheet.addStyle(".heading1");
    style.setAttribute("font-size", "22px");
    style.setAttribute("font-weight", "bold");
    style.setAttribute("border-top", "100mm");

    style = stylesheet.addStyle(".heading2");
    style.setAttribute("font-size", "18px");
    style.setAttribute("font-weight", "bold");

    style = stylesheet.addStyle(".heading3");
    style.setAttribute("font-size", "14px");
    style.setAttribute("font-weight", "bold");

    style = stylesheet.addStyle(".heading4");
    style.setAttribute("font-size", "10px");
    style.setAttribute("font-weight", "bold");

    style = stylesheet.addStyle(".alignCenter");
    style.setAttribute("text-align", "center");

    //Print the report
    Banana.Report.preview(report, stylesheet);

}

Format values

Format numbers


// Banana Accounting Extension Javascript
// @api = 1.0
// @id = ch.banana.uni.app.tutorialformatnumbers
// @description = Tutorial: Format numbers
// @task = app.command
// @doctype = nodocument
// @publisher = Banana.ch SA
// @pubdate = 2019-01-25
// @inputdatasource = none
// @timeout = -1

function exec() {
    //Create the report
    var report = Banana.Report.newReport('Report title');

    //Convert the value
    var convertedAmount1 = Banana.Converter.toLocaleNumberFormat("1200.65");
    var convertedAmount2 = Banana.Converter.toLocaleNumberFormat("0");
    var convertedAmount3 = Banana.Converter.toLocaleNumberFormat(1234.56);
    var convertedAmount4 = Banana.Converter.toLocaleNumberFormat(1);

    //Add the converted amount to the report's paragraph
    report.addParagraph(convertedAmount1);
    report.addParagraph(convertedAmount2);
    report.addParagraph(convertedAmount3);
    report.addParagraph(convertedAmount4);

    //Print the report
    var stylesheet = Banana.Report.newStyleSheet();
    Banana.Report.preview(report, stylesheet);

}

Format dates


// Banana Accounting Extension Javascript
// @api = 1.0
// @id = ch.banana.uni.app.tutorialformatdates
// @description = Tutorial: Format dates
// @task = app.command
// @doctype = nodocument
// @publisher = Banana.ch SA
// @pubdate = 2019-01-25
// @inputdatasource = none
// @timeout = -1

function exec() {
    //Create the report
    var report = Banana.Report.newReport('Report title');

    //Convert the value: parameter is a string
    var date1 = Banana.Converter.toLocaleDateFormat('2015-12-31');

    //Convert the value: parameter is a date object
    var d = new Date();
    var date2 = Banana.Converter.toLocaleDateFormat(d);

    //Add the converted amount to the report's paragraph
    report.addParagraph(date1);
    report.addParagraph(date2);

    //Print the report
    var stylesheet = Banana.Report.newStyleSheet();
    Banana.Report.preview(report, stylesheet);

}

Format transactions journal data


// Banana Accounting Extension Javascript
// @api = 1.0
// @id = ch.banana.uni.app.tutorialformattransactionjournaldata
// @description = Tutorial: Format transactions journal data
// @task = app.command
// @doctype = nodocument
// @publisher = Banana.ch SA
// @pubdate = 2019-01-25
// @inputdatasource = none
// @timeout = -1

function exec() {
    //Create the report
    var report = Banana.Report.newReport('Report title');

    //Create the table that will be printed on the report
    var table = report.addTable("myTable");

    //Add column titles to the table report
    var tableHeader = table.getHeader();
    tableRow = tableHeader.addRow();
    tableRow.addCell('Data', 'boldStyle');
    tableRow.addCell('Account', 'boldStyle');
    tableRow.addCell('Description', 'boldStyle');
    tableRow.addCell('Amount', 'boldStyle');

    //Create a table with all transactions
    var journal = Banana.document.journal(Banana.document.ORIGINTYPE_CURRENT, Banana.document.ACCOUNTTYPE_NORMAL);

    //Read some values of the journal table
    for (var i = 0; i < journal.rowCount; i++) {
        var tRow = journal.row(i);

        //Add the values taken from the rows to the respective cells of the table
        //For the dates and the amounts we apply the format functions
        tableRow = table.addRow();
        tableRow.addCell(Banana.Converter.toLocaleDateFormat(tRow.value('JDate')));
        tableRow.addCell(tRow.value('JAccount'));
        tableRow.addCell(tRow.value('JAccountDescription'));
        tableRow.addCell(Banana.Converter.toLocaleNumberFormat(tRow.value('JAmount')), 'alignRight');
    }

    //Create the styleSheet
    var stylesheet = Banana.Report.newStyleSheet();

    //Add borders to the table
    var style = stylesheet.addStyle("table");
    stylesheet.addStyle("table.myTable td", "border: thin solid black");

    //Add the right alignment for the amount
    style = stylesheet.addStyle(".alignRight");
    style.setAttribute("text-align", "right");

    //Add the bold style for the header
    style = stylesheet.addStyle(".boldStyle");
    style.setAttribute("font-weight", "bold");

    //Print the report
    Banana.Report.preview(report, stylesheet);

}

Numbers operations with SDecimal() functions

Basic mathematical operations (sum, subtract, multiply, divide)


// Banana Accounting Extension Javascript
// @api = 1.0
// @id = ch.banana.uni.app.tutorialbasicmathematicaloperations
// @description = Tutorial: Basic mathematical operations (sum, subtract, multiply, divide)
// @task = app.command
// @doctype = nodocument
// @publisher = Banana.ch SA
// @pubdate = 2019-01-25
// @inputdatasource = none
// @timeout = -1

function exec() {
    //Create the report
    var report = Banana.Report.newReport('Report title');

    //Do some mathematical operations and add the results to the report

    //Sum
    var sum = Banana.SDecimal.add('6.50', '3.50'); // return '10.00'
    report.addParagraph(sum);

    //Subtract
    var sub = Banana.SDecimal.subtract('10', '3'); // return '7'
    report.addParagraph(sub);

    //Multiply
    var mul = Banana.SDecimal.multiply('6', '3'); // return '18'
    report.addParagraph(mul);

    //Divide
    var div = Banana.SDecimal.divide('6', '3'); // return '2'
    report.addParagraph(div);

    //Print the report
    var stylesheet = Banana.Report.newStyleSheet();
    Banana.Report.preview(report, stylesheet);

}

abs function


// Banana Accounting Extension Javascript
// @api = 1.0
// @id = ch.banana.uni.app.tutorialabsfunction
// @description = Tutorial: abs function 
// @task = app.command
// @doctype = nodocument
// @publisher = Banana.ch SA
// @pubdate = 2019-01-25
// @inputdatasource = none
// @timeout = -1

function exec() {
    //Create the report
    var report = Banana.Report.newReport('Report title');

    //Return the value without the sign
    var absValue = Banana.SDecimal.abs('-10');

    //Add a paragraph to the report
    report.addParagraph(absValue);

    //Print the report
    var stylesheet = Banana.Report.newStyleSheet();
    Banana.Report.preview(report, stylesheet);

}

Compare two values


// Banana Accounting Extension Javascript
// @api = 1.0
// @id = ch.banana.uni.app.tutorialcomparevalues
// @description = Tutorial: Compare two values
// @task = app.command
// @doctype = nodocument
// @publisher = Banana.ch SA
// @pubdate = 2019-01-25
// @inputdatasource = none
// @timeout = -1

function exec() {
    //Create the report
    var report = Banana.Report.newReport('Report title');

    // Compare the values:
    // return 1 if value1 > value2
    // return 0 if value1 = value2
    // return -1 if value1 < value2

    var compareValue = Banana.SDecimal.compare('5', '2');

    //Add a paragraph to the report
    report.addParagraph(compareValue);

    //Print the report
    var stylesheet = Banana.Report.newStyleSheet();
    Banana.Report.preview(report, stylesheet);

}

Invert sign of a value


// Banana Accounting Extension Javascript
// @api = 1.0
// @id = ch.banana.uni.app.tutorialinvertsign
// @description = Tutorial: Invert value sign
// @task = app.command
// @doctype = nodocument
// @publisher = Banana.ch SA
// @pubdate = 2019-01-25
// @inputdatasource = none
// @timeout = -1

function exec() {
    //Create the report
    var report = Banana.Report.newReport('Report title');

    //Invert the sign:
    //if positive return a negative value
    //if negative return a positive value

    var invertValue = Banana.SDecimal.invert('4');

    //Add a paragraph to the report
    report.addParagraph(invertValue);

    //Print the report
    var stylesheet = Banana.Report.newStyleSheet();
    Banana.Report.preview(report, stylesheet);

}

Check the sign of a value


// Banana Accounting Extension Javascript
// @api = 1.0
// @id = ch.banana.uni.app.tutorialcheckvaluesign
// @description = Tutorial: Check value sign
// @task = app.command
// @doctype = nodocument
// @publisher = Banana.ch SA
// @pubdate = 2019-01-25
// @inputdatasource = none
// @timeout = -1

function exec() {
    //Create the report
    var report = Banana.Report.newReport('Report title');

    //Check the sign of the value:
    //return 1 if value > 0
    //return 0 if  value = 0
    //return -1 if value <0

    var signValue = Banana.SDecimal.sign('-6');

    //Add a paragraph to the report
    report.addParagraph(signValue);

    //Print the report
    var stylesheet = Banana.Report.newStyleSheet();
    Banana.Report.preview(report, stylesheet);

}

Number of decimals and rounding properties


// Banana Accounting Extension Javascript
// @api = 1.0
// @id = ch.banana.uni.app.tutorialdecimalsrounding
// @description = Tutorial: Number of decimals and rounding properties
// @task = app.command
// @doctype = nodocument
// @publisher = Banana.ch SA
// @pubdate = 2019-01-25
// @inputdatasource = none
// @timeout = -1

function exec() {
    //Create the report
    var report = Banana.Report.newReport('Report title');

    //Divide without properties
    var result1 = Banana.SDecimal.divide('10', '3.25'); //return '3.3333333333333333333333333'
    report.addParagraph(result1);

    //Divide with number of decimals property
    var result2 = Banana.SDecimal.divide('10', '3.25', {
        'decimals': 4
    });
    report.addParagraph(result2);

    //Divide with number of decimals and rounding properties
    var result3 = Banana.SDecimal.divide('10', '3.25', {
        'decimals': 2,
        'mode': Banana.SDecimal.HALF_UP
    });
    report.addParagraph(result3);

    //Print the report
    var stylesheet = Banana.Report.newStyleSheet();
    Banana.Report.preview(report, stylesheet);

}

User interface functions

Dialog information


// Banana Accounting Extension Javascript
// @api = 1.0
// @id = ch.banana.uni.app.tutorialinformationdialog
// @description = Tutorial: Information dialog
// @task = app.command
// @doctype = nodocument
// @publisher = Banana.ch SA
// @pubdate = 2019-01-25
// @inputdatasource = none
// @timeout = -1

function exec() {
    //Open a dialog window with an information
    Banana.Ui.showInformation('Title', 'This is the information message!');
}

Dialog question


// Banana Accounting Extension Javascript
// @api = 1.0
// @id = ch.banana.uni.app.tutorialquestiondialog
// @description = Tutorial: Question dialog
// @task = app.command
// @doctype = nodocument
// @publisher = Banana.ch SA
// @pubdate = 2019-01-25
// @inputdatasource = none
// @timeout = -1

function exec() {
    //Create a report
    var report = Banana.Report.newReport("Report title");

    //Open a dialog window with a question
    var question = Banana.Ui.showQuestion('Question title', 'Print the report?');

    //If 'true' do something... 
    if (question) {
        //...for example add some text to the paragraph
        report.addParagraph('The answer was YES!');

        //...then print the report
        var stylesheet = Banana.Report.newStyleSheet();
        Banana.Report.preview(report, stylesheet);
    }

}

Dialog show text


// Banana Accounting Extension Javascript
// @api = 1.0
// @id = ch.banana.uni.app.tutorialshowtextdialog
// @description = Tutorial: Show text dialog
// @task = app.command
// @doctype = nodocument
// @publisher = Banana.ch SA
// @pubdate = 2019-01-25
// @inputdatasource = none
// @timeout = -1

function exec() {
    //Open a dialog window showing the text.
    //In this case we want to show the table Accounts as html file
    Banana.Ui.showText(Banana.document.table('Accounts').toHtml(['Account', 'Group', 'Description', 'Balance'], true));

}

Dialog input text


// Banana Accounting Extension Javascript
// @api = 1.0
// @id = ch.banana.uni.app.tutorialinputtextdialog
// @description = Tutorial: Input text dialog
// @task = app.command
// @doctype = nodocument
// @publisher = Banana.ch SA
// @pubdate = 2019-01-25
// @inputdatasource = none
// @timeout = -1

function exec() {
    //Create a report
    var report = Banana.Report.newReport("Report title");

    //Open a dialog window asking the user to insert some text
    //The text inserted is saved into a variable
    var textInsertedByUser = Banana.Ui.getText('This is a dialog window', 'Insert some text', '');

    //Add to the paragraph the text inserted by the user
    report.addParagraph(textInsertedByUser);

    //Print the report
    var stylesheet = Banana.Report.newStyleSheet();
    Banana.Report.preview(report, stylesheet);
}

Dialog item selection


// Banana Accounting Extension Javascript
// @api = 1.0
// @id = ch.banana.uni.app.tutorialitemselectiondialog
// @description = Tutorial: Item selection dialog
// @task = app.command
// @doctype = nodocument
// @publisher = Banana.ch SA
// @pubdate = 2019-01-25
// @inputdatasource = none
// @timeout = -1

function exec() {
    //Create the report
    var report = Banana.Report.newReport('Report title');

    //Open a dialog window asking to select an item from the list
    //The selected item is then saved into a variable
    var itemSelected = Banana.Ui.getItem('Input', 'Choose a value', ['Item ONE', 'Item TWO', 'Item THREE', 'Item FOUR', 'Item FIVE'], 2, false);

    //Add the selected item to the paragraph
    report.addParagraph(itemSelected);

    //Print the report
    var stylesheet = Banana.Report.newStyleSheet();
    Banana.Report.preview(report, stylesheet);
}

Dialog period selection


// Banana Accounting Extension Javascript
// @api = 1.0
// @id = ch.banana.uni.app.tutorialperiodselectiondialog
// @description = Tutorial: Period selection dialog
// @task = app.command
// @doctype = nodocument
// @publisher = Banana.ch SA
// @pubdate = 2019-01-25
// @inputdatasource = none
// @timeout = -1

function exec() {
    //Create a report
    var report = Banana.Report.newReport("Report title");

    //Open a dialog windows to choose a period
    var date = Banana.Ui.getPeriod('Period selection', '2015-01-01', '2015-12-31');

    //Add the date information to the report
    report.addParagraph(date.startDate);
    report.addParagraph(date.endDate);
    report.addParagraph(date.hasSelection);

    //Print the report
    var stylesheet = Banana.Report.newStyleSheet();
    Banana.Report.preview(report, stylesheet);
}

Dialog multiple items selection


// Banana Accounting Extension Javascript
// @api = 1.0
// @id = ch.banana.uni.app.tutorialmultipleitemsselectiondialog
// @description = Tutorial: Multiple items selection dialog
// @task = app.command
// @doctype = nodocument
// @publisher = Banana.ch SA
// @pubdate = 2019-01-25
// @inputdatasource = none
// @timeout = -1

function exec() {
    //Create the report
    var report = Banana.Report.newReport('Report title');

    //Open a dialog window asking to select an item from the list
    //The selected item is then saved into a variable
    var itemsSelected = Banana.Ui.getItems(
       'Input', 
       'Choose one or more values', 
       ['Item ONE', 'Item TWO', 'Item THREE', 'Item FOUR', 'Item FIVE'], 
       ['Item ONE', 'Item THREE']
    );

    if (!itemsSelected) {
       // User pressed cancel
       return;
    }

    //Add the selected item to the paragraph
    report.addParagraph("Selected items:");

    if (itemsSelected.length > 0) {
        for (var i = 0; i < itemsSelected.length; ++i) {
            report.addParagraph("- " + itemsSelected[i]);
        }
    } else {
        report.addParagraph("No item selected");
    }

    //Print the report
    var stylesheet = Banana.Report.newStyleSheet();
    Banana.Report.preview(report, stylesheet);
}

Message functions

Show messages


// Banana Accounting Extension Javascript
// @api = 1.0
// @id = ch.banana.uni.app.tutorialshowmessages
// @description = Tutorial: Show messages
// @task = app.command
// @doctype = nodocument
// @publisher = Banana.ch SA
// @pubdate = 2019-01-25
// @inputdatasource = none
// @timeout = -1

function exec() {
    Banana.document.clearMessages();

    //Step 1 - table access
    var transactionTable = Banana.document.table('Transactions');

    //Step 2 - loop on each row of the table
    for (var i = 0; i < transactionTable.rowCount; i++) {

        var tRow = transactionTable.row(i);

        //Check the length of the description
        if (tRow.value('Description').length > 20 && tRow.value('Description').length < 30) {
            Banana.document.addMessage("Warning: row " + tRow.rowNr + ", description's length is " + tRow.value('Description').length + "!");
        } else if (tRow.value('Description').length >= 30) {
            Banana.document.addMessage("Error: row " + tRow.rowNr + ", description's length is " + tRow.value('Description').length + "!");
        }
    }

}

Show messages linked to table Transaction


// Banana Accounting Extension Javascript
// @api = 1.0
// @id = ch.banana.uni.app.tutorialshowmessageslinkedtotable
// @description = Tutorial: Show messages linked to table Transactions
// @task = app.command
// @doctype = nodocument
// @publisher = Banana.ch SA
// @pubdate = 2019-01-25
// @inputdatasource = none
// @timeout = -1

function exec() {
    Banana.document.clearMessages();

    //Step 1 - table access
    var transactionTable = Banana.document.table('Transactions');

    //Step 2 - loop on each row of the table
    for (var i = 0; i < transactionTable.rowCount; i++) {

        var tRow = transactionTable.row(i);

        //Check the length of the description
        if (tRow.value('Description').length > 20 && tRow.value('Description').length < 30) {
            tRow.addMessage("Warning: description's length is " + tRow.value('Description').length + "!");
        } else if (tRow.value('Description').length >= 30) {
            tRow.addMessage("Error: description's length is " + tRow.value('Description').length + "!");
        }
    }

}

Clear all messages


// Banana Accounting Extension Javascript
// @api = 1.0
// @id = ch.banana.uni.app.tutorialclearmessages
// @description = Tutorial: Clear all messages
// @task = app.command
// @doctype = nodocument
// @publisher = Banana.ch SA
// @pubdate = 2019-01-25
// @inputdatasource = none
// @timeout = -1

function exec() {
    Banana.document.clearMessages();
}

Save settings

Save settings


// Banana Accounting Extension Javascript
// @api = 1.0
// @id = ch.banana.uni.app.tutorialsavesettings
// @description = Tutorial: Save settings
// @task = app.command
// @doctype = nodocument
// @publisher = Banana.ch SA
// @pubdate = 2019-01-25
// @inputdatasource = none
// @timeout = -1

function exec() {

    //Create the report
    var report = Banana.Report.newReport('Report title');

    //Readscript settings
    var readText = Banana.document.scriptReadSettings();

    //If there is a saved setting we can use it
    if (readText) {

        //Use the JSON.parse() to convert a JSON text into a JavaScript object
        var object = JSON.parse(readText);

        //Add a paragraph with the saved and parsed text
        report.addParagraph('Previously saved value: ' + object);

        //Print the report
        var stylesheet = Banana.Report.newStyleSheet();
        Banana.Report.preview(report, stylesheet);
    }

    // If it doesn't exists a saved setting yet (which it happens the very first time the script is executed),
    //	it is necessary to create and save it

    //For example using an dialog window to insert some text
    var text = Banana.Ui.getText('Save settings example', 'Insert some text', '');

    //Convert a JavaScript value into a JSON string using the JSON.stringify() function
    var textToSave = JSON.stringify(text);

    //Save script settings
    var savedText = Banana.document.scriptSaveSettings(textToSave);

}

Retrieving informations from "File and account properties..."

Retrieve basic accounting informations


// Banana Accounting Extension Javascript
// @api = 1.0
// @id = ch.banana.uni.app.tutorialretrievebasicaccountinginformation
// @description = Tutorial: Retrieve basic accounting information
// @task = app.command
// @doctype = nodocument
// @publisher = Banana.ch SA
// @pubdate = 2019-01-25
// @inputdatasource = none
// @timeout = -1

function exec() {
    //Create report
    var report = Banana.Report.newReport('Report title');

    //Get some value of the accounting file 
    var fileName = Banana.document.info("Base", "FileName");
    var decimalsAmounts = Banana.document.info("Base", "DecimalsAmounts");
    var headerLeft = Banana.document.info("Base", "HeaderLeft");
    var headerRight = Banana.document.info("Base", "HeaderRight");
    var basicCurrency = Banana.document.info("AccountingDataBase", "BasicCurrency");

    //For openingDate and closureDate
    var startDate = Banana.document.info('AccountingDataBase', 'OpeningDate');
    var endDate = Banana.document.info('AccountingDataBase', 'ClosureDate');

    //For file accounting type
    var fileType = Banana.document.info("Base", "FileType");
    var fileGroup = Banana.document.info("Base", "FileTypeGroup");
    var fileNumber = Banana.document.info("Base", "FileTypeNumber");

    //Add the informations to the report
    report.addParagraph(fileName);
    report.addParagraph(decimalsAmounts);
    report.addParagraph(headerLeft);
    report.addParagraph(headerRight);
    report.addParagraph(basicCurrency);
    report.addParagraph(startDate);
    report.addParagraph(endDate);
    report.addParagraph(fileType);
    report.addParagraph(fileGroup);
    report.addParagraph(fileNumber);

    //Print the report
    var stylesheet = Banana.Report.newStyleSheet();
    Banana.Report.preview(report, stylesheet);

}

Retrieve address informations


// Banana Accounting Extension Javascript
// @api = 1.0
// @id = ch.banana.uni.app.tutorialretrieveaddressinformation
// @description = Tutorial: Retrieve address information
// @task = app.command
// @doctype = nodocument
// @publisher = Banana.ch SA
// @pubdate = 2019-01-25
// @inputdatasource = none
// @timeout = -1

function exec() {
    //Create report
    var report = Banana.Report.newReport('Report title');

    //Save informations
    var company = Banana.document.info('AccountingDataBase', 'Company');
    var courtesy = Banana.document.info('AccountingDataBase', 'Courtesy');
    var name = Banana.document.info('AccountingDataBase', 'Name');
    var familyName = Banana.document.info('AccountingDataBase', 'FamilyName');
    var address1 = Banana.document.info('AccountingDataBase', 'Address1');
    var address2 = Banana.document.info('AccountingDataBase', 'Address2');
    var zip = Banana.document.info('AccountingDataBase', 'Zip');
    var city = Banana.document.info('AccountingDataBase', 'City');
    var state = Banana.document.info('AccountingDataBase', 'State');
    var country = Banana.document.info('AccountingDataBase', 'Country');
    var web = Banana.document.info('AccountingDataBase', 'Web');
    var email = Banana.document.info('AccountingDataBase', 'Email');
    var phone = Banana.document.info('AccountingDataBase', 'Phone');
    var mobile = Banana.document.info('AccountingDataBase', 'Mobile');
    var fax = Banana.document.info('AccountingDataBase', 'Fax');
    var fiscalNumber = Banana.document.info('AccountingDataBase', 'FiscalNumber');
    var vatNumber = Banana.document.info('AccountingDataBase', 'VatNumber');

    //Add the informations to the report
    report.addParagraph(company);
    report.addParagraph(courtesy);
    report.addParagraph(name);
    report.addParagraph(familyName);
    report.addParagraph(address1);
    report.addParagraph(address2);
    report.addParagraph(zip);
    report.addParagraph(city);
    report.addParagraph(state);
    report.addParagraph(country);
    report.addParagraph(web);
    report.addParagraph(email);
    report.addParagraph(phone);
    report.addParagraph(mobile);
    report.addParagraph(fax);
    report.addParagraph(fiscalNumber);
    report.addParagraph(vatNumber);

    //Print the report
    var stylesheet = Banana.Report.newStyleSheet();
    Banana.Report.preview(report, stylesheet);

}

Retrieving data from Banana tables

Retrieve rows table values using findRowByValue() function


// Banana Accounting Extension Javascript
// @api = 1.0
// @id = ch.banana.uni.app.tutorialretrieverowstablevaluesusingfindrowbyvalue
// @description = Tutorial: Retrieve rows table values using findRowByValue() function
// @task = app.command
// @doctype = nodocument
// @publisher = Banana.ch SA
// @pubdate = 2019-01-25
// @inputdatasource = none
// @timeout = -1

function exec() {
    //Create report
    var report = Banana.Report.newReport("Report title");

    //Save the row extracted from the table
    var rowToExtract = Banana.document.table('Accounts').findRowByValue('Account', '1000');

    //Add the Account, Description and Balance informations to the report
    report.addParagraph(rowToExtract.value('Account'));
    report.addParagraph(rowToExtract.value('Description'));
    report.addParagraph(rowToExtract.value('Balance'));

    //Print the report
    var stylesheet = Banana.Report.newStyleSheet();
    Banana.Report.preview(report, stylesheet);

}

Retrieve specific rows table values


// Banana Accounting Extension Javascript
// @api = 1.0
// @id = ch.banana.uni.app.tutorialretrievespecificrowstablevalues
// @description = Tutorial: Retrieve specific rows table values
// @task = app.command
// @doctype = nodocument
// @publisher = Banana.ch SA
// @pubdate = 2019-01-25
// @inputdatasource = none
// @timeout = -1

function exec() {
    //Create report
    var report = Banana.Report.newReport("Report title");

    // Step 1 - table access: specify the name of the table ("Accounts", "Transactions", "VatCodes", ...)
    var accountsTable = Banana.document.table("Accounts");

    //Step 2 - row selection: 	it is important to note that the rows of the Banana table start counting from 0,
    //so keep in mind to specify one number less than the desired one 
    //1st row = 0
    //2nd row = 1
    //3rd row = 2
    //4th row = 3
    //...

    var row3 = accountsTable.row(2); // We want the third row

    // Step 3 - select all the desired columns
    var account = row3.value("Account");
    var description = row3.value("Description");
    var balance = row3.value("Balance");

    //Add the informations to the report
    report.addParagraph(account);
    report.addParagraph(description);
    report.addParagraph(balance);

    //Print the report
    var stylesheet = Banana.Report.newStyleSheet();
    Banana.Report.preview(report, stylesheet);
}

Retrieve rows table values


// Banana Accounting Extension Javascript
// @api = 1.0
// @id = ch.banana.uni.app.tutorialretrieverowstablevalues
// @description = Tutorial: Retrieve rows table values
// @task = app.command
// @doctype = nodocument
// @publisher = Banana.ch SA
// @pubdate = 2019-01-25
// @inputdatasource = none
// @timeout = -1

function exec() {
    //Create a report
    var report = Banana.Report.newReport('Report title');

    //Step 1 - table access
    var accountsTable = Banana.document.table('Accounts');

    //Step 2 - loop on each row of the table, instead of specifying a single row
    for (var rowNumber = 0; rowNumber < accountsTable.rowCount; rowNumber++) {

        var tRow = accountsTable.row(rowNumber);

        //Step 3 - select the desired columns
        var account = tRow.value('Account');
        var description = tRow.value('Description');
        var balance = tRow.value('Balance');

        //Add the informations to the report
        report.addParagraph(account + ', ' + description + ', ' + balance);
    }

    //Print the report
    var stylesheet = Banana.Report.newStyleSheet();
    Banana.Report.preview(report, stylesheet);

}

Retrieve rows table with account number


// Banana Accounting Extension Javascript
// @api = 1.0
// @id = ch.banana.uni.app.tutorialretretrieverowstablewithaccountnumber
// @description = Tutorial: Retrieve rows table with account number
// @task = app.command
// @doctype = nodocument
// @publisher = Banana.ch SA
// @pubdate = 2019-01-25
// @inputdatasource = none
// @timeout = -1

function exec() {
    //Create a report
    var report = Banana.Report.newReport('Report title');

    //Step 1 - table access
    var accountsTable = Banana.document.table('Accounts');

    //Step 2 - loop on each row of the table, instead of specifying a single row
    for (var rowNumber = 0; rowNumber < accountsTable.rowCount; rowNumber++) {

        var tRow = accountsTable.row(rowNumber);

        //If the row has an account number we save the values and print them
        if (tRow.value('Account')) {

            //Step 3 - select the desired columns
            var account = tRow.value('Account');
            var description = tRow.value('Description');
            var balance = tRow.value('Balance');

            //Add the informations to the report
            report.addParagraph(account + ', ' + description + ', ' + balance);
        }
    }

    //Print the report
    var stylesheet = Banana.Report.newStyleSheet();
    Banana.Report.preview(report, stylesheet);

}

Print all table rows in a table format


// Banana Accounting Extension Javascript
// @api = 1.0
// @id = ch.banana.uni.app.tutorialretprintrowstablewithaccountnumberintableformat
// @description = Tutorial: Print rows table with account number in table format
// @task = app.command
// @doctype = nodocument
// @publisher = Banana.ch SA
// @pubdate = 2019-01-25
// @inputdatasource = none
// @timeout = -1

function exec() {
    //Create a report
    var report = Banana.Report.newReport('Report title');

    //Create the table that will be printed on the report
    var table = report.addTable("myTable");

    //Add column titles to the table report
    var tableHeader = table.getHeader();
    tableRow = tableHeader.addRow();
    tableRow.addCell("Account");
    tableRow.addCell("Description");
    tableRow.addCell("Balance");


    //Step 1 - table access
    var accountsTable = Banana.document.table('Accounts');

    //Step 2 - loop on each row of the table, instead of specifying a single row
    for (var rowNumber = 0; rowNumber < accountsTable.rowCount; rowNumber++) {

        var tRow = accountsTable.row(rowNumber);

        //If the row has an account number we save the values and print them
        if (tRow.value('Account')) {

            //Step 3 - select the desired columns
            var account = tRow.value('Account');
            var description = tRow.value('Description');
            var balance = tRow.value('Balance');

            //Add the values taken from the rows to the respective cells of the table
            tableRow = table.addRow();
            tableRow.addCell(account);
            tableRow.addCell(description);
            tableRow.addCell(balance);
        }
    }

    //Create the styleSheet to 
    var stylesheet = Banana.Report.newStyleSheet();

    //Create a table style adding the border
    var style = stylesheet.addStyle("table");
    stylesheet.addStyle("table.myTable td", "border: thin solid black");

    //Print the report
    Banana.Report.preview(report, stylesheet);

}

Find all rows that match a condition using findRows


// Banana Accounting Extension Javascript
// @api = 1.0
// @id = ch.banana.uni.app.tutorialfindrows
// @description = Tutorial: Retrieve all rows table that match a condition using findRows
// @task = app.command
// @doctype = nodocument
// @publisher = Banana.ch SA
// @pubdate = 2019-01-25
// @inputdatasource = none
// @timeout = -1

function exec() {
    var tableAccounts = Banana.document.table('Accounts');
    var rows = tableAccounts.findRows(accountStartsWith25XX);
    var accounts = [];
    for(var i = 0; i < rows.length; i++) {
       accounts.push(rows[i].value('Account'));
    }
    Banana.Ui.showInformation('Info', 'Accounts that start with 25XX: ' + accounts.join(';')); 
}

function accountStartsWith25XX(rowObj,rowNr,table) {
   // Return true if account start with '25XX'
   return rowObj.value('Account').startsWith('25');
}

Retrieve all rows that match a condition using extractRows


// Banana Accounting Extension Javascript
// @api = 1.0
// @id = ch.banana.uni.app.tutorialextractrows
// @description = Tutorial: Retrieve all rows table that match a condition using extractRows
// @task = app.command
// @doctype = nodocument
// @publisher = Banana.ch SA
// @pubdate = 2019-01-25
// @inputdatasource = none
// @timeout = -1

function exec() {
    var tableAccounts = Banana.document.table('Accounts');
    tableAccounts.extractRows(accountStartsWith25XX, 'Accounts that start with 25XX');
}

function accountStartsWith25XX(rowObj,rowNr,table) {
   // Return true if account start with '25XX'
   return rowObj.value('Account').startsWith('25');
}

Retriveing data with currentBalance() function

Amount of opening for all transactions for an account


// Banana Accounting Extension Javascript
// @api = 1.0
// @id = ch.banana.uni.app.tutorialcurrentbalancesingleaccount
// @description = Tutorial: Retrieve amount of an account using currentBalance() function
// @task = app.command
// @doctype = nodocument
// @publisher = Banana.ch SA
// @pubdate = 2019-01-25
// @inputdatasource = none
// @timeout = -1

function exec() {
    //Create the report
    var report = Banana.Report.newReport('Report title');

    //Take opening sum for a non-specific period: period taken from Banana
    var amount1 = Banana.document.currentBalance('1000', '', '').opening;

    //Take opening sum for a specific period
    var amount2 = Banana.document.currentBalance('1000', '2015-01-05', '2015-02-07').opening;

    //Add a paragraph with the amounts just calculated
    report.addParagraph(Banana.document.accountDescription('1000'));
    report.addParagraph(amount1);
    report.addParagraph(amount2);

    //Print the report
    var stylesheet = Banana.Report.newStyleSheet();
    Banana.Report.preview(report, stylesheet);

}

Amount of opening for all transactions for multiple accounts


// Banana Accounting Extension Javascript
// @api = 1.0
// @id = ch.banana.uni.app.tutorialcurrentbalancemultipleaccounts
// @description = Tutorial: Retrieve amount of multiple accounts using currentBalance() function
// @task = app.command
// @doctype = nodocument
// @publisher = Banana.ch SA
// @pubdate = 2019-01-25
// @inputdatasource = none
// @timeout = -1

function exec() {
    //Create the report
    var report = Banana.Report.newReport('Report title');

    //Take opening sum for a non-specific period: period taken from Banana
    var amount1 = Banana.document.currentBalance('1000|1010|1011', '', '').opening;

    //Take opening sum for a specific period
    var amount2 = Banana.document.currentBalance('1000|1010|1011', '2015-01-05', '2015-02-07').opening;

    //Add a paragraph with the amounts just calculated
    report.addParagraph(amount1);
    report.addParagraph(amount2);

    //Print the report
    var stylesheet = Banana.Report.newStyleSheet();
    Banana.Report.preview(report, stylesheet);

}

Amount of opening for all transactions for a single group


// Banana Accounting Extension Javascript
// @api = 1.0
// @id = ch.banana.uni.app.tutorialcurrentbalancesinglegroup
// @description = Tutorial: Retrieve amount of single group using currentBalance() function
// @task = app.command
// @doctype = nodocument
// @publisher = Banana.ch SA
// @pubdate = 2019-01-25
// @inputdatasource = none
// @timeout = -1

function exec() {
    //Create the report
    var report = Banana.Report.newReport('Report title');

    //Take opening sum for a non-specific period: period taken from Banana
    var amount1 = Banana.document.currentBalance('Gr=100', '', '').opening;

    //Take opening sum for a specific period
    var amount2 = Banana.document.currentBalance('Gr=100', '2015-01-05', '2015-02-07').opening;

    //Add a paragraph with the amounts just calculated
    report.addParagraph(Banana.document.accountDescription('Gr=100'));
    report.addParagraph(amount1);
    report.addParagraph(amount2);

    //Print the report
    var stylesheet = Banana.Report.newStyleSheet();
    Banana.Report.preview(report, stylesheet);

}

Amount of opening for all transactions for multiple groups


// Banana Accounting Extension Javascript
// @api = 1.0
// @id = ch.banana.uni.app.tutorialcurrentbalancemultiplegroups
// @description = Tutorial: Retrieve amount of multiple groups using currentBalance() function
// @task = app.command
// @doctype = nodocument
// @publisher = Banana.ch SA
// @pubdate = 2019-01-25
// @inputdatasource = none
// @timeout = -1

function exec() {
    //Create the report
    var report = Banana.Report.newReport('Report title');

    //Take opening sum for a non-specific period: period taken from Banana
    var amount1 = Banana.document.currentBalance('Gr=100|110|120', '', '').opening;

    //Take opening sum for a specific period
    var amount2 = Banana.document.currentBalance('Gr=100|110|120', '2015-01-05', '2015-02-07').opening;

    //Add a paragraph with the amounts just calculated
    report.addParagraph(amount1);
    report.addParagraph(amount2);

    //Print the report
    var stylesheet = Banana.Report.newStyleSheet();
    Banana.Report.preview(report, stylesheet);

}

Amount of opening for all transactions for a BClass


// Banana Accounting Extension Javascript
// @api = 1.0
// @id = ch.banana.uni.app.tutorialcurrentbalancesinglebclass
// @description = Tutorial: Retrieve amount of a single BClass using currentBalance() function
// @task = app.command
// @doctype = nodocument
// @publisher = Banana.ch SA
// @pubdate = 2019-01-25
// @inputdatasource = none
// @timeout = -1

function exec() {
    //Create the report
    var report = Banana.Report.newReport('Report title');

    //Take opening sum for a non-specific period: period taken from Banana
    var amount1 = Banana.document.currentBalance('BClass=1', '', '').opening;

    //Take opening sum for a specific period
    var amount2 = Banana.document.currentBalance('BClass=1', '2015-01-05', '2015-02-07').opening;

    //Add a paragraph with the amounts just calculated
    report.addParagraph(amount1);
    report.addParagraph(amount2);

    //Print the report
    var stylesheet = Banana.Report.newStyleSheet();
    Banana.Report.preview(report, stylesheet);

}

Amount of opening for all transactions for multiple BClass values


// Banana Accounting Extension Javascript
// @api = 1.0
// @id = ch.banana.uni.app.tutorialcurrentbalancemultiplebclass
// @description = Tutorial: Retrieve amount of multiple BClass using currentBalance() function
// @task = app.command
// @doctype = nodocument
// @publisher = Banana.ch SA
// @pubdate = 2019-01-25
// @inputdatasource = none
// @timeout = -1

function exec() {
    //Create the report
    var report = Banana.Report.newReport('Report title');

    //Take opening sum for a non-specific period: period taken from Banana
    var amount1 = Banana.document.currentBalance('BClass=1|2', '', '').opening;

    //Take opening sum for a specific period
    var amount2 = Banana.document.currentBalance('BClass=1|2', '2015-01-05', '2015-02-07').opening;

    //Add a paragraph with the amounts just calculated
    report.addParagraph(amount1);
    report.addParagraph(amount2);

    //Print the report
    var stylesheet = Banana.Report.newStyleSheet();
    Banana.Report.preview(report, stylesheet);

}

Retriveing data with vatCurrentBalance() function

Sum the Vat amounts for the specified vat code


// Banana Accounting Extension Javascript
// @api = 1.0
// @id = ch.banana.uni.app.tutorialvatcurrentbalancesinglevatcode
// @description = Tutorial: Sum the VAT amounts for single VAT code using vatCurrentBalance() function
// @task = app.command
// @doctype = nodocument
// @publisher = Banana.ch SA
// @pubdate = 2019-01-25
// @inputdatasource = none
// @timeout = -1

function exec() {
    //Create the report
    var report = Banana.Report.newReport('Report title');

    //Take the vatAmount sum for a non-specific period: period taken from Banana
    var amount1 = Banana.document.vatCurrentBalance('S10', '', '').vatAmount;

    //Take the vatAmount sum for a specific period
    var amount2 = Banana.document.vatCurrentBalance('S10', '2015-01-01', '2015-01-06').vatAmount;

    //Add a paragraph with the amounts just calculated
    report.addParagraph(amount1);
    report.addParagraph(amount2);

    //Print the report
    var stylesheet = Banana.Report.newStyleSheet();
    Banana.Report.preview(report, stylesheet);

}

Sum the Vat amounts for multiple vat codes


// Banana Accounting Extension Javascript
// @api = 1.0
// @id = ch.banana.uni.app.tutorialvatcurrentbalancemultiplevatcodes
// @description = Tutorial: Sum the VAT amounts for multiple VAT codes using vatCurrentBalance() function
// @task = app.command
// @doctype = nodocument
// @publisher = Banana.ch SA
// @pubdate = 2019-01-25
// @inputdatasource = none
// @timeout = -1

function exec() {
    //Create the report
    var report = Banana.Report.newReport('Report title');

    //Take the vatAmount sum for a non-specific period: period taken from Banana
    var amount1 = Banana.document.vatCurrentBalance('S10|P10', '', '').vatAmount;

    //Take the vatAmount sum for a specific period
    var amount2 = Banana.document.vatCurrentBalance('S10|P10', '2015-02-01', '2015-02-28').vatAmount;

    //Add a paragraph with the amounts just calculated
    report.addParagraph(amount1);
    report.addParagraph(amount2);

    //Print the report
    var stylesheet = Banana.Report.newStyleSheet();
    Banana.Report.preview(report, stylesheet);

}

Retriveing data with currentCard() function

For a given account without specifying the period


// Banana Accounting Extension Javascript
// @api = 1.0
// @id = ch.banana.uni.app.tutorialcurrentcardnoperiod
// @description = Tutorial: Use currentCard() function for a given account
// @task = app.command
// @doctype = nodocument
// @publisher = Banana.ch SA
// @pubdate = 2019-01-25
// @inputdatasource = none
// @timeout = -1

function exec() {
    //Create the report
    var report = Banana.Report.newReport('Report title');

    //Create a table with all the transactions of the given account
    var transTab = Banana.document.currentCard('1010', '', '');

    //For each row of the table we save the values
    for (var i = 0; i < transTab.rowCount; i++) {
        var tRow = transTab.row(i);

        var date = tRow.value('JDate');
        var account = tRow.value('JAccount');
        var description = tRow.value("JDescription");
        var debit = tRow.value('JDebitAmount');
        var credit = tRow.value('JCreditAmount');
        var balance = tRow.value('JBalance');

        //Add a paragraph with the values just calculated
        report.addParagraph(date + ', ' + account + ', ' + description + ', ' + debit + ', ' + credit + ', ' + balance);
    }

    //Print the report
    var stylesheet = Banana.Report.newStyleSheet();
    Banana.Report.preview(report, stylesheet);

}

For a given account and period


// Banana Accounting Extension Javascript
// @api = 1.0
// @id = ch.banana.uni.app.tutorialcurrentcardwithperiod
// @description = Tutorial: Use currentCard() function for a given account and period
// @task = app.command
// @doctype = nodocument
// @publisher = Banana.ch SA
// @pubdate = 2019-01-25
// @inputdatasource = none
// @timeout = -1

function exec() {
    //Create the report
    var report = Banana.Report.newReport('Report title');

    //Create a table with all transactions for the given account and period
    var transTab = Banana.document.currentCard('1010', '2015-01-03', '2015-02-07');

    //For each row of the table we save the values
    for (var i = 0; i < transTab.rowCount; i++) {
        var tRow = transTab.row(i);

        var date = tRow.value('JDate');
        var account = tRow.value('JAccount');
        var description = tRow.value("JDescription");
        var debit = tRow.value('JDebitAmount');
        var credit = tRow.value('JCreditAmount');
        var balance = tRow.value('JBalance');

        //Add a paragraph with the values just calculated
        report.addParagraph(date + ', ' + account + ', ' + description + ', ' + debit + ', ' + credit + ', ' + balance);
    }

    //Print the report
    var stylesheet = Banana.Report.newStyleSheet();
    Banana.Report.preview(report, stylesheet);

}

Retriveing data with journal() function

Get all transaction for normal accounts


// Banana Accounting Extension Javascript
// @api = 1.0
// @id = ch.banana.uni.app.tutorialjournal
// @description = Tutorial: Retrieve all transactions for normal accounts using the journal() function
// @task = app.command
// @doctype = nodocument
// @publisher = Banana.ch SA
// @pubdate = 2019-01-25
// @inputdatasource = none
// @timeout = -1

function exec() {
    //Create the report
    var report = Banana.Report.newReport('Report title');

    //Create a table with all transactions
    var journal = Banana.document.journal(Banana.document.ORIGINTYPE_CURRENT, Banana.document.ACCOUNTTYPE_NORMAL);

    //Read the table and save some values
    for (var i = 0; i < journal.rowCount; i++) {
        var tRow = journal.row(i);

        var date = tRow.value('JDate');
        var account = tRow.value('JAccount');
        var accDescription = tRow.value('JAccountDescription');
        var description = tRow.value('JDescription');
        var vatCode = tRow.value('JVatCodeWithoutSign');
        var vatDescription = tRow.value('JVatCodeDescription');
        var amount = tRow.value('JAmount');

        //Add to the paragraph the values just saved
        report.addParagraph(date + ', ' + account + ', ' + accDescription + ', ' + description + ', ' + vatCode + ', ' + vatDescription + ', ' + amount);
    }

    //Print the report
    var stylesheet = Banana.Report.newStyleSheet();
    Banana.Report.preview(report, stylesheet);

}

Reading and writing xml files

Reading an xml file


// Banana Accounting Extension Javascript
// @api = 1.0
// @id = ch.banana.uni.app.tutorialreadxmlfile
// @description = Tutorial: Read an XML file
// @task = app.command
// @doctype = nodocument
// @publisher = Banana.ch SA
// @pubdate = 2019-01-25
// @inputdatasource = none
// @timeout = -1


var xml =   '<Library updated="2018-09-03">' +                            //  To simplify,
    '<Book>' +                                                  //  the variable
    '<Title>Paths of colours</Title>' +                       //  xml contains
    '<Author>Rosa Indaco</Author>' +                          //  the xml string
    '</Book>' +
    '<Book>' +
    '<Title>Accounting exercises</Title>' +
    '<Author>Su Zhang</Author>' +
    '</Book>' +
    '</Library>';

var bookList = "";                                                        //  Create string for the output
var xmlFile = Banana.Xml.parse(xml);                                      //  Create XML Parse object
var xmlRoot = xmlFile.firstChildElement('Library');                       //  Find the first tag "Library" in the XML
var updateDate = xmlRoot.attribute('updated');                            //  Take attribute assigned to the tag
bookList += "Books in the library on " + updateDate + "\n\n";             //  Append to the output string

var bookNode = xmlRoot.firstChildElement('Book');                         //  Take the first Book
while (bookNode) {                                                        //  As long as there are books, repeat
    var title = bookNode.firstChildElement('Title').text;                 //  Find the first tag "Title" in the XML
    var authorNode = bookNode.firstChildElement('Author');                //  Find the first tag "Author" in the XML
    var author = authorNode ? authorNode.text : 'unknow';                 //  Check whether there is a tag "Author"
    bookList += title + " - " + author + "\n";                            //  Append to the output string
    bookNode = bookNode.nextSiblingElement('Book');                       //  Go to the next book
}

Banana.Ui.showText("List of books present in the xml file", bookList);    //  Output the results


Writing an xml file


// Banana Accounting Extension Javascript
// @api = 1.0
// @id = ch.banana.uni.app.tutorialwritexmlfile
// @description = Tutorial: Write an XML file
// @task = app.command
// @doctype = nodocument
// @publisher = Banana.ch SA
// @pubdate = 2019-01-25
// @inputdatasource = none
// @timeout = -1

function exec() {

    var xmlDocument = Banana.Xml.newDocument("Library");                    //  Create XML file
    xmlDocument.addComment("This is the generated xml file");               //  Add comment to the file
    var rootNode = xmlDocument.addElement("Library");                       //  Set root tag "Library"
    rootNode.setAttribute("updated", "2018-09-03");      //  Set attribute to the tag "Library"

    var bookNode = rootNode.addElement("Book");                             //  Create tag "Book" child of "Library"
    bookNode.addElement("Title").addTextNode("Paths of colours");           //  Create tag "Title" child of "Book"
    bookNode.addElement("Author").addTextNode("Rosa Indaco");               //  Create tag "Author" child of "Book"

    var bookNode = rootNode.addElement("Book");                             //  Create tag "Book" child of "Library"
    bookNode.addElement("Title").addTextNode("Accounting exercises");       //  Create tag "Title" child of "Book"
    bookNode.addElement("Author").addTextNode("Su Zhang");                  //  Create tag "Author" child of "Book"

    var xmlString = Banana.Xml.save(xmlDocument);                           //  Create output

    // Show the xml file
    Banana.Ui.showText("Xml file", xmlString);                              //  Generate output
}

Validate an xml file via xsd


// Banana Accounting Extension Javascript
// @api = 1.0
// @id = ch.banana.uni.app.tutorialxmlvalidation
// @description = Tutorial: Validate an XML file via XSD
// @task = app.command
// @doctype = nodocument
// @publisher = Banana.ch SA
// @pubdate = 2019-01-25
// @inputdatasource = none
// @timeout = -1

function exec() {

    // The xml file
    var xml = '<Library updated="2018-09-03"> \
		<Book> \
			<Title>Paths of colours</Title> \
			<Author>Rosa Indaco</Author> \
		</Book> \
		<Book> \
			<Title>Accounting exercises</Title> \
			<Author>Su Zhang</Author> \
		</Book> \
   </Library>';

    // Parse the xml
    var xmlFile = Banana.Xml.parse(xml);

    // Validate the xml
    var valid = Banana.Xml.validate(xmlFile, 'documents:xsd');
    if (valid) {
        Banana.Ui.showInformation('Validation result', 'The xml is valid');
    } else  {
        Banana.Ui.showInformation('Validation result', 'The xml is not valid: ' + Banana.Xml.errorString);
    }
}

Filter for xml file


// Banana Accounting Extension Javascript
// @api = 1.0
// @id = ch.banana.uni.app.tutorialgeneralxmlFile
// @description = Tutorial: General functions on XML files
// @task = app.command
// @doctype = nodocument
// @publisher = Banana.ch SA
// @pubdate = 2020-03-27
// @inputdatasource = none
// @timeout = -1

function exec(){
    const xml = '<solarSystem updated="2020-03-27">' +
                    '<Planet>' +
                        '<name>Sun</name>' +
                        '<sizeOrder>1</sizeOrder>' +
                    '</Planet>' +
                    '<Planet>' +
                        '<name>Earth</name>' +
                        '<sizeOrder>6</sizeOrder>' +
                        '<satellite>Moon</satellite>' +
                    '</Planet>' +
                    '<Planet>' +
                        '<name>Jupiter</name>' +
                        '<sizeOrder>2</sizeOrder>' +
                        '<satellite>Callisto</satellite>' +
                    '</Planet>' +
                    '<Planet>' +
                        '<name>Saturn</name>' +
                        '<sizeOrder>3</sizeOrder>' +
                        '<satellite>Cronus</satellite>' +
                    '</Planet>' +
                    '<Planet>' +
                        '<name>Mars</name>' +
                        '<sizeOrder>8</sizeOrder>' +
                        '<satellite>Phobos</satellite>' +
                    '</Planet>' +
                    '<Planet>' +
                        '<name>Neptune</name>' +
                        '<sizeOrder>5</sizeOrder>' +
                        '<satellite>Triton</satellite>' +
                    '</Planet>' +
                    '<Planet>' +
                        '<name>Mercury</name>' +
                        '<sizeOrder>9</sizeOrder>' +
                    '</Planet>' +
                    '<Planet>' +
                        '<name>Venus</name>' +
                        '<sizeOrder>7</sizeOrder>' +
                    '</Planet>' +
                    '<Planet>' +
                        '<name>Uranus</name>' +
                        '<sizeOrder>4</sizeOrder>' +
                        '<satellite>Ariel</satellite>' +
                    '</Planet>' +
        '</solarSystem>';

    var outputString = "";
    var xmlFile = Banana.Xml.parse(xml);
    var xmlRoot = xmlFile.firstChildElement('solarSystem');
    var updateDate = xmlRoot.attribute('updated');
    outputString += "Planets in the solar System with satellites updated on " + updateDate + "\n\n";

    var planet = xmlRoot.firstChildElement('Planet');
    while (planet) {
        if (planet.hasChildElements('satellite')) {
            var planetName = planet.firstChildElement('name').text;
            var planetSatellite = planet.firstChildElement('satellite').text;
            outputString += "name: " + planetName + ", satellite: " + planetSatellite + "\n";
        }
        planet = planet.nextSiblingElement('Planet');
    }
    Banana.Ui.showText("Planets in the XML with satellites", outputString);
}

Export Transaction table to xml file


// Banana Accounting Extension Javascript
// @id = ch.banana.apps.export
// @api = 1.0
// @pubdate = 2016-04-08
// @doctype = *.*
// @description = Export into an xml file (.xml)
// @task = export.file
// @exportfiletype = xml
// @timeout = -1

function exec() {
    var xmlDocument = Banana.Xml.newDocument("Transactions");
    xmlDocument.addComment("This is the generated xml file");
    var rootNode = xmlDocument.addElement("transactions");
    var tableTransactions = Banana.document.table('Transactions');
    if (!tableTransactions) {
        return;
    }
    for (i=0;i<tableTransactions.rowCount;i++) {
        if (tableTransactions.row(i).value('Amount')) {
            var transactionNode = rootNode.addElement("transaction");
            transactionNode.addElement("date").addTextNode(tableTransactions.row(i).value('Date'));
            transactionNode.addElement("description").addTextNode(tableTransactions.row(i).value('Description'));
            transactionNode.addElement("amount").addTextNode(tableTransactions.row(i).value('Amount'));
        }
    }
    var xmlString = Banana.Xml.save(xmlDocument);
    Banana.Ui.showText("Xml file", xmlString);
    return xmlString;
}

Import Transactions from xml file


// Banana Accounting Extension Javascript
// @api = 1.0
// @pubdate = 2020-04-02
// @id = ch.banana.uni.app.tutorialretrieverowstablevalues
// @description = Import transactions
// @task = import.transactions
// @outputformat = transactions.simple
// @doctype = *
// @inputdatasource = openfiledialog
// @inputencoding = latin1
// @inputfilefilter = Text files (*.xml);;All files (*.*)
// @publisher = Banana.ch SA
// @timeout = -1

function exec(inputFile) {
    var CSV_String = "Date" + '","' + "Description" + '","' + "Income" + '","' + "Expenses" + '\n';
    var xmlFile = Banana.Xml.parse(inputFile);
    var xmlRoot = xmlFile.firstChildElement('transactions');
    var transactionNode = xmlRoot.firstChildElement('transaction');
    while (transactionNode) {
        var date = transactionNode.firstChildElement('date').text;
        var description = transactionNode.firstChildElement('description').text;
        var income = transactionNode.firstChildElement('income').text;
        var expenses = transactionNode.firstChildElement('expenses').text;
        CSV_String += ('"' + date + '","' + description + '","' + income + '","' + expenses + '"' + '\n');
        transactionNode = transactionNode.nextSiblingElement('transaction');
    }
    Banana.console.debug(CSV_String);
    var csvFile = Banana.Converter.csvToArray(CSV_String, ',', '"');
    var tsvFile = Banana.Converter.arrayToTsv(csvFile);
    return tsvFile;
}

Debugging

Ouput messages to the debug panel


// Banana Accounting Extension Javascript
// @api = 1.0
// @id = ch.banana.uni.app.tutorialdebugmessages
// @description = Tutorial: Output messages to the debug panel
// @task = app.command
// @doctype = nodocument
// @publisher = Banana.ch SA
// @pubdate = 2019-01-25
// @inputdatasource = none
// @timeout = -1

function exec() {
    Banana.console.log("An info message");
    Banana.console.debug("A debug message");
    Banana.console.warn("A warning message"); // Since Banana 9.0.4 use Banana.console.warning
    Banana.console.critical("A critical message");
}

Progress bar and timeout

Script timeout


// Banana Accounting Extension Javascript
// @api = 1.0
// @id = ch.banana.uni.app.tutorialscripttimeout
// @description = Tutorial: Progress bar
// @task = app.command
// @doctype = nodocument
// @publisher = Banana.ch SA
// @pubdate = 2019-12-06
// @inputdatasource = none
// @timeout = 2000

function exec() {
	while(true) {
    }     
}

Progess bar


// Banana Accounting Extension Javascript
// @api = 1.0
// @id = ch.banana.uni.app.tutorialprogressbar
// @description = Tutorial: Progress bar
// @task = app.command
// @doctype = nodocument
// @publisher = Banana.ch SA
// @pubdate = 2019-12-06
// @inputdatasource = none
// @timeout = 20000

function exec() {
     
    var nr = 100000000;
 
    var progressBar = Banana.application.progressBar;
    progressBar.start(nr); 
    
    for (var i = 0; i < nr; ++i) {
       if (!progressBar.step(1)) {
           break; // Operation canceled by the user
     	}
    }
}

User interface

QTableWidget interaction


// Banana Accounting Extension Javascript
/**
 * This example shows the use of a QTableWidget
 * The user interface is defined in file "ch.banana.tutorial.tablewidget.ui"
 * This example is also found in the tutorial file "embedded_javascript_tutorial1.ac2"
 */
// @id = ch.banana.exmaple.tablewidget
// @version = 1.0
// @date = 2019-12-06
// @publisher = Banana.ch SA
// @description = Tutorial QTableWidget
// @task = app.command
// @inputdatasource = none
// @timeout = -1
// @doctype = nodocument

var param = {};

/** Dialog's functions declaration */
var dialog = Banana.Ui.createUi("documents:740.ui");
var table = dialog.findChild("tableWidget");
var buttonBox = dialog.findChild("buttonBox");
var statusLabel = dialog.findChild("statusLabel");

/** Main function */
function exec(inData) {
    //Check the version of Banana
    var requiredVersion = "9.1.0.0";
    if (!Banana.compareVersion || Banana.compareVersion(Banana.application.version, requiredVersion) < 0) {
        Banana.Ui.showInformation("You need Banana 9.1.0.0 or newer to run this example");
        return;
	}

    // Connect button box signals
    buttonBox.accepted.connect(function() {dialog.close();});
    buttonBox.rejected.connect(function() {dialog.close();});

    // Set the table read
    // table.setEditTriggers(0); // see flags QAbstractItemView::EditTriggers

    // Set columns
    table.setColumnCount(4);
    table.setHorizontalHeaderLabels(["A", "B", "C", "D"])
    table.setColumnWidth(1, 200);

    // Set rows
    table.setRowCount(5);

    // Set cell text
    table.item(1,0).text = "hello";

    // Set cell colors
    var item2 = table.item(1,1);
    item2.text = "colors";
    item2.background = "red";
    item2.foreground = "white";

    // Set cell checkable
    var item3 = table.item(1,2);
    item3.flags = 0x33; // See flags Qt::ItemFlags
    item3.text = "check-it";
    item3.checkState = 0; // See flags Qt::CheckState

    // Set current cell
    // table.setCurrentCell(1,0);

    // Connect table's signals after the table is populated
    table.cellClicked.connect(updateStatusBar);
    table.cellChanged.connect(updateStatusBar);

    // Show dialog
    Banana.application.progressBar.pause();
    dialog.exec();
    Banana.application.progressBar.resume();

    // Get table size
    Banana.console.log("Table size: " + table.rowCount + ", " + table.columnCount);

    // Get current position
    Banana.console.log("Current position: " + table.currentRow + ", " + table.currentColumn);

    // Get current text
    var curItem = table.currentItem();
    Banana.console.log("Current text: " + curItem.text);

    // Get item check state
    Banana.console.log("Item check state: " + (item3.checkState === 2 ? "checked" : "unchecked"));
}

// Update status bar with useful info about the current cell
function updateStatusBar() {
    var curItem = table.currentItem();
    if (curItem) {
        var msg = "Cell " + (table.currentRow + 1) + "," + (table.currentColumn + 1) + " clicked. ";
        if (curItem.text.length > 0)
            msg += "Text: '" + curItem.text + "'. ";
        if (curItem.checkState === 2)
            msg += "Checked: true.";
        statusLabel.text = msg;
    } else {
        statusLabel.text = "";
    }
}

- User interface for QTableWidget interaction


// Banana Accounting Extension Javascript
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>Dialog</class>
 <widget class="QDialog" name="Dialog">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>584</width>
    <height>411</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>QTableWidget Tutorial</string>
  </property>
  <layout class="QVBoxLayout" name="verticalLayout">
   <item>
    <widget class="QTableWidget" name="tableWidget"/>
   </item>
   <item>
    <layout class="QHBoxLayout" name="horizontalLayout">
     <item>
      <widget class="QLabel" name="statusLabel">
       <property name="text">
        <string/>
       </property>
      </widget>
     </item>
    </layout>
   </item>
   <item>
    <widget class="QDialogButtonBox" name="buttonBox">
     <property name="standardButtons">
      <set>QDialogButtonBox::Ok</set>
     </property>
    </widget>
   </item>
  </layout>
 </widget>
 <tabstops>
  <tabstop>tableWidget</tabstop>
 </tabstops>
 <resources/>
 <connections/>
</ui>

Find dialog


// Banana Accounting Extension Javascript
/** 
* This example search a text in all the tables of the document, 
* and show the matches in the messages pane.
*/
// @id = ch.banana.scripts.find
// @version = 1.3
// @date = 2019-12-06
// @publisher = Banana.ch SA
// @description = Find in whole accounting
// @description.it = Cerca in tutta la contabilità
// @description.de = Suchen in der gesamten Buchhaltung
// @description.fr = Chercher dans toute la comptabilité
// @task = app.command
// @inputdatasource = none
// @timeout = -1

/**
 * param values are loaded from Banana.document, edited through dialog and saved to Banana.document
 * This array object is like a map (associative array) i.e. "key":"value", see initParam()
 * Examples of keys: searchText, wholeText, ...
 */
var param = {};

/** Dialog's functions declaration */
var dialog = Banana.Ui.createUi("documents:742.ui"); // ch.banana.scripts.find.ui

/** Dialog Objects declaration */
var findNextButton = dialog.findChild('findNextButton');
var helpButton = dialog.findChild('helpButton');
var closeButton = dialog.findChild('closeButton');
var searchTextLineEdit = dialog.findChild('searchTextLineEdit');
var matchCaseCheckBox = dialog.findChild('matchCaseCheckBox');
var wholeTextCheckBox = dialog.findChild('wholeTextCheckBox');


dialog.checkdata = function () {
    var valid = true;

    if (searchTextLineEdit.text.length <= 0) {
        Banana.Ui.showInformation("Error", "Search text can't be empty");
        valid = false;
    }

    if (valid) {
        dialog.accept();
    }
};

dialog.showHelp = function () {
    Banana.Ui.showHelp("ch.banana.script.find");
};

dialog.closeDialog = function () {
    dialog.close();
};

/** Dialog's events declaration */
findNextButton.clicked.connect(dialog, dialog.checkdata);
helpButton.clicked.connect(dialog, dialog.showHelp);
closeButton.clicked.connect(dialog, dialog.closeDialog);

/** Main function */
function exec(inData) {
    //calls dialog
    // var rtnDialog = true;
    var rtnDialog = dialogExec();

    //search text in the whole accounting
    if (rtnDialog && Banana.document) {
        Banana.document.clearMessages();
        searchInTables();
    }
}

/** Show the dialog and set the parameters */
function dialogExec() {
    // Read saved script settings
    initParam();
    if (Banana.document) {
        var data = Banana.document.getScriptSettings();
        if (data.length > 0) {
            param = JSON.parse(data);
        }
    }

    // Text at cursor position
    var cursor = Banana.document.cursor;
    var columnName = cursor.tableName === 'Documents' && cursor.columnName == 'Attachments' ? 'Description' : cursor.columnName;
    param["searchText"] = Banana.document.value(cursor.tableName,cursor.rowNr,columnName);
    searchTextLineEdit.text = param["searchText"];
    // Set dialog parameters
    if (param["matchCase"] == "true")
        matchCaseCheckBox.checked = true;
    else
        matchCaseCheckBox.checked = false;
    if (param["wholeText"] == "true")
        wholeTextCheckBox.checked = true;
    else
        wholeTextCheckBox.checked = false;

    Banana.application.progressBar.pause();
    var dlgResult = dialog.exec();
    Banana.application.progressBar.resume();

    if (dlgResult !== 1)
        return false;

    // Read dialog parameters
    param["searchText"] = searchTextLineEdit.text;
    if (matchCaseCheckBox.checked)
        param["matchCase"] = "true";
    else
        param["matchCase"] = "false";
    if (wholeTextCheckBox.checked)
        param["wholeText"] = "true";
    else
        param["wholeText"] = "false";

    // Save script settings
    var paramString = JSON.stringify(param);
    var value = Banana.document.setScriptSettings(paramString);

    return true;
}

/** Initialize dialog values with default values */
function initParam() {
    param = {
        "searchText": "",
        "matchCase": "false",
        "wholeText": "false"
    };
}

/** Search a text in the accounting's tables */
function searchInTables() {
    var searchText = param["searchText"];
    if (param["matchCase"] === "false")
        searchText = searchText.toLowerCase();
    var tables = Banana.document.tableNames;
    // Tables
    for (var t=0; t < tables.length; t++) {
        var table = Banana.document.table(tables[t]);
        var columns = table.columnNames;
        // Rows
        for (var r=0; r < table.rowCount; r++) {
            // Columns
            for (var c=0; c < columns.length; c++) {
                var textFound = false;
                var text = table.value(r, columns[c]);
                if (param["matchCase"] === "false")
                    text = text.toLowerCase();
                // Find text
                if (param["wholeText"] === "true") {
                    if (text === searchText)
                        textFound = true;
                } else {
                    if (text.indexOf(searchText) >= 0)
                        textFound = true;
                }
                // Show message
                if (textFound) {
                    table.addMessage("Text \"" + param["searchText"] +
                        "\" found in \"" + table.value(r, columns[c]) + "\"", r, columns[c]);
                }
            }
        }
    }
}

- User interface for Find dialog


// Banana Accounting Extension Javascript
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>DlgFind</class>
 <widget class="QDialog" name="DlgFind">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>438</width>
    <height>193</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>Find</string>
  </property>
  <layout class="QVBoxLayout" name="verticalLayout_2">
   <item>
    <layout class="QGridLayout" name="gridLayout">
     <property name="horizontalSpacing">
      <number>40</number>
     </property>
     <item row="0" column="0">
      <widget class="QLabel" name="searchTextLabel">
       <property name="text">
        <string>Search &text</string>
       </property>
      </widget>
     </item>
     <item row="0" column="1">
      <widget class="QLineEdit" name="searchTextLineEdit"/>
     </item>
    </layout>
   </item>
   <item>
    <widget class="QGroupBox" name="groupBox">
     <property name="title">
      <string>Options</string>
     </property>
     <property name="flat">
      <bool>false</bool>
     </property>
     <property name="checkable">
      <bool>false</bool>
     </property>
     <layout class="QVBoxLayout" name="verticalLayout">
      <item>
       <widget class="QCheckBox" name="matchCaseCheckBox">
        <property name="text">
         <string>&Match case</string>
        </property>
       </widget>
      </item>
      <item>
       <widget class="QCheckBox" name="wholeTextCheckBox">
        <property name="text">
         <string>&Whole text</string>
        </property>
       </widget>
      </item>
     </layout>
    </widget>
   </item>
   <item>
    <spacer name="verticalSpacer">
     <property name="orientation">
      <enum>Qt::Vertical</enum>
     </property>
     <property name="sizeHint" stdset="0">
      <size>
       <width>20</width>
       <height>15</height>
      </size>
     </property>
    </spacer>
   </item>
   <item>
    <layout class="QHBoxLayout" name="horizontalLayout">
     <item>
      <spacer name="horizontalSpacer">
       <property name="orientation">
        <enum>Qt::Horizontal</enum>
       </property>
       <property name="sizeHint" stdset="0">
        <size>
         <width>80</width>
         <height>20</height>
        </size>
       </property>
      </spacer>
     </item>
     <item>
      <widget class="QPushButton" name="findNextButton">
       <property name="text">
        <string>&Find</string>
       </property>
      </widget>
     </item>
     <item>
      <widget class="QPushButton" name="helpButton">
       <property name="text">
        <string>Help</string>
       </property>
      </widget>
     </item>
     <item>
      <widget class="QPushButton" name="closeButton">
       <property name="text">
        <string>Close</string>
       </property>
      </widget>
     </item>
    </layout>
   </item>
  </layout>
 </widget>
 <tabstops>
  <tabstop>matchCaseCheckBox</tabstop>
  <tabstop>findNextButton</tabstop>
 </tabstops>
 <resources/>
 <connections/>
</ui>

Export

Export tutorial1 javascript codes


// Banana Accounting Extension Javascript
// @id = ch.banana.apps.exportjavascriptcodestutorial1.js
// @api = 1.0
// @publisher = Banana.ch SA
// @description = Tutorial: Export javascript codes of the Tutorial 1 examples
// @task = export.file
// @doctype = *.*
// @docproperties = 
// @timeout = -1
// @exportfiletype = js

//Main function
function exec() {

    //Check if we are on an opened document
    if (!Banana.document) {
        return;
    }

    //Take the table Documents
    var documents = Banana.document.table('Documents');

    //We check if the table Documents exists, then we can take all the codes 
    //If the table Documents doesn't exists, then we stop the script execution
    if (!documents) {
        return;
    } else {

        //We use this variable to save all the codes
        var jsCode = '';

        jsCode += '// Copyright [2018] [Banana.ch SA - Lugano Switzerland]' + '\n';
        jsCode += '// ' + '\n';
        jsCode += '// Licensed under the Apache License, Version 2.0 (the "License");' + '\n';
        jsCode += '// you may not use this file except in compliance with the License.' + '\n';
        jsCode += '// You may obtain a copy of the License at' + '\n';
        jsCode += '// ' + '\n';
        jsCode += '//     http://www.apache.org/licenses/LICENSE-2.0' + '\n';
        jsCode += '// ' + '\n';
        jsCode += '// Unless required by applicable law or agreed to in writing, software' + '\n';
        jsCode += '// distributed under the License is distributed on an "AS IS" BASIS,' + '\n';
        jsCode += '// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.' + '\n';
        jsCode += '// See the License for the specific language governing permissions and' + '\n';
        jsCode += '// limitations under the License.' + '\n' + '\n' + '\n' + '\n' + '\n' + '\n' + '\n';

        //Function call to get all the tutorial's codes
        jsCode = getJavascriptCode(jsCode, documents);
    }

    return jsCode;
}




//Function that, for each tutorial's example, gets the javascript code and save it into the jsCode variable.
function getJavascriptCode(jsCode, documents) {

    //Read row by row the table Documents
    var len = documents.rowCount;
    for (var i = 0; i < len; i++) {

        //Create a variable for the row
        var tRow = documents.row(i);

        //We get some values
        var fName = Banana.document.info("Base", "FileName").replace(/^.*[\\\/]/, ''); //the file name (without path)
        var id = tRow.value("RowId"); //the id of the example
        var description = tRow.value("Description"); //the description of the example
        var attachments = tRow.value("Attachments"); //the javascript code of the example

        //We consider only the rows that contain an id, a description and an attachment
        if (id && description && attachments) {

            //At the beginning of each code, we insert some information
            jsCode += "/** " + '\n';
            jsCode += "    File:        " + fName + '\n';
            jsCode += "    Id:          " + id + '\n';
            jsCode += "    Description: " + description + '\n';
            jsCode += "*/" + '\n';

            //we add the code of the attachments
            jsCode += attachments;

            //At the end of each code, we insert some new lines
            jsCode += '\n' + '\n' + '\n' + '\n' + '\n' + '\n' + '\n';
        }
    }

    //Finally we return the variable containing all the codes of all the examples
    return jsCode;
}

Export tutorial1 javascript codes to HTML


// Banana Accounting Extension Javascript
// @id = ch.banana.apps.exporthtmljavascriptcodestutorial1.js
// @api = 1.0
// @publisher = Banana.ch SA
// @description = Tutorial: Export javascript codes of the Tutorial 1 examples in HTML
// @task = export.file
// @doctype = *.*
// @docproperties = 
// @timeout = -1
// @exportfiletype = html

//Main function
function exec() {

    //Check if we are on an opened document
    if (!Banana.document) {
        return;
    }

    //Take the table Documents
    var documents = Banana.document.table('Documents');

    //We check if the table Documents exists, then we can take all the codes 
    //If the table Documents doesn't exists, then we stop the script execution
    if (!documents) {
        return;
    } else {

        //We use this variable to save all the codes
        var jsCode = '';

        //Function call to get all the tutorial's codes
        jsCode = getJavascriptCode(jsCode, documents);
    }

    return jsCode;
}

//Function that, for each tutorial's example, gets the javascript code and save it into the jsCode variable.
function getJavascriptCode(jsCode, documents) {

    //Read row by row the table Documents
    var len = documents.rowCount;
    for (var i = 0; i < len; i++) {

        //Create a variable for the row
        var tRow = documents.row(i);

        //We get some values
        var fName = Banana.document.info("Base", "FileName").replace(/^.*[\\\/]/, ''); //the file name (without path)
        var section = tRow.value("Section"); // the section column used to define the title type
        var id = tRow.value("RowId"); //the id of the example
        var description = tRow.value("Description"); //the description of the example
        var attachments = tRow.value("Attachments"); //the javascript code of the example
        attachments = attachments.replace(/</g,'&lt;'); // replace the < symbol
        attachments = attachments.replace(/>/g,'&gt;'); // replace the > symbol

        //For tilte h2 we conside only rows with section h2
        if (section && section === "h2") {
            jsCode += '<h2>' + description + '</h2>\n';
        }
        
        //For js codes we consider only rows with section h3, id, description and attachment
        else if (section && section === "h3" && id && description && attachments) {
            jsCode += '<h3>' + description + '</h3>\n';
            jsCode += '<pre><code class="language-javascript">\n';
            jsCode += '// Banana Accounting Extension Javascript' + '\n';
            jsCode += attachments;
            jsCode += '\n';
            jsCode += '</code></pre>\n';
        }
    }

    //Finally we return the variable containing all the codes of all the examples
    return jsCode;
}

Embedded Extensions examples Tutorial 2 (Reports)

Complete examples

Account statement - account and period choice


// Banana Accounting Extension Javascript
// @api = 1.0
// @id = ch.banana.uni.app.tutorialaccountstatement
// @description = Tutorial: Return an account statement for the given account and period
// @task = app.command
// @doctype = nodocument
// @publisher = Banana.ch SA
// @pubdate = 2019-01-25
// @inputdatasource = none
// @timeout = -1

function exec() {
    //Create the report
    var report = Banana.Report.newReport('Report title');

    //Function call to add the footer to the report
    addFooter(report);

    //Function call to take a list of all the account numbers
    var accountList = getAccountsList();

    //Open a dialog window asking to select an item from the list. The selected item is then saved into a variable
    var accountNumber = Banana.Ui.getItem('Account selection', 'Select an account', accountList, 0, false);

    //Open a dialog windows taskint the user to choose a period
    //Return an object with the attributes 'startDate', 'endDate' and 'hasSelection' or undefined if the user clicked cancel
    var date = Banana.Ui.getPeriod('Period selection', '2015-01-01', '2015-12-31');

    //Check if user has clicked cancel, then use the default startDate/endDate (whole year)
    if (date) {
        var openingDate = date.startDate;
        var closureDate = date.endDate;
    }


    //--------------//
    //	1. TITLE	//
    //--------------//
    //Take the description of the given account and add it to the paragraph
    var accountDescription = Banana.document.table('Accounts').findRowByValue('Account', accountNumber).value('Description');
    report.addParagraph("Account statement" + " - " + accountNumber + " " + accountDescription, "styleTitle styleBottomBorder");
    report.addParagraph(" ");


    //--------------//
    //	2. TABLE 	//
    //--------------//
    //Create a table object with all transactions for the given account and period
    var transTab = Banana.document.currentCard(accountNumber, openingDate, closureDate);

    //Create the table that will be printed on the report
    var table = report.addTable("myTable");

    //Add column titles to the table report
    var tableHeader = table.getHeader();
    tableRow = tableHeader.addRow();
    tableRow.addCell("Date", "styleTableHeader");
    tableRow.addCell("Description", "styleTableHeader");
    tableRow.addCell("Contra Account", "styleTableHeader");
    tableRow.addCell("Debit amount", "styleTableHeader");
    tableRow.addCell("Credit amount", "styleTableHeader");
    tableRow.addCell("Balance", "styleTableHeader");

    //Add the values taken from each row of the table (except the last one) to the respective cells of the table
    for (var i = 0; i < transTab.rowCount - 1; i++) {
        var tRow = transTab.row(i);
        tableRow = table.addRow();
        tableRow.addCell(Banana.Converter.toLocaleDateFormat(tRow.value('JDate')));
        tableRow.addCell(tRow.value("JDescription"));
        tableRow.addCell(tRow.value("JContraAccount"), "styleAccount");
        tableRow.addCell(Banana.Converter.toLocaleNumberFormat(tRow.value('JDebitAmountAccountCurrency')), "styleAmount");
        tableRow.addCell(Banana.Converter.toLocaleNumberFormat(tRow.value('JCreditAmountAccountCurrency')), "styleAmount");
        if (Banana.SDecimal.sign(tRow.value('JBalanceAccountCurrency')) >= 0) {
            tableRow.addCell(Banana.Converter.toLocaleNumberFormat(tRow.value('JBalanceAccountCurrency')), "styleAmount");
        } else {
            tableRow.addCell(Banana.Converter.toLocaleNumberFormat(tRow.value('JBalanceAccountCurrency')), "styleBalance styleAmount");
        }
    }

    //We add last row (totals) separately because we want to apply a special style only to this row
    for (var i = transTab.rowCount - 1; i < transTab.rowCount; i++) {
        var tRow = transTab.row(i);
        tableRow = table.addRow();
        tableRow.addCell(Banana.Converter.toLocaleDateFormat(tRow.value('JDate')), "styleTotal");
        tableRow.addCell(tRow.value("JDescription"), "styleTotal");
        tableRow.addCell(tRow.value("JContraAccount"), "styleAccount styleTotal");
        tableRow.addCell(Banana.Converter.toLocaleNumberFormat(tRow.value('JDebitAmountAccountCurrency')), "styleAmount styleTotal");
        tableRow.addCell(Banana.Converter.toLocaleNumberFormat(tRow.value('JCreditAmountAccountCurrency')), "styleAmount styleTotal");
        if (Banana.SDecimal.sign(tRow.value('JBalanceAccountCurrency')) >= 0) {
            tableRow.addCell(Banana.Converter.toLocaleNumberFormat(tRow.value('JBalanceAccountCurrency')), "styleAmount styleTotal");
        } else {
            tableRow.addCell(Banana.Converter.toLocaleNumberFormat(tRow.value('JBalanceAccountCurrency')), "styleBalance styleAmount styleTotal");
        }
    }

    //Functin call to create all the styles
    var stylesheet = createStyleSheet();

    //Print the report
    Banana.Report.preview(report, stylesheet);
}



//This function adds a Footer to the report
function addFooter(report) {
    report.getFooter().addClass(".img");
    report.getFooter().addImage("documents:logo");
}


//This function take from Banana table 'Accounts' all the account numbers and create a list
function getAccountsList() {
    var arrList = [];
    for (var i = 0; i < Banana.document.table('Accounts').rowCount; i++) {
        var tRow = Banana.document.table('Accounts').row(i);
        if (tRow.value("Account")) {
            arrList.push(tRow.value("Account"));
        }
    }
    return arrList;
}


//The main purpose of this function is to create styles for the report print
function createStyleSheet() {

    var stylesheet = Banana.Report.newStyleSheet();

    //Page style
    var pageStyle = stylesheet.addStyle("@page");
    pageStyle.setAttribute("margin", "20m 15mm 15mm 25mm");

    //Footer style
    style = stylesheet.addStyle(".img");
    style.setAttribute("width", "50%");
    style.setAttribute("height", "auto");
    style.setAttribute("text-align", "center");

    //Title style
    style = stylesheet.addStyle(".styleTitle");
    style.setAttribute("font-size", "14pt");
    style.setAttribute("font-weight", "bold");

    //Bottom border
    style = stylesheet.addStyle(".styleBottomBorder");
    style.setAttribute("border-bottom", "1px solid black");

    //Create a table style adding the border
    style = stylesheet.addStyle("table");
    style.setAttribute("width", "100%");
    stylesheet.addStyle("table.myTable td", "border: thin solid black");

    //Style for the table titles
    style = stylesheet.addStyle(".styleTableHeader");
    style.setAttribute("font-weight", "bold");
    style.setAttribute("padding-bottom", "5px");
    style.setAttribute("background-color", "#ffd100");
    style.setAttribute("color", "#1b365d");

    //Style for account numbers
    style = stylesheet.addStyle(".styleAccount");
    style.setAttribute("padding-bottom", "5px");
    style.setAttribute("text-align", "center");

    //Style for amounts
    style = stylesheet.addStyle(".styleAmount");
    style.setAttribute("padding-bottom", "5px");
    style.setAttribute("text-align", "right");

    //Style for balances
    style = stylesheet.addStyle(".styleBalance");
    style.setAttribute("color", "red");

    //Style for the total of the table
    style = stylesheet.addStyle(".styleTotal");
    style.setAttribute("font-weight", "bold");
    style.setAttribute("padding-bottom", "5px");
    style.setAttribute("background-color", "#b7c3e0");
    style.setAttribute("border-bottom", "1px double black");

    return stylesheet;
}

Account statement - more accounts print (without accounts/period choice)


// Banana Accounting Extension Javascript
// @api = 1.0
// @id = ch.banana.uni.app.tutorialaccountstatements
// @description = Tutorial: Return a multiple accounts statements
// @task = app.command
// @doctype = nodocument
// @publisher = Banana.ch SA
// @pubdate = 2019-01-25
// @inputdatasource = none
// @timeout = -1

function exec() {
    //Create the report
    var report = Banana.Report.newReport('Report title');

    //Function call to add the footer to the report
    addFooter(report);

    //First function call to create the page of the report
    createReport(report, '1010', '2015-01-01', '2015-12-31');

    //Add a page break after the first page
    report.addPageBreak();

    //Second function call to create the second page of the report
    createReport(report, '1011', '2015-01-01', '2015-12-31');

    //Functin call to create all the styles
    var stylesheet = createStyleSheet();

    //Print the report
    Banana.Report.preview(report, stylesheet);
}


//The purpose of this function is to create a report using the given accounts number and period
function createReport(report, accountNumber, openingDate, closureDate) {


    //--------------//
    //	1. ADDRESS	//
    //--------------//

    //Read from the table 'Address' some informations and save them
    var namePrefix = Banana.document.table('Accounts').findRowByValue('Account', accountNumber).value('NamePrefix');
    var firstName = Banana.document.table('Accounts').findRowByValue('Account', accountNumber).value('FirstName');
    var familyName = Banana.document.table('Accounts').findRowByValue('Account', accountNumber).value('FamilyName');
    var organisationName = Banana.document.table('Accounts').findRowByValue('Account', accountNumber).value('OrganisationName');
    var address = Banana.document.table('Accounts').findRowByValue('Account', accountNumber).value('Street');
    var countryCode = Banana.document.table('Accounts').findRowByValue('Account', accountNumber).value('CountryCode');
    var postalCode = Banana.document.table('Accounts').findRowByValue('Account', accountNumber).value('PostalCode');
    var locality = Banana.document.table('Accounts').findRowByValue('Account', accountNumber).value('Locality');
    var telephone = Banana.document.table('Accounts').findRowByValue('Account', accountNumber).value('PhoneMain');
    var email = Banana.document.table('Accounts').findRowByValue('Account', accountNumber).value('EmailWork');




    //Add a section to the paragraph for the address
    var sectionAddress = report.addSection("styleAddress");

    //Check if there are the desired address informations, then add them to the paragraph
    if (organisationName) {
        sectionAddress.addParagraph(organisationName);
    }

    if (namePrefix) {
        sectionAddress.addParagraph(namePrefix);
    }
    if (firstName && familyName) {
        sectionAddress.addParagraph(firstName + ' ' + familyName);
    }
    if (address) {
        sectionAddress.addParagraph(address);
    }
    if (countryCode && postalCode && locality) {
        sectionAddress.addParagraph(countryCode + ' - ' + postalCode + ' ' + locality);
    }
    if (telephone) {
        sectionAddress.addParagraph('Tel: ' + telephone);
    }
    if (email) {
        sectionAddress.addParagraph('Email: ' + email);
    }


    //--------------//
    //	2. TITLE	//
    //--------------//
    //Take the description of the given account and add it to the paragraph
    var accountDescription = Banana.document.table('Accounts').findRowByValue('Account', accountNumber).value('Description');
    report.addParagraph(" ", "styleParagraph");
    report.addParagraph("Account statement" + " - " + accountNumber + " " + accountDescription, "styleTitle styleBottomBorder");
    report.addParagraph(" ");


    //--------------//
    //	3. TABLE 	//
    //--------------//
    //Create a table object with all transactions for the given account and period
    var transTab = Banana.document.currentCard(accountNumber, openingDate, closureDate);

    //Create the table that will be printed on the report
    var table = report.addTable("myTable");

    //Add column titles to the table report
    var tableHeader = table.getHeader();
    tableRow = tableHeader.addRow();
    tableRow.addCell("Date", "styleTableHeader");
    tableRow.addCell("Description", "styleTableHeader");
    tableRow.addCell("Contra Account", "styleTableHeader");
    tableRow.addCell("Debit amount", "styleTableHeader");
    tableRow.addCell("Credit amount", "styleTableHeader");
    tableRow.addCell("Balance", "styleTableHeader");

    //Add the values taken from each row of the table (except the last one) to the respective cells of the table
    for (var i = 0; i < transTab.rowCount - 1; i++) {
        var tRow = transTab.row(i);
        tableRow = table.addRow();
        tableRow.addCell(Banana.Converter.toLocaleDateFormat(tRow.value('JDate')));
        tableRow.addCell(tRow.value("JDescription"));
        tableRow.addCell(tRow.value("JContraAccount"), "styleAccount");
        tableRow.addCell(Banana.Converter.toLocaleNumberFormat(tRow.value('JDebitAmountAccountCurrency')), "styleAmount");
        tableRow.addCell(Banana.Converter.toLocaleNumberFormat(tRow.value('JCreditAmountAccountCurrency')), "styleAmount");
        if (Banana.SDecimal.sign(tRow.value('JBalanceAccountCurrency')) >= 0) {
            tableRow.addCell(Banana.Converter.toLocaleNumberFormat(tRow.value('JBalanceAccountCurrency')), "styleAmount");
        } else {
            tableRow.addCell(Banana.Converter.toLocaleNumberFormat(tRow.value('JBalanceAccountCurrency')), "styleBalance styleAmount");
        }
    }

    //We add last row (totals) separately because we want to apply a special style only to this row
    for (var i = transTab.rowCount - 1; i < transTab.rowCount; i++) {
        var tRow = transTab.row(i);
        tableRow = table.addRow();
        tableRow.addCell(Banana.Converter.toLocaleDateFormat(tRow.value('JDate')), "styleTotal");
        tableRow.addCell(tRow.value("JDescription"), "styleTotal");
        tableRow.addCell(tRow.value("JContraAccount"), "styleAccount styleTotal");
        tableRow.addCell(Banana.Converter.toLocaleNumberFormat(tRow.value('JDebitAmountAccountCurrency')), "styleAmount styleTotal");
        tableRow.addCell(Banana.Converter.toLocaleNumberFormat(tRow.value('JCreditAmountAccountCurrency')), "styleAmount styleTotal");
        if (Banana.SDecimal.sign(tRow.value('JBalanceAccountCurrency')) >= 0) {
            tableRow.addCell(Banana.Converter.toLocaleNumberFormat(tRow.value('JBalanceAccountCurrency')), "styleAmount styleTotal");
        } else {
            tableRow.addCell(Banana.Converter.toLocaleNumberFormat(tRow.value('JBalanceAccountCurrency')), "styleBalance styleAmount styleTotal");
        }
    }
}



//This function adds a Footer to the report
function addFooter(report) {
    report.getFooter().addClass(".img");
    report.getFooter().addImage("documents:logo");
}


//The main purpose of this function is to create styles for the report print
function createStyleSheet() {

    var stylesheet = Banana.Report.newStyleSheet();

    //Page style
    var pageStyle = stylesheet.addStyle("@page");
    pageStyle.setAttribute("margin", "20m 15mm 15mm 25mm");

    //Footer style
    style = stylesheet.addStyle(".img");
    style.setAttribute("width", "50%");
    style.setAttribute("height", "auto");
    style.setAttribute("text-align", "center");

    //Title style
    style = stylesheet.addStyle(".styleTitle");
    style.setAttribute("font-size", "14pt");
    style.setAttribute("font-weight", "bold");

    //Bottom border
    style = stylesheet.addStyle(".styleBottomBorder");
    style.setAttribute("border-bottom", "1px solid black");

    //Create a table style adding the border
    style = stylesheet.addStyle("table");
    style.setAttribute("width", "100%");
    stylesheet.addStyle("table.myTable td", "border: thin solid black");

    //Style for the table titles
    style = stylesheet.addStyle(".styleTableHeader");
    style.setAttribute("font-weight", "bold");
    style.setAttribute("padding-bottom", "5px");
    style.setAttribute("background-color", "#ffd100");
    style.setAttribute("color", "#1b365d");

    //Style for account numbers
    style = stylesheet.addStyle(".styleAccount");
    style.setAttribute("padding-bottom", "5px");
    style.setAttribute("text-align", "center");

    //Style for amounts
    style = stylesheet.addStyle(".styleAmount");
    style.setAttribute("padding-bottom", "5px");
    style.setAttribute("text-align", "right");

    //Style for balances
    style = stylesheet.addStyle(".styleBalance");
    style.setAttribute("color", "red");

    //Style for the total of the table
    style = stylesheet.addStyle(".styleTotal");
    style.setAttribute("font-weight", "bold");
    style.setAttribute("padding-bottom", "5px");
    style.setAttribute("background-color", "#b7c3e0");
    style.setAttribute("border-bottom", "1px double black");

    style = stylesheet.addStyle(".styleAddress");
    style.setAttribute("position", "absolute");
    style.setAttribute("left", "0mm");
    style.setAttribute("top", "0mm");
    style.setAttribute("width", "80mm");
    style.setAttribute("height", "30mm");
    style.setAttribute("overflow-shrink-max", "0.6");
    style.setAttribute("overflow", "shrink");

    //Add a space between the date and the title
    style = stylesheet.addStyle(".styleParagraph");
    style.setAttribute("margin", "112px");

    return stylesheet;
}

Export

Export a value from Banana into a text file (.txt)


// Banana Accounting Extension Javascript
// @id = ch.banana.apps.export
// @api = 1.0
// @doctype = *.*
// @description = Tutorial: Export a value from Banana into a text file (*.txt)
// @task = export.file
// @exportfiletype = txt
// @timeout = -1

// It is possible to export data only into external files.
// In this example we take an account balance and we export it into a txt file.
// A dialog window asks the user to insert the file name and choose where to save it.

function exec() {

    //Take the balance (opening + debit-credit) for the given account and period
    var exportResult = Banana.document.currentBalance('1000', '2015-01-05', '2015-02-07').balance;

    //Specifies a value to be returned that will be exported into the txt file
    return exportResult;

}

Export Tutorial2 javascript codes


// Banana Accounting Extension Javascript
// @id = ch.banana.apps.exportjavascriptcodestutorial2.js
// @api = 1.0
// @publisher = Banana.ch SA
// @description = Tutorial: Export javascript codes of the tutorial 2 examples
// @task = export.file
// @doctype = *.*
// @docproperties = 
// @timeout = -1
// @exportfiletype = js
//Main function
function exec() {

    //Check if we are on an opened document
    if (!Banana.document) {
        return;
    }

    //Take the table Documents
    var documents = Banana.document.table('Documents');

    //We check if the table Documents exists, then we can take all the codes 
    //If the table Documents doesn't exists, then we stop the script execution
    if (!documents) {
        return;
    } else {

        //We use this variable to save all the codes
        var jsCode = '';

        jsCode += '// Copyright [2018] [Banana.ch SA - Lugano Switzerland]' + '\n';
        jsCode += '// ' + '\n';
        jsCode += '// Licensed under the Apache License, Version 2.0 (the "License");' + '\n';
        jsCode += '// you may not use this file except in compliance with the License.' + '\n';
        jsCode += '// You may obtain a copy of the License at' + '\n';
        jsCode += '// ' + '\n';
        jsCode += '//     http://www.apache.org/licenses/LICENSE-2.0' + '\n';
        jsCode += '// ' + '\n';
        jsCode += '// Unless required by applicable law or agreed to in writing, software' + '\n';
        jsCode += '// distributed under the License is distributed on an "AS IS" BASIS,' + '\n';
        jsCode += '// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.' + '\n';
        jsCode += '// See the License for the specific language governing permissions and' + '\n';
        jsCode += '// limitations under the License.' + '\n' + '\n' + '\n' + '\n' + '\n' + '\n' + '\n';

        //Function call to get all the tutorial's codes
        jsCode = getJavascriptCode(jsCode, documents);
    }

    return jsCode;
}




//Function that, for each tutorial's example, gets the javascript code and save it into the jsCode variable.
function getJavascriptCode(jsCode, documents) {

    //Read row by row the table Documents
    var len = documents.rowCount;
    for (var i = 0; i < len; i++) {

        //Create a variable for the row
        var tRow = documents.row(i);

        //We get some values
        var fName = Banana.document.info("Base", "FileName").replace(/^.*[\\\/]/, ''); //the file name (without path)
        var id = tRow.value("RowId"); //the id of the example
        var description = tRow.value("Description"); //the description of the example
        var attachments = tRow.value("Attachments"); //the javascript code of the example

        //We consider only the rows that contain an id, a description and an attachment
        if (id && description && attachments) {

            //At the beginning of each code, we insert some information
            jsCode += "/** " + '\n';
            jsCode += "    File:        " + fName + '\n';
            jsCode += "    Id:          " + id + '\n';
            jsCode += "    Description: " + description + '\n';
            jsCode += "*/" + '\n';

            //we add the code of the attachments
            jsCode += attachments;

            //At the end of each code, we insert some new lines
            jsCode += '\n' + '\n' + '\n' + '\n' + '\n' + '\n' + '\n';
        }
    }

    //Finally we return the variable containing all the codes of all the examples
    return jsCode;
}

Export Tutorial2 javascript codes to HTML


// Banana Accounting Extension Javascript
// @id = ch.banana.apps.exporthtmljavascriptcodestutorial2.js
// @api = 1.0
// @publisher = Banana.ch SA
// @description = Tutorial: Export javascript codes of the Tutorial 2 examples in HTML
// @task = export.file
// @doctype = *.*
// @docproperties = 
// @timeout = -1
// @exportfiletype = html

//Main function
function exec() {

    //Check if we are on an opened document
    if (!Banana.document) {
        return;
    }

    //Take the table Documents
    var documents = Banana.document.table('Documents');

    //We check if the table Documents exists, then we can take all the codes 
    //If the table Documents doesn't exists, then we stop the script execution
    if (!documents) {
        return;
    } else {

        //We use this variable to save all the codes
        var jsCode = '';

        //Function call to get all the tutorial's codes
        jsCode = getJavascriptCode(jsCode, documents);
    }

    return jsCode;
}

//Function that, for each tutorial's example, gets the javascript code and save it into the jsCode variable.
function getJavascriptCode(jsCode, documents) {

    //Read row by row the table Documents
    var len = documents.rowCount;
    for (var i = 0; i < len; i++) {

        //Create a variable for the row
        var tRow = documents.row(i);

        //We get some values
        var fName = Banana.document.info("Base", "FileName").replace(/^.*[\\\/]/, ''); //the file name (without path)
        var section = tRow.value("Section"); // the section column used to define the title type
        var id = tRow.value("RowId"); //the id of the example
        var description = tRow.value("Description"); //the description of the example
        var attachments = tRow.value("Attachments"); //the javascript code of the example
        attachments = attachments.replace(/</g,'<'); // replace the < symbol
        attachments = attachments.replace(/>/g,'>'); // replace the > symbol

        //For tilte h2 we conside only rows with section h2
        if (section && section === "h2") {
            jsCode += '<h2>' + description + '</h2>\n';
        }
        
        //For js codes we consider only rows with section h3, id, description and attachment
        else if (section && section === "h3" && id && description && attachments) {
            jsCode += '<h3>' + description + '</h3>\n';
            jsCode += '<pre><code class="language-javascript">\n';
            jsCode += '// Banana Accounting Extension Javascript' + '\n';
            jsCode += attachments;
            jsCode += '\n';
            jsCode += '</code></pre>\n';
        }
    }

    //Finally we return the variable containing all the codes of all the examples
    return jsCode;
}

Embedded Extensions examples Tutorial 3 Import CSV

On this page we show some examples of transactions import extensions.

Each example consists of:

  • The complete JavaScript code of the import extension.
  • The .CSV files in different formats with the transactions data to be imported.
    Some examples contains anonymized data, the description text has no meanings. 
  • The output in .TSV format (Tabulator Separated Values) of the import extension, which contains the transformed csv data in the “transactions.simple” format used to import the transactions in Banana Accounting.

Import transactions example 1

Migros Bank (Switzerland) import transactions with multiple csv formats


// Banana Accounting Extension Javascript
// @id = ch.banana.switzerland.import.migrosbank.tutorial
// @api = 1.0
// @pubdate = 2023-10-10
// @publisher = Banana.ch SA
// @description = Migros Bank - Import account statement .csv (Banana+ Advanced)
// @description.en = Migros Bank - Import account statement .csv (Banana+ Advanced)
// @description.de = Migros Bank - Bewegungen importieren .csv (Banana+ Advanced)
// @description.fr = Migros Bank - Importer mouvements .csv (Banana+ Advanced)
// @description.it = Migros Bank - Importa movimenti .csv (Banana+ Advanced)
// @doctype = *
// @docproperties =
// @task = import.transactions
// @outputformat = transactions.simple
// @inputdatasource = openfiledialog
// @timeout = -1
// @inputencoding = latin1
// @inputfilefilter = Text files (*.txt *.csv);;All files (*.*)
// @inputfilefilter.de = Text (*.txt *.csv);;Alle Dateien (*.*)
// @inputfilefilter.fr = Texte (*.txt *.csv);;Tous (*.*)
// @inputfilefilter.it = Testo (*.txt *.csv);;Tutti i files (*.*)

/**
 * Parse the data and return the data to be imported as a tab separated file.
 */
function exec(string, isTest) {

   var importUtilities = new ImportUtilities(Banana.document);

   if (isTest !== true && !importUtilities.verifyBananaAdvancedVersion())
      return "";

   var fieldSeparator = findSeparator(string);
   var transactions = Banana.Converter.csvToArray(string, fieldSeparator, '"');

   // Format 1
   var format1 = new MBFormat1();
   if (format1.match(transactions)) {
      transactions = format1.convert(transactions);
      Banana.console.log(Banana.Converter.arrayToTsv(transactions));
      return Banana.Converter.arrayToTsv(transactions);
   }

   importUtilities.getUnknownFormatError();

   return "";
}

/**
 * Migros Bank Format 1 A):
 * Kontoauszug bis: 04.09.2023 ;;;
 * ;;;
 * Kontonummer: 543.278.22;;;
 * Bezeichnung: Privat;;;
 * Saldo: CHF 38547.7;;;
 * ;;;
 * Ramer E. & Ramer-Zahner D.;;;
 * In den Steinreben 6C;;;
 * 4153 Reinach BL;;;
 * ;;;
 * ;;;
 * Datum;Buchungstext;Betrag;Valuta
 * 04.09.23;Zahlungseingang;1838.00;04.09.23
 * 04.09.23;Zahlungs;-204.45;04.09.23
 * 
 * Migros Bank Format 1 B), valutare in futuro se fare un formato differente per conti privati,
 * per ora cambia solo la data e le intestazioni:
 * Moristra rerva eo:;2023-09-13
 * Moristra rerva lant:;2023-10-10
 * ;
 * Sciercipsidea:;Rerva haragine
 * ;
 * ;
 * ;
 * Data;Testo di registrazione;Importo;Valuta
 * 15.09.2023;Frunt stantuisu me quaesecerinum XXX/UT/PUS, Dis Frangunattis 47h, 1782 Raraequone;-105.45;15.09.2023
 * 15.09.2023;DIDUNT Humquit-Costripe EO, Dis Volluvis 1, 7888 Prescrente;-230.95;15.09.2023
 * 19.09.2023;CLAVIANTO AUFERVA EO, DIS MINENT 8, 6686 COLUMEA;-150.80;19.09.2023
 */
var MBFormat1 = class MBFormat1 {

   constructor() {
      this.colDate = 0;
      this.colDescr = 1;
      this.colAmount = 2;
      this.colDateValuta = 3;

      this.colCount = 4;
      this.decimalSeparator = ".";
      this.dateFormat = "dd.mm.yy";
   }

   /** Return true if the transactions match this format */
   match(transactions) {

      if (transactions.length === 0)
         return false;

      for (var i = 0; i < transactions.length; i++) {
         var transaction = transactions[i];

         var formatMatched = false;
         /* array should have all columns */
         if (transaction.length == this.colCount)
            formatMatched = true;
         else
            formatMatched = false;

         if (formatMatched && transaction[this.colDate] &&
            transaction[this.colDate].match(/^(0[1-9]|[12][0-9]|3[01])[-.](0[1-9]|1[0-2])[-.]\d{2}$/)) {
            formatMatched = true;
         } else if (formatMatched && transaction[this.colDate] &&
            transaction[this.colDate].match(/^(0[1-9]|[12][0-9]|3[01])[-.](0[1-9]|1[0-2])[-.]\d{4}$/)) {
            this.dateFormat = "dd.mm.yyyy";
            formatMatched = true;
         }
         else {
            formatMatched = false;
         }

         if (formatMatched && transaction[this.colDateValuta] &&
            transaction[this.colDateValuta].match(/\b\d{2}[.-]\d{2}[.-](?:\d{2}|\d{4})\b/g)) {
            formatMatched = true;
         }
         else {
            formatMatched = false;
         }

         if (formatMatched) {
            return true;
         }
      }

      return false;
   }

   /** Convert the transaction to the format to be imported */
   convert(rows) {
      var transactionsToImport = [];

      for (var i = 0; i < rows.length; i++) {
         let transaction = rows[i];
         if (transaction.length == this.colCount &&
            transaction[this.colDate].match(/^(0[1-9]|[12][0-9]|3[01])[-.](0[1-9]|1[0-2])[-.](\d{4}|\d{2})$/)) {
            transactionsToImport.push(this.mapTransaction(rows[i]));
         }
      }

      // Sort rows by date
      if (this.dateFormat !== "dd.mm.yyyy") // transactions in the format B are already provided in the correct order.
         transactionsToImport = transactionsToImport.reverse();

      // Add header and return
      var header = [["Date", "Doc", "Description", "Income", "Expenses"]];
      return header.concat(transactionsToImport);
   }

   /** Return the transaction converted in the import format */
   mapTransaction(transaction) {
      var mappedLine = [];

      mappedLine.push(Banana.Converter.toInternalDateFormat(transaction[this.colDate], this.dateFormat));
      mappedLine.push(""); // Doc is empty for now
      mappedLine.push(transaction[this.colDescr]);
      var amount = transaction[this.colAmount];
      if (amount.length > 0) {
         //check decimal separator, if is comma, we replace it.
         if (amount.indexOf(",") >= 0)
            amount = amount.replace(',', '.');
         if (amount[0] === "-") {
            amount = amount.replace(/-/g, ''); //remove minus sign
            mappedLine.push("");
            mappedLine.push(Banana.Converter.toInternalNumberFormat(amount, "."));

         } else {
            mappedLine.push(Banana.Converter.toInternalNumberFormat(amount, "."));
            mappedLine.push("");
         }
      }

      return mappedLine;
   }

}

/**
 * The function findSeparator is used to find the field separator.
 */
function findSeparator(string) {

   var commaCount = 0;
   var semicolonCount = 0;
   var tabCount = 0;

   for (var i = 0; i < 1000 && i < string.length; i++) {
      var c = string[i];
      if (c === ',')
         commaCount++;
      else if (c === ';')
         semicolonCount++;
      else if (c === '\t')
         tabCount++;
   }

   if (tabCount > commaCount && tabCount > semicolonCount) {
      return '\t';
   }
   else if (semicolonCount > commaCount) {
      return ';';
   }

   return ',';
}


/*
* class ImportUtilities
* Contains methods that can be shared by extensions for importing bank data
*/

var ImportUtilities = class ImportUtilities {
    constructor(banDocument) {
        this.banDocument = banDocument;
        if (this.banDocument === undefined)
            this.banDocument = Banana.document;
    }

    //The purpose of this function is to convert all the data into a format supported by Banana
    convertToBananaFormat(intermediaryData) {
        var columnTitles = [];

        //Create titles only for fields not starting with "_"
        for (var name in intermediaryData[0]) {
            if (name.substring(0, 1) !== "_") {
                columnTitles.push(name);
            }
        }
        //Function call Banana.Converter.objectArrayToCsv() to create a CSV with new data just converted
        var convertedCsv = Banana.Converter.objectArrayToCsv(columnTitles, intermediaryData, "\t");

        return convertedCsv;
    }

    // Convert to an array of objects where each object property is the banana columnNameXml
    convertCsvToIntermediaryData(inData, convertionParam) {
        var form = [];
        var intermediaryData = [];
        //Add the header if present 
        if (convertionParam.header) {
            inData = convertionParam.header + inData;
        }

        //Read the CSV file and create an array with the data
        var csvFile = Banana.Converter.csvToArray(inData, convertionParam.separator, convertionParam.textDelim);

        //Variables used to save the columns titles and the rows values
        var columns = this.getHeaderData(csvFile, convertionParam.headerLineStart); //array
        var rows = this.getRowData(csvFile, convertionParam.dataLineStart); //array of array

        //Load the form with data taken from the array. Create objects
        this.loadForm(form, columns, rows);

        //Create the new CSV file with converted data
        var convertedRow;
        //For each row of the form, we call the rowConverter() function and we save the converted data
        for (var i = 0; i < form.length; i++) {
            convertedRow = convertionParam.rowConverter(form[i]);
            intermediaryData.push(convertedRow);
        }

        //Return the converted CSV data into the Banana document table
        return intermediaryData;
    }

    // Convert to an array of objects where each object property is the banana columnNameXml
    convertHtmlToIntermediaryData(inData, convertionParam) {
        var form = [];
        var intermediaryData = [];

        //Read the HTML file and create an array with the data
        var htmlFile = [];
        var htmlRows = inData.match(/<tr[^>]*>.*?<\/tr>/gi);
        for (var rowNr = 0; rowNr < htmlRows.length; rowNr++) {
            var htmlRow = [];
            var htmlFields = htmlRows[rowNr].match(/<t(h|d)[^>]*>.*?<\/t(h|d)>/gi);
            for (var fieldNr = 0; fieldNr < htmlFields.length; fieldNr++) {
                var htmlFieldRe = />(.*)</g.exec(htmlFields[fieldNr]);
                htmlRow.push(htmlFieldRe.length > 1 ? htmlFieldRe[1] : "");
            }
            htmlFile.push(htmlRow);
        }

        //Variables used to save the columns titles and the rows values
        var columns = this.getHeaderData(htmlFile, convertionParam.headerLineStart); //array
        var rows = this.getRowData(htmlFile, convertionParam.dataLineStart); //array of array

        //Convert header names
        for (var i = 0; i < columns.length; i++) {
            var convertedHeader = columns[i];
            convertedHeader = convertedHeader.toLowerCase();
            convertedHeader = convertedHeader.replace(" ", "_");
            var indexOfHeader = columns.indexOf(convertedHeader);
            if (indexOfHeader >= 0 && indexOfHeader < i) { // Header alreay exist
                //Avoid headers with same name adding an incremental index
                var newIndex = 2;
                while (columns.indexOf(convertedHeader + newIndex.toString()) !== -1 && newIndex < 99)
                    newIndex++;
                convertedHeader = convertedHeader + newIndex.toString()
            }
            columns[i] = convertedHeader;
        }

        // Banana.console.log(JSON.stringify(columns, null, "   "));

        //Load the form with data taken from the array. Create objects
        this.loadForm(form, columns, rows);

        //Create the new CSV file with converted data
        var convertedRow;
        //For each row of the form, we call the rowConverter() function and we save the converted data
        for (var i = 0; i < form.length; i++) {
            convertedRow = convertionParam.rowConverter(form[i]);
            intermediaryData.push(convertedRow);
        }

        //Return the converted CSV data into the Banana document table
        return intermediaryData;
    }

    // Convert to an array of objects where each object property is the banana columnNameXml
    convertToIntermediaryData(inData, convertionParam) {
        if (convertionParam.format === "html") {
            return this.convertHtmlToIntermediaryData(inData, convertionParam);
        } else {
            return this.convertCsvToIntermediaryData(inData, convertionParam);
        }
    }

    //The purpose of this function is to return all the titles of the columns
    getHeaderData(csvFile, startLineNumber) {
        if (!startLineNumber) {
            startLineNumber = 0;
        }
        var headerData = csvFile[startLineNumber];
        for (var i = 0; i < headerData.length; i++) {

            headerData[i] = headerData[i].trim();

            if (!headerData[i]) {
                headerData[i] = i;
            }

            //Avoid duplicate headers
            var headerPos = headerData.indexOf(headerData[i]);
            if (headerPos >= 0 && headerPos < i) { // Header already exist
                var postfixIndex = 2;
                while (headerData.indexOf(headerData[i] + postfixIndex.toString()) !== -1 && postfixIndex <= 99)
                    postfixIndex++; // Append an incremental index
                headerData[i] = headerData[i] + postfixIndex.toString()
            }

        }
        return headerData;
    }

    getLang() {

        var lang = 'en';
        if (this.banDocument)
            lang = this.banDocument.locale;
        else if (Banana.application.locale)
            lang = Banana.application.locale;
        if (lang.length > 2)
            lang = lang.substr(0, 2);
        return lang;
    }
    
    //The purpose of this function is to return all the data of the rows
    getRowData(csvFile, startLineNumber) {
        if (!startLineNumber) {
            startLineNumber = 1;
        }
        var rowData = [];
        for (var i = startLineNumber; i < csvFile.length; i++) {
            rowData.push(csvFile[i]);
        }
        return rowData;
    }

    //The purpose of this function is to load all the data (titles of the columns and rows) and create a list of objects.
    //Each object represents a row of the csv file
    loadForm(form, columns, rows) {
        for (var j = 0; j < rows.length; j++) {
            var obj = {};
            for (var i = 0; i < columns.length; i++) {
                obj[columns[i]] = rows[j][i];
            }
            form.push(obj);
        }
    }

    // The purpose of this function is to sort the data
    sortData(intermediaryData, convertionParam) {
        if (convertionParam.sortColums && convertionParam.sortColums.length) {
            intermediaryData.sort(
                function (row1, row2) {
                    for (var i = 0; i < convertionParam.sortColums.length; i++) {
                        var columnName = convertionParam.sortColums[i];
                        if (row1[columnName] > row2[columnName])
                            return 1;
                        else if (row1[columnName] < row2[columnName])
                            return -1;
                    }
                    return 0;
                });

            if (convertionParam.sortDescending)
                intermediaryData.reverse();
        }

        return intermediaryData;
    }

    verifyBananaPlusVersion() {
        if (!this.banDocument)
            return false;

        var BAN_VERSION_MIN = "10.0";

        // supported Version
        if (Banana.compareVersion && Banana.compareVersion(Banana.application.version, BAN_VERSION_MIN) >= 0) {
            return true;
        }

        // not supported version
        var lang = this.getLang();
        var msg = "This extension requires Banana Accounting+";
        this.banDocument.addMessage(msg, "ID_ERR_LICENSE_NOTVALID");
        return false;
    }

    //Check if the version of Banana Accounting is compatible with this class
    verifyBananaAdvancedVersion() {
        if (!this.banDocument)
            return false;

        if (!Banana.application.license || Banana.application.license.licenseType !== "advanced") {
            var lang = this.getLang();
            var msg = "This extension requires Banana Accounting+ Advanced";
            this.banDocument.addMessage(msg, "ID_ERR_LICENSE_NOTVALID");
            return false;
        }

        return true;
    }

    getErrorMessage(errorId, lang) {
        if (!lang)
            lang = 'en';
        switch (errorId) {
            case "ID_ERR_FORMAT_UNKNOWN":
                 if (lang == 'it')
                    return "Formato del file *.csv non riconosciuto";
                 else if (lang == 'fr')
                    return "Format de fichier *.csv non reconnu";
                 else if (lang == 'de')
                    return "Unerkanntes *.csv-Dateiformat";
                 else
                    return "Unrecognised *.csv file format";
        }
        return '';
     }

    getLang() {
        var lang = 'en';
        if (Banana.application.locale)
            lang = Banana.application.locale;
        if (lang.length > 2)
            lang = lang.substring(0, 2);
        return lang;
     }

    getUnknownFormatError(){
        let errId = "ID_ERR_FORMAT_UNKNOWN"; //error
        let lang = this.getLang();
        let msg = this.getErrorMessage(errId, lang);
        Banana.document.addMessage(msg, errId);
    }
}

Input data - csv_migrosbank_example_format1_20230906.csv

Input text passed to the exec() function.


Alevenequis xxx: 78.71.5440 ;;;
;;;
Ausplecutum: 457.211.73;;;
Agnuluviant: Neriam;;;
Oprit: BOX 54060.3;;;
;;;
Agile G. & Agile-Tumere L.;;;
Ut lum Ascungilis 1Q;;;
4153 Reinach BL;;;
;;;
;;;
Datum;Buchungstext;Betrag;Valuta
04.09.23;Partuducartudie;1838.00;04.09.23

Output data - csv_migrosbank_example_format1_20230906.tsv

Output text returned by the exec() function.


Date\tDoc\tDescription\tIncome\tExpenses
2023-09-04\t\tPartuducartudie\t1838.00\t

Input data - csv_migrosbank_example_format1_20230908.csv

Input text passed to the exec() function.


Officulo patem crem: 42.35.6581 ;;;
;;;
Office eo patem: 133.273.44;;;
Tabilliquaest: Patem plucinto;;;
Widum: SUB 30221.84;;;
;;;
Amniuntum-Reles Addunnes;;;
Flone H. Nolvo 13 O;;;
10090 Villarbasse TO;;;
;;;
;;;
Data;Testo di registrazione;Importo;Valuta
06.09.23;Anispus rentemple;975.00;06.09.23
04.09.23;Rentemple;-204.45;04.09.23
01.09.23;Anispus rentemple;2300.00;01.09.23
01.09.23;Anispus rentemple;2400.00;01.09.23
31.08.23;Anispus rentemple;2610.00;31.08.23
30.08.23;Rentemple;-166.00;30.08.23
29.08.23;Rentemple;-161.55;29.08.23
29.08.23;Anispus rentemple;140.00;29.08.23
28.08.23;Anispus rentemple;2640.00;28.08.23
18.08.23;Anispus rentemple;2190.00;18.08.23
10.08.23;Vicum eo eviore patem usa 84.60.6581 vi 80.60.6581;-3.00;10.08.23
10.08.23;Raepribulle multubiti morpus usa 84.60.6581 vi 80.60.6581 triente 67010185;-5.00;10.08.23
08.08.23;Rentemple;-216.50;08.08.23
07.08.23;Anispus rentemple;975.00;07.08.23
04.08.23;Anispus rentemple;2500.00;04.08.23
03.08.23;Anispus rentemple;2300.00;03.08.23
31.07.23;Anispus rentemple;2610.00;31.07.23
31.07.23;Anispus rentemple;2540.00;31.07.23
28.07.23;Anispus rentemple;140.00;28.07.23
28.07.23;Anispus rentemple;2640.00;28.07.23
25.07.23;Rentemple;-371.75;25.07.23
24.07.23;Rentemple;-708.25;24.07.23
20.07.23;Anispus rentemple;2190.00;20.07.23
18.07.23;Rentemple;-51.76;18.07.23

Output data - csv_migrosbank_example_format1_20230908.tsv

Output text returned by the exec() function.


Date\tDoc\tDescription\tIncome\tExpenses
2023-07-18\t\tRentemple\t\t51.76
2023-07-20\t\tAnispus rentemple\t2190.00\t
2023-07-24\t\tRentemple\t\t708.25
2023-07-25\t\tRentemple\t\t371.75
2023-07-28\t\tAnispus rentemple\t2640.00\t
2023-07-28\t\tAnispus rentemple\t140.00\t
2023-07-31\t\tAnispus rentemple\t2540.00\t
2023-07-31\t\tAnispus rentemple\t2610.00\t
2023-08-03\t\tAnispus rentemple\t2300.00\t
2023-08-04\t\tAnispus rentemple\t2500.00\t
2023-08-07\t\tAnispus rentemple\t975.00\t
2023-08-08\t\tRentemple\t\t216.50
2023-08-10\t\tRaepribulle multubiti morpus usa 84.60.6581 vi 80.60.6581 triente 67010185\t\t5.00
2023-08-10\t\tVicum eo eviore patem usa 84.60.6581 vi 80.60.6581\t\t3.00
2023-08-18\t\tAnispus rentemple\t2190.00\t
2023-08-28\t\tAnispus rentemple\t2640.00\t
2023-08-29\t\tAnispus rentemple\t140.00\t
2023-08-29\t\tRentemple\t\t161.55
2023-08-30\t\tRentemple\t\t166.00
2023-08-31\t\tAnispus rentemple\t2610.00\t
2023-09-01\t\tAnispus rentemple\t2400.00\t
2023-09-01\t\tAnispus rentemple\t2300.00\t
2023-09-04\t\tRentemple\t\t204.45
2023-09-06\t\tAnispus rentemple\t975.00\t

Input data - csv_migrosbank_example_format1_20231010.csv

Input text passed to the exec() function.


Moristra rerva eo:;2023-09-13
Moristra rerva lant:;2023-10-10
;
Sciercipsidea:;Rerva haragine
;
;
;
Data;Testo di registrazione;Importo;Valuta
15.09.2023;Frunt stantuisu me quaesecerinum XXX/UT/PUS, Dis Frangunattis 47h, 1782 Raraequone;-105.45;15.09.2023
15.09.2023;DIDUNT Humquit-Costripe EO, Dis Volluvis 1, 7888 Prescrente;-230.95;15.09.2023
19.09.2023;CLAVIANTO AUFERVA EO, DIS MINENT 8, 6686 COLUMEA;-150.80;19.09.2023
20.09.2023;Quibula Mindum Eaniertus Habit, Dis cat Brende 28, 1880 Reline, QL3478184100087423875;2190.00;20.09.2023
22.09.2023;CONDUONE PAVIONENT, DIS CAT BRENDE 48, 1880 RELINE, AG04803336078414G776D;6534.85;22.09.2023
28.09.2023;CURICA MITATE, DIS FERBO MEN 87 UT 1880 RELINE, EC143102680158501757U;2640.00;28.09.2023
29.09.2023;Perrede Deceripam, Insunt Pulus 60, 8533 Inatu Serneripe, HK3780577203472652012;140.00;29.09.2023
29.09.2023;Eorore Quam, Dis cat Brende 28, UT-1880 Reline, NW8367667075683018488;2610.00;29.09.2023
29.09.2023;Ludicita Lumilla, Dis Ferbo Men 87, 1880 Reline, JX6823625561033213063;2400.00;29.09.2023
03.10.2023;Biberi Tere, Dis cat Brende 28, 1880 Reline, YD8147137112546525622;2300.00;03.10.2023
06.10.2023;Aufere Adecro in commensectum, Horint Coluva-Exerent 56, 8367 Ireèex, WG0071635058848171552;975.00;06.10.2023
09.10.2023;Nanno Spirit Ripere, Dis cat Brende 28, 1880 Reline, XA1816106073487284374;2500.00;09.10.2023
10.10.2023;Mendisistra sustudabo difico nos 44.60.3252 at 18.60.3252 sideano 23418626;-5.00;10.10.2023
10.10.2023;Humea me punium rerva nos 44.60.3252 at 18.60.3252;-3.00;10.10.2023

Output data - csv_migrosbank_example_format1_20231010.tsv

Output text returned by the exec() function.


Date\tDoc\tDescription\tIncome\tExpenses
2023-09-15\t\tFrunt stantuisu me quaesecerinum XXX/UT/PUS, Dis Frangunattis 47h, 1782 Raraequone\t\t105.45
2023-09-15\t\tDIDUNT Humquit-Costripe EO, Dis Volluvis 1, 7888 Prescrente\t\t230.95
2023-09-19\t\tCLAVIANTO AUFERVA EO, DIS MINENT 8, 6686 COLUMEA\t\t150.80
2023-09-20\t\tQuibula Mindum Eaniertus Habit, Dis cat Brende 28, 1880 Reline, QL3478184100087423875\t2190.00\t
2023-09-22\t\tCONDUONE PAVIONENT, DIS CAT BRENDE 48, 1880 RELINE, AG04803336078414G776D\t6534.85\t
2023-09-28\t\tCURICA MITATE, DIS FERBO MEN 87 UT 1880 RELINE, EC143102680158501757U\t2640.00\t
2023-09-29\t\tPerrede Deceripam, Insunt Pulus 60, 8533 Inatu Serneripe, HK3780577203472652012\t140.00\t
2023-09-29\t\tEorore Quam, Dis cat Brende 28, UT-1880 Reline, NW8367667075683018488\t2610.00\t
2023-09-29\t\tLudicita Lumilla, Dis Ferbo Men 87, 1880 Reline, JX6823625561033213063\t2400.00\t
2023-10-03\t\tBiberi Tere, Dis cat Brende 28, 1880 Reline, YD8147137112546525622\t2300.00\t
2023-10-06\t\tAufere Adecro in commensectum, Horint Coluva-Exerent 56, 8367 Ireèex, WG0071635058848171552\t975.00\t
2023-10-09\t\tNanno Spirit Ripere, Dis cat Brende 28, 1880 Reline, XA1816106073487284374\t2500.00\t
2023-10-10\t\tMendisistra sustudabo difico nos 44.60.3252 at 18.60.3252 sideano 23418626\t\t5.00
2023-10-10\t\tHumea me punium rerva nos 44.60.3252 at 18.60.3252\t\t3.00

Import transactions example 2

Postfinance (Switzerland) import transactions with multiple csv formats


// Banana Accounting Extension Javascript
// @id = ch.banana.switzerland.import.postfinance.tutorial
// @api = 1.0
// @pubdate = 2023-09-29
// @publisher = Banana.ch SA
// @description = Postfinance - Import account statement .csv (Banana+ Advanced)
// @description.de = Postfinance - Bewegungen importieren .csv (Banana+ Advanced)
// @description.en = Postfinance - Import account statement .csv (Banana+ Advanced)
// @description.fr = Postfinance - Importer mouvements .csv (Banana+ Advanced)
// @description.it = Postfinance - Importa movimenti .csv (Banana+ Advanced)
// @doctype = *
// @docproperties =
// @task = import.transactions
// @outputformat = transactions.simple
// @inputdatasource = openfiledialog
// @inputencoding = latin1
// @inputfilefilter = Text files (*.txt *.csv);;All files (*.*)
// @inputfilefilter.de = Text (*.txt *.csv);;Alle Dateien (*.*)
// @inputfilefilter.fr = Texte (*.txt *.csv);;Tous (*.*)
// @inputfilefilter.it = Testo (*.txt *.csv);;Tutti i files (*.*)

/**
 * Parse the data and return the data to be imported as a tab separated file.
 */
function exec(inData, isTest) {

   if (!inData) return "";

   var importUtilities = new ImportUtilities(Banana.document);

   if (isTest !== true && !importUtilities.verifyBananaAdvancedVersion())
      return "";

   if (inData.indexOf("<html") >= 0) {
      var formatHtml1 = new PFHtmlFormat1();
      var rows = formatHtml1.convert(inData);
      var csv = Banana.Converter.objectArrayToCsv(
         ["Date", "DateValue", "Description", "Income", "Expenses"],
         rows);
      return csv;

   } else {
      var fieldSeparator = findSeparator(inData);
      let inDataCleared = clearText(inData);
      var transactions = Banana.Converter.csvToArray(inDataCleared, fieldSeparator);

      // Format SBU 1
      var formatSBU1 = new PFCSVFormatSBU1();
      if (formatSBU1.match(transactions)) {
         transactions = formatSBU1.convert(transactions);
         Banana.console.log(Banana.Converter.arrayToTsv(transactions));
         return Banana.Converter.arrayToTsv(transactions);
      }

      // Credit Card format 1
      var format1_CreditCard = new PFCSVFormat1_CreditCard();
      if (format1_CreditCard.match(transactions)) {
         transactions = format1_CreditCard.convert(transactions);
         Banana.console.log(Banana.Converter.arrayToTsv(transactions));
         return Banana.Converter.arrayToTsv(transactions);
      }

      // Format 1
      var format1 = new PFCSVFormat1();
      if (format1.match(transactions)) {
         transactions = format1.convert(transactions);
         Banana.console.log(Banana.Converter.arrayToTsv(transactions));
         return Banana.Converter.arrayToTsv(transactions);
      }

      // Format 2
      var format2 = new PFCSVFormat2();
      if (format2.match(transactions)) {
         transactions = format2.convert(transactions);
         Banana.console.log(Banana.Converter.arrayToTsv(transactions));
         return Banana.Converter.arrayToTsv(transactions);
      }

      // Format 3
      var format3 = new PFCSVFormat3();
      if (format3.match(transactions)) {
         transactions = format3.convert(transactions);
         Banana.console.log(Banana.Converter.arrayToTsv(transactions));
         return Banana.Converter.arrayToTsv(transactions);
      }

      // Format 4
      var format4 = new PFCSVFormat4();
      if (format4.match(transactions)) {
         transactions = format4.convert(transactions);
         Banana.console.log(Banana.Converter.arrayToTsv(transactions));
         return Banana.Converter.arrayToTsv(transactions);
      }

      // Format 5
      var format5 = new PFCSVFormat5();
      if (format5.match(transactions)) {
         transactions = format5.convert(transactions);
         Banana.console.log(Banana.Converter.arrayToTsv(transactions));
         return Banana.Converter.arrayToTsv(transactions);
      }

      // Format 6, works with translated column headers.
      var format6 = new PFCSVFormat6();
      // getFormattedData () works with specifics headers and to translate them. 
      let transactionsData = format6.getFormattedData(transactions, importUtilities);
      if (format6.match(transactionsData)) {
         let convTransactions = format6.convert(transactionsData);
         Banana.console.log(Banana.Converter.arrayToTsv(transactions));
         return Banana.Converter.arrayToTsv(convTransactions);
      }
   }

   importUtilities.getUnknownFormatError();

   return "";
}

/**
 * Pulisce il testo dai doppi a capo, con la versione 6 del formato csv, per qualche motivo quando il file .csv
 * viene aperto su windows vengono aggiunti degli a capo aggiuntivi (uno o più).
 * Ogni riga dovrebbe contenere un "\r\n" non di più, anche quelle vuote.
 */
function clearText(text) {
   // Sostituisce tutte le occorrenze multiple di "\r\r\n" con un singolo "\r\n"
   return text.replace(/\r\r\n/g, "\r\n");
}

/**
 * PFCSV Format 6, since february 2024.
 * Date de début:;26.02.2022;;;;;
 * Date de fin:;26.02.2024;;;;;
 * Catégorie:;Tous;;;;;
 * Compte:;CH00000000000000000000;;;;;
 * Monnaie:;CHF;;;;;
 * ;;;;;;
 * Date;Type de transaction;Texte de notification;Crédit en CHF;Débit en CHF;Label;Catégorie
 * ;;;;;;
 * 26.02.2024;Enregistrement comptable;Descr;;-100;;Dépenses autres
 * 26.02.2024;Enregistrement comptable;Descr;;-15;;Dépenses autres
 * ;;;;;;
 * Disclaimer:;;;;;;
 * Le contenu du document a été généré à partir des paramètres de filtrage des clientes et des clients. PostFinance n’est pas responsable du contenu et de son exhaustivité.;;;;;;
 */
function PFCSVFormat6() {

   this.getFormattedData = function (transactions, importUtilities) {
      let headerLineStart = this.getHeaderLineStart(transactions);
      let dataLineStart = headerLineStart == 6 ? 8 : 7;
      // We do a copy as the getHeaderData modifies the content and we need to keep the original version clean.
      var transactionsCopy = transactions.map(function (arr) {
         return arr.slice();
      });
      if (transactionsCopy.length < dataLineStart)
         return [];
      let columns = importUtilities.getHeaderData(transactionsCopy, headerLineStart); //array
      let rows = importUtilities.getRowData(transactionsCopy, dataLineStart); //array of array
      let form = [];

      /** We convert the original headers into a custom format to be able to work with the same
       * format regardless of original's headers language or the position of the header column.
       * We need to translate all the .csv fields as the loadForm() method expects the header and
       * the rows to have the same length.
       * */
      let convertedColumns = [];

      convertedColumns = this.convertHeaderDe(columns, convertedColumns);
      if (convertedColumns.length > 0) {
         importUtilities.loadForm(form, convertedColumns, rows);
         return form;
      }
      // Convert headers from italian. 
      convertedColumns = this.convertHeaderIt(columns, convertedColumns);
      if (convertedColumns.length > 0) {
         importUtilities.loadForm(form, convertedColumns, rows);
         return form;
      }
      // Convert headers from french.
      convertedColumns = this.convertHeaderFr(columns, convertedColumns);
      if (convertedColumns.length > 0) {
         importUtilities.loadForm(form, convertedColumns, rows);
         return form;
      }
      // Convert headers from english.
      convertedColumns = this.convertHeaderEn(columns, convertedColumns);
      if (convertedColumns.length > 0) {
         importUtilities.loadForm(form, convertedColumns, rows);
         return form;
      }

      return [];

   }

   /**
    * With this format, if user does not explicitly set the End Date of the movements
    * when is exporting, then the header and data start at a different row, then we have
    * to figure out if the end date is present or not.
    */
   this.getHeaderLineStart = function (transactions) {
      let endDate = transactions[1][1];
      if (endDate.match(/^\d{2}.\d{2}.\d{4}$/))
         return 6; // the Header is on row 6.
      else
         return 5; // the Header is on row 5.
   }

   this.convertHeaderDe = function (columns) {
      let convertedColumns = [];
      for (var i = 0; i < columns.length; i++) {
         switch (columns[i]) {
            case "Datum":
               convertedColumns[i] = "Date";
               break;
            case "Bewegungstyp":
               convertedColumns[i] = "Type";
               break;
            case "Avisierungstext":
               convertedColumns[i] = "Description";
               break;
            case "Gutschrift in CHF":
            case "Gutschrift in EUR":
            case "Gutschrift in USD":
               convertedColumns[i] = "Income";
               break;
            case "Lastschrift in CHF":
            case "Lastschrift in EUR":
            case "Lastschrift in USD":
               convertedColumns[i] = "Expenses";
               break;
            case "Label":
               convertedColumns[i] = "Label";
               break;
            case "Kategorie":
               convertedColumns[i] = "Category";
               break;
            default:
               break;
         }
      }

      if (convertedColumns.indexOf("Date") < 0
         || convertedColumns.indexOf("Description") < 0
         || convertedColumns.indexOf("Income") < 0
         || convertedColumns.indexOf("Expenses") < 0) {
         return [];
      }
      return convertedColumns;
   }

   this.convertHeaderIt = function (columns, convertedColumns) {
      for (var i = 0; i < columns.length; i++) {
         switch (columns[i]) {
            case "Data":
               convertedColumns[i] = "Date";
               break;
            case "Tipo di movimento":
               convertedColumns[i] = "Type";
               break;
            case "Testo di avviso":
               convertedColumns[i] = "Description";
               break;
            case "Accredito in CHF":
            case "Accredito in EUR":
            case "Accredito in USD":
               convertedColumns[i] = "Income";
               break;
            case "Addebito in CHF":
            case "Addebito in EUR":
            case "Addebito in USD":
               convertedColumns[i] = "Expenses";
               break;
            case "Tag":
               convertedColumns[i] = "Label";
               break;
            case "Categoria":
               convertedColumns[i] = "Category";
               break;
            default:
               break;
         }
      }

      if (convertedColumns.indexOf("Date") < 0
         || convertedColumns.indexOf("Description") < 0
         || convertedColumns.indexOf("Income") < 0
         || convertedColumns.indexOf("Expenses") < 0) {
         return [];
      }

      return convertedColumns;
   }

   this.convertHeaderFr = function (columns, convertedColumns) {
      for (var i = 0; i < columns.length; i++) {
         switch (columns[i]) {
            case "Date":
               convertedColumns[i] = "Date";
               break;
            case "Type de transaction":
               convertedColumns[i] = "Type";
               break;
            case "Texte de notification":
               convertedColumns[i] = "Description";
               break;
            case "Crédit en CHF":
            case "Crédit en EUR":
            case "Crédit en USD":
               convertedColumns[i] = "Income";
               break;
            case "Débit en CHF":
            case "Débit en EUR":
            case "Débit en USD":
               convertedColumns[i] = "Expenses";
               break;
            case "Label":
               convertedColumns[i] = "Label";
               break;
            case "Catégorie":
               convertedColumns[i] = "Category";
               break;
            default:
               break;
         }
      }

      if (convertedColumns.indexOf("Date") < 0
         || convertedColumns.indexOf("Description") < 0
         || convertedColumns.indexOf("Income") < 0
         || convertedColumns.indexOf("Expenses") < 0) {
         return [];
      }

      return convertedColumns;
   }

   this.convertHeaderEn = function (columns, convertedColumns) {
      for (var i = 0; i < columns.length; i++) {
         switch (columns[i]) {
            case "Date":
               convertedColumns[i] = "Date";
               break;
            case "Type of transaction":
               convertedColumns[i] = "Type";
               break;
            case "Notification text":
               convertedColumns[i] = "Description";
               break;
            case "Credit in CHF":
            case "Credit in EUR":
            case "Credit in USD":
               convertedColumns[i] = "Income";
               break;
            case "Debit in CHF":
            case "Debit in EUR":
            case "Debit in USD":
               convertedColumns[i] = "Expenses";
               break;
            case "Tag":
               convertedColumns[i] = "Label";
               break;
            case "Category":
               convertedColumns[i] = "Category";
               break;
            default:
               break;
         }
      }

      if (convertedColumns.indexOf("Date") < 0
         || convertedColumns.indexOf("Description") < 0
         || convertedColumns.indexOf("Income") < 0
         || convertedColumns.indexOf("Expenses") < 0) {
         return [];
      }

      return convertedColumns;
   }

   /** Return true if the transactions match this format */
   this.match = function (transactionsData) {
      if (transactionsData.length === 0)
         return false;

      for (var i = 0; i < transactionsData.length; i++) {
         var transaction = transactionsData[i];
         var formatMatched = true;

         if (formatMatched && transaction["Date"] && transaction["Date"].length >= 10 &&
            transaction["Date"].match(/^\d{2}.\d{2}.\d{4}$/))
            formatMatched = true;
         else
            formatMatched = false;

         if (formatMatched)
            return true;
      }

      return false;
   }

   this.convert = function (transactionsData) {
      var transactionsToImport = [];

      for (var i = 0; i < transactionsData.length; i++) {
         if (transactionsData[i]["Date"] && transactionsData[i]["Date"].length >= 10 &&
            transactionsData[i]["Date"].match(/^\d{2}.\d{2}.\d{4}$/)) {
            transactionsToImport.push(this.mapTransaction(transactionsData[i]));
         }
      }

      // Sort rows by date
      transactionsToImport = transactionsToImport.reverse();

      // Add header and return
      var header = [["Date", "DateValue", "Doc", "ExternalReference", "Description", "Income", "Expenses"]];
      return header.concat(transactionsToImport);
   }

   this.mapTransaction = function (transaction) {
      let mappedLine = [];

      mappedLine.push(Banana.Converter.toInternalDateFormat(transaction["Date"], "dd.mm.yyyy"));
      mappedLine.push(Banana.Converter.toInternalDateFormat("", "dd.mm.yyyy"));
      mappedLine.push("");
      mappedLine.push("");
      let trDescription = transaction["Description"] + ", " + transaction["Type"];
      mappedLine.push(trDescription);
      mappedLine.push(Banana.Converter.toInternalNumberFormat(transaction["Income"], '.'));
      let expAmount = transaction["Expenses"].replace(/-/g, ''); //remove minus sign
      mappedLine.push(Banana.Converter.toInternalNumberFormat(expAmount, '.'));

      return mappedLine;
   }

}

/**
 * Credit Card format 1
 * Kartenkonto:;0000 1234 5467 7654
 * Karte:;XXXX XXXX XXXX 1111 PostFinance Visa Business Card
 * Datum;Buchungsdetails;Gutschrift in CHF;Lastschrift in CHF
 * 2023-08-24;"Tankstelle Marche        Brugg BE";;-94.70
 * 2023-08-21;"Tankstelle Marche        Brugg BE";;-114.05
 * 2023-08-10;"6131 STORNO JAHRESPREIS";80.00;
**/
function PFCSVFormat1_CreditCard() {

   this.colDate = 0;
   this.colDescr = 1;
   this.colCredit = 2;
   this.colDebit = 3;

   this.dateFormat = 'dd-mm-yyyy';

   /** Return true if the transactions match this format */
   this.match = function (transactions) {
      if (transactions.length === 0)
         return false;

      for (i = 0; i < transactions.length; i++) {
         var transaction = transactions[i];

         var formatMatched = false;
         if (transaction.length === this.colDebit + 1)
            formatMatched = true;
         else
            formatMatched = false;
         if (formatMatched && transaction[this.colDate].match(/[0-9]{2}(\-)[0-9]{2}(\-)[0-9]{4}/g)) {
            formatMatched = true;
         } else if (formatMatched && transaction[this.colDate].match(/[0-9]{4}(\-)[0-9]{2}(\-)[0-9]{2}/g)) {
            formatMatched = true;
            this.dateFormat = 'yyyy-mm-dd';
         } else {
            formatMatched = false;
         }

         if (formatMatched)
            return true;
      }

      return false;
   }

   /** Convert the transaction to the format to be imported */
   this.convert = function (transactions) {
      var transactionsToImport = [];

      // Filter and map rows
      for (i = 0; i < transactions.length; i++) {
         var transaction = transactions[i];
         if (transaction.length < (this.colAmount + 1))
            continue;
         if (transaction[this.colDate] && transaction[this.colDate].match(/[0-9]{2,4}(\-)[0-9]{2}(\-)[0-9]{2,4}/g)
            && transaction[this.colDate].length == 10)
            transactionsToImport.push(this.mapTransaction(transaction));
      }

      // Sort rows by date (just invert)
      transactionsToImport = transactionsToImport.reverse();

      // Add header and return
      var header = [["Date", "Doc", "Description", "Income", "Expenses"]];
      return header.concat(transactionsToImport);
   }


   this.mapTransaction = function (element) {
      var mappedLine = [];

      mappedLine.push(Banana.Converter.toInternalDateFormat(element[this.colDate], this.dateFormat));
      mappedLine.push(""); // Doc is empty for now
      var tidyDescr = element[this.colDescr].replace(/ {2,}/g, ''); //remove white spaces
      mappedLine.push(Banana.Converter.stringToCamelCase(tidyDescr));
      var amount = element[this.colCredit].replace(/-/g, ''); //remove minus sign
      mappedLine.push(Banana.Converter.toInternalNumberFormat(amount));
      amount = element[this.colDebit].replace(/-/g, ''); //remove minus sign
      mappedLine.push(Banana.Converter.toInternalNumberFormat(amount));

      return mappedLine;
   }
}
/**
 * PFCSV Format 5
 * Example: pfcsv.#20230901
**/
function PFCSVFormat5() {

   this.colDate = 0;
   this.colMovType = 1;
   this.colDescr = 2;
   this.colCredit = 3;
   this.colDebit = 4;

   this.dateFormat = 'dd.mm.yyyy';

   /** Return true if the transactions match this format */
   this.match = function (transactions) {
      if (transactions.length === 0)
         return false;

      for (i = 0; i < transactions.length; i++) {
         var transaction = transactions[i];

         var formatMatched = false;
         if (transaction.length === this.colDebit + 1)
            formatMatched = true;
         else
            formatMatched = false;

         if (formatMatched && transaction[this.colDate].match(/[0-9]{2}(\.)[0-9]{2}(\.)[0-9]{2}/g)) {
            formatMatched = true;
         } else {
            formatMatched = false;
         }

         if (formatMatched)
            return true;
      }

      return false;
   }

   /** Convert the transaction to the format to be imported */
   this.convert = function (transactions) {
      var transactionsToImport = [];

      // Filter and map rows
      for (i = 0; i < transactions.length; i++) {
         var transaction = transactions[i];
         if (transaction.length < (this.colAmount + 1))
            continue;
         if (transaction[this.colDate] && transaction[this.colDate].match(/[0-9]{2}(\.)[0-9]{2}(\.)[0-9]{4}/g) && transaction[this.colDate].length == 10)
            transactionsToImport.push(this.mapTransaction(transaction));
      }

      // Sort rows by date (just invert)
      transactionsToImport = transactionsToImport.reverse();

      // Add header and return
      var header = [["Date", "Doc", "Description", "Income", "Expenses"]];
      return header.concat(transactionsToImport);
   }


   this.mapTransaction = function (element) {
      var mappedLine = [];

      mappedLine.push(Banana.Converter.toInternalDateFormat(element[this.colDate], this.dateFormat));
      mappedLine.push(""); // Doc is empty for now
      var tidyDescr = element[this.colDescr].replace(/ {2,}/g, ''); //remove white spaces
      mappedLine.push(Banana.Converter.stringToCamelCase(tidyDescr));
      mappedLine.push(Banana.Converter.toInternalNumberFormat(element[this.colCredit], '.'));
      amountDebit = element[this.colDebit].replace(/-/g, ''); //remove minus sign
      mappedLine.push(Banana.Converter.toInternalNumberFormat(amountDebit, '.'));

      return mappedLine;
   }
}

/**
 * PFCSV Format 4
 * Example: pfcsv.#20230509
 * Fœnum porto natio:;0000 8003 3386 9363
 * Natio:;LIAM LIAM LIAM 5526 PecuLeverba Aturaequat Cocet Voluna
 * Tuundit nostinsan:;06.04.2022 - 05.05.2022
 * Data;Denominazione;Accredito in CHF;Addebito in CHF;Importo in CHF
 * 2022-05-04;"ARTION *PRATIUNDICO      52163467544  XXX";;52.00;
 * 2022-05-04;"1.7% SUPPL. CHF ALL'ESTERO";;0.88;
 * 2022-05-04;"ARTION *EXPECT CUNT      1324126664   NOS";;21.93;
 * 2022-05-03;"ARTION *EXPECT CUNT      1324126664   NOS";;11.11;
 * 2022-05-03;"ARTION *MENTIO SET       1324126664   STO";;15.00;
 * 2022-05-03;"1.7% SUPPL. CHF ALL'ESTERO";;0.26;
 * 2022-05-02;"PATTINDE NATHOC FŒNUM NATIO";300.00;;
 * 2022-05-01;"ARATIMOTE PATUBIT        MODO CONDE MONCH NIS 0.56 Effect 8.1480 ost 37.77.6604 TER 0.62 8.52% de todivispect cor pasus fertumquobsemo TER 0.77";;8.44;
**/
function PFCSVFormat4() {

   this.colDate = 0;
   this.colDescr = 1;
   this.colCredit = 2;
   this.colDebit = 3;
   this.colAmount = 4;

   this.dateFormat = 'dd-mm-yyyy';

   /** Return true if the transactions match this format */
   this.match = function (transactions) {
      if (transactions.length === 0)
         return false;

      for (i = 0; i < transactions.length; i++) {
         var transaction = transactions[i];

         var formatMatched = false;
         if (transaction.length === this.colAmount + 1)
            formatMatched = true;
         else
            formatMatched = false;
         if (formatMatched && transaction[this.colDate].match(/[0-9]{2}(\-)[0-9]{2}(\-)[0-9]{4}/g)) {
            formatMatched = true;
         } else if (formatMatched && transaction[this.colDate].match(/[0-9]{4}(\-)[0-9]{2}(\-)[0-9]{2}/g)) {
            formatMatched = true;
            this.dateFormat = 'yyyy-mm-dd';
         } else {
            formatMatched = false;
         }

         if (formatMatched)
            return true;
      }

      return false;
   }

   /** Convert the transaction to the format to be imported */
   this.convert = function (transactions) {
      var transactionsToImport = [];

      // Filter and map rows
      for (i = 0; i < transactions.length; i++) {
         var transaction = transactions[i];
         if (transaction.length < (this.colAmount + 1))
            continue;
         if (transaction[this.colDate].match(/[0-9]{2,4}(\-)[0-9]{2}(\-)[0-9]{2,4}/g) && transaction[this.colDate].length == 10)
            transactionsToImport.push(this.mapTransaction(transaction));
      }

      // Sort rows by date (just invert)
      transactionsToImport = transactionsToImport.reverse();

      // Add header and return
      var header = [["Date", "Doc", "Description", "Income", "Expenses"]];
      return header.concat(transactionsToImport);
   }


   this.mapTransaction = function (element) {
      var mappedLine = [];

      mappedLine.push(Banana.Converter.toInternalDateFormat(element[this.colDate], this.dateFormat));
      mappedLine.push(""); // Doc is empty for now
      var tidyDescr = element[this.colDescr].replace(/ {2,}/g, ''); //remove white spaces
      mappedLine.push(Banana.Converter.stringToCamelCase(tidyDescr));
      var amount = element[this.colCredit].replace(/-/g, ''); //remove minus sign
      mappedLine.push(Banana.Converter.toInternalNumberFormat(amount));
      amount = element[this.colDebit].replace(/-/g, ''); //remove minus sign
      mappedLine.push(Banana.Converter.toInternalNumberFormat(amount));

      return mappedLine;
   }
}

/**
 * PF Html Format 1
 * Html table with the followings colums:
 * 0:Details; 1:Date; 2:Description; 3:Income; 4:Expenses; 5:DateValue; 6:Balance;
**/
function PFHtmlFormat1() {

   /** This function defines the convertion of the single html table rows to Banana fields.
     This is the only function to be adapted to the desired format. */
   this.htmlRowToObject = function (htmlString) {

      // Extract html fields (tags td)
      var htmlTableFields = htmlString.match(/<td[^>]*>[\s\S]*?<\/td>/g); //[\s\S]*? match all chars non gready
      if (!htmlTableFields)
         return null;

      // Verify fields count
      if (htmlTableFields.length < 6)
         return null;

      // Verify if date field match
      var date = this.htmlText(htmlTableFields[1]);
      if (!date.match(/[0-9.]{8}/))
         return null;

      // Convert row
      var rowObject = {};
      rowObject.Date = Banana.Converter.toInternalDateFormat(this.htmlText(htmlTableFields[1]));
      rowObject.Description = Banana.Converter.stringToCamelCase(this.htmlText(htmlTableFields[2]));
      rowObject.ContraAccount = "";
      rowObject.Income = Banana.Converter.toInternalNumberFormat(this.htmlText(htmlTableFields[3]));
      rowObject.Expenses = Banana.Converter.toInternalNumberFormat(this.htmlText(htmlTableFields[4]));
      rowObject.DateValue = Banana.Converter.toInternalDateFormat(this.htmlText(htmlTableFields[5]));
      rowObject._Balance = Banana.Converter.toInternalNumberFormat(this.htmlText(htmlTableFields[6]));
      return rowObject;
   }

   /** This function extract from the html the data to be imported in Banana Accounting.
       It use the function htmlRowToObject to convert the single data rows. */
   this.convert = function (htmlString) {
      var rows = [];
      var htmlTables = htmlString.match(/<tbody[^>]*>[\s\S]*?<\/tbody>/g);  //[\s\S]*? match all chars non gready
      if (htmlTables) {
         for (var t = 0; t < htmlTables.length; t++) {
            var htmlTableRows = htmlTables[t].match(/<tr[^>]*>[\s\S]*?<\/tr>/g); //[\s\S]*? match all chars non gready
            if (htmlTableRows) {
               for (var r = 0; r < htmlTableRows.length; r++) {
                  var row = this.htmlRowToObject(htmlTableRows[r]);
                  if (row) {
                     rows.push(row);
                  }
               }
            }
         }
      }
      return rows;
   }

   /** This function extract the text inside an html element */
   this.htmlText = function (htmlString) {
      // Read text from html string
      // The text is found between each ">...<" sequence
      var retText = "";
      var htmlTexts = htmlString.match(/>[^<]+</g);
      if (htmlTexts) {
         for (var i = 0; i < htmlTexts.length; i++) {
            var htmlSubText = htmlTexts[i];
            if (htmlSubText.length > 2)
               retText = retText + htmlSubText.substr(1, htmlSubText.length - 2);
         }
      }

      // Remove line feeds
      retText = retText.replace(/^[ \n\r]+/, ""); // at the beginning
      retText = retText.replace(/[ \n\r]+$/, ""); // at the end
      retText = retText.replace(/ *[\n\r]+ */g, ", "); // in the middle

      return retText;
   }
}

/**
 * PFCSV Format 3
 * Example: pfcsv.#20101031
 * BookingDate;BookingText;Details;ValutaDate;DebitAmount;CreditAmount;Balance
 * 31.10.2010;FÜR DAS ONLINE-SET SEPTEMBER XXXX;;31.10.2010;;0.00;5831.73
 * 29.10.2010;E-FINANCE XXX;1;29.10.2010;-45.00;;5831.73
 * 29.10.2010;E-FINANCE XXX;1;29.10.2010;-131.55;;
 * Example: pfcsv.#20131231
 * Buchung;Buchungstext;Details;Valuta;Belastung;Gutschrift;Saldo;Kategorie;Familienmitglied;Kommentar
 * "31.12.2013";"ZINSABSCHLUSS 010113 - 311213";"";"31.12.2013";"-0.15";"";"2549.30";"";"";""
 * "24.12.2013";"KAUF/DIENSTLEISTUNG
 * VOM 23.12.2013
 * KARTEN NR. 82770597
 * CUCINA PERO AG
 * WƒDENSWIL";"1";"23.12.2013";"-124.00";"";"2549.45";"";"";""
**/
function PFCSVFormat3() {

   this.colDate = 0;
   this.colDescr = 1;
   this.colDateValuta = 3;
   this.colDebit = 4;
   this.colCredit = 5;
   this.colBalance = 6;
   this.colComment = 9;

   /** Return true if the transactions match this format */
   this.match = function (transactions) {
      if (transactions.length === 0)
         return false;

      for (i = 0; i < transactions.length; i++) {
         var transaction = transactions[i];

         var formatMatched = false;
         if (transaction.length === (this.colBalance + 1) ||
            transaction.length === (this.colComment + 1))
            formatMatched = true;
         else
            formatMatched = false;

         if (formatMatched && transaction[this.colDate].match(/[0-9]{2,4}(\.|-)[0-9]{2}(\.|-)[0-9]{2,4}/g) &&
            transaction[this.colDate].length === 10)
            formatMatched = true;
         else
            formatMatched = false;

         if (formatMatched && transaction[this.colDateValuta].match(/[0-9]{2,4}(\.|-)[0-9]{2}(\.|-)[0-9]{2,4}/g) &&
            transaction[this.colDateValuta].length === 10)
            formatMatched = true;
         else
            formatMatched = false;

         if (formatMatched)
            return true;
      }

      return false;
   }

   /** Convert the transaction to the format to be imported */
   this.convert = function (transactions) {
      var transactionsToImport = [];

      // Filter and map rows
      for (i = 0; i < transactions.length; i++) {
         var transaction = transactions[i];
         if (transaction.length < (this.colBalance + 1))
            continue;
         if (transaction[this.colDate].match(/[0-9]{2,4}(\.|-)[0-9]{2}(\.|-)[0-9]{2,4}/g) && transaction[this.colDate].length == 10 &&
            transaction[this.colDateValuta].match(/[0-9]{2,4}(\.|-)[0-9]{2}(\.|-)[0-9]{2,4}/g) && transaction[this.colDateValuta].length == 10)
            transactionsToImport.push(this.mapTransaction(transaction));
      }

      // Sort rows by date (just invert)
      transactionsToImport = transactionsToImport.reverse();

      // Add header and return
      var header = [["Date", "DateValue", "Doc", "Description", "Income", "Expenses"]];
      return header.concat(transactionsToImport);
   }


   this.mapTransaction = function (element) {
      var mappedLine = [];

      mappedLine.push(Banana.Converter.toInternalDateFormat(element[this.colDate], 'dd-mm-yyyy'));
      mappedLine.push(Banana.Converter.toInternalDateFormat(element[this.colDateValuta], 'dd-mm-yyyy'));
      mappedLine.push(""); // Doc is empty for now
      var tidyDescr = element[this.colDescr].replace(/ {2,}/g, ''); //remove white spaces
      mappedLine.push(Banana.Converter.stringToCamelCase(tidyDescr));
      var amount = element[this.colCredit].replace(/-/g, ''); //remove minus sign
      mappedLine.push(Banana.Converter.toInternalNumberFormat(amount));
      amount = element[this.colDebit].replace(/-/g, ''); //remove minus sign
      mappedLine.push(Banana.Converter.toInternalNumberFormat(amount));

      return mappedLine;
   }
}


/**
 * PFCSV Format 2
 * Example: pfcsv.#private20090401
 * Example: pfcsv.#private20090401
 * Data	Testo d'avviso	Accredito	Addebito	Data della valuta	Saldo	
 * 20090401	/t ACQUISTO/SERVIZIO DEL XX.XX.XXXX  CARTA N. XXX	/t99.9	/t20090331	/t			/t
 * 20090331	/t ORDINE DEBIT DIRECT NUMERO CLIENTE XXX			/t85.9	/t20090331	/t7881.35	/t
 * 20090330	/t ACQUISTO/SERVIZIO DEL XX.XX.XXXX  CARTA N. XXX	/t43	/t20090328	/t7967.25	/t
 *
 *
**/
function PFCSVFormat2() {

   this.colDate = 0;
   this.colDescr = 1;
   this.colCredit = 2;
   this.colDebit = 3;
   this.colDateValuta = 4;
   this.colBalance = 5;

   /** Return true if the transactions match this format */
   this.match = function (transactions) {
      if (transactions.length === 0)
         return false;

      for (i = 0; i < transactions.length; i++) {
         var transaction = transactions[i];

         var formatMatched = false;
         if (transaction.length === (this.colBalance + 2))
            formatMatched = true;
         else
            formatMatched = false;

         if (formatMatched && transaction[this.colDate].match(/[0-9]{6}/g)
            && transaction[this.colDate].length === 8)
            formatMatched = true;

         if (formatMatched && transaction[this.colDateValuta].match(/[0-9]{6}/g) &&
            transaction[this.colDateValuta].length == 8)
            formatMatched = true;
         else
            formatMatched = false;

         if (formatMatched)
            return true;
      }

      return false;
   }

   /** Convert the transaction to the format to be imported */
   this.convert = function (transactions) {
      var transactionsToImport = [];

      // Filter and map rows
      for (i = 0; i < transactions.length; i++) {
         var transaction = transactions[i];
         if (transaction.length < (this.colBalance + 1))
            continue;
         if (transaction[this.colDate].match(/[0-9]+/g) && transaction[this.colDate].length == 8 &&
            transaction[this.colDateValuta].match(/[0-9]+/g) && transaction[this.colDateValuta].length == 8)
            transactionsToImport.push(this.mapTransaction(transaction));
      }

      // Sort rows by date (just invert)
      transactionsToImport = transactionsToImport.reverse();

      // Add header and return
      var header = [["Date", "DateValue", "Doc", "Description", "Income", "Expenses"]];
      return header.concat(transactionsToImport);
   }


   this.mapTransaction = function (element) {
      var mappedLine = [];

      mappedLine.push(element[this.colDate]);
      mappedLine.push(element[this.colDateValuta]);
      mappedLine.push(""); // Doc is empty for now
      var tidyDescr = element[this.colDescr].replace(/ {2,}/g, ' '); //remove white spaces
      mappedLine.push(Banana.Converter.stringToCamelCase(tidyDescr));
      mappedLine.push(Banana.Converter.toInternalNumberFormat(element[this.colCredit]));
      mappedLine.push(Banana.Converter.toInternalNumberFormat(element[this.colDebit]));

      return mappedLine;
   }
}


/**
 * PFCSV Format 1
 * Example: pfcsv.#20030903-B
 * Example: pfcsv.#20121101-B
 * Example: pfcsv.#20160707
 * Data;Descrizione della transazione;Accreditamento;Debito;Valuta;Saldo
 * 31.08.2003;Saldo;;;;50078.40
 * 01.09.2003;"YELLOWNET SAMMELAUFTRAG NR. X,YELLOWNET NUMMER XXXXXX";;-28.60;01.09.2003;50049.80
 * 01.09.2003;"AUFTRAG DEBIT DIRECT,AUFTRAGSNUMMER X,KUNDENNUMMER XXXX";26.80;;01.09.2003;50076.60
**/

function PFCSVFormat1() {

   this.colDate = 0;
   this.colDescr = 1;
   this.colCredit = 2;
   this.colDebit = 3;
   this.colDateValuta = 4;
   this.colBalance = 5;

   this.dateFormat = 'dd-mm-yyyy';
   this.decimalSeparator = '.';


   /** Return true if the transactions match this format */
   this.match = function (transactions) {
      if (transactions.length === 0)
         return false;

      for (i = 0; i < transactions.length; i++) {
         var transaction = transactions[i];

         var formatMatched = false;
         if (transaction.length === (this.colBalance + 1) || transaction.length === (this.colBalance + 2))
            formatMatched = true;
         else
            formatMatched = false;

         if (formatMatched && transaction[this.colDate].match(/[0-9]{2}(\.)[0-9]{2}(\.)[0-9]{2}/g)) {
            this.dateFormat = 'dd.mm.yy';
            formatMatched = true;
         } else if (formatMatched && transaction[this.colDate].match(/[0-9]{2}(\.|-)[0-9]{2}(\.|-)[0-9]{4}/g)) {
            formatMatched = true;
         } else if (formatMatched && transaction[this.colDate].match(/[0-9]{4}(\.|-)[0-9]{2}(\.|-)[0-9]{2}/g)) {
            formatMatched = true;
            this.dateFormat = 'yyyy-mm-dd';
         } else {
            formatMatched = false;
         }

         if (formatMatched && transaction[this.colDateValuta].match(/[0-9]{2,4}(\.|-)[0-9]{2}(\.|-)[0-9]{2,4}/g))
            formatMatched = true;
         else
            formatMatched = false;

         if (formatMatched)
            return true;
      }

      return false;
   }


   /** Convert the transaction to the format to be imported */
   this.convert = function (transactions) {
      var transactionsToImport = [];

      // Filter and map rows
      for (i = 0; i < transactions.length; i++) {
         var transaction = transactions[i];
         if (transaction.length < (this.colBalance + 1))
            continue;
         if (transaction[this.colDate].match(/[0-9\.]{3}/g) && transaction[this.colDateValuta].match(/[0-9\.]{3}/g))
            transactionsToImport.push(this.mapTransaction(transaction));
      }

      // Sort rows by date
      transactionsToImport = this.sort(transactionsToImport);

      // Add header and return
      var header = [["Date", "DateValue", "Doc", "Description", "Income", "Expenses"]];
      return header.concat(transactionsToImport);
   }


   /** Sort transactions by date */
   this.sort = function (transactions) {
      if (transactions.length <= 0)
         return transactions;
      var i = 0;
      var previousDate = transactions[0][this.colDate];
      while (i < transactions.length) {
         var date = transactions[i][this.colDate];
         if (previousDate.length > 0 && previousDate > date)
            return transactions.reverse();
         else if (previousDate.length > 0 && previousDate < date)
            return transactions;
         i++;
      }
      return transactions;
   }

   this.mapTransaction = function (element) {
      var mappedLine = [];

      mappedLine.push(Banana.Converter.toInternalDateFormat(element[this.colDate], this.dateFormat));
      mappedLine.push(Banana.Converter.toInternalDateFormat(element[this.colDateValuta], this.dateFormat));
      mappedLine.push(""); // Doc is empty for now
      var tidyDescr = element[this.colDescr].replace(/ {2,}/g, ''); //remove white spaces
      mappedLine.push(Banana.Converter.stringToCamelCase(tidyDescr));
      var amount = element[this.colCredit].replace(/\+/g, ''); //remove plus sign
      mappedLine.push(Banana.Converter.toInternalNumberFormat(amount, this.decimalSeparator));
      amount = element[this.colDebit].replace(/-/g, ''); //remove minus sign
      mappedLine.push(Banana.Converter.toInternalNumberFormat(amount, this.decimalSeparator));

      return mappedLine;
   }
}

/**
 * The function findSeparator is used to find the field separator.
 */
function findSeparator(string) {

   var commaCount = 0;
   var semicolonCount = 0;
   var tabCount = 0;

   for (var i = 0; i < 1000 && i < string.length; i++) {
      var c = string[i];
      if (c === ',')
         commaCount++;
      else if (c === ';')
         semicolonCount++;
      else if (c === '\t')
         tabCount++;
   }

   if (tabCount > commaCount && tabCount > semicolonCount) {
      return '\t';
   }
   else if (semicolonCount > commaCount) {
      return ';';
   }

   return ',';
}

/**
 * PFCSV Smart Business Format 1
 * Example: pfcsv.#20180220-SBU
 * "client_name";"paid_date";"paid_amount"
 * "Schaub Thomas";"21.02.2018";"100.00"
 * "Prins Carla";"20.02.2018";"150.00"
 * "Mario Wlotzka";"15.02.2018";"960.00"
**/

function PFCSVFormatSBU1() {

   this.colDate = 1;
   this.colDescr = 0;
   this.colCredit = 2;

   this.dateFormat = 'dd.mm.yyyy';
   this.decimalSeparator = '.';

   /** Return true if the transactions match this format */
   this.match = function (transactions) {
      if (transactions.length === 0)
         return false;

      for (i = 0; i < transactions.length; i++) {
         var transaction = transactions[i];

         var formatMatched = false;
         if (transaction.length === (this.colCredit + 1))
            formatMatched = true;
         else
            formatMatched = false;

         if (formatMatched && transaction[this.colDate].match(/[0-9]{2}(\.|-)[0-9]{2}(\.|-)[0-9]{4}/g)) {
            formatMatched = true;
         } else {
            formatMatched = false;
         }

         if (formatMatched)
            return true;
      }

      return false;
   }


   /** Convert the transaction to the format to be imported */
   this.convert = function (transactions) {
      var transactionsToImport = [];

      // Filter and map rows
      for (var i = 0; i < transactions.length; i++) {
         var transaction = transactions[i];
         if (transaction.length < this.colCredit)
            continue;
         if (transaction[this.colDate].match(/[0-9\.]{3}/g))
            transactionsToImport.push(this.mapTransaction(transaction));
      }

      // Sort rows by date
      transactionsToImport = this.sort(transactionsToImport);

      // Add header and return
      var header = [["Date", "DateValue", "Doc", "Description", "Income", "Expenses"]];
      return header.concat(transactionsToImport);
   }


   /** Sort transactions by date */
   this.sort = function (transactions) {
      if (transactions.length <= 0)
         return transactions;
      var i = 0;
      var previousDate = transactions[0][this.colDate];
      while (i < transactions.length) {
         var date = transactions[i][this.colDate];
         if (previousDate.length > 0 && previousDate > date)
            return transactions.reverse();
         else if (previousDate.length > 0 && previousDate < date)
            return transactions;
         i++;
      }
      return transactions;
   }

   this.mapTransaction = function (element) {
      var mappedLine = [];

      mappedLine.push(Banana.Converter.toInternalDateFormat(element[this.colDate], this.dateFormat));
      mappedLine.push("");
      mappedLine.push("");
      mappedLine.push(element[this.colDescr]);
      mappedLine.push(Banana.Converter.toInternalNumberFormat(element[this.colCredit], this.decimalSeparator));
      mappedLine.push("");

      return mappedLine;
   }
}

/**
 * The function findSeparator is used to find the field separator.
 */
function findSeparator(string) {

   var commaCount = 0;
   var semicolonCount = 0;
   var tabCount = 0;

   for (var i = 0; i < 1000 && i < string.length; i++) {
      var c = string[i];
      if (c === ',')
         commaCount++;
      else if (c === ';')
         semicolonCount++;
      else if (c === '\t')
         tabCount++;
   }

   if (tabCount > commaCount && tabCount > semicolonCount) {
      return '\t';
   }
   else if (semicolonCount > commaCount) {
      return ';';
   }

   return ',';
}

/*
* class ImportUtilities
* Contains methods that can be shared by extensions for importing bank data
*/

var ImportUtilities = class ImportUtilities {
    constructor(banDocument) {
        this.banDocument = banDocument;
        if (this.banDocument === undefined)
            this.banDocument = Banana.document;
    }

    //The purpose of this function is to convert all the data into a format supported by Banana
    convertToBananaFormat(intermediaryData) {
        var columnTitles = [];

        //Create titles only for fields not starting with "_"
        for (var name in intermediaryData[0]) {
            if (name.substring(0, 1) !== "_") {
                columnTitles.push(name);
            }
        }
        //Function call Banana.Converter.objectArrayToCsv() to create a CSV with new data just converted
        var convertedCsv = Banana.Converter.objectArrayToCsv(columnTitles, intermediaryData, "\t");

        return convertedCsv;
    }

    // Convert to an array of objects where each object property is the banana columnNameXml
    convertCsvToIntermediaryData(inData, convertionParam) {
        var form = [];
        var intermediaryData = [];
        //Add the header if present 
        if (convertionParam.header) {
            inData = convertionParam.header + inData;
        }

        //Read the CSV file and create an array with the data
        var csvFile = Banana.Converter.csvToArray(inData, convertionParam.separator, convertionParam.textDelim);

        //Variables used to save the columns titles and the rows values
        var columns = this.getHeaderData(csvFile, convertionParam.headerLineStart); //array
        var rows = this.getRowData(csvFile, convertionParam.dataLineStart); //array of array

        //Load the form with data taken from the array. Create objects
        this.loadForm(form, columns, rows);

        //Create the new CSV file with converted data
        var convertedRow;
        //For each row of the form, we call the rowConverter() function and we save the converted data
        for (var i = 0; i < form.length; i++) {
            convertedRow = convertionParam.rowConverter(form[i]);
            intermediaryData.push(convertedRow);
        }

        //Return the converted CSV data into the Banana document table
        return intermediaryData;
    }

    // Convert to an array of objects where each object property is the banana columnNameXml
    convertHtmlToIntermediaryData(inData, convertionParam) {
        var form = [];
        var intermediaryData = [];

        //Read the HTML file and create an array with the data
        var htmlFile = [];
        var htmlRows = inData.match(/<tr[^>]*>.*?<\/tr>/gi);
        for (var rowNr = 0; rowNr < htmlRows.length; rowNr++) {
            var htmlRow = [];
            var htmlFields = htmlRows[rowNr].match(/<t(h|d)[^>]*>.*?<\/t(h|d)>/gi);
            for (var fieldNr = 0; fieldNr < htmlFields.length; fieldNr++) {
                var htmlFieldRe = />(.*)</g.exec(htmlFields[fieldNr]);
                htmlRow.push(htmlFieldRe.length > 1 ? htmlFieldRe[1] : "");
            }
            htmlFile.push(htmlRow);
        }

        //Variables used to save the columns titles and the rows values
        var columns = this.getHeaderData(htmlFile, convertionParam.headerLineStart); //array
        var rows = this.getRowData(htmlFile, convertionParam.dataLineStart); //array of array

        //Convert header names
        for (var i = 0; i < columns.length; i++) {
            var convertedHeader = columns[i];
            convertedHeader = convertedHeader.toLowerCase();
            convertedHeader = convertedHeader.replace(" ", "_");
            var indexOfHeader = columns.indexOf(convertedHeader);
            if (indexOfHeader >= 0 && indexOfHeader < i) { // Header alreay exist
                //Avoid headers with same name adding an incremental index
                var newIndex = 2;
                while (columns.indexOf(convertedHeader + newIndex.toString()) !== -1 && newIndex < 99)
                    newIndex++;
                convertedHeader = convertedHeader + newIndex.toString()
            }
            columns[i] = convertedHeader;
        }

        // Banana.console.log(JSON.stringify(columns, null, "   "));

        //Load the form with data taken from the array. Create objects
        this.loadForm(form, columns, rows);

        //Create the new CSV file with converted data
        var convertedRow;
        //For each row of the form, we call the rowConverter() function and we save the converted data
        for (var i = 0; i < form.length; i++) {
            convertedRow = convertionParam.rowConverter(form[i]);
            intermediaryData.push(convertedRow);
        }

        //Return the converted CSV data into the Banana document table
        return intermediaryData;
    }

    // Convert to an array of objects where each object property is the banana columnNameXml
    convertToIntermediaryData(inData, convertionParam) {
        if (convertionParam.format === "html") {
            return this.convertHtmlToIntermediaryData(inData, convertionParam);
        } else {
            return this.convertCsvToIntermediaryData(inData, convertionParam);
        }
    }

    //The purpose of this function is to return all the titles of the columns
    getHeaderData(csvFile, startLineNumber) {
        if (!startLineNumber) {
            startLineNumber = 0;
        }
        var headerData = csvFile[startLineNumber];
        for (var i = 0; i < headerData.length; i++) {

            headerData[i] = headerData[i].trim();

            if (!headerData[i]) {
                headerData[i] = i;
            }

            //Avoid duplicate headers
            var headerPos = headerData.indexOf(headerData[i]);
            if (headerPos >= 0 && headerPos < i) { // Header already exist
                var postfixIndex = 2;
                while (headerData.indexOf(headerData[i] + postfixIndex.toString()) !== -1 && postfixIndex <= 99)
                    postfixIndex++; // Append an incremental index
                headerData[i] = headerData[i] + postfixIndex.toString()
            }

        }
        return headerData;
    }

    getLang() {

        var lang = 'en';
        if (this.banDocument)
            lang = this.banDocument.locale;
        else if (Banana.application.locale)
            lang = Banana.application.locale;
        if (lang.length > 2)
            lang = lang.substr(0, 2);
        return lang;
    }
    
    //The purpose of this function is to return all the data of the rows
    getRowData(csvFile, startLineNumber) {
        if (!startLineNumber) {
            startLineNumber = 1;
        }
        var rowData = [];
        for (var i = startLineNumber; i < csvFile.length; i++) {
            rowData.push(csvFile[i]);
        }
        return rowData;
    }

    //The purpose of this function is to load all the data (titles of the columns and rows) and create a list of objects.
    //Each object represents a row of the csv file
    loadForm(form, columns, rows) {
        for (var j = 0; j < rows.length; j++) {
            var obj = {};
            for (var i = 0; i < columns.length; i++) {
                obj[columns[i]] = rows[j][i];
            }
            form.push(obj);
        }
    }

    // The purpose of this function is to sort the data
    sortData(intermediaryData, convertionParam) {
        if (convertionParam.sortColums && convertionParam.sortColums.length) {
            intermediaryData.sort(
                function (row1, row2) {
                    for (var i = 0; i < convertionParam.sortColums.length; i++) {
                        var columnName = convertionParam.sortColums[i];
                        if (row1[columnName] > row2[columnName])
                            return 1;
                        else if (row1[columnName] < row2[columnName])
                            return -1;
                    }
                    return 0;
                });

            if (convertionParam.sortDescending)
                intermediaryData.reverse();
        }

        return intermediaryData;
    }

    verifyBananaPlusVersion() {
        if (!this.banDocument)
            return false;

        var BAN_VERSION_MIN = "10.0";

        // supported Version
        if (Banana.compareVersion && Banana.compareVersion(Banana.application.version, BAN_VERSION_MIN) >= 0) {
            return true;
        }

        // not supported version
        var lang = this.getLang();
        var msg = "This extension requires Banana Accounting+";
        this.banDocument.addMessage(msg, "ID_ERR_LICENSE_NOTVALID");
        return false;
    }

    //Check if the version of Banana Accounting is compatible with this class
    verifyBananaAdvancedVersion() {
        if (!this.banDocument)
            return false;

        if (!Banana.application.license || Banana.application.license.licenseType !== "advanced") {
            var lang = this.getLang();
            var msg = "This extension requires Banana Accounting+ Advanced";
            this.banDocument.addMessage(msg, "ID_ERR_LICENSE_NOTVALID");
            return false;
        }

        return true;
    }

    getErrorMessage(errorId, lang) {
        if (!lang)
            lang = 'en';
        switch (errorId) {
            case "ID_ERR_FORMAT_UNKNOWN":
                 if (lang == 'it')
                    return "Formato del file *.csv non riconosciuto";
                 else if (lang == 'fr')
                    return "Format de fichier *.csv non reconnu";
                 else if (lang == 'de')
                    return "Unerkanntes *.csv-Dateiformat";
                 else
                    return "Unrecognised *.csv file format";
        }
        return '';
     }

    getLang() {
        var lang = 'en';
        if (Banana.application.locale)
            lang = Banana.application.locale;
        if (lang.length > 2)
            lang = lang.substring(0, 2);
        return lang;
     }

    getUnknownFormatError(){
        let errId = "ID_ERR_FORMAT_UNKNOWN"; //error
        let lang = this.getLang();
        let msg = this.getErrorMessage(errId, lang);
        Banana.document.addMessage(msg, errId);
    }
}

Input data - csv_postfinance_example_format1_CreditCard_20230929.csv

Input text passed to the exec() function.


Kartenkonto:;0000 1234 5467 7654
Karte:;XXXX XXXX XXXX 1111 PostFinance Visa Business Card
Datum;Buchungsdetails;Gutschrift in CHF;Lastschrift in CHF
2023-08-24;"Tankstelle";;-94.70
2023-08-21;"Tankstelle";;-114.05
2023-08-10;"6131 STORNO JAHRESPREIS";80.00;

Disclaimer:
Der Dokumentinhalt wurde durch Filtereinstellungen der Kund:innen generiert. PostFinance ist für den Inhalt und die Vollständigkeit nicht verantwortlich.

Output data - csv_postfinance_example_format1_CreditCard_20230929.tsv

Output text returned by the exec() function.


Date\tDoc\tDescription\tIncome\tExpenses
2023-08-10\t\t6131 Storno Jahrespreis\t80.00\t
2023-08-21\t\tTankstelle\t\t114.05
2023-08-24\t\tTankstelle\t\t94.70

Input data - csv_postfinance_example_format2_20090401.csv

Input text passed to the exec() function.


Data	Testo d'avviso	Accredito	Addebito	Data della valuta	Saldo	
20090401	ACQUISTO/SERVIZIO DES 31.03.2009  UNDAE W. 47655099 AURSENTATREPE-8250 LENSIT C. A LENSIT		71.8	66667620		
20090331	ORDINE PERIS NAVITI HORTIS PERUCUM 109631  PRIETE NABO ARITATIS OS HORTIS EO VOCTENENDIT: 000000000001107565001543539 PARTERIT EVENT PRINNAM DES 26.03.2009 W. REPEM UNDAE 1107 5650 0154 3539		85.9	20090331	7881.35	
20090330	ACQUISTO/SERVIZIO DES 28.03.2009  UNDAE W. 47655099 ACUTION ASTO RODO POST		30	11025450	1423.50	
20090330	ACQUISTO/SERVIZIO DES 28.03.2009  UNDAE W. 47655099 ACUTION ASTO RODO POST		47	11025450		
20090330	ACQUISTO/SERVIZIO DES 29.03.2009  UNDAE W. 47655099 DICITIMPTIONSI EST POST		81	78665580		
20090330	ACQUISTO/SERVIZIO DES 27.03.2009  UNDAE W. 47655099 MARTURRIUNGI EO VOLANEREM ANT LENSIT		58	55580477		
20090326	OPERAZIONE VENT CHRARDUCI ALIUNDI DES 25.03.2009 UNDAE W. 47655099 UNCELUM ALIUNDI REPERTUS		556.5	28683443	5582.50	
20090325	GIRATA EX RET MODEST 8465 DITIUNIS: /SECT SECTIA ET: WNQGR7Q513MBKEVW ACINSA OS OS NOSSUS ET: 79777957 090325CH81880950 IUSUPARTERIPUS: 690505731	4570.25		20090325	8388.85	
20090322	OPERAZIONE VENT CHRARDUCI ALIUNDI DES 21.03.2009 UNDAE W. 47655099 UNCELUM ALIUNDI REPERTUS		1606.8	13330272	8568.5	
20090319	PRELIEVO DE HABERUNT DES 19.03.2009  UNDAE W. 47655099 ANTINDIS: PUTULO PUTULO		636	68200818	2224.0	
20090316	OPERAZIONE VENT CHRARDUCI ALIUNDI DES 13.03.2009 UNDAE W. 47655099 UNCELUM ALIUNDI REPERTUS		8233	86784617	8577.0	
20090316	ACQUISTO/SERVIZIO DES 14.03.2009  UNDAE W. 47655099 IURRIDICUM PUPILODEANTE ET DIO QUA VOCARURA		32	68227047		
20090316	ACQUISTO/SERVIZIO DES 12.03.2009  UNDAE W. 47655099 SOMULTUSA OS PLUCET		30	60283240		
20090316	ACQUISTO/SERVIZIO DES 14.03.2009  UNDAE W. 47655099 FRUCEM B. VOCARURA B. VOCARURA		22	68227047		
20090311	OPERAZIONE VENT CHRARDUCI ALIUNDI DES 10.03.2009 UNDAE W. 47655099 UNCELUM ALIUNDI REPERTUS		4018	23204372	35808.0	
20090309	ACQUISTO/SERVIZIO DES 07.03.2009  UNDAE W. 47655099 PLEX-8801 B. VOCARURA QUA VOCARURA		50.34	17246434	83387.0	
20090309	ACQUISTO/SERVIZIO DES 07.03.2009  UNDAE W. 47655099 FRUCEM B. VOCARURA B. VOCARURA		31.34	17246434		
20090309	ACQUISTO/SERVIZIO DES 07.03.2009  UNDAE W. 47655099 AURSENTATREPE-7652 QUA VENTABO QUA VOCARURA		318	17246434		
20090309	ACQUISTO/SERVIZIO DES 05.03.2009  UNDAE W. 47655099 SERE-NUNT, COCTUUNT-POSTERTE PUTULO PHRATUS 52.00		52	20090305		
20090306	PRELIEVO DE HABERUNT DES 06.03.2009  UNDAE W. 47655099 ANTINDIS: LENSIT CUNGUA OBABO LENSIT		472	32032225	83803.1	
20090306	ACQUISTO/SERVIZIO DES 05.03.2009  UNDAE W. 47655099 FRUCEM FORGENS FORGENS		7.1	24280353		
20090304	ACQUISTO/SERVIZIO DES 03.03.2009  UNDAE W. 47655099 POSTO BONCH BONCH		50.50	84402074	84572.7	
20090303	ORDINE PERIS NAVITI HORTIS PERUCUM 109631  PRIETE NABO ARITATIS OS HORTIS EO VOCTENENDIT: 000000000001107565001543539 PARTERIT EVENT PRINNAM DES 26.02.2009 W. REPEM UNDAE 1107 5650 0154 3539		92.4	20090303	16062.65	


Output data - csv_postfinance_example_format2_20090401.tsv

Output text returned by the exec() function.


Date\tDateValue\tDoc\tDescription\tIncome\tExpenses
20090303\t20090303\t\tOrdine Peris Naviti Hortis Perucum 109631 Priete Nabo Aritatis Os Hortis Eo Voctenendit: 000000000001107565001543539 Parterit Event Prinnam Des 26.02.2009 W. Repem Undae 1107 5650 0154 3539\t\t92.4
20090304\t84402074\t\tAcquisto/servizio Des 03.03.2009 Undae W. 47655099 Posto Bonch Bonch\t\t50.50
20090306\t24280353\t\tAcquisto/servizio Des 05.03.2009 Undae W. 47655099 Frucem Forgens Forgens\t\t7.1
20090306\t32032225\t\tPrelievo De Haberunt Des 06.03.2009 Undae W. 47655099 Antindis: Lensit Cungua Obabo Lensit\t\t472
20090309\t20090305\t\tAcquisto/servizio Des 05.03.2009 Undae W. 47655099 Sere-Nunt, Coctuunt-Posterte Putulo Phratus 52.00\t\t52
20090309\t17246434\t\tAcquisto/servizio Des 07.03.2009 Undae W. 47655099 Aursentatrepe-7652 Qua Ventabo Qua Vocarura\t\t318
20090309\t17246434\t\tAcquisto/servizio Des 07.03.2009 Undae W. 47655099 Frucem B. Vocarura B. Vocarura\t\t31.34
20090309\t17246434\t\tAcquisto/servizio Des 07.03.2009 Undae W. 47655099 Plex-8801 B. Vocarura Qua Vocarura\t\t50.34
20090311\t23204372\t\tOperazione Vent Chrarduci Aliundi Des 10.03.2009 Undae W. 47655099 Uncelum Aliundi Repertus\t\t4018
20090316\t68227047\t\tAcquisto/servizio Des 14.03.2009 Undae W. 47655099 Frucem B. Vocarura B. Vocarura\t\t22
20090316\t60283240\t\tAcquisto/servizio Des 12.03.2009 Undae W. 47655099 Somultusa Os Plucet\t\t30
20090316\t68227047\t\tAcquisto/servizio Des 14.03.2009 Undae W. 47655099 Iurridicum Pupilodeante Et Dio Qua Vocarura\t\t32
20090316\t86784617\t\tOperazione Vent Chrarduci Aliundi Des 13.03.2009 Undae W. 47655099 Uncelum Aliundi Repertus\t\t8233
20090319\t68200818\t\tPrelievo De Haberunt Des 19.03.2009 Undae W. 47655099 Antindis: Putulo Putulo\t\t636
20090322\t13330272\t\tOperazione Vent Chrarduci Aliundi Des 21.03.2009 Undae W. 47655099 Uncelum Aliundi Repertus\t\t1606.8
20090325\t20090325\t\tGirata Ex Ret Modest 8465 Ditiunis: /sect Sectia Et: WNQGR7Q513MBKEVW Acinsa Os Os Nossus Et: 79777957 090325CH81880950 Iusuparteripus: 690505731\t4570.25\t
20090326\t28683443\t\tOperazione Vent Chrarduci Aliundi Des 25.03.2009 Undae W. 47655099 Uncelum Aliundi Repertus\t\t556.5
20090330\t55580477\t\tAcquisto/servizio Des 27.03.2009 Undae W. 47655099 Marturriungi Eo Volanerem Ant Lensit\t\t58
20090330\t78665580\t\tAcquisto/servizio Des 29.03.2009 Undae W. 47655099 Dicitimptionsi Est Post\t\t81
20090330\t11025450\t\tAcquisto/servizio Des 28.03.2009 Undae W. 47655099 Acution Asto Rodo Post\t\t47
20090330\t11025450\t\tAcquisto/servizio Des 28.03.2009 Undae W. 47655099 Acution Asto Rodo Post\t\t30
20090331\t20090331\t\tOrdine Peris Naviti Hortis Perucum 109631 Priete Nabo Aritatis Os Hortis Eo Voctenendit: 000000000001107565001543539 Parterit Event Prinnam Des 26.03.2009 W. Repem Undae 1107 5650 0154 3539\t\t85.9
20090401\t66667620\t\tAcquisto/servizio Des 31.03.2009 Undae W. 47655099 Aursentatrepe-8250 Lensit C. A Lensit\t\t71.8

Input data - csv_postfinance_example_format3_20101031.csv

Input text passed to the exec() function.


BookingDate;BookingText;Details;ValutaDate;DebitAmount;CreditAmount;Balance
31.10.2010;HS IGE HYACTA-XXX ANDUXEMUM 4682 - VIVIVIOFIG DEAGNA INE PAM 3555.77;;31.10.2010;;0.00;5831.73
29.10.2010;W-GANGUNT 75-13080-1 - MAGNUNANS;2;29.10.2010;-45.00;;5831.73
29.10.2010;W-GANGUNT 75-88716-7 - DIUSQUAM OSTO;2;29.10.2010;-131.55;;
29.10.2010;W-GANGUNT 75-73547-1 - MULTIUNT DE. TABICUSTIO VI;2;29.10.2010;-110.00;;
29.10.2010;W-GANGUNT 75-87515-1 - LIQUIT-PONTARE;2;29.10.2010;-119.65;;
29.10.2010;W-GANGUNT 75-88560-5 - MULTIUNT DE. TABICUSTIO VI;2;29.10.2010;-140.00;;
29.10.2010;W-GANGUNT 75-87515-1 - LIQUIT-PONTARE;2;29.10.2010;-249.30;;
29.10.2010;W-GANGUNT 75-28743-1 - MATE - TRUNT UT;2;29.10.2010;-122.85;;
29.10.2010;W-GANGUNT 75-45521-6 - SUBSTO BY;2;29.10.2010;-115.50;;
29.10.2010;W-GANGUNT 75-36815-7 - VERANEST;2;29.10.2010;-300.00;;
29.10.2010;W-GANGUNT 75-2055-5 - DE.SOLONE EXACTIANAVIT UT;2;29.10.2010;-1400.00;;
25.10.2010;SOLO DEM RIDEO 28-2-2 - TEM UT;2;25.10.2010;;110.00;8565.58
19.10.2010;REGERUMERI - XXX INE. RIDEO XXX 10.63.4682;2;19.10.2010;;500.00;8455.58
13.10.2010;OVUM/SEDIVISCATIRIS - XXX 36.63.4682;2;11.10.2010;-39.95;;7955.58
06.10.2010;SOLO DEM RIDEO 28-2-2 - TEM UT;2;06.10.2010;;392.00;7995.53
06.10.2010;REGERUMERI - XXX INE. RIDEO XXX 30.63.4682;2;06.10.2010;;900.00;
04.10.2010;OVUM/SEDIVISCATIRIS - XXX 25.63.4682;2;02.10.2010;-30.15;;6703.53
30.09.2010;HS IGE HYACTA-XXX LODUCI 4682 - VIVIVIOFIG HABULAC INE PAM 3555.77;;30.09.2010;-3.00;;6733.68
30.09.2010;W-GANGUNT 75-27675-7 - TESTANES (PROXITA) UT, AGNA;2;30.09.2010;-36.25;;
30.09.2010;W-GANGUNT 75-34663-7 - SENIURA ASTIURUNGOBTIS UT;2;30.09.2010;-58.85;;
30.09.2010;W-GANGUNT 75-88560-5 - MULTIUNT DE. TABICUSTIO VI;2;30.09.2010;-140.00;;
30.09.2010;W-GANGUNT 75-7670-4 - PRIATE CHABIT;2;30.09.2010;-141.50;;
30.09.2010;W-GANGUNT 75-87515-1 - LIQUIT-PONTARE;2;30.09.2010;-203.95;;
30.09.2010;W-GANGUNT 75-87515-1 - LIQUIT-PONTARE;2;30.09.2010;-249.30;;
30.09.2010;W-GANGUNT 75-36815-7 - VERANEST;2;30.09.2010;-300.00;;
30.09.2010;W-GANGUNT 75-2055-5 - DE.SOLONE EXACTIANAVIT UT;2;30.09.2010;-1400.00;;
30.09.2010;SOLO DEM HYACTA-RAL 700 - HUMELUMENTEM:;2;30.09.2010;;400.00;
28.09.2010;REGERUMERI XXX INE. RIDEO - XXX 51.28.4682;2;28.09.2010;;600.00;8866.53
28.09.2010;OVUM/SEDIVISCATIRIS - XXX 58.28.4682;2;24.09.2010;-49.00;;
27.09.2010;W-GANGUNT 24315 - PENDIRESTIFFICTRANSOLUNIUS DIO;2;27.09.2010;-282.60;;8315.53
20.09.2010;REGERUMERI XXX INE. RIDEO - XXX 58.28.4682;2;20.09.2010;;500.00;8598.13
17.09.2010;REGERUMERI XXX INE. RIDEO - XXX 75.28.4682;2;17.09.2010;;700.00;8098.13
14.09.2010;OVUM/SEDIVISCATIRIS - XXX 63.28.4682;2;10.09.2010;-44.95;;7398.13
03.09.2010;REGERUMERI XXX INE. RIDEO - XXX 63.28.4682;2;03.09.2010;;1200.00;7443.08
02.09.2010;OVUM/SEDIVISCATIRIS - XXX 28.28.4682;2;31.08.2010;-42.75;;6243.08
01.09.2010;REGERUMERI XXX INE. RIDEO - XXX 75.28.4682;2;01.09.2010;;800.00;6285.83

Output data - csv_postfinance_example_format3_20101031.tsv

Output text returned by the exec() function.


Date\tDateValue\tDoc\tDescription\tIncome\tExpenses
2010-09-01\t2010-09-01\t\tRegerumeri Xxx Ine. Rideo - Xxx 75.28.4682\t800.00\t
2010-09-02\t2010-08-31\t\tOvum/sediviscatiris - Xxx 28.28.4682\t\t42.75
2010-09-03\t2010-09-03\t\tRegerumeri Xxx Ine. Rideo - Xxx 63.28.4682\t1200.00\t
2010-09-14\t2010-09-10\t\tOvum/sediviscatiris - Xxx 63.28.4682\t\t44.95
2010-09-17\t2010-09-17\t\tRegerumeri Xxx Ine. Rideo - Xxx 75.28.4682\t700.00\t
2010-09-20\t2010-09-20\t\tRegerumeri Xxx Ine. Rideo - Xxx 58.28.4682\t500.00\t
2010-09-27\t2010-09-27\t\tW-Gangunt 24315 - Pendirestiffictransolunius Dio\t\t282.60
2010-09-28\t2010-09-24\t\tOvum/sediviscatiris - Xxx 58.28.4682\t\t49.00
2010-09-28\t2010-09-28\t\tRegerumeri Xxx Ine. Rideo - Xxx 51.28.4682\t600.00\t
2010-09-30\t2010-09-30\t\tSolo Dem Hyacta-Ral 700 - Humelumentem:\t400.00\t
2010-09-30\t2010-09-30\t\tW-Gangunt 75-2055-5 - De.Solone Exactianavit Ut\t\t1400.00
2010-09-30\t2010-09-30\t\tW-Gangunt 75-36815-7 - Veranest\t\t300.00
2010-09-30\t2010-09-30\t\tW-Gangunt 75-87515-1 - Liquit-Pontare\t\t249.30
2010-09-30\t2010-09-30\t\tW-Gangunt 75-87515-1 - Liquit-Pontare\t\t203.95
2010-09-30\t2010-09-30\t\tW-Gangunt 75-7670-4 - Priate Chabit\t\t141.50
2010-09-30\t2010-09-30\t\tW-Gangunt 75-88560-5 - Multiunt De. Tabicustio Vi\t\t140.00
2010-09-30\t2010-09-30\t\tW-Gangunt 75-34663-7 - Seniura Astiurungobtis Ut\t\t58.85
2010-09-30\t2010-09-30\t\tW-Gangunt 75-27675-7 - Testanes (Proxita) Ut, Agna\t\t36.25
2010-09-30\t2010-09-30\t\tH�s Ige Hyacta-Xxx Loduci 4682 - Vivivio�fig Habulac Ine Pam 3555.77\t\t3.00
2010-10-04\t2010-10-02\t\tOvum/sediviscatiris - Xxx 25.63.4682\t\t30.15
2010-10-06\t2010-10-06\t\tRegerumeri - Xxx Ine. Rideo Xxx 30.63.4682\t900.00\t
2010-10-06\t2010-10-06\t\tSolo Dem Rideo 28-2-2 - Tem Ut\t392.00\t
2010-10-13\t2010-10-11\t\tOvum/sediviscatiris - Xxx 36.63.4682\t\t39.95
2010-10-19\t2010-10-19\t\tRegerumeri - Xxx Ine. Rideo Xxx 10.63.4682\t500.00\t
2010-10-25\t2010-10-25\t\tSolo Dem Rideo 28-2-2 - Tem Ut\t110.00\t
2010-10-29\t2010-10-29\t\tW-Gangunt 75-2055-5 - De.Solone Exactianavit Ut\t\t1400.00
2010-10-29\t2010-10-29\t\tW-Gangunt 75-36815-7 - Veranest\t\t300.00
2010-10-29\t2010-10-29\t\tW-Gangunt 75-45521-6 - Substo By\t\t115.50
2010-10-29\t2010-10-29\t\tW-Gangunt 75-28743-1 - Mate - Trunt Ut\t\t122.85
2010-10-29\t2010-10-29\t\tW-Gangunt 75-87515-1 - Liquit-Pontare\t\t249.30
2010-10-29\t2010-10-29\t\tW-Gangunt 75-88560-5 - Multiunt De. Tabicustio Vi\t\t140.00
2010-10-29\t2010-10-29\t\tW-Gangunt 75-87515-1 - Liquit-Pontare\t\t119.65
2010-10-29\t2010-10-29\t\tW-Gangunt 75-73547-1 - Multiunt De. Tabicustio Vi\t\t110.00
2010-10-29\t2010-10-29\t\tW-Gangunt 75-88716-7 - Diusquam Osto\t\t131.55
2010-10-29\t2010-10-29\t\tW-Gangunt 75-13080-1 - Magnunans\t\t45.00
2010-10-31\t2010-10-31\t\tH�s Ige Hyacta-Xxx Anduxemum 4682 - Vivivio�fig De�agna Ine Pam 3555.77\t0.00\t

Import transactions example 3

Ubs (Switzerland) import transactions with multiple csv formats


// Banana Accounting Extension Javascript
// @id = ch.banana.switzerland.import.ubs.tutorial
// @api = 1.0
// @pubdate = 2024-06-24
// @publisher = Banana.ch SA
// @description = UBS - Import account statement .csv (Banana+ Advanced)
// @description.en = UBS - Import account statement .csv (Banana+ Advanced)
// @description.de = UBS - Bewegungen importieren .csv (Banana+ Advanced)
// @description.fr = UBS - Importer mouvements .csv (Banana+ Advanced)
// @description.it = UBS - Importa movimenti .csv (Banana+ Advanced)
// @doctype = *
// @docproperties =
// @task = import.transactions
// @outputformat = transactions.simple
// @inputdatasource = openfiledialog
// @inputencoding = latin1
// @inputfilefilter = Text files (*.txt *.csv);;All files (*.*)
// @inputfilefilter.de = Text (*.txt *.csv);;Alle Dateien (*.*)
// @inputfilefilter.fr = Texte (*.txt *.csv);;Tous (*.*)
// @inputfilefilter.it = Testo (*.txt *.csv);;Tutti i files (*.*)
// @timeout = -1



/*
* class ImportUtilities
* Contains methods that can be shared by extensions for importing bank data
*/
var ImportUtilities = class ImportUtilities {
    constructor(banDocument) {
        this.banDocument = banDocument;
        if (this.banDocument === undefined)
            this.banDocument = Banana.document;
    }

    //The purpose of this function is to convert all the data into a format supported by Banana
    convertToBananaFormat(intermediaryData) {
        var columnTitles = [];

        //Create titles only for fields not starting with "_"
        for (var name in intermediaryData[0]) {
            if (name.substring(0, 1) !== "_") {
                columnTitles.push(name);
            }
        }
        //Function call Banana.Converter.objectArrayToCsv() to create a CSV with new data just converted
        var convertedCsv = Banana.Converter.objectArrayToCsv(columnTitles, intermediaryData, "\t");

        return convertedCsv;
    }

    // Convert to an array of objects where each object property is the banana columnNameXml
    convertCsvToIntermediaryData(inData, convertionParam) {
        var form = [];
        var intermediaryData = [];
        //Add the header if present 
        if (convertionParam.header) {
            inData = convertionParam.header + inData;
        }

        //Read the CSV file and create an array with the data
        var csvFile = Banana.Converter.csvToArray(inData, convertionParam.separator, convertionParam.textDelim);

        //Variables used to save the columns titles and the rows values
        var columns = this.getHeaderData(csvFile, convertionParam.headerLineStart); //array
        var rows = this.getRowData(csvFile, convertionParam.dataLineStart); //array of array

        //Load the form with data taken from the array. Create objects
        this.loadForm(form, columns, rows);

        //Create the new CSV file with converted data
        var convertedRow;
        //For each row of the form, we call the rowConverter() function and we save the converted data
        for (var i = 0; i < form.length; i++) {
            convertedRow = convertionParam.rowConverter(form[i]);
            intermediaryData.push(convertedRow);
        }

        //Return the converted CSV data into the Banana document table
        return intermediaryData;
    }

    // Convert to an array of objects where each object property is the banana columnNameXml
    convertHtmlToIntermediaryData(inData, convertionParam) {
        var form = [];
        var intermediaryData = [];

        //Read the HTML file and create an array with the data
        var htmlFile = [];
        var htmlRows = inData.match(/<tr[^>]*>.*?<\/tr>/gi);
        for (var rowNr = 0; rowNr < htmlRows.length; rowNr++) {
            var htmlRow = [];
            var htmlFields = htmlRows[rowNr].match(/<t(h|d)[^>]*>.*?<\/t(h|d)>/gi);
            for (var fieldNr = 0; fieldNr < htmlFields.length; fieldNr++) {
                var htmlFieldRe = />(.*)</g.exec(htmlFields[fieldNr]);
                htmlRow.push(htmlFieldRe.length > 1 ? htmlFieldRe[1] : "");
            }
            htmlFile.push(htmlRow);
        }

        //Variables used to save the columns titles and the rows values
        var columns = this.getHeaderData(htmlFile, convertionParam.headerLineStart); //array
        var rows = this.getRowData(htmlFile, convertionParam.dataLineStart); //array of array

        //Convert header names
        for (var i = 0; i < columns.length; i++) {
            var convertedHeader = columns[i];
            convertedHeader = convertedHeader.toLowerCase();
            convertedHeader = convertedHeader.replace(" ", "_");
            var indexOfHeader = columns.indexOf(convertedHeader);
            if (indexOfHeader >= 0 && indexOfHeader < i) { // Header alreay exist
                //Avoid headers with same name adding an incremental index
                var newIndex = 2;
                while (columns.indexOf(convertedHeader + newIndex.toString()) !== -1 && newIndex < 99)
                    newIndex++;
                convertedHeader = convertedHeader + newIndex.toString()
            }
            columns[i] = convertedHeader;
        }

        // Banana.console.log(JSON.stringify(columns, null, "   "));

        //Load the form with data taken from the array. Create objects
        this.loadForm(form, columns, rows);

        //Create the new CSV file with converted data
        var convertedRow;
        //For each row of the form, we call the rowConverter() function and we save the converted data
        for (var i = 0; i < form.length; i++) {
            convertedRow = convertionParam.rowConverter(form[i]);
            intermediaryData.push(convertedRow);
        }

        //Return the converted CSV data into the Banana document table
        return intermediaryData;
    }

    // Convert to an array of objects where each object property is the banana columnNameXml
    convertToIntermediaryData(inData, convertionParam) {
        if (convertionParam.format === "html") {
            return this.convertHtmlToIntermediaryData(inData, convertionParam);
        } else {
            return this.convertCsvToIntermediaryData(inData, convertionParam);
        }
    }

    //The purpose of this function is to return all the titles of the columns
    getHeaderData(csvFile, startLineNumber) {
        if (!startLineNumber) {
            startLineNumber = 0;
        }
        var headerData = csvFile[startLineNumber];
        for (var i = 0; i < headerData.length; i++) {

            headerData[i] = headerData[i].trim();

            if (!headerData[i]) {
                headerData[i] = i;
            }

            //Avoid duplicate headers
            var headerPos = headerData.indexOf(headerData[i]);
            if (headerPos >= 0 && headerPos < i) { // Header already exist
                var postfixIndex = 2;
                while (headerData.indexOf(headerData[i] + postfixIndex.toString()) !== -1 && postfixIndex <= 99)
                    postfixIndex++; // Append an incremental index
                headerData[i] = headerData[i] + postfixIndex.toString()
            }

        }
        return headerData;
    }

    getLang() {

        var lang = 'en';
        if (this.banDocument)
            lang = this.banDocument.locale;
        else if (Banana.application.locale)
            lang = Banana.application.locale;
        if (lang.length > 2)
            lang = lang.substr(0, 2);
        return lang;
    }
    
    //The purpose of this function is to return all the data of the rows
    getRowData(csvFile, startLineNumber) {
        if (!startLineNumber) {
            startLineNumber = 1;
        }
        var rowData = [];
        for (var i = startLineNumber; i < csvFile.length; i++) {
            rowData.push(csvFile[i]);
        }
        return rowData;
    }

    //The purpose of this function is to load all the data (titles of the columns and rows) and create a list of objects.
    //Each object represents a row of the csv file
    loadForm(form, columns, rows) {
        for (var j = 0; j < rows.length; j++) {
            var obj = {};
            for (var i = 0; i < columns.length; i++) {
                obj[columns[i]] = rows[j][i];
            }
            form.push(obj);
        }
    }

    // The purpose of this function is to sort the data
    sortData(intermediaryData, convertionParam) {
        if (convertionParam.sortColums && convertionParam.sortColums.length) {
            intermediaryData.sort(
                function (row1, row2) {
                    for (var i = 0; i < convertionParam.sortColums.length; i++) {
                        var columnName = convertionParam.sortColums[i];
                        if (row1[columnName] > row2[columnName])
                            return 1;
                        else if (row1[columnName] < row2[columnName])
                            return -1;
                    }
                    return 0;
                });

            if (convertionParam.sortDescending)
                intermediaryData.reverse();
        }

        return intermediaryData;
    }

    verifyBananaPlusVersion() {
        if (!this.banDocument)
            return false;

        var BAN_VERSION_MIN = "10.0";

        // supported Version
        if (Banana.compareVersion && Banana.compareVersion(Banana.application.version, BAN_VERSION_MIN) >= 0) {
            return true;
        }

        // not supported version
        var lang = this.getLang();
        var msg = "This extension requires Banana Accounting+";
        this.banDocument.addMessage(msg, "ID_ERR_LICENSE_NOTVALID");
        return false;
    }

    //Check if the version of Banana Accounting is compatible with this class
    verifyBananaAdvancedVersion() {
        if (!this.banDocument)
            return false;

        if (!Banana.application.license || Banana.application.license.licenseType !== "advanced") {
            var lang = this.getLang();
            var msg = "This extension requires Banana Accounting+ Advanced";
            this.banDocument.addMessage(msg, "ID_ERR_LICENSE_NOTVALID");
            return false;
        }

        return true;
    }

    getErrorMessage(errorId, lang) {
        if (!lang)
            lang = 'en';
        switch (errorId) {
            case "ID_ERR_FORMAT_UNKNOWN":
                 if (lang == 'it')
                    return "Formato del file *.csv non riconosciuto";
                 else if (lang == 'fr')
                    return "Format de fichier *.csv non reconnu";
                 else if (lang == 'de')
                    return "Unerkanntes *.csv-Dateiformat";
                 else
                    return "Unrecognised *.csv file format";
        }
        return '';
     }

    getLang() {
        var lang = 'en';
        if (Banana.application.locale)
            lang = Banana.application.locale;
        if (lang.length > 2)
            lang = lang.substring(0, 2);
        return lang;
     }

    getUnknownFormatError(){
        let errId = "ID_ERR_FORMAT_UNKNOWN"; //error
        let lang = this.getLang();
        let msg = this.getErrorMessage(errId, lang);
        Banana.document.addMessage(msg, errId);
    }
}



/**
 * Parse the data and return the data to be imported as a tab separated file.
 */
function exec(inData, isTest) {
    if (!inData) return "";

    /** in the new version of format 3 (June 2024) we could have the situation where descriptions have
     * trhee pairs of quotes: """description text""". This situation cause problems
     * when the API Banana.Converter.csvToArray() read the content, the text is ignored. This happen
     * when the text description contains a semicolon ';'.
     * For the time being, we 'clean' the text of these quotes pairs by replacing them with normal quotes pairs.
     *  */
    inData = inData.replace(/"""/g, '"');

    var importUtilities = new ImportUtilities(Banana.document);

    if (isTest !== true && !importUtilities.verifyBananaAdvancedVersion())
        return "";

    convertionParam = defineConversionParam(inData);
    //Add the header if present
    if (convertionParam.header) {
        inData = convertionParam.header + inData;
    }
    let transactions = Banana.Converter.csvToArray(
        inData,
        convertionParam.separator,
        convertionParam.textDelim
    );

    // Format Credit Card
    var formatCc1Ubs = new UBSFormatCc1();
    if (formatCc1Ubs.match(transactions)) {
        transactions = formatCc1Ubs.convert(transactions);
        Banana.console.log(Banana.Converter.arrayToTsv(transactions));
        return Banana.Converter.arrayToTsv(transactions);
    }

    // Format 1
    var format1Ubs = new UBSFormat1();
    if (format1Ubs.match(transactions)) {
        transactions = format1Ubs.convert(transactions);
        Banana.console.log(Banana.Converter.arrayToTsv(transactions));
        return Banana.Converter.arrayToTsv(transactions);
    }

    // Format 2
    var format2Ubs = new UBSFormat2();
    if (format2Ubs.match(transactions)) {
        transactions = format2Ubs.convert(transactions, convertionParam);
        transactions = format2Ubs.postProcessIntermediaryData(transactions);
        Banana.console.log(Banana.Converter.arrayToTsv(transactions));
        return Banana.Converter.arrayToTsv(transactions);
    }

    // Format 3
    var format3Ubs = new UBSFormat3();
    if (format3Ubs.match(transactions)) {
        transactions = format3Ubs.convert(transactions, convertionParam);
        transactions = format3Ubs.postProcessIntermediaryData(transactions);
        Banana.console.log(Banana.Converter.arrayToTsv(transactions));
        return Banana.Converter.arrayToTsv(transactions);
    }

    importUtilities.getUnknownFormatError();

    return "";
}

/**
 * UBS Format 1
 *
 * Valuation date;Banking relationship;Portfolio;Product;IBAN;Ccy.;Date from;Date to;Description;Trade date;Booking date;Value date;Description 1;Description 2;Description 3;Transaction no.;Exchange rate in the original amount in settlement currency;Individual amount;Debit;Credit;Balance
 * 07.07.17;0240 00254061;;0240 00254061.01C;CH62 0024 4240 2340 6101 C;CHF;01.02.17;30.06.17;UBS Business Current Account;30.06.17;30.06.17;30.06.17;e-banking Order;BWS - CHENEVAL, BWS GERMANLINGUA,  DE DE DE 80331     MUECHEN,  INVOICE : M25252,  STD: ALICE CHENEVAL;;ZD81181TI0690091;;;3'416.82;;206'149.34
 * 07.07.17;0240 00254061;;0240 00254061.01C;CH62 0024 4240 2340 6101 C;CHF;01.02.17;30.06.17;UBS Business Current Account;30.06.17;30.06.17;30.06.17;Amount paid;;;ZD81181TI0690091;1.113334;3069;;;
 */
function UBSFormat1() {
    // Index of columns in csv file
    this.colCount = 21;

    this.colCurrency = 5;
    this.colDate = 10;
    this.colDateValuta = 11;
    this.colDescr1 = 12;
    this.colDescr2 = 13;
    this.colDescr3 = 14;
    this.colDescr4 = 15;
    this.colExchRate = -5;
    this.colDetails = -4;
    this.colDebit = -3;
    this.colCredit = -2;
    this.colBalance = -1;

    /** Return true if the transactions match this format */
    this.match = function (transactions) {
        if (transactions.length === 0) return false;

        for (i = 0; i < transactions.length; i++) {
            var transaction = transactions[i];

            var formatMatched = false;
            /* array should have all columns */
            if (transaction.length >= this.colCount) formatMatched = true;
            else formatMatched = false;

            if (formatMatched &&
                transaction[this.colDate] &&
                transaction[this.colDate].match(/^[0-9]+\.[0-9]+\.[0-9]+$/))
                formatMatched = true;
            else formatMatched = false;

            if (
                formatMatched &&
                transaction[this.colDateValuta] &&
                transaction[this.colDateValuta].match(/^[0-9]+\.[0-9]+\.[0-9]+$/)
            )
                formatMatched = true;
            else formatMatched = false;

            if (formatMatched) return true;
        }

        return false;
    };

    /** Convert the transaction to the format to be imported */
    this.convert = function (transactions) {
        this.colCount =
            transactions.length > 1 ? transactions[0].length : this.colCount;

        transactions = this.convertTransactions(transactions);

        if (transactions.length > 1) {
            //Sort by date
            if (transactions[0][0] > transactions[transactions.length - 1][0]) {
                //Sort transactions
                transactions = transactions.reverse();
            }
        }

        var header = [
            [
                "Date",
                "DateValue",
                "Doc",
                "ExternalReference",
                "Description",
                "Income",
                "Expenses",
                "IsDetail",
            ],
        ];
        return header.concat(transactions);
    };

    /** Convert the transaction to the format to be imported */
    this.convertTransactions = function (transactions) {
        var transactionsToImport = [];

        /** Complete, filter and map rows */
        var lastCompleteTransaction = null;

        for (var i = 0; i < transactions.length; i++) {
            var mappedTransaction = [];
            var transaction = transactions[i];

            if (transaction.length <= this.colDate ||
                !transaction[this.colDate].match(/[0-9\.]+/g))
                continue;

            if (transaction.length >= this.colCount + this.colBalance &&
                (transaction[this.colCount + this.colDebit] && (transaction[this.colCount + this.colDebit].length > 0) ||
                    (transaction[this.colCount + this.colCredit] && transaction[this.colCount + this.colCredit].length > 0) ||
                    (transaction[this.colCount + this.colBalance] && transaction[this.colCount + this.colBalance].length > 0))) {
                // Is a complete row
                if (lastCompleteTransaction) {
                    mappedTransaction = this.mapTransaction(lastCompleteTransaction);
                    mappedTransaction.push("");
                    transactionsToImport.push(mappedTransaction);
                    lastCompleteTransaction = null;
                }
                lastCompleteTransaction = transaction;
            } else if (transaction.length >= this.colCount + this.colDetails &&
                transaction[this.colCount + this.colDetails].length > 0) {
                // Is a detail row
                if (transaction[this.colCount + this.colExchRate].match(/[0-9]+\.[0-9]+/g)) {
                    // Is a multicurrency detail row
                    if (lastCompleteTransaction) {
                        mappedTransaction = this.mapTransaction(lastCompleteTransaction);
                        mappedTransaction.push("");
                        transactionsToImport.push(mappedTransaction);
                        lastCompleteTransaction = null;
                    }
                } else {
                    // Is a normal detail row
                    if (transaction[this.colDescr1] === "Cashpayment charges deducted by post" &&
                        lastCompleteTransaction && lastCompleteTransaction[this.colDescr1] === "Incomings UBS BESR Quick") {
                        // Post charges are contabilised at the end of the period, skip this row
                        if (lastCompleteTransaction !== null) {
                            mappedTransaction = this.mapTransaction(lastCompleteTransaction);
                            mappedTransaction.push("");
                            transactionsToImport.push(mappedTransaction);
                            lastCompleteTransaction = null;
                        }
                    } else {
                        if (lastCompleteTransaction !== null) {
                            mappedTransaction = this.mapTransaction(lastCompleteTransaction);
                            mappedTransaction.push("S");
                            transactionsToImport.push(mappedTransaction);
                            lastCompleteTransaction = null;
                        }
                        mappedTransaction = this.mapDetailTransaction(transaction);
                        mappedTransaction.push("D");
                        transactionsToImport.push(mappedTransaction);
                    }
                }
            }
        }

        if (lastCompleteTransaction !== null) {
            transactionsToImport.push(this.mapTransaction(lastCompleteTransaction));
        }

        return transactionsToImport;
    };

    this.mapTransaction = function (element) {
        var mappedLine = [];

        if (
            element[this.colDate] === null ||
            element[this.colDescr1] === null ||
            element[this.colDescr2] === null ||
            element[this.colDescr3] === null ||
            element[this.colDescr4] === null ||
            element[element.length + this.colCredit] === null ||
            element[element.length + this.colDebit] === null
        ) {
            mappedLine.push("");
            mappedLine.push("");
            mappedLine.push("Error importing data");
            mappedLine.push("");
            mappedLine.push("");
            return mappedLine;
        }

        mappedLine.push(
            Banana.Converter.toInternalDateFormat(element[this.colDate], "dd.mm.yyyy")
        );
        mappedLine.push(
            Banana.Converter.toInternalDateFormat(
                element[this.colDateValuta],
                "dd.mm.yyyy"
            )
        );
        mappedLine.push("");
        mappedLine.push(element[this.colDescr4]); //transaction number.
        mappedLine.push(this.mapDescription(element));
        mappedLine.push(
            Banana.Converter.toInternalNumberFormat(
                element[element.length + this.colCredit],
                "."
            )
        );
        mappedLine.push(
            Banana.Converter.toInternalNumberFormat(
                element[element.length + this.colDebit],
                "."
            )
        );

        return mappedLine;
    };

    this.mapDetailTransaction = function (element) {
        var mappedLine = [];

        if (
            element[this.colDate] === null ||
            element[this.colDescr1] === null ||
            element[this.colDescr2] === null ||
            element[this.colDescr3] === null ||
            element[this.colDescr4] === null ||
            element[element.length + this.colCredit] === null ||
            element[element.length + this.colDebit] === null
        ) {
            mappedLine.push("");
            mappedLine.push("");
            mappedLine.push("");
            mappedLine.push("Error importing data");
            mappedLine.push("");
            mappedLine.push("");
            return mappedLine;
        }

        mappedLine.push(
            Banana.Converter.toInternalDateFormat(element[this.colDate], "dd.mm.yyyy")
        );
        mappedLine.push(
            Banana.Converter.toInternalDateFormat(
                element[this.colDateValuta],
                "dd.mm.yyyy"
            )
        );
        mappedLine.push("");
        mappedLine.push(element[this.colDescr4]);
        mappedLine.push(this.mapDescription(element));
        mappedLine.push(
            Banana.Converter.toInternalNumberFormat(
                element[this.colCount + this.colDetails].replace(",", ""),
                "."
            )
        );
        mappedLine.push("");

        return mappedLine;
    };

    /**
     * Return the descrition for the requested line.
     */
    this.mapDescription = function (line) {
        var descr = line[this.colDescr1];
        if (line[this.colDescr2].length) descr += ", " + line[this.colDescr2];
        if (line[this.colDescr3].length) descr += ", " + line[this.colDescr3];

        descr = Banana.Converter.stringToCamelCase(descr);
        descr = descr.replace(/"/g, '\\"');
        return '"' + descr + '"';
    };
}

/**
 * UBS Format 2
 *
 * This new format (09.2022) use the import utilities class.
 * This format has no detail rows.
 *
 * Numero di conto:;0234 00103914.40;
 * IBAN:;CH29 0023 4234 1039 1440 G;
 * Dal:;2022-09-15;
 * Al:;2022-09-19;
 * Saldo iniziale:;1719.34;
 * Saldo finale:;631.07;
 * Valutazione in:;CHF;
 * Numero di transazioni in questo periodo:;13;
 *
 * Data dell'operazione;Ora dell'operazione;Data di registrazione;Data di valuta;Moneta;Importo della transazione;Addebito/Accredito;Saldo;N. di transazione;Descrizione1;Descrizione2;Descrizione3;Note a piè di pagina;
 * 2022-09-19;;2022-09-19;2022-09-19;CHF;-150.00;Addebito;600;123456TI1234567;"Versamento";"Ordine di pagamento via e-banking";;;
 * 2022-09-19;;2022-09-19;2022-09-19;CHF;-92.00;Addebito;692;2345678TI2345678;"Versamento";"Ordine di pagamento via e-banking";;;
 * 2022-09-19;;2022-09-19;2022-09-19;CHF;-10.00;Addebito;702;3456789TI3456789;"Versamento";"Ordine di pagamento via e-banking";;;
 * 2022-09-19;;2022-09-19;2022-09-19;CHF;-40.00;Addebito;742;4567890TI4567890;"Versamento";"Ordine di pagamento via e-banking";;;
 *
 */
var UBSFormat2 = class UBSFormat2 extends ImportUtilities {
    // Index of columns in csv file

    constructor(banDocument) {
        super(banDocument);
        this.colCount = 12;

        this.colDateOperation = 0;
        this.colDateValuta = 3;
        this.colAmount = 5;
        this.colOpType = 6;
        this.colTransNr = 8;
        this.colDescr1 = 9;
        this.colDescr2 = 10;
        this.colDescr3 = 11;

        //Index of columns in import format.
        this.newColDate = 0;
        this.newColDescription = 2;
        this.newColExpenses = 4;
    }

    /** Return true if the transactions match this format */
    match(transactions) {
        if (transactions.length === 0) return false;

        for (i = 0; i < transactions.length; i++) {
            var transaction = transactions[i];

            var formatMatched = false;
            /* array should have all columns */
            if (transaction.length >= this.colCount) formatMatched = true;
            else formatMatched = false;

            if (
                formatMatched &&
                transaction[this.colDateOperation] &&
                transaction[this.colDateOperation].match(/^[0-9]+\-[0-9]+\-[0-9]+$/)
            )
                formatMatched = true;
            else formatMatched = false;

            if (
                formatMatched &&
                transaction[this.colDateValuta] &&
                transaction[this.colDateValuta].match(/^[0-9]+\-[0-9]+\-[0-9]+$/)
            )
                if (
                    formatMatched &&
                    transaction[this.colOpType] &&
                    transaction[this.colOpType].match(/[a-zA-Z]/)
                )
                    formatMatched = true;
                else formatMatched = false;

            if (formatMatched) return true;
        }

        return false;
    }

    /** Convert the transaction to the format to be imported */
    convert(transactions, convertionParam) {
        var transactionsToImport = [];

        // Filter and map rows
        for (i = 0; i < transactions.length; i++) {
            var transaction = transactions[i];
            if (transaction.length < this.colCount + 1) continue;
            if (
                transaction[this.colDateOperation].match(/[0-9\.]+/g) &&
                transaction[this.colDateOperation].length === 10
            )
                transactionsToImport.push(this.mapTransaction(transaction));
        }

        /**
         * Sort rows by date
         * SPECIFY THE COLUMN TO USE FOR SORTING
         * If sortColums is empty the data are not sorted
         * */
        convertionParam.sortColums = [0, 2]; //0 = "Date" field position, 2 = "Description" field position.
        convertionParam.sortDescending = false;
        transactionsToImport = sort(transactionsToImport, convertionParam);

        // Add header and return
        var header = [
            [
                "Date",
                "DateValue",
                "Description",
                "ExternalReference",
                "Expenses",
                "Income",
            ],
        ];
        return header.concat(transactionsToImport);
    }

    mapTransaction(element) {
        var mappedLine = [];

        let dateText = "";
        let dateValueText = "";

        dateText = element[this.colDateOperation].substring(0, 10);
        dateValueText = element[this.colDateValuta].substring(0, 10);

        mappedLine.push(
            Banana.Converter.toInternalDateFormat(dateText, "yyyy-mm-dd")
        );
        mappedLine.push(
            Banana.Converter.toInternalDateFormat(dateValueText, "yyyy-mm-dd")
        );
        // wrap descr to bypass TipoFileImporta::IndovinaSeparatore problem
        mappedLine.push(
            element[this.colDescr1] +
            " " +
            element[this.colDescr2] +
            " " +
            element[this.colDescr3]
        );
        mappedLine.push(element[this.colTransNr]);

        if (element[this.colAmount].indexOf("-") == -1) {
            mappedLine.push("");
            mappedLine.push(
                Banana.Converter.toInternalNumberFormat(
                    element[this.colAmount],
                    this.decimalSeparator
                )
            );
        } else {
            mappedLine.push(
                Banana.Converter.toInternalNumberFormat(
                    element[this.colAmount],
                    this.decimalSeparator
                )
            );
            mappedLine.push("");
        }
        return mappedLine;
    }

    //The purpose of this function is to let the user specify how to convert the categories
    postProcessIntermediaryData(transactions) {
        let processesData = [];

        if (transactions.length < 1) return processesData;

        processesData = transactions;
        /** INSERT HERE THE LIST OF ACCOUNTS NAME AND THE CONVERSION NUMBER
         *   If the content of "Account" is the same of the text
         *   it will be replaced by the account number given */
        //Accounts conversion
        var accounts = {
            //...
        };

        /** INSERT HERE THE LIST OF CATEGORIES NAME AND THE CONVERSION NUMBER
         *   If the content of "ContraAccount" is the same of the text
         *   it will be replaced by the account number given */

        //Categories conversion
        var categories = {
            //...
        };

        //Apply the conversions
        for (var i = 1; i < processesData.length; i++) {
            var convertedData = processesData[i];
            //Invert values
            if (convertedData[this.newColExpenses]) {
                convertedData[this.newColExpenses] = Banana.SDecimal.invert(
                    convertedData[this.newColExpenses]
                );
            }
        }

        return processesData;
    }
};

/**
 * UBS Format 3
 *
 * This new format (08.11.2022) use the import utilities class.
 * This format has no detail rows.
 *
 * Numero di conto:;0234 00103914.40;
 * IBAN:;CH29 0023 4234 1039 1440 G;
 * Dal:;2022-09-15;
 * Al:;2022-09-19;
 * Saldo iniziale:;1719.34;
 * Saldo finale:;631.07;
 * Valutazione in:;CHF;
 * Numero di transazioni in questo periodo:;13;
 *
 * Data dell'operazione;Ora dell'operazione;Data di registrazione;Data di valuta;Moneta;Debit amount;Credit amount;Individual amount;Saldo;N. di transazione;Descrizione1;Descrizione2;Descrizione3;Note a piè di pagina;
 * 2022-11-07;;2022-11-07;2022-11-07;TEM;-99.80;;;2215.89;1770311TO2672955;"Meniunis (Suractae) VI,Maxi Habyssidedertis 6, 3542 Aerna";"Cepate in consolest tam g-possica";"Decilausa vi. NUS: 35 47463 30382 81016 85544 75378, Experi in consolest: 2213 / Osit: 37.57.84 - 62.57.84 / Mendimus audio at: 75.64.4848, Sologit vi. CAPH: JA18 6457 3522 2051 7571 2, Suisi: B-Possica TEM Suractae, Offereganga vi. 6068584LN1841647";;
 * 2022-11-07;;2022-11-07;2022-11-07;TEM;-52.10;;;2315.69;1670311TO2672937;"Meniunis (Suractae) VI,Maxi Habyssidedertis 6, 3542 Aerna";"Cepate in consolest tam g-possica";"Decilausa vi. NUS: 35 47463 30382 27465 62135 38521, Experi in consolest: 2213 / Osit: 37.80.84 - 16.80.84 / Mendimus audio at: 81.57.4848, Sologit vi. CAPH: JA18 6457 3522 2051 7571 2, Suisi: B-Possica TEM Suractae, Offereganga vi. 7510665VI0356053";;
 * 2022-11-07;17:10:46;;2022-11-07;TEM;-4.35;;;2367.79;9999311BN1710030;"CERA SEPTEMPTO,SEPTEMPTO";"18264075-0 07/24, Pagamento carta di debito";"Offereganga vi. 7740420TJ8353344";;
 * 2022-11-07;07:55:57;;2022-11-07;TEM;-2.70;;;2372.14;9999311BN0755924;"CERA SEPTEMPTO,SEPTEMPTO";"18264075-0 07/24, Pagamento carta di debito";"Offereganga vi. 3275420YO4320201";;
 * 2022-11-07;17:21:52;;2022-11-07;TEM;-2.30;;;2374.84;9999311BN1721198;"POR SALL. SED. MANTUMN PARATE,PARATE";"18264075-0 07/24, Pagamento carta di debito";"Offereganga vi. 7518748DV2785407";;
 * 2022-11-06;15:31:05;2022-11-07;2022-11-06;TEM;-6.80;;;2377.14;9930811BN5353554;"Parescro Tratiantro VI,6010 Recto";"18264075-0 07/24, Pagamento carta di debito";"Offereganga vi. 8740610YF3026752";;
 *
 */
var UBSFormat3 = class UBSFormat3 extends ImportUtilities {
    // Index of columns in *.csv file

    constructor(banDocument) {
        super(banDocument);
        //original file columns
        this.colCount = 13;

        this.colDateOperation = 0;
        this.colDateValuta = 3;
        this.colDebitAmt = 5;
        this.colCreditAmt = 6;
        this.colTransNr = 9;
        this.colDescr1 = 10;
        this.colDescr2 = 11;
        this.colDescr3 = 12;

        this.decimalSeparator = ".";

        //Index of columns in import format.
        this.newColDate = 0;
        this.newColDescription = 2;
        this.newColExpenses = 4;
    }

    /** Return true if the transactions match this format */
    match(transactions) {
        if (transactions.length === 0) return false;

        for (i = 0; i < transactions.length; i++) {
            var transaction = transactions[i];

            var formatMatched = false;
            /* array should have all columns */
            if (transaction.length >= this.colCount) formatMatched = true;
            else formatMatched = false;

            if (
                formatMatched &&
                transaction[this.colDateOperation] &&
                transaction[this.colDateOperation].match(/^[0-9]+\-[0-9]+\-[0-9]+$/)
            )
                formatMatched = true;
            else formatMatched = false;

            if (
                formatMatched &&
                transaction[this.colDateValuta] &&
                transaction[this.colDateValuta].match(/^[0-9]+\-[0-9]+\-[0-9]+$/)
            )
                formatMatched = true;
            else formatMatched = false;

            if (
                (formatMatched && transaction[this.colDebitAmt]) ||
                transaction[this.colCreditAmt]
            )
                formatMatched = true;
            else formatMatched = false;

            if (formatMatched) return true;
        }

        return false;
    }

    /** Convert the transaction to the format to be imported */
    convert(transactions, convertionParam) {
        var transactionsToImport = [];

        // Filter and map rows
        for (i = 0; i < transactions.length; i++) {
            var transaction = transactions[i];
            if (transaction.length < this.colCount + 1) continue;
            if (
                transaction[this.colDateOperation].match(/[0-9\.]+/g) &&
                transaction[this.colDateOperation].length === 10
            )
                transactionsToImport.push(this.mapTransaction(transaction));
        }

        /**
         * Sort rows by date
         * SPECIFY THE COLUMN TO USE FOR SORTING
         * If sortColums is empty the data are not sorted
         * */
        convertionParam.sortColums = [this.newColDate, this.newColDescription]; //0 = "Date" field position, 2 = "Description" field position.
        convertionParam.sortDescending = false;
        transactionsToImport = sort(transactionsToImport, convertionParam);

        // Add header and return
        var header = [
            [
                "Date",
                "DateValue",
                "Description",
                "ExternalReference",
                "Expenses",
                "Income",
            ],
        ];
        return header.concat(transactionsToImport);
    }

    mapTransaction(element) {
        var mappedLine = [];

        let dateText = "";
        let dateValueText = "";

        dateText = element[this.colDateOperation].substring(0, 10);
        dateValueText = element[this.colDateValuta].substring(0, 10);

        mappedLine.push(
            Banana.Converter.toInternalDateFormat(dateText, "yyyy-mm-dd")
        );
        mappedLine.push(
            Banana.Converter.toInternalDateFormat(dateValueText, "yyyy-mm-dd")
        );
        // wrap descr to bypass TipoFileImporta::IndovinaSeparatore problem
        mappedLine.push(element[this.colDescr1] + " " + element[this.colDescr2]);
        mappedLine.push(element[this.colTransNr]);

        mappedLine.push(
            Banana.Converter.toInternalNumberFormat(
                element[this.colDebitAmt],
                this.decimalSeparator
            )
        );
        mappedLine.push(
            Banana.Converter.toInternalNumberFormat(
                element[this.colCreditAmt],
                this.decimalSeparator
            )
        );

        return mappedLine;
    }

    //The purpose of this function is to let the user specify how to convert the categories
    postProcessIntermediaryData(transactions) {
        let processesData = [];

        if (transactions.length < 1) return processesData;

        processesData = transactions;
        /** INSERT HERE THE LIST OF ACCOUNTS NAME AND THE CONVERSION NUMBER
         *   If the content of "Account" is the same of the text
         *   it will be replaced by the account number given */
        //Accounts conversion
        var accounts = {
            //...
        };

        /** INSERT HERE THE LIST OF CATEGORIES NAME AND THE CONVERSION NUMBER
         *   If the content of "ContraAccount" is the same of the text
         *   it will be replaced by the account number given */

        //Categories conversion
        var categories = {
            //...
        };

        //Apply the conversions
        for (var i = 1; i < processesData.length; i++) {
            var convertedData = processesData[i];
            //Invert values
            if (convertedData[this.newColExpenses]) {
                convertedData[this.newColExpenses] = Banana.SDecimal.invert(
                    convertedData[this.newColExpenses]
                );
            }
        }

        return processesData;
    }
};

/**
 * UBS Credit Card Format 1
 *
 * sep=;
 * Numéro de compte;Numéro de carte;Titulaire de compte/carte;Date d'achat;Texte comptable;Secteur;Montant;Monnaie originale;Cours;Monnaie;Débit;Crédit;Ecriture
 * ZZZZ ZZZZ ZZZZ;;SIMON JEAN;19.10.2017;Report de solde;;;;;CHF;;0.00;
 * ZZZZ ZZZZ ZZZZ;XXXX XXXX XXXX XXXX;JEAN SIMON;16.11.2017;Cafe de Paris Geneve CHE;Restaurants, Bar;119.40;CHF;;;;;
 * ZZZZ ZZZZ ZZZZ;XXXX XXXX XXXX XXXX;JEAN SIMON;13.11.2017;www.banana.ch            LUGANO       CHE;Magasin d ordinateurs;189.00;CHF;;CHF;189.00;;15.11.2017
 */
function UBSFormatCc1() {
    // Index of columns in csv file
    this.colCount = 13;

    this.colCardNo = 2;
    this.colDateDoc = 3;
    this.colDescr = 4;
    this.colCurrency = -4;
    this.colDebit = -3;
    this.colCredit = -2;
    this.colDate = -1;

    /** Return true if the transactions match this format */
    this.match = function (transactions) {
        if (transactions.length === 0) return false;

        for (i = 0; i < transactions.length; i++) {
            var transaction = transactions[i];

            var formatMatched = false;
            /* array should have all columns */
            if (transaction.length >= this.colCount) formatMatched = true;
            else formatMatched = false;

            if (
                formatMatched &&
                transaction[this.colCount + this.colDate] &&
                transaction[this.colCount + this.colDate].match(
                    /^[0-9]+\.[0-9]+\.[0-9]+$/
                )
            )
                formatMatched = true;
            else formatMatched = false;

            if (formatMatched) return true;
        }

        return false;
    };

    /** Convert the transaction to the format to be imported */
    this.convert = function (transactions) {
        this.colCount =
            transactions.length > 1 ? transactions[0].length : this.colCount;

        transactions = this.convertTransactions(transactions);

        if (transactions.length > 1) {
            //Sort by date
            if (transactions[0][0] > transactions[transactions.length - 1][0]) {
                //Sort transactions
                transactions = transactions.reverse();
            }
        }

        var header = [
            ["Date", "DateDocument", "Doc", "Description", "Income", "Expenses"],
        ];
        return header.concat(transactions);
    };

    /** Convert the transaction to the format to be imported */
    this.convertTransactions = function (transactions) {
        var transactionsToImport = [];

        /** Complete, filter and map rows */
        var lastCompleteTransaction = null;

        for (
            var i = 0; i < transactions.length; i++ // First row contains the header
        ) {
            var transaction = transactions[i];
            if (
                transaction.length <= this.colCount + this.colDate ||
                !transaction[transaction.length + this.colDate].match(
                    /^[0-9]+\.[0-9]+\.[0-9]+$/
                )
            )
                continue;
            var mappedTransaction = this.mapTransaction(transaction);
            transactionsToImport.push(mappedTransaction);
        }

        return transactionsToImport;
    };

    this.mapTransaction = function (element) {
        var mappedLine = [];

        if (
            element[element.length + this.colDate] === null ||
            element[this.colDescr] === null ||
            element[element.length + this.colCredit] === null ||
            element[element.length + this.colDebit] === null
        ) {
            mappedLine.push("");
            mappedLine.push("");
            mappedLine.push("Error importing data");
            mappedLine.push("");
            mappedLine.push("");
            return mappedLine;
        }

        var descr;

        mappedLine.push(
            Banana.Converter.toInternalDateFormat(
                element[element.length + this.colDate],
                "dd.mm.yyyy"
            )
        );
        mappedLine.push("");
        mappedLine.push("");
        mappedLine.push(this.convertDescription(element[this.colDescr]));
        mappedLine.push(
            Banana.Converter.toInternalNumberFormat(
                element[element.length + this.colCredit],
                "."
            )
        );
        mappedLine.push(
            Banana.Converter.toInternalNumberFormat(
                element[element.length + this.colDebit],
                "."
            )
        );

        return mappedLine;
    };

    this.convertDescription = function (text) {
        var convertedDescr = text.replace(/  +/g, " ");
        convertedDescr = convertedDescr.replace(/"/g, '\\"');
        return '"' + convertedDescr + '"'; // Banana.Converter.stringToCamelCase(convertedDescr);
    };
}

function defineConversionParam(inData) {
    var csvData = Banana.Converter.csvToArray(inData);
    var header = String(csvData[0]);
    var convertionParam = {};
    /** SPECIFY THE SEPARATOR AND THE TEXT DELIMITER USED IN THE CSV FILE */
    convertionParam.format = "csv"; // available formats are "csv", "html"
    //get text delimiter
    convertionParam.textDelim = '"';
    // get separator
    convertionParam.separator = findSeparator(inData);

    return convertionParam;
}

/** Sort transactions by date and description */
function sort(transactions, convertionParam) {
    if (
        transactions.length <= 0 ||
        !convertionParam.sortColums ||
        convertionParam.sortColums.length <= 0
    )
        return transactions;

    transactions.sort(function (row1, row2) {
        for (var i = 0; i < convertionParam.sortColums.length; i++) {
            var columnIndex = convertionParam.sortColums[i];
            if (row1[columnIndex] > row2[columnIndex]) return 1;
            else if (row1[columnIndex] < row2[columnIndex]) return -1;
        }
        return 0;
    });

    if (convertionParam.sortDescending) transactions.reverse();

    return transactions;
}

function findSeparator(string) {

    var commaCount = 0;
    var semicolonCount = 0;
    var tabCount = 0;

    for (var i = 0; i < 1000 && i < string.length; i++) {
        var c = string[i];
        if (c === ',')
            commaCount++;
        else if (c === ';')
            semicolonCount++;
        else if (c === '\t')
            tabCount++;
    }

    if (tabCount > commaCount && tabCount > semicolonCount) {
        return '\t';
    }
    else if (semicolonCount > commaCount) {
        return ';';
    }

    return ',';
}

Input data - csv_ubs_example_format1_20230905_03.csv

Input text passed to the exec() function.


Valuation date,Banking relationship,Portfolio,Product,IBAN,Ccy.,Date from,Date to,Description,Trade date,Booking date,Value date,Description 1,Description 2,Description 3,Transaction no.,Exchange rate in the original amount in settlement currency,Individual amount,Debit,Credit,Balance
30.08.2023,1111 11111111,,1111 11111111.01X,VN16 6883 8648 7677 1877 A,LAM,31.07.2023,31.08.2023,QUI Patumere Cognumn Funtali,31.08.2023,31.08.2023,31.08.2023,Secitque dedra,4-12.2023 BVG monthly pa,"ingit, GROBAE COLO, AN DUONE 1017, 600457761231762504643213385, 1561140RG4305540",9930743LK8430013,,,557.50,,4'371.73
30.08.2023,1111 11111111,,1111 11111111.01X,VN16 6883 8648 7677 1877 A,LAM,31.07.2023,31.08.2023,QUI Patumere Cognumn Funtali,31.08.2023,31.08.2023,31.08.2023,R-Audideo diambile,,,9930743LK8430013,,,,,
30.08.2023,1111 11111111,,1111 11111111.01X,VN16 6883 8648 7677 1877 A,LAM,31.07.2023,31.08.2023,QUI Patumere Cognumn Funtali,31.08.2023,31.08.2023,31.08.2023,Viscus lis at minitudunto congerate parto,,,9930743LK8430013,,,,,
30.08.2023,1111 11111111,,1111 11111111.01X,VN16 6883 8648 7677 1877 A,LAM,31.07.2023,31.08.2023,QUI Patumere Cognumn Funtali,31.08.2023,31.08.2023,31.08.2023,Coniunt secitque ellura,,,9930743LP7352170,,,1'469.95,,4'929.23
30.08.2023,1111 11111111,,1111 11111111.01X,VN16 6883 8648 7677 1877 A,LAM,31.07.2023,31.08.2023,QUI Patumere Cognumn Funtali,31.08.2023,31.08.2023,31.08.2023,DORPUPRA VOLOCULUVIT,,,9930743LP7352170,,-1327.500000000,,,
30.08.2023,1111 11111111,,1111 11111111.01X,VN16 6883 8648 7677 1877 A,LAM,31.07.2023,31.08.2023,QUI Patumere Cognumn Funtali,31.08.2023,31.08.2023,31.08.2023,PLEGENT COR INE,AN SECIBURIA (ALIQUID) 4045,,9930743LP7352170,,-75.000000000,,,
30.08.2023,1111 11111111,,1111 11111111.01X,VN16 6883 8648 7677 1877 A,LAM,31.07.2023,31.08.2023,QUI Patumere Cognumn Funtali,31.08.2023,31.08.2023,31.08.2023,DITA PATUMERE DE,,,9930743LP7352170,,-67.450000000,,,
30.08.2023,1111 11111111,,1111 11111111.01X,VN16 6883 8648 7677 1877 A,LAM,31.07.2023,31.08.2023,QUI Patumere Cognumn Funtali,30.08.2023,30.08.2023,30.08.2023,d-audideo Volury,OPUGAVIT XXX,"8820 WAEDENSWIL, AAPARTMENT RENTAL FEE SEP 23, Credit / debit advice",9702242TO3295927,,,,3'700.00,6'399.18
30.08.2023,1111 11111111,,1111 11111111.01X,VN16 6883 8648 7677 1877 A,LAM,31.07.2023,31.08.2023,QUI Patumere Cognumn Funtali,25.08.2023,25.08.2023,25.08.2023,Creta d-audideo Dedra,TUM,"AN FICA 66 8840, 404800036845402515030151356",5730237TI4790761,,,165.00,,2'699.18
30.08.2023,1111 11111111,,1111 11111111.01X,VN16 6883 8648 7677 1877 A,LAM,31.07.2023,31.08.2023,QUI Patumere Cognumn Funtali,25.08.2023,25.08.2023,25.08.2023,R-Audideo diambile,,,5730237TI4790761,,,,,
30.08.2023,1111 11111111,,1111 11111111.01X,VN16 6883 8648 7677 1877 A,LAM,31.07.2023,31.08.2023,QUI Patumere Cognumn Funtali,25.08.2023,25.08.2023,25.08.2023,Viscus lis at minitudunto congerate parto,,,5730237TI4790761,,,,,
30.08.2023,1111 11111111,,1111 11111111.01X,VN16 6883 8648 7677 1877 A,LAM,31.07.2023,31.08.2023,QUI Patumere Cognumn Funtali,24.08.2023,24.08.2023,24.08.2023,Creta d-audideo Dedra,RES,"AN VIT 3534, 803023367811405547285483243",2530236TI4589949,,,1'674.15,,2'864.18
30.08.2023,1111 11111111,,1111 11111111.01X,VN16 6883 8648 7677 1877 A,LAM,31.07.2023,31.08.2023,QUI Patumere Cognumn Funtali,24.08.2023,24.08.2023,24.08.2023,R-Audideo diambile,,,2530236TI4589949,,,,,
30.08.2023,1111 11111111,,1111 11111111.01X,VN16 6883 8648 7677 1877 A,LAM,31.07.2023,31.08.2023,QUI Patumere Cognumn Funtali,24.08.2023,24.08.2023,24.08.2023,Viscus lis at minitudunto congerate parto,,,2530236TI4589949,,,,,
30.08.2023,1111 11111111,,1111 11111111.01X,VN16 6883 8648 7677 1877 A,LAM,31.07.2023,31.08.2023,QUI Patumere Cognumn Funtali,21.08.2023,21.08.2023,21.08.2023,Creta d-audideo Dedra,VOLONCENDE FRUM ET,"AN REGRUMEA 5037, 246531233028703100220735546",5530233TI3956784,,,705.30,,4'538.33
30.08.2023,1111 11111111,,1111 11111111.01X,VN16 6883 8648 7677 1877 A,LAM,31.07.2023,31.08.2023,QUI Patumere Cognumn Funtali,21.08.2023,21.08.2023,21.08.2023,R-Audideo diambile,,,5530233TI3956784,,,,,
30.08.2023,1111 11111111,,1111 11111111.01X,VN16 6883 8648 7677 1877 A,LAM,31.07.2023,31.08.2023,QUI Patumere Cognumn Funtali,21.08.2023,21.08.2023,21.08.2023,Viscus lis at minitudunto congerate parto,,,5530233TI3956784,,,,,
30.08.2023,1111 11111111,,1111 11111111.01X,VN16 6883 8648 7677 1877 A,LAM,31.07.2023,31.08.2023,QUI Patumere Cognumn Funtali,02.08.2023,02.08.2023,02.08.2023,d-audideo Dedra,"VOLUNDO MANGUN, FLUVIDENT 8,","6318 WALCHWIL, CH, COMPENSATION FROM KSG",9930212TI9569993,,,5'000.00,,5'243.63
30.08.2023,1111 11111111,,1111 11111111.01X,VN16 6883 8648 7677 1877 A,LAM,31.07.2023,31.08.2023,QUI Patumere Cognumn Funtali,02.08.2023,02.08.2023,02.08.2023,R-Audideo diambile,,,9930212TI9569993,,,,,
30.08.2023,1111 11111111,,1111 11111111.01X,VN16 6883 8648 7677 1877 A,LAM,31.07.2023,31.08.2023,QUI Patumere Cognumn Funtali,02.08.2023,02.08.2023,02.08.2023,Viscus lis at minitudunto congerate parto,,,9930212TI9569993,,,,,
30.08.2023,1111 11111111,,1111 11111111.01X,VN16 6883 8648 7677 1877 A,LAM,31.07.2023,31.08.2023,QUI Patumere Cognumn Funtali,31.07.2023,31.07.2023,31.07.2023,Coniunt secitque ellura,,,9930712LP4015960,,,1'469.95,,10'243.63
30.08.2023,1111 11111111,,1111 11111111.01X,VN16 6883 8648 7677 1877 A,LAM,31.07.2023,31.08.2023,QUI Patumere Cognumn Funtali,31.07.2023,31.07.2023,31.07.2023,DORPUPRA VOLOCULUVIT,,,9930712LP4015960,,-1327.500000000,,,
30.08.2023,1111 11111111,,1111 11111111.01X,VN16 6883 8648 7677 1877 A,LAM,31.07.2023,31.08.2023,QUI Patumere Cognumn Funtali,31.07.2023,31.07.2023,31.07.2023,PLEGENT COR INE,AN SECIBURIA (ALIQUID) 4045,,9930712LP4015960,,-75.000000000,,,
30.08.2023,1111 11111111,,1111 11111111.01X,VN16 6883 8648 7677 1877 A,LAM,31.07.2023,31.08.2023,QUI Patumere Cognumn Funtali,31.07.2023,31.07.2023,31.07.2023,DITA PATUMERE DE,,,9930712LP4015960,,-67.450000000,,,
30.08.2023,1111 11111111,,1111 11111111.01X,VN16 6883 8648 7677 1877 A,LAM,31.07.2023,31.08.2023,QUI Patumere Cognumn Funtali,31.07.2023,31.07.2023,31.07.2023,Secitque dedra,4-12.2023 BVG MONTHLY PA,"INGIT, GROBAE COLO, AN DUONE 1017, 600457761231762504643213385",9930712LK6453313,,,557.50,,11'713.58
30.08.2023,1111 11111111,,1111 11111111.01X,VN16 6883 8648 7677 1877 A,LAM,31.07.2023,31.08.2023,QUI Patumere Cognumn Funtali,31.07.2023,31.07.2023,31.07.2023,R-Audideo diambile,,,9930712LK6453313,,,,,
30.08.2023,1111 11111111,,1111 11111111.01X,VN16 6883 8648 7677 1877 A,LAM,31.07.2023,31.08.2023,QUI Patumere Cognumn Funtali,31.07.2023,31.07.2023,31.07.2023,Viscus lis at minitudunto congerate parto,,,9930712LK6453313,,,,,
30.08.2023,1111 11111111,,1111 11111111.01X,VN16 6883 8648 7677 1877 A,LAM,31.07.2023,31.08.2023,QUI Patumere Cognumn Funtali,31.07.2023,31.07.2023,31.07.2023,Cebitom at perange alurem,,,BJ31418NJ8204828,,,0.00,,12'271.08
30.08.2023,1111 11111111,,1111 11111111.01X,VN16 6883 8648 7677 1877 A,LAM,31.07.2023,31.08.2023,QUI Patumere Cognumn Funtali,31.07.2023,31.07.2023,31.07.2023,01.07.2023 - 31.07.2023,,,BJ31418NJ8204828,,,,,
30.08.2023,1111 11111111,,1111 11111111.01X,VN16 6883 8648 7677 1877 A,LAM,31.07.2023,31.08.2023,QUI Patumere Cognumn Funtali,31.07.2023,31.07.2023,31.07.2023,Centuide PER-mantumn,,,BJ31418NJ8204828,,-0.200000000,,,
30.08.2023,1111 11111111,,1111 11111111.01X,VN16 6883 8648 7677 1877 A,LAM,31.07.2023,31.08.2023,QUI Patumere Cognumn Funtali,31.07.2023,31.07.2023,31.07.2023,Viscus lis at minitudunto congerate parto,,,BJ31418NJ8204828,,0.200000000,,,
30.08.2023,1111 11111111,,1111 11111111.01X,VN16 6883 8648 7677 1877 A,LAM,31.07.2023,31.08.2023,QUI Patumere Cognumn Funtali,31.07.2023,31.07.2023,31.07.2023,R-Audideo diambile,,,BJ31418NJ8204828,,-0.300000000,,,
30.08.2023,1111 11111111,,1111 11111111.01X,VN16 6883 8648 7677 1877 A,LAM,31.07.2023,31.08.2023,QUI Patumere Cognumn Funtali,31.07.2023,31.07.2023,31.07.2023,Viscus lis at minitudunto congerate parto,,,BJ31418NJ8204828,,0.300000000,,,
30.08.2023,1111 11111111,,1111 11111111.01X,VN16 6883 8648 7677 1877 A,LAM,31.07.2023,31.08.2023,QUI Patumere Cognumn Funtali,31.07.2023,31.07.2023,31.07.2023,Secitque dedra diambile,,,BJ31418NJ8204828,,-0.900000000,,,
30.08.2023,1111 11111111,,1111 11111111.01X,VN16 6883 8648 7677 1877 A,LAM,31.07.2023,31.08.2023,QUI Patumere Cognumn Funtali,31.07.2023,31.07.2023,31.07.2023,Viscus lis at minitudunto congerate parto,,,BJ31418NJ8204828,,0.900000000,,,
30.08.2023,1111 11111111,,1111 11111111.01X,VN16 6883 8648 7677 1877 A,LAM,31.07.2023,31.08.2023,QUI Patumere Cognumn Funtali,31.07.2023,31.07.2023,31.07.2023,d-audideo Volury,OPUGAVIT XXX,"8820 WAEDENSWIL, AAPARTMENT RENTAL FEE AUG 23, Credit / debit advice",9902211TO9260785,,,,3'700.00,12'271.08
,,,,,,,,,,,,,,,,,,,,
Flassus cebitom,Quonent cebitom,,,,,,,,,,,,,,,,,,,
1111.123333,34545.000000007,,,,,,,,,,,,,,,,,,,

Output data - csv_ubs_example_format1_20230905_03.tsv

Output text returned by the exec() function.


Date\tDateValue\tDoc\tExternalReference\tDescription\tIncome\tExpenses\tIsDetail
2023-07-31\t2023-07-31\t\t9902211TO9260785\t\"D-Audideo Volury, Opugavit Xxx, 8820 Waedenswil, Aapartment Rental Fee Aug 23, Credit / Debit Advice\"\t3700.00\t
2023-07-31\t2023-07-31\t\tBJ31418NJ8204828\t\"Viscus Lis At Minitudunto Congerate Parto\"\t0.900000000\t\tD
2023-07-31\t2023-07-31\t\tBJ31418NJ8204828\t\"Secitque Dedra Diambile\"\t-0.900000000\t\tD
2023-07-31\t2023-07-31\t\tBJ31418NJ8204828\t\"Viscus Lis At Minitudunto Congerate Parto\"\t0.300000000\t\tD
2023-07-31\t2023-07-31\t\tBJ31418NJ8204828\t\"R-Audideo Diambile\"\t-0.300000000\t\tD
2023-07-31\t2023-07-31\t\tBJ31418NJ8204828\t\"Viscus Lis At Minitudunto Congerate Parto\"\t0.200000000\t\tD
2023-07-31\t2023-07-31\t\tBJ31418NJ8204828\t\"Centuide Per-Mantumn\"\t-0.200000000\t\tD
2023-07-31\t2023-07-31\t\tBJ31418NJ8204828\t\"Cebitom At Perange Alurem\"\t\t0.00\tS
2023-07-31\t2023-07-31\t\t9930712LK6453313\t\"Secitque Dedra, 4-12.2023 Bvg Monthly Pa, Ingit, Grobae Colo, An Duone 1017, 600457761231762504643213385\"\t\t557.50\t
2023-07-31\t2023-07-31\t\t9930712LP4015960\t\"Dita Patumere De\"\t-67.450000000\t\tD
2023-07-31\t2023-07-31\t\t9930712LP4015960\t\"Plegent Cor Ine, An Seciburia (Aliquid) 4045\"\t-75.000000000\t\tD
2023-07-31\t2023-07-31\t\t9930712LP4015960\t\"Dorpupra Voloculuvit\"\t-1327.500000000\t\tD
2023-07-31\t2023-07-31\t\t9930712LP4015960\t\"Coniunt Secitque Ellura\"\t\t1469.95\tS
2023-08-02\t2023-08-02\t\t9930212TI9569993\t\"D-Audideo Dedra, Volundo Mangun, Fluvident 8,, 6318 Walchwil, Ch, Compensation From Ksg\"\t\t5000.00\t
2023-08-21\t2023-08-21\t\t5530233TI3956784\t\"Creta D-Audideo Dedra, Voloncende Frum Et, An Regrumea 5037, 246531233028703100220735546\"\t\t705.30\t
2023-08-24\t2023-08-24\t\t2530236TI4589949\t\"Creta D-Audideo Dedra, Res, An Vit 3534, 803023367811405547285483243\"\t\t1674.15\t
2023-08-25\t2023-08-25\t\t5730237TI4790761\t\"Creta D-Audideo Dedra, Tum, An Fica 66 8840, 404800036845402515030151356\"\t\t165.00\t
2023-08-30\t2023-08-30\t\t9702242TO3295927\t\"D-Audideo Volury, Opugavit Xxx, 8820 Waedenswil, Aapartment Rental Fee Sep 23, Credit / Debit Advice\"\t3700.00\t\t
2023-08-31\t2023-08-31\t\t9930743LP7352170\t\"Dita Patumere De\"\t-67.450000000\t\tD
2023-08-31\t2023-08-31\t\t9930743LP7352170\t\"Plegent Cor Ine, An Seciburia (Aliquid) 4045\"\t-75.000000000\t\tD
2023-08-31\t2023-08-31\t\t9930743LP7352170\t\"Dorpupra Voloculuvit\"\t-1327.500000000\t\tD
2023-08-31\t2023-08-31\t\t9930743LP7352170\t\"Coniunt Secitque Ellura\"\t\t1469.95\tS
2023-08-31\t2023-08-31\t\t9930743LK8430013\t\"Secitque Dedra, 4-12.2023 Bvg Monthly Pa, Ingit, Grobae Colo, An Duone 1017, 600457761231762504643213385, 1561140RG4305540\"\t\t557.50\t

Input data - csv_ubs_example_format2_de_20220928.csv

Input text passed to the exec() function.


Kontonummer:;12345678910;
IBAN:;CH12345678910;
Von:;2022-09-26;
Bis:;2022-09-28;
Anfangssaldo:;1000.00;
Schlusssaldo:;1441.21;
Bewertet in:;CHF;
Anzahl Transaktionen in diesem Zeitraum:;10;

Abschlussdatum;Abschlusszeit;Buchungsdatum;Valutadatum;Währung;Transaktionsbetrag;Belastung/Gutschrift;Saldo;Transaktions-Nr.;Beschreibung1;Beschreibung2;Beschreibung3;Fussnoten;
2022-09-28;16:08:11;;2022-09-28;CHF;100.00;Gutschrift;1100.00;12345678910;"Gutschrift";"Einzahlung Bancomat";;;
2022-09-28;12:48:24;;2022-09-28;CHF;-20.00;Belastung;1000.00;12345678910;"FILIALE";"123456-0 07/24, Bezug Bancomat";;;
2022-09-27;13:32:48;2022-09-29;2022-09-27;CHF;-5.35;Belastung;1005.35;12345678910;"Lidl Viganello";"123456-0 07/24, Zahlung Debitkarte";;;
2022-09-27;16:39:42;2022-09-28;2022-09-27;CHF;-2.30;Belastung;1007.85;12345678910;"TPL Piaz. Mol. Billett L";"123456-0 07/24, Zahlung Debitkarte";;;
2022-09-26;;2022-09-26;2022-09-26;CHF;-135.00;Belastung;1142.85;12345678910;"Zahlung ";"Zahlung  e-banking-Vergutungsauftrag";;;
2022-09-26;;2022-09-26;2022-09-26;CHF;-38.01;Belastung;1180.86;12345678910;"Zahlung ";"e-banking-Vergutungsauftrag";;;
2022-09-26;;2022-09-26;2022-09-26;CHF;-35.00;Belastung;1215.86;12345678910;"Zahlung";"Zahlung";;;
2022-09-26;;2022-09-26;2022-09-26;CHF;-31.75;Belastung;1306.21;12345678910;"Zahlung ";"e-banking-Vergutungsauftrag";;;
2022-09-26;;2022-09-26;2022-09-26;CHF;-58.60;Belastung;1364.81;12345678910;"Sunrise UPC Sagl";"e-banking-Vergutungsauftrag";;;
2022-09-26;;2022-09-26;2022-09-26;CHF;-76.40;Belastung;1441.21;12345678910;"Salt Mobile SA";"e-banking-Vergutungsauftrag";;;

Output data - csv_ubs_example_format2_de_20220928.tsv

Output text returned by the exec() function.


Date\tDateValue\tDescription\tExternalReference\tExpenses\tIncome
2022-09-26\t2022-09-26\tSalt Mobile SA e-banking-Vergutungsauftrag \t12345678910\t76.40\t
2022-09-26\t2022-09-26\tSunrise UPC Sagl e-banking-Vergutungsauftrag \t12345678910\t58.60\t
2022-09-26\t2022-09-26\tZahlung  Zahlung  e-banking-Vergutungsauftrag \t12345678910\t135.00\t
2022-09-26\t2022-09-26\tZahlung  e-banking-Vergutungsauftrag \t12345678910\t31.75\t
2022-09-26\t2022-09-26\tZahlung  e-banking-Vergutungsauftrag \t12345678910\t38.01\t
2022-09-26\t2022-09-26\tZahlung Zahlung \t12345678910\t35.00\t
2022-09-27\t2022-09-27\tLidl Viganello 123456-0 07/24, Zahlung Debitkarte \t12345678910\t5.35\t
2022-09-27\t2022-09-27\tTPL Piaz. Mol. Billett L 123456-0 07/24, Zahlung Debitkarte \t12345678910\t2.30\t
2022-09-28\t2022-09-28\tFILIALE 123456-0 07/24, Bezug Bancomat \t12345678910\t20.00\t
2022-09-28\t2022-09-28\tGutschrift Einzahlung Bancomat \t12345678910\t\t100.00

Input data - csv_ubs_example_format3_it_triplequotes_20240621.csv

Input text passed to the exec() function.


Numero di conto:;0000 00000800.00;;;;;;;;;;;;;
IBAN:;CH00 0000 0000 0000 0000 N;;;;;;;;;;;;;
Dal:;;;;;;;;;;;;;;
Al:;;;;;;;;;;;;;;
Saldo iniziale:;1117.58;;;;;;;;;;;;;
Saldo finale:;22.97;;;;;;;;;;;;;
Valutazione in:;CHF;;;;;;;;;;;;;
Numero di transazioni in questo periodo:;6;;;;;;;;;;;;;
Data dell'operazione;Ora dell'operazione;Data di registrazione;Data di valuta;Moneta;Addebito;Accredito;Importo singolo;Saldo;N. di transazione;Descrizione1;Descrizione2;Descrizione3;Note a piè di pagina;
2024-06-19;12:48:15;;2024-06-19;CHF;-20.00;;;22.97;11111111;"""FILIALE;VIGANELLO""";"""11111111-0 07/24; Prelevamento Bancomat""";No di transazioni: 111111;;
2024-06-19;;2024-06-19;2024-06-19;CHF;-450.00;;;42.97;11111112;"""Descrizione1""";"""Descrizione2; Descrizione2""";"""Descrizione3""";;
2024-06-17;17:19:57;2024-06-19;2024-06-17;CHF;-15.40;;;492.97;11111113;"""Descrizione1;6962 Viganello""";"""11111111-0 07/24; Pagamento carta di debito""";No di transazioni: 11111112334;;
2024-06-17;17:31:13;2024-06-18;2024-06-17;CHF;-5.20;;;508.37;11111114;"""Descrizione1;6900 Lugano""";"""11111111-0 07/24; Pagamento carta di debito""";No di transazioni: 11234534553;;
2024-06-18;;2024-06-18;2024-06-18;CHF;;200.00;;513.57;11111115;"""Descrizione1;6900 Lugano""";Accredito;No di transazioni: 12678345643;;
2024-05-31;17:23:44;2024-06-03;2024-05-31;CHF;-20.00;;;1117.58;11111116;BR Lugano;"""111111111-0 07/24; Prelevamento Bancomat""";"""Spese: Prelevamento di contanti al Bancomat di banche terze/Postomat in Svizzera; No di transazioni: 1268923683""";;

Output data - csv_ubs_example_format3_it_triplequotes_20240621.tsv

Output text returned by the exec() function.


Date\tDateValue\tDescription\tExternalReference\tExpenses\tIncome
2024-05-31\t2024-05-31\tBR Lugano 111111111-0 07/24; Prelevamento Bancomat\t11111116\t20.00\t
2024-06-17\t2024-06-17\tDescrizione1;6900 Lugano 11111111-0 07/24; Pagamento carta di debito\t11111114\t5.20\t
2024-06-17\t2024-06-17\tDescrizione1;6962 Viganello 11111111-0 07/24; Pagamento carta di debito\t11111113\t15.40\t
2024-06-18\t2024-06-18\tDescrizione1;6900 Lugano Accredito\t11111115\t\t200.00
2024-06-19\t2024-06-19\tDescrizione1 Descrizione2; Descrizione2\t11111112\t450.00\t
2024-06-19\t2024-06-19\tFILIALE;VIGANELLO 11111111-0 07/24; Prelevamento Bancomat\t11111111\t20.00\t

Export

Export Tutorial3 javascript codes, csv and tsv to HTML


// Banana Accounting Extension Javascript
// @id = ch.banana.apps.exporthtmljavascriptcodestutorial3.js
// @api = 1.0
// @publisher = Banana.ch SA
// @description = Tutorial: Export javascript codes, csv and tsv of the Tutorial 3 examples in HTML
// @task = export.file
// @doctype = *.*
// @docproperties = 
// @timeout = -1
// @exportfiletype = html
// @visibility = never

//Main function
function exec() {

    //Check if we are on an opened document
    if (!Banana.document) {
        return;
    }

    //Take the table Documents
    var documents = Banana.document.table('Documents');

    //We check if the table Documents exists, then we can take all the codes 
    //If the table Documents doesn't exists, then we stop the script execution
    if (!documents) {
        return;
    } else {

        //Variable to save the html text
        var htmlText = '';

        htmlText += '<p>On this page we show some examples of transactions import extensions.</p>\n';
        htmlText += '<p>Each example consists of:</p>\n';
        htmlText += '<ul>\n';
        htmlText += '<li>The complete <strong>JavaScript code of the import extension</strong>.</li>\n';
        htmlText += '<li>The <strong>.CSV</strong> files in different formats with the transactions data to be imported.<br>Some examples contains <strong>anonymized data</strong>, the description text has no meanings. </li>\n';
        htmlText += '<li>The output in <strong>.TSV</strong> format (Tabulator Separated Values) of the import extension, which contains the transformed csv data in the “transactions.simple” format used to import the transactions in Banana Accounting.</li>\n';
        htmlText += '</ul>\n';

        //Get all the tutorial javascript codes
        htmlText = getJavascriptCode(htmlText, documents);
    }

    return htmlText;
}

//Function that gets the javascript code of each tutorial example and save it into the htmlText.
function getJavascriptCode(htmlText, documents) {

    //Read row by row the table Documents
    var len = documents.rowCount;
    for (var i = 0; i < len; i++) {

        //Create a variable for the row
        var tRow = documents.row(i);

        //We get some values
        var fName = Banana.document.info("Base", "FileName").replace(/^.*[\\\/]/, ''); //the file name (without path)
        var section = tRow.value("Section"); // the section column used to define the title type
        var id = tRow.value("RowId"); //the id of the example
        var description = tRow.value("Description"); //the description of the example
        var attachments = tRow.value("Attachments"); //the javascript code of the example
        attachments = attachments.replace(/</g,'<'); // replace the < symbol
        attachments = attachments.replace(/>/g,'>'); // replace the > symbol

        //For tilte h2 we consider only rows with section h2
        if (section && section === "h2") {
            htmlText += '<h2>' + description + '</h2>\n';
        }
        
        //For js codes we consider only rows with section h3, id, description and attachment
        else if (section && section === "h3" && id && description && attachments) {
            htmlText += '<h3>' + description + '</h3>\n';
            htmlText += '<pre><code class="language-javascript">\n';
            htmlText += '// Banana Accounting Extension Javascript' + '\n';
            htmlText += attachments;
            htmlText += '\n';
            htmlText += '</code></pre>\n';
            htmlText += getCsvAndTsv(id, documents); // adds csv and tsv
        }
    }

    //Returns the html text which contains titles, javascript codes and csv examples
    return htmlText;
}

//Function that gets the Csv with the bank movements and the Tsv (tabulator separated values).
//The Tsv contains the Csv data after being processed by the import extension.
//The Tsv has a specific format used by Banana Accounting to import transactions.
function getCsvAndTsv(jsCodeId, documents) {
    
    //Variable to save the csv and tsv (tabulator separated values)
    var text = '';

    //Remove '.js' from the jsCodeId string, we use the id number to get the tsv
    jsCodeId = jsCodeId.replace(/\.js$/, "");

    //Read row by row the table Documents
    var len = documents.rowCount;
    for (var i = 0; i < len; i++) {

        //Create a variable for the row
        var tRow = documents.row(i);

        //We get some values
        var section = tRow.value("Section"); // the section column used to define the title type
        var id = tRow.value("RowId"); //the id of the example
        var description = tRow.value("Description"); //the description of the example
        var attachments = tRow.value("Attachments"); //the csv/tsv text of the example

        //For the csv/tsv text we consider only rows with section h4, id that starts with the same id of the import extension, description and attachment
        if (section === "h4" && id.startsWith(jsCodeId) && description && attachments) {

            var strtype = '';
            var strIntro = '';
            if (id.indexOf(".csv") > -1) {
               strtype = "Input data - ";
               strIntro ="Input text passed to the exec() function.";
            } else if (id.indexOf(".tsv") > -1) {
               strtype = "Output data - ";
               strIntro ="Output text returned by the exec() function.";
            }

            text += '<h4>' + strtype + description + '</h4>\n';
            text += '<p>'+ strIntro + '</p>\n';
            text += '<pre><code class="language-plaintext">\n';
            text += attachments;
            text += '\n';
            text += '</code></pre>\n';
        }
    }
    return text;
}

Working with XML

Introduction

This page contains some code examples of how to read, write and more in general how to work with XML files in Banana.

All the code reported above as example can be found in the following file: embedded_javascript_tutorial1.ac2

XML object

XML files are used to save a large amount of data in an organized way.

Using the Banana APIs is possible to retrieve data from XML files and also create new XML files.

Read XML

In order to read an XML string we have to follow the following steps:

  1. Create the XML parse object
  2. Take the root of the XML
  3. Find the data we want to retrieve from the file using firstChildElement([text]).
  4. Iterate all over the file as long as we always have something from step 3.

Read XML Code Sample: Simple XML



var xml =   '<Library updated="2018-09-03">' +                            //  To simplify,
              '<Book>' +                                                  //  the variable
                '<Title>Paths of colours</Title>' +                       //  xml contains
                '<Author>Rosa Indaco</Author>' +                          //  the xml string
              '</Book>' +
              '<Book>' +
                '<Title>Accounting exercises</Title>' +
                '<Author>Su Zhang</Author>' +
              '</Book>' +
            '</Library>';

var bookList = "";                                                        //  Create string for the output
var xmlFile = Banana.Xml.parse(xml);                                      //  Create XML Parse object
var xmlRoot = xmlFile.firstChildElement('Library');                       //  Find the first tag "Library" in the XML
var updateDate = xmlRoot.attribute('updated');                            //  Take attribute assigned to the tag
bookList += "Books in the library on " + updateDate + "\n\n";             //  Append to the output string

var bookNode = xmlRoot.firstChildElement('Book');                         //  Take the first Book
while (bookNode) {                                                        //  As long as there are books, repeat
    var title = bookNode.firstChildElement('Title').text;                 //  Find the first tag "Title" in the XML
    var authorNode = bookNode.firstChildElement('Author');                //  Find the first tag "Author" in the XML
    var author = authorNode ? authorNode.text : 'unknow';                 //  Check whether there is a tag "Author"
    bookList += title + " - " + author + "\n";                            //  Append to the output string
    bookNode = bookNode.nextSiblingElement('Book');                       //  Go to the next book
}

Banana.Ui.showText("List of books present in the xml file", bookList);    //  Output the results

 

Output:

image1

 

Write XML

In order to write and create an XML file we have to follow the following steps:

  1. Create the XML document object.
  2. Create a root tag for the document using newDocument([text]).
  3. Add new tag using addElement([text]).
  4. Add text for the tag with addTextNode([text]).

Write XML Code Sample: Simple XML

    var xmlDocument = Banana.Xml.newDocument("Library");                    //  Create XML file
    xmlDocument.addComment("This is the generated xml file");               //  Add comment to the file
    var rootNode = xmlDocument.addElement("Library");                       //  Set root tag "Library"
    rootNode.setAttribute("updated", "2018-09-03");                         //  Set attribute to the tag "Library"

    var bookNode = rootNode.addElement("Book");                             //  Create tag "Book" child of "Library"
    bookNode.addElement("Title").addTextNode("Paths of colours");           //  Create tag "Title" child of "Book"
    bookNode.addElement("Author").addTextNode("Rosa Indaco");               //  Create tag "Author" child of "Book"

    var bookNode = rootNode.addElement("Book");                             //  Create tag "Book" child of "Library"
    bookNode.addElement("Title").addTextNode("Accounting exercises");       //  Create tag "Title" child of "Book"
    bookNode.addElement("Author").addTextNode("Su Zhang");                  //  Create tag "Author" child of "Book"

    var xmlString = Banana.Xml.save(xmlDocument);                           //  Create output

    Banana.Ui.showText("Xml file", xmlString);                              //  Generate output

Output:
image2_result

Validate

Is possible to validate if a file is in fact an XML which contains tag with the validate() function.


    var xmlFile = Banana.Xml.parse(xml);                                    //  Create the XML file

    var valid = Banana.Xml.validate(xmlFile, 'documents:xsd');              //  Validate the XML
    if (valid) {
        Banana.Ui.showInformation('Validation result', 'The xml is valid');
    } else  {
        Banana.Ui.showInformation('Validation result', 'The xml is not valid: ' + Banana.Xml.errorString);
    }

Output:
image3_result

General examples with XML

In this section we are going to provide some examples of how to use API functions for XML files, here below there is the XML file used for the first example:


<solarSystem updated="2020-03-27">
    <Planet>
        <name>Sun</name>
        <sizeOrder>1</sizeOrder>
    </Planet>
    <Planet>
        <name>Earth</name>
        <sizeOrder>6</sizeOrder>
        <satellite>Moon</satellite>
    </Planet>
    <Planet>
        <name>Jupiter</name>
        <sizeOrder>2</sizeOrder>
        <satellite>Callisto</satellite>
    </Planet>
    <Planet>
        <name>Saturn</name>
        <sizeOrder>3</sizeOrder>
        <satellite>Cronus</satellite>
    </Planet>
    <Planet>
        <name>Mars</name>
        <sizeOrder>8</sizeOrder>
        <satellite>Phobos</satellite>
    </Planet>
    <Planet>
        <name>Neptune</name>
        <sizeOrder>5</sizeOrder>
        <satellite>Triton</satellite>
    </Planet>
    <Planet>
        <name>Mercury</name>
        <sizeOrder>9</sizeOrder>
    </Planet>
    <Planet>
        <name>Venus</name>
        <sizeOrder>7</sizeOrder>
    </Planet>
    <Planet>
        <name>Uranus</name>
        <sizeOrder>4</sizeOrder>
        <satellite>Ariel</satellite>
    </Planet>
</solarSystem>

Check if a tag has a specific Child tag

the following is a code example of how to return and print only planets with a satellite, the other will be excluded, therefore will be used the function hasChildElements([text]).

var outputString = "";
    var xmlFile = Banana.Xml.parse(xml);
    var xmlRoot = xmlFile.firstChildElement('solarSystem');
    var updateDate = xmlRoot.attribute('updated');
    outputString += "Planets in the solar System with satellites updated on " + updateDate + "\n\n";

    var planet = xmlRoot.firstChildElement('Planet');                                   //  Take the first tag "Planet"
    while (planet) {                                                                    //  As long as we have planets
        if (planet.hasChildElements('satellite')) {                                     //  Check if the planet has tag "satellite"
            var planetName = planet.firstChildElement('name').text;                     //  Save the planetName
            var planetSatellite = planet.firstChildElement('satellite').text;           //  Save the satelliteName
            outputString += "name: "+planetName+", satellite: "+planetSatellite+"\n";
        }
        planet = planet.nextSiblingElement('Planet');                                   //  Move to the next planet
    }
    Banana.Ui.showText("Planets in the XML with satellites", outputString);             //  Show the output

Output:

image4_result

Export Transactions in XML format

Here there is an example of how to take various fields from the table Transactions in one of our .ac2 files and then print them in an xml files.

Naturally it is possible to change various parameter as for example the table we want to take the data. But also the number and type of fields we want to print in our file, in order to do so we have to follow the following steps:

  1. Decide which table we want to retrieve informations.
  2. Remember to handle the error for which the file doesn't have the desired table.
  3. Loop through the entire table taking only the desired fields.
  4. Decide the indentation for the file by appending to the strings special characters like '\t' (tab) or '\n' (new line).

// @id = ch.banana.apps.export
// @api = 1.0
// @pubdate = 2016-04-08
// @doctype = *.*
// @description = Export into an xml file (.xml)
// @task = export.file
// @exportfiletype = xml
// @timeout = -1

function exec() {
    var xmlDocument = Banana.Xml.newDocument("Transactions");
    xmlDocument.addComment("This is the generated xml file");
    var rootNode = xmlDocument.addElement("transactions");
    var tableTransactions = Banana.document.table('Transactions');
    if (!tableTransactions) {
        return;
    }
    for (i=0;i<tabletransactions.rowcount;i++) {
        if (tableTransactions.row(i).value('Amount')) {
            var transactionNode = rootNode.addElement("transaction");
            transactionNode.addElement("date").addTextNode(tableTransactions.row(i).value('Date'));
            transactionNode.addElement("description").addTextNode(tableTransactions.row(i).value('Description'));
            transactionNode.addElement("amount").addTextNode(tableTransactions.row(i).value('Amount'));
        }
    }
    var xmlString = Banana.Xml.save(xmlDocument);
    Banana.Ui.showText("Xml file", xmlString);
    return xmlString;

The code above and his explanation are available also here.

Import transactions from XML

The following is an example of how to add new transactions to a .ac2 file in Banana, below is possible to see the XML file used as example:


<transactions>
    <transaction>
        <date>2020-01-01</date>
        <description>Red car sale</description>
        <income>10000.50</income>
        <expenses></expenses>
    </transaction>
    <transaction>
        <date>2020-03-15</date>
        <description>ATM withdrawal</description>
        <income></income>
        <expenses>600.00</expenses>
    </transaction>
    <transaction>
        <date>2020-02-14</date>
        <description>Bought silver ring</description>
        <income></income>
        <expenses>2000.00</expenses>
    </transaction>
    <transaction>
        <date>2020-04-01</date>
        <description>Yellow rubber duck sale</description>
        <income>4200.00</income>
        <expenses></expenses>
    </transaction>
</transactions>

We can eventually modify the XML file above by adding new fields for our transactions and obviously add new transactions to the file, but in order to transfer data from the file into Banana we have to follow these instructions:

  1. We are going to transfer the data from XML to CSV and then from CSV to TSV which are Tab separated files.
  2. Create the CSV string we are going to append data from our file.
  3. As we did before in the Read XML example we have to iterate through the XML file we take in input and then append the values of the tags into our CSV string, this time we have to remember to format the string by adding special character like '\n' (new line), ',' (comma), ''' (apostrophe) or '"' (quotation mark).
  4. once this process is complete and we have our CSV String ready we have to convert it into an Array using csvToArray(string, [, separator, textdelim]) function and once again convert the resulting array into a TSV using arrayToTSV(table, [, defaultChar]) and return the TSV.

// @api = 1.0
// @pubdate = 2020-04-02
// @id = ch.banana.uni.app.tutorialretrieverowstablevalues
// @description = Import transactions
// @task = import.transactions
// @outputformat = transactions.simple
// @doctype = *
// @inputdatasource = openfiledialog
// @inputencoding = latin1
// @inputfilefilter = Text files (*.xml);;All files (*.*)
// @publisher = Banana.ch SA
// @timeout = -1

function exec(inputFile) {
    var CSV_String = "Date" + '","' + "Description" + '","' + "Income" + '","' + "Expenses" + '\n';
    var xmlFile = Banana.Xml.parse(inputFile);
    var xmlRoot = xmlFile.firstChildElement('transactions');
    var transactionNode = xmlRoot.firstChildElement('transaction');
    while (transactionNode) {
        var date = transactionNode.firstChildElement('date').text;
        var description = transactionNode.firstChildElement('description').text;
        var income = transactionNode.firstChildElement('income').text;
        var expenses = transactionNode.firstChildElement('expenses').text;
        CSV_String += ('"' + date + '","' + description + '","' + income + '","' + expenses + '"' + '\n');
        transactionNode = transactionNode.nextSiblingElement('transaction');
    }
    var csvFile = Banana.Converter.csvToArray(CSV_String, ',', '"');
    var tsvFile = Banana.Converter.arrayToTsv(csvFile);
    return tsvFile;
}