diff --git a/ajax/output/getEntityData.php b/ajax/output/getEntityData.php
new file mode 100644
index 0000000000000000000000000000000000000000..1a3f8845019cdd16b7dfa41b4da5597a608cf159
--- /dev/null
+++ b/ajax/output/getEntityData.php
@@ -0,0 +1,28 @@
+<?php
+
+use QUI\ERP\Output\Output as ERPOutput;
+
+/**
+ * Returns the invoice templates
+ *
+ * @return array|false - Entity data or false
+ */
+QUI::$Ajax->registerFunction(
+    'package_quiqqer_erp_ajax_output_getEntityData',
+    function ($entityId, $package) {
+        \QUI\System\Log::writeRecursive($entityId);
+        \QUI\System\Log::writeRecursive($package);
+
+        $OutputProvider = ERPOutput::getOutputProviderByPackage($package);
+
+        if (empty($OutputProvider)) {
+            return false;
+        }
+
+        return [
+            'email' => $OutputProvider::getEmailAddress($entityId)
+        ];
+    },
+    ['entityId', 'package'],
+    'Permission::checkAdminUser'
+);
diff --git a/ajax/output/getTemplates.php b/ajax/output/getTemplates.php
new file mode 100644
index 0000000000000000000000000000000000000000..b2edd95459a8ce6b77bb0cb811e37d0242402ac4
--- /dev/null
+++ b/ajax/output/getTemplates.php
@@ -0,0 +1,23 @@
+<?php
+
+/**
+ * This file contains package_quiqqer_invoice_ajax_invoices_settings_templates
+ */
+
+use QUI\ERP\Accounting\Invoice\Settings;
+
+/**
+ * Returns the invoice templates
+ *
+ * @return array
+ */
+QUI::$Ajax->registerFunction(
+    'package_quiqqer_erp_ajax_output_getTemplates',
+    function ($entityType) {
+        // @todo build Output class and fetch templates
+
+        return Settings::getInstance()->getAvailableTemplates();
+    },
+    ['entityType'],
+    'Permission::checkAdminUser'
+);
diff --git a/bin/backend/controls/OutputDialog.css b/bin/backend/controls/OutputDialog.css
new file mode 100644
index 0000000000000000000000000000000000000000..fcd786df399dca5826eeb8305fcd32d06d368aca
--- /dev/null
+++ b/bin/backend/controls/OutputDialog.css
@@ -0,0 +1,34 @@
+.quiqqer-invoice-printDialog {
+    padding: 10px !important;
+}
+
+.quiqqer-invoice-printDialog form {
+    float: left;
+    width: 400px;
+}
+
+.quiqqer-invoice-printDialog form table {
+    border: none;
+    padding: 0;
+}
+
+.quiqqer-invoice-printDialog select {
+    width: 100%;
+}
+
+.quiqqer-invoice-printDialog-preview {
+    background-color: #efefef;
+    float: left;
+    height: 100%;
+    overflow: auto;
+    padding: 20px;
+    width: calc(100% - 400px);
+}
+
+.quiqqer-invoice-printDialog-invoice-preview {
+    background: #fff;
+    box-shadow: 0 0 0 0.75pt #d1d1d1, 0 0 3pt 0.75pt #ccc;
+    border: none;
+    display: block;
+    margin: 0 auto;
+}
\ No newline at end of file
diff --git a/bin/backend/controls/OutputDialog.html b/bin/backend/controls/OutputDialog.html
new file mode 100644
index 0000000000000000000000000000000000000000..ff6027b1c6dd4891c6719714e4483ac066f5917a
--- /dev/null
+++ b/bin/backend/controls/OutputDialog.html
@@ -0,0 +1,52 @@
+<form>
+    <table class="data-table data-table-flexbox">
+        <tbody>
+        <tr>
+            <td>
+                <label class="field-container">
+                    <span class="field-container-item" title="{{labelEntityId}}">
+                        {{labelEntityId}}
+                    </span>
+                    <span class="field-container-field">
+                        {{entityId}}
+                    </span>
+                </label>
+            </td>
+        </tr>
+        <tr>
+            <td>
+                <label class="field-container">
+                    <span class="field-container-item" title="{{textTemplate}}">
+                        {{textTemplate}}
+                    </span>
+                    <select name="template" class="field-container-field"></select>
+                </label>
+            </td>
+        </tr>
+        <tr>
+            <td>
+                <label class="field-container">
+                    <span class="field-container-item" title="{{textOutput}}">
+                        {{textOutput}}
+                    </span>
+                    <span class="field-container-field field-container-field-no-padding field-output"></span>
+                </label>
+            </td>
+        </tr>
+        <tr style="display: none">
+            <td>
+                <label class="field-container">
+                    <span class="field-container-item" title="{{textOutput}}">
+                        {{textEmail}}
+                    </span>
+                    <input type="text"
+                           name="recipient"
+                           placeholder="email@domain.com"
+                           class="field-container-field"/>
+                </label>
+            </td>
+        </tr>
+        </tbody>
+    </table>
+</form>
+<div class="quiqqer-invoice-printDialog-preview"></div>
\ No newline at end of file
diff --git a/bin/backend/controls/OutputDialog.js b/bin/backend/controls/OutputDialog.js
new file mode 100644
index 0000000000000000000000000000000000000000..e5b1e33f958ef6a7345a5adf44ff3ac2fc9804f9
--- /dev/null
+++ b/bin/backend/controls/OutputDialog.js
@@ -0,0 +1,498 @@
+/**
+ * @module package/quiqqer/erp/bin/backend/controls/OutputDialog
+ * @author www.pcsg.de (Patrick Müller)
+ */
+define('package/quiqqer/erp/bin/backend/controls/OutputDialog', [
+
+    'qui/QUI',
+    'qui/controls/windows/Confirm',
+    'qui/controls/buttons/Select',
+    'qui/controls/Sandbox',
+
+    'Ajax',
+    'Locale',
+    'Mustache',
+    'Users',
+    'package/quiqqer/erp/bin/Invoices',
+
+    'text!package/quiqqer/erp/bin/backend/controls/OutputDialog.html',
+    'css!package/quiqqer/erp/bin/backend/controls/OutputDialog.css'
+
+], function (QUI, QUIConfirm, QUISelect, QUISandbox, QUIAjax, QUILocale, Mustache, Users, Invoices, template) {
+    "use strict";
+
+    var lg = 'quiqqer/erp';
+
+    return new Class({
+
+        Extends: QUIConfirm,
+        type   : 'package/quiqqer/erp/bin/backend/controls/OutputDialog',
+
+        Binds: [
+            '$onOpen',
+            '$onOutputChange',
+            '$onPrintFinish'
+        ],
+
+        options: {
+            entityId : false,    // Clean entity ID WITHOUT prefix and suffix
+            'package': false,    // Output provider package
+
+            //downloadUrl: false
+
+            maxHeight: 800,
+            maxWidth : 1400
+        },
+
+        initialize: function (options) {
+            this.parent(options);
+
+            this.setAttributes({
+                icon         : 'fa fa-print',
+                title        : QUILocale.get(lg, 'dialog.print.title'),
+                autoclose    : false,
+                cancel_button: {
+                    textimage: 'fa fa-close',
+                    text     : QUILocale.get('quiqqer/system', 'close')
+                }
+            });
+
+            this.$Output      = null;
+            this.$Preview     = null;
+            this.$invoiceData = null;
+            this.$cutomerMail = null;
+
+            this.addEvents({
+                onOpen     : this.$onOpen,
+                onSubmit   : this.$onSubmit,
+                onOpenBegin: function () {
+                    var winSize = QUI.getWindowSize();
+                    var height  = 800;
+                    var width   = 1400;
+
+                    if (winSize.y * 0.9 < height) {
+                        height = winSize.y * 0.9;
+                    }
+
+                    if (winSize.x * 0.9 < width) {
+                        width = winSize.x * 0.9;
+                    }
+
+                    this.setAttribute('maxHeight', height);
+                    this.setAttribute('maxWidth', width);
+                }.bind(this)
+            });
+        },
+
+        /**
+         * event: on open
+         */
+        $onOpen: function () {
+            var self    = this,
+                Content = this.getContent();
+
+            this.Loader.show();
+            this.getContent().set('html', '');
+
+            var onError = function (error) {
+                console.error(error);
+
+                self.close().then(function () {
+                    self.destroy();
+                });
+
+                QUI.getMessageHandler().then(function (MH) {
+                    if (typeof error === 'object' && typeof error.getMessage !== 'undefined') {
+                        MH.addError(error.getMessage());
+                        return;
+                    }
+
+                    MH.addError(error);
+                });
+            };
+
+            if (!this.getAttribute('invoiceId')) {
+                onError('No invoice ID was given.');
+                return;
+            }
+
+            Promise.all([
+                Invoices.get(this.getAttribute('invoiceId')),
+                Invoices.getTemplates(),
+                Invoices.getInvoicePreview(this.getAttribute('invoiceId'))
+            ]).then(function (result) {
+                var templates = result[1],
+                    html      = result[2],
+                    prfx      = '';
+
+                self.$invoiceData = result[0];
+
+                if (typeof self.$invoiceData.id_prefix !== 'undefined') {
+                    prfx = self.$invoiceData.id_prefix;
+                }
+
+                Content.set({
+                    html: Mustache.render(template, {
+                        invoiceNumber    : prfx + self.$invoiceData.id,
+                        textInvoiceNumber: QUILocale.get(lg, 'dialog.print.data.number'),
+                        textOutput       : QUILocale.get(lg, 'dialog.print.data.output'),
+                        textTemplate     : QUILocale.get(lg, 'dialog.print.data.template'),
+                        textEmail        : QUILocale.get('quiqqer/quiqqer', 'recipient')
+                    })
+                });
+
+                Content.addClass('quiqqer-invoice-printDialog');
+
+                self.$Preview = Content.getElement('.quiqqer-invoice-printDialog-preview');
+
+                new QUISandbox({
+                    content: html,
+                    styles : {
+                        height : 1240,
+                        padding: 20,
+                        width  : 874
+                    },
+                    events : {
+                        onLoad: function (Box) {
+                            Box.getElm().addClass('quiqqer-invoice-printDialog-invoice-preview');
+                        }
+                    }
+                }).inject(self.$Preview);
+
+                var Form     = Content.getElement('form'),
+                    selected = '';
+
+                for (var i = 0, len = templates.length; i < len; i++) {
+                    new Element('option', {
+                        value: templates[i].name,
+                        html : templates[i].title
+                    }).inject(Form.elements.template);
+
+                    if (templates[i].default) {
+                        selected = templates[i].name;
+                    }
+                }
+
+                Form.elements.template.value = selected;
+
+                self.$Output = new QUISelect({
+                    localeStorage: 'quiqqer-invoice-print-dialog',
+                    name         : 'output',
+                    styles       : {
+                        border: 'none',
+                        width : '100%'
+                    },
+                    events       : {
+                        onChange: self.$onOutputChange
+                    }
+                });
+
+
+                self.$Output.appendChild(
+                    QUILocale.get(lg, 'dialog.print.data.output.print'),
+                    'print',
+                    'fa fa-print'
+                );
+
+                self.$Output.appendChild(
+                    QUILocale.get(lg, 'dialog.print.data.output.pdf'),
+                    'pdf',
+                    'fa fa-file-pdf-o'
+                );
+
+                self.$Output.appendChild(
+                    QUILocale.get(lg, 'dialog.print.data.output.email'),
+                    'email',
+                    'fa fa-envelope-o'
+                );
+
+                self.$Output.inject(Content.getElement('.field-output'));
+
+                if (typeof self.$invoiceData.customer_data !== 'undefined') {
+                    var data = JSON.decode(self.$invoiceData.customer_data);
+
+                    if (data && typeof data.email !== 'undefined') {
+                        self.$cutomerMail = data.email;
+                    }
+
+                    if (data && (self.$cutomerMail === null || self.$cutomerMail === '')) {
+                        return new Promise(function (resolve) {
+                            // get customer id
+                            Users.get(data.id).load().then(function (User) {
+                                self.$cutomerMail = User.getAttribute('email');
+                                resolve();
+                            }).catch(function (Exception) {
+                                //onError(Exception);
+                                resolve();
+                            });
+                        });
+                    }
+                }
+            }).then(function () {
+                self.$onOutputChange();
+                self.Loader.hide();
+            }).catch(function (e) {
+                onError(e);
+            });
+        },
+
+        /**
+         * event: on submit
+         */
+        $onSubmit: function () {
+            var self = this,
+                Run  = Promise.resolve();
+
+            this.Loader.show();
+
+            switch (this.$Output.getValue()) {
+                case 'print':
+                    Run = this.print();
+                    break;
+
+                case 'pdf':
+                    Run = this.saveAsPdf();
+                    break;
+
+                case 'email':
+                    Run = this.sendAsEmail();
+                    break;
+            }
+
+            Run.then(function () {
+                self.Loader.hide();
+            });
+        },
+
+        /**
+         * Print the invoice
+         *
+         * @return {Promise}
+         */
+        print: function () {
+            var self      = this,
+                invoiceId = this.getAttribute('invoiceId');
+
+            return new Promise(function (resolve) {
+                var id      = 'print-invoice-' + invoiceId,
+                    Content = self.getContent(),
+                    Form    = Content.getElement('form');
+
+                self.Loader.show();
+
+                new Element('iframe', {
+                    src   : URL_OPT_DIR + 'quiqqer/erp/bin/backend/printInvoice.php?' + Object.toQueryString({
+                        invoiceId: invoiceId,
+                        oid      : self.getId(),
+                        template : Form.elements.template.value
+                    }),
+                    id    : id,
+                    styles: {
+                        position: 'absolute',
+                        top     : -200,
+                        left    : -200,
+                        width   : 50,
+                        height  : 50
+                    }
+                }).inject(document.body);
+
+                self.addEvent('onPrintFinish', function (self, pId) {
+                    if (pId === invoiceId) {
+                        resolve();
+                    }
+                });
+            });
+        },
+
+        /**
+         * event: on print finish
+         *
+         * @param {String|Number} id
+         */
+        $onPrintFinish: function (id) {
+            this.fireEvent('printFinish', [this, id]);
+
+            (function () {
+                document.getElements('#print-invoice-' + id).destroy();
+                this.close();
+            }).delay(1000, this);
+        },
+
+        /**
+         * Export the invoice as PDF
+         *
+         * @return {Promise}
+         */
+        saveAsPdf: function () {
+            var self      = this,
+                invoiceId = this.getAttribute('invoiceId');
+
+            return new Promise(function (resolve) {
+                var id      = 'download-invoice-' + invoiceId,
+                    Content = self.getContent(),
+                    Form    = Content.getElement('form');
+
+                new Element('iframe', {
+                    src   : URL_OPT_DIR + 'quiqqer/erp/bin/backend/downloadInvoice.php?' + Object.toQueryString({
+                        invoiceId: invoiceId,
+                        oid      : self.getId(),
+                        template : Form.elements.template.value
+                    }),
+                    id    : id,
+                    styles: {
+                        position: 'absolute',
+                        top     : -200,
+                        left    : -200,
+                        width   : 50,
+                        height  : 50
+                    }
+                }).inject(document.body);
+
+                (function () {
+                    resolve();
+                }).delay(2000, this);
+
+                (function () {
+                    document.getElements('#' + id).destroy();
+                }).delay(20000, this);
+            });
+        },
+
+        /**
+         * Send the invoice to an E-Mail
+         *
+         * @return {Promise}
+         */
+        sendAsEmail: function () {
+            var self      = this,
+                invoiceId = this.getAttribute('invoiceId'),
+                recipient = this.getElm().getElement('[name="recipient"]').value;
+
+            return new Promise(function (resolve) {
+                var id      = 'mail-invoice-' + invoiceId,
+                    Content = self.getContent(),
+                    Form    = Content.getElement('form');
+
+                new Element('iframe', {
+                    src   : URL_OPT_DIR + 'quiqqer/erp/bin/backend/sendInvoice.php?' + Object.toQueryString({
+                        invoiceId: invoiceId,
+                        oid      : self.getId(),
+                        recipient: recipient,
+                        template : Form.elements.template.value
+                    }),
+                    id    : id,
+                    styles: {
+                        position: 'absolute',
+                        top     : -200,
+                        left    : -200,
+                        width   : 50,
+                        height  : 50
+                    }
+                }).inject(document.body);
+
+                (function () {
+                    document.getElements('#' + id).destroy();
+                    resolve();
+                }).delay(20000, this);
+            });
+        },
+
+        /**
+         * event : on output change
+         *
+         * @return {Promise}
+         */
+        $onOutputChange: function () {
+            var Recipient = this.getElm().getElement('[name="recipient"]');
+
+            Recipient.getParent('tr').setStyle('display', 'none');
+
+            switch (this.$Output.getValue()) {
+                case 'print':
+                    this.$onChangeToPrint();
+                    break;
+
+                case 'pdf':
+                    this.$onChangeToPDF();
+                    break;
+
+                case 'email':
+                    this.$onChangeToEmail();
+                    break;
+            }
+        },
+
+        /**
+         * event: on output change -> to print
+         */
+        $onChangeToPrint: function () {
+            var Submit = this.getButton('submit');
+
+            Submit.setAttribute('text', QUILocale.get(lg, 'dialog.print.data.output.print.btn'));
+            Submit.setAttribute('textimage', 'fa fa-print');
+        },
+
+        /**
+         * event: on output change -> to pdf
+         */
+        $onChangeToPDF: function () {
+            var Submit = this.getButton('submit');
+
+            Submit.setAttribute('text', QUILocale.get(lg, 'dialog.print.data.output.pdf.btn'));
+            Submit.setAttribute('textimage', 'fa fa-file-pdf-o');
+        },
+
+        /**
+         * event: on output change -> to Email
+         */
+        $onChangeToEmail: function () {
+            var Submit    = this.getButton('submit');
+            var Recipient = this.getElm().getElement('[name="recipient"]');
+
+            Recipient.getParent('tr').setStyle('display', null);
+
+            Submit.setAttribute('text', QUILocale.get(lg, 'dialog.print.data.output.email.btn'));
+            Submit.setAttribute('textimage', 'fa fa-envelope-o');
+
+            if (this.$cutomerMail && Recipient.value === '') {
+                Recipient.value = this.$cutomerMail;
+            }
+
+            Recipient.focus();
+        },
+
+        /**
+         * Get data of the entity that is outputted
+         *
+         * @return {Promise}
+         */
+        $getEntityData: function() {
+            var self = this;
+
+            return new Promise(function (resolve, reject) {
+                QUIAjax.get('', resolve, {
+                    'package' : 'quiqqer/erp',
+                    entityType: self.getAttribute('entityType'),
+                    onError   : reject
+                })
+            });
+        },
+
+        /**
+         * Fetch available templates based on entity type
+         *
+         * @return {Promise}
+         */
+        $getTemplates: function () {
+            var self = this;
+
+            return new Promise(function (resolve, reject) {
+                QUIAjax.get('', resolve, {
+                    'package' : 'quiqqer/erp',
+                    entityType: self.getAttribute('entityType'),
+                    onError   : reject
+                })
+            });
+        }
+    });
+});
\ No newline at end of file
diff --git a/src/QUI/ERP/Output/Output.php b/src/QUI/ERP/Output/Output.php
new file mode 100644
index 0000000000000000000000000000000000000000..e318f6288f16ff3b2a2c323ce0691454873e99e4
--- /dev/null
+++ b/src/QUI/ERP/Output/Output.php
@@ -0,0 +1,100 @@
+<?php
+
+namespace QUI\ERP\Output;
+
+use QUI;
+use QUI\ERP\Accounting\Invoice\Settings;
+
+/**
+ * Class Output
+ *
+ * Main handler for serving previews, PDFs and downloads for QUIQQER ERP documents
+ */
+class Output
+{
+    /**
+     * Get the ERP Output Provider for a specific package
+     *
+     * @param string $package
+     * @return OutputProviderInterface|false - OutputProvider class (static) or false if
+     */
+    public static function getOutputProviderByPackage(string $package)
+    {
+//        $cache = 'quiqqer/backendsearch/providers';
+//
+//        try {
+//            return QUI\Cache\Manager::get($cache);
+//        } catch (QUI\Cache\Exception $Exception) {
+//        }
+
+        $packages = QUI::getPackageManager()->getInstalled();
+        $provider = [];
+
+        foreach ($packages as $installedPackage) {
+            try {
+                $Package = QUI::getPackage($installedPackage['name']);
+
+                if (!$Package->isQuiqqerPackage()) {
+                    continue;
+                }
+
+                if ($Package->getName() !== $package) {
+                    continue;
+                }
+
+                $packageProvider = $Package->getProvider();
+
+                if (empty($packageProvider['erpOutput'])) {
+                    continue;
+                }
+
+                if (!\class_exists($packageProvider['erpOutput'])) {
+                    continue;
+                }
+
+                return $packageProvider['erpOutput'];
+            } catch (QUI\Exception $Exception) {
+                QUI\System\Log::writeException($Exception);
+                return false;
+            }
+        }
+
+//        QUI\Cache\Manager::set($cache, $provider);
+
+        return false;
+    }
+
+    public static function getTemplates(string $package)
+    {
+        $result   = [];
+        $packages = QUI::getPackageManager()->getInstalled();
+        $default  = Settings::get('invoice', 'template');
+
+        $defaultIsDisabled = Settings::get('invoice', 'deactivateDefaultTemplate');
+
+        foreach ($packages as $package) {
+            $Package  = QUI::getPackage($package['name']);
+            $composer = $Package->getComposerData();
+
+            if ($defaultIsDisabled && $Package->getName() === 'quiqqer/invoice-accounting-template') {
+                continue;
+            }
+
+            if (!isset($composer['type'])) {
+                continue;
+            }
+
+            if ($composer['type'] !== 'quiqqer-invoice-template') {
+                continue;
+            }
+
+            $result[] = [
+                'name'    => $Package->getName(),
+                'title'   => $Package->getTitle(),
+                'default' => $Package->getName() === $default ? 1 : 0
+            ];
+        }
+
+        return $result;
+    }
+}
diff --git a/src/QUI/ERP/Output/OutputProviderInterface.php b/src/QUI/ERP/Output/OutputProviderInterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..3ad0580172002ed910261a3b662e2afcbbf8ded8
--- /dev/null
+++ b/src/QUI/ERP/Output/OutputProviderInterface.php
@@ -0,0 +1,39 @@
+<?php
+
+namespace QUI\ERP\Output;
+
+use QUI\HtmlToPdf\Document;
+
+/**
+ * Interface OutputProviderInterface
+ *
+ * Main interface for all ERP Output providers
+ */
+interface OutputProviderInterface
+{
+    /**
+     * Get preview HTML of an entity output
+     *
+     * @param string|int $entityId
+     * @param string $template
+     * @return string - Preview HTML
+     */
+    public static function getPreview($entityId, string $template);
+
+    /**
+     * Get PDF Document of an entity output
+     *
+     * @param string|int $entityId
+     * @param string $template
+     * @return Document
+     */
+    public static function getPDFDocument($entityId, string $template);
+
+    /**
+     * Get e-mail address of the document recipient
+     *
+     * @param string|int $entityId
+     * @return string|false - E-Mail address or false if no e-mail address available
+     */
+    public static function getEmailAddress($entityId);
+}