diff --git a/ajax/output/getEntityData.php b/ajax/output/getEntityData.php
new file mode 100644
index 0000000000000000000000000000000000000000..5d1d74652c3ad2b6077b8e228b0e25e8da29016d
--- /dev/null
+++ b/ajax/output/getEntityData.php
@@ -0,0 +1,46 @@
+<?php
+
+use QUI\ERP\Output\Output as ERPOutput;
+use QUI\Utils\Security\Orthos;
+
+/**
+ * Returns basic entity data used in OutputDialog
+ *
+ * @param string|int $entityId
+ * @param string $entityType
+ * @return array|false - Entity data or false
+ */
+QUI::$Ajax->registerFunction(
+    'package_quiqqer_erp_ajax_output_getEntityData',
+    function ($entityId, $entityType) {
+        $OutputProvider = ERPOutput::getOutputProviderByEntityType(Orthos::clear($entityType));
+
+        if (empty($OutputProvider)) {
+            return false;
+        }
+
+        $hideSystemDefaultTemplate = false;
+
+        try {
+            $Conf             = QUI::getPackage('quiqqer/erp')->getConfig();
+            $defaultTemplates = $Conf->get('output', 'default_templates');
+
+            if (!empty($defaultTemplates)) {
+                $defaultTemplates = \json_decode($defaultTemplates, true);
+
+                if (!empty($defaultTemplates[$entityType])) {
+                    $hideSystemDefaultTemplate = $defaultTemplates[$entityType]['hideSystemDefault'];
+                }
+            }
+        } catch (\Exception $Exception) {
+            QUI\System\Log::writeException($Exception);
+        }
+
+        return [
+            'email'                     => $OutputProvider::getEmailAddress(Orthos::clear($entityId)),
+            'hideSystemDefaultTemplate' => $hideSystemDefaultTemplate
+        ];
+    },
+    ['entityId', 'entityType'],
+    'Permission::checkAdminUser'
+);
diff --git a/ajax/output/getMailData.php b/ajax/output/getMailData.php
new file mode 100644
index 0000000000000000000000000000000000000000..e7628aa087952c24f3345783a0b1d451ad9c4f1f
--- /dev/null
+++ b/ajax/output/getMailData.php
@@ -0,0 +1,31 @@
+<?php
+
+use QUI\ERP\Output\Output as ERPOutput;
+use QUI\Utils\Security\Orthos;
+
+/**
+ * Returns e-mail data for an output document
+ *
+ * @return array
+ */
+QUI::$Ajax->registerFunction(
+    'package_quiqqer_erp_ajax_output_getMailData',
+    function ($entityId, $entityType) {
+        $OutputProvider = ERPOutput::getOutputProviderByEntityType(Orthos::clear($entityType));
+        $mailData       = [
+            'subject' => '',
+            'content' => ''
+        ];
+
+        if (empty($OutputProvider)) {
+            return $mailData;
+        }
+
+        $mailData['subject'] = $OutputProvider::getMailSubject($entityId);
+        $mailData['content'] = $OutputProvider::getMailBody($entityId);
+
+        return $mailData;
+    },
+    ['entityId', 'entityType'],
+    'Permission::checkAdminUser'
+);
diff --git a/ajax/output/getPreview.php b/ajax/output/getPreview.php
new file mode 100644
index 0000000000000000000000000000000000000000..a7a147c1b61d40381db5d0746cab1c084c1337ca
--- /dev/null
+++ b/ajax/output/getPreview.php
@@ -0,0 +1,33 @@
+<?php
+
+use QUI\ERP\Output\Output as ERPOutput;
+use QUI\Utils\Security\Orthos;
+
+/**
+ * Returns the invoice templates
+ *
+ * @return array
+ */
+QUI::$Ajax->registerFunction(
+    'package_quiqqer_erp_ajax_output_getPreview',
+    function ($entity, $template) {
+        $entity   = Orthos::clearArray(\json_decode($entity, true));
+        $template = Orthos::clearArray(\json_decode($template, true));
+
+        try {
+            return ERPOutput::getDocumentHtml(
+                $entity['id'],
+                $entity['type'],
+                null,
+                ERPOutput::getOutputTemplateProviderByPackage($template['provider']),
+                $template['id'],
+                true
+            );
+        } catch (\Exception $Exception) {
+            QUI\System\Log::writeException($Exception);
+            return '';
+        }
+    },
+    ['entity', 'template'],
+    'Permission::checkAdminUser'
+);
diff --git a/ajax/output/getTemplates.php b/ajax/output/getTemplates.php
new file mode 100644
index 0000000000000000000000000000000000000000..94d7881d3fc8e593bc4bb6d2abd3827f56230a8b
--- /dev/null
+++ b/ajax/output/getTemplates.php
@@ -0,0 +1,19 @@
+<?php
+
+use QUI\ERP\Output\Output as ERPOutput;
+use QUI\Utils\Security\Orthos;
+
+/**
+ * Returns available output templates
+ *
+ * @param string $entityType (optional) - Restrict templates to those for $entityType
+ * @return array
+ */
+QUI::$Ajax->registerFunction(
+    'package_quiqqer_erp_ajax_output_getTemplates',
+    function ($entityType = null) {
+        return ERPOutput::getTemplates(Orthos::clear($entityType));
+    },
+    ['entityType'],
+    'Permission::checkAdminUser'
+);
diff --git a/ajax/output/sendMail.php b/ajax/output/sendMail.php
new file mode 100644
index 0000000000000000000000000000000000000000..70bb39c7c63a642ff58c86e49784daea0bc3fe68
--- /dev/null
+++ b/ajax/output/sendMail.php
@@ -0,0 +1,56 @@
+<?php
+
+use QUI\ERP\Output\Output as ERPOutput;
+use QUI\Utils\Security\Orthos;
+
+/**
+ * Returns e-mail data for an output document
+ *
+ * @param string|int $entityId
+ * @param string $entityType
+ * @param string $template
+ * @param string $templateProvider
+ * @param string $mailSubject (optional)
+ * @param string $mailContent (optional)
+ *
+ * @return void
+ */
+QUI::$Ajax->registerFunction(
+    'package_quiqqer_erp_ajax_output_sendMail',
+    function ($entityId, $entityType, $template, $templateProvider, $mailSubject, $mailContent) {
+        try {
+            $entityType = Orthos::clear($entityType);
+
+            $OutputProvider   = ERPOutput::getOutputProviderByEntityType($entityType);
+            $TemplateProvider = ERPOutput::getOutputTemplateProviderByPackage(Orthos::clear($templateProvider));
+
+            if (empty($TemplateProvider)) {
+                $TemplateProvider = ERPOutput::getDefaultOutputTemplateProviderForEntityType($entityType);
+            }
+
+            ERPOutput::sendPdfViaMail(
+                $entityId,
+                $entityType,
+                $OutputProvider,
+                $TemplateProvider,
+                Orthos::clear($template),
+                null,
+                $mailSubject,
+                $mailContent
+            );
+        } catch (\Exception $Exception) {
+            QUI\System\Log::writeException($Exception);
+
+            throw new \QUI\Exception([
+                'quiqqer/erp',
+                'exception.ajax.output.sendMail.error'
+            ]);
+        }
+
+        QUI::getMessagesHandler()->addSuccess(
+            QUI::getLocale()->get('quiqqer/erp', 'Output.send.success')
+        );
+    },
+    ['entityId', 'entityType', 'template', 'templateProvider', 'mailSubject', 'mailContent'],
+    'Permission::checkAdminUser'
+);
diff --git a/bin/backend/controls/OutputDialog.css b/bin/backend/controls/OutputDialog.css
new file mode 100644
index 0000000000000000000000000000000000000000..8a4a10cec9cee0b195fe8e2709796211796f9153
--- /dev/null
+++ b/bin/backend/controls/OutputDialog.css
@@ -0,0 +1,48 @@
+.quiqqer-erp-outputDialog {
+    padding: 10px !important;
+    display: flex;
+}
+
+.quiqqer-erp-outputDialog .field-container-item {
+    width: 150px !important;
+}
+
+.quiqqer-erp-outputDialog-options {
+    padding: 5px;
+    width: 400px;
+}
+
+.quiqqer-erp-outputDialog form table {
+    border: none;
+    padding: 0;
+}
+
+.quiqqer-erp-outputDialog select {
+    width: 100%;
+}
+
+.quiqqer-erp-outputDialog-preview {
+    background-color: #efefef;
+    float: left;
+    height: 100%;
+    overflow: auto;
+    padding: 20px;
+    width: calc(100% - 400px);
+}
+
+.quiqqer-erp-outputDialog-preview {
+    background: #fff;
+    box-shadow: 0 0 0 0.75pt #d1d1d1, 0 0 3pt 0.75pt #ccc;
+    border: none;
+    display: block;
+    margin: 0 auto;
+}
+.quiqqer-erp-outputDialog-openMailEditor {
+    background-color: #dedede;
+    border-bottom-left-radius: 5px;
+    border-bottom-right-radius: 5px;
+    cursor: pointer;
+    float: right;
+    padding: 5px;
+}
+
diff --git a/bin/backend/controls/OutputDialog.html b/bin/backend/controls/OutputDialog.html
new file mode 100644
index 0000000000000000000000000000000000000000..0b28317d083ac202dae20a7ba197de252a7dfe80
--- /dev/null
+++ b/bin/backend/controls/OutputDialog.html
@@ -0,0 +1,77 @@
+<div class="quiqqer-erp-outputDialog-options">
+    <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="{{labelTemplate}}">
+                        {{labelTemplate}}
+                    </span>
+                        <select name="template" class="field-container-field"></select>
+                    </label>
+                </td>
+            </tr>
+            <tr>
+                <td>
+                    <label class="field-container">
+                    <span class="field-container-item" title="{{labelOutputType}}">
+                        {{labelOutputType}}
+                    </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="{{labelEmail}}">
+                        {{labelEmail}}
+                    </span>
+                        <input type="text"
+                               name="recipient"
+                               placeholder="email@domain.com"
+                               class="field-container-field"/>
+                    </label>
+                    {{#mailEditor}}
+                    <div class="quiqqer-erp-outputDialog-openMailEditor">
+                        <span class="fa fa-envelope"></span>
+                        <span>{{labelOpenMailEditor}}</span>
+                    </div>
+                    {{/mailEditor}}
+                </td>
+            </tr>
+            {{#showMarkAsSentOption}}
+            <tr>
+                <td>
+                    <label class="field-container">
+                    <span class="field-container-item" title="{{labelMarkAsSent}}">
+                        {{labelMarkAsSent}}
+                    </span>
+                        <div class="field-container-field">
+                            <input type="checkbox" name="mark_as_sent"/>
+                        </div>
+                    </label>
+                    <div class="field-container-item-desc">
+                        {{{descMarkAsSent}}}
+                    </div>
+                </td>
+            </tr>
+            {{/showMarkAsSentOption}}
+            </tbody>
+        </table>
+    </form>
+</div>
+<div class="quiqqer-erp-outputDialog-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..0c88ea12746465648559dc7c4d1390ab9162b68e
--- /dev/null
+++ b/bin/backend/controls/OutputDialog.js
@@ -0,0 +1,597 @@
+/**
+ * @module package/quiqqer/erp/bin/backend/controls/OutputDialog
+ * @author www.pcsg.de (Patrick Müller)
+ *
+ * @event onOuput [FormData, this] - Fires if the user submits the popup with a chosen output format
+ */
+define('package/quiqqer/erp/bin/backend/controls/OutputDialog', [
+
+    'qui/QUI',
+    'qui/controls/windows/Confirm',
+    'qui/controls/buttons/Select',
+    'qui/controls/elements/Sandbox',
+
+    'qui/utils/Form',
+
+    'Ajax',
+    'Locale',
+    'Mustache',
+    'Users',
+
+    'text!package/quiqqer/erp/bin/backend/controls/OutputDialog.html',
+    'css!package/quiqqer/erp/bin/backend/controls/OutputDialog.css'
+
+], function (QUI, QUIConfirm, QUISelect, QUISandbox, QUIFormUtils, QUIAjax, QUILocale, Mustache, Users, template) {
+    "use strict";
+
+    var lg = 'quiqqer/erp';
+
+    return new Class({
+
+        Extends: QUIConfirm,
+        type   : 'package/quiqqer/erp/bin/backend/controls/OutputDialog',
+
+        Binds: [
+            '$onOpen',
+            '$onOutputChange',
+            '$onPrintFinish',
+            '$getPreview',
+            '$onChangeToEmail',
+            '$onChangeToPDF',
+            '$onChangeToPrint'
+        ],
+
+        options: {
+            entityId  : false,  // Clean entity ID WITHOUT prefix and suffix
+            entityType: false,  // Entity type (e.g. "Invoice")
+
+            showMarkAsSentOption: false,    // show checkbox for "Mark as sent"
+            mailEditor          : true,     // shows editable subject and body for mail output
+
+            maxHeight: 800,
+            maxWidth : 1400
+        },
+
+        initialize: function (options) {
+            this.parent(options);
+
+            this.setAttributes({
+                icon         : 'fa fa-print',
+                title        : QUILocale.get(lg, 'controls.OutputDialog.title'),
+                autoclose    : false,
+                cancel_button: {
+                    textimage: 'fa fa-close',
+                    text     : QUILocale.get('quiqqer/system', 'close')
+                }
+            });
+
+            this.$Output       = null;
+            this.$Preview      = null;
+            this.$customerMail = null;
+            this.$Template     = null;
+
+            this.$Mail = {
+                subject: false,
+                content: false
+            };
+
+            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) {
+                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);
+                });
+            };
+
+            Content.set({
+                html: Mustache.render(template, {
+                    entityId            : this.getAttribute('entityId'),
+                    labelEntityId       : QUILocale.get(lg, 'controls.OutputDialog.labelEntityId'),
+                    labelTemplate       : QUILocale.get(lg, 'controls.OutputDialog.labelTemplate'),
+                    labelOutputType     : QUILocale.get(lg, 'controls.OutputDialog.labelOutputType'),
+                    labelEmail          : QUILocale.get('quiqqer/quiqqer', 'recipient'),
+                    showMarkAsSentOption: this.getAttribute('showMarkAsSentOption') ? true : false,
+                    mailEditor          : this.getAttribute('mailEditor') ? true : false,
+                    labelOpenMailEditor : QUILocale.get(lg, 'controls.OutputDialog.labelOpenMailEditor'),
+                    labelMarkAsSent     : QUILocale.get(lg, 'controls.OutputDialog.labelMarkAsSent'),
+                    descMarkAsSent      : QUILocale.get(lg, 'controls.OutputDialog.descMarkAsSent')
+                })
+            });
+
+            Content.addClass('quiqqer-erp-outputDialog');
+
+            // "To mail editor" button
+            if (this.getAttribute('mailEditor')) {
+                Content.getElement('.quiqqer-erp-outputDialog-openMailEditor').addEvent('click', function () {
+                    require(['package/quiqqer/erp/bin/backend/controls/OutputMailEditor'], function (OutputMailEditor) {
+                        new OutputMailEditor({
+                            entityId  : self.getAttribute('entityId'),
+                            entityType: self.getAttribute('entityType'),
+
+                            mailSubject: self.$Mail.subject,
+                            mailContent: self.$Mail.content,
+
+                            events: {
+                                onMailSubmit: function (MailData) {
+                                    self.$Mail = MailData;
+                                }
+                            }
+                        }).open();
+                    });
+                });
+            }
+
+            this.$Output = new QUISelect({
+                localeStorage: 'quiqqer-erp-output-dialog',
+                name         : 'output',
+                styles       : {
+                    border: 'none',
+                    width : '100%'
+                },
+                events       : {
+                    onChange: self.$onOutputChange
+                }
+            });
+
+            this.$Output.appendChild(
+                QUILocale.get(lg, 'controls.OutputDialog.data.output.print'),
+                'print',
+                'fa fa-print'
+            );
+
+            this.$Output.appendChild(
+                QUILocale.get(lg, 'controls.OutputDialog.data.output.pdf'),
+                'pdf',
+                'fa fa-file-pdf-o'
+            );
+
+            this.$Output.appendChild(
+                QUILocale.get(lg, 'controls.OutputDialog.data.output.email'),
+                'email',
+                'fa fa-envelope-o'
+            );
+
+            this.$Output.inject(Content.getElement('.field-output'));
+
+            Promise.all([
+                this.$getTemplates(),
+                this.$getEntityData()
+            ]).then(function (result) {
+                var templates  = result[0];
+                var EntityData = result[1];
+
+                var Form     = Content.getElement('form'),
+                    Selected = false;
+
+                if (!templates.length) {
+                    new Element('option', {
+                        value: '#',
+                        html : QUILocale.get(lg, 'controls.OutputDialog.no_templates_found'),
+                    }).inject(Form.elements.template);
+
+                    Form.elements.template.disabled = true;
+
+                    var PreviewContent = self.getContent().getElement('.quiqqer-erp-outputDialog-preview');
+
+                    new Element('div', {
+                        'class': 'quiqqer-erp-outputDialog-nopreview',
+                        html   : QUILocale.get(lg, 'controls.OutputDialog.no_preview')
+                    }).inject(PreviewContent);
+
+                    self.$Output.disable();
+                    self.getButton('submit').disable();
+
+                    self.Loader.hide();
+                    return;
+                }
+
+                for (var i = 0, len = templates.length; i < len; i++) {
+                    var Template = templates[i];
+
+                    if (Template.isSystemDefault && EntityData.hideSystemDefaultTemplate) {
+                        continue;
+                    }
+
+                    new Element('option', {
+                        value          : Template.id,
+                        html           : Template.title,
+                        'data-provider': Template.provider
+                    }).inject(Form.elements.template);
+
+                    if (!Selected && Template.isDefault) {
+                        Selected = Template;
+                    }
+                }
+
+                Form.elements.template.addEvent('change', function (event) {
+                    self.$Template = {
+                        id      : event.target.value,
+                        provider: event.target.getElement('option[value="' + event.target.value + '"]')
+                            .get('data-provider')
+                    };
+
+                    self.$renderPreview();
+                });
+
+                // Set initial template and render preview
+                Form.elements.template.value = Selected.id;
+                self.$Template               = {
+                    id      : Selected.id,
+                    provider: Selected.provider
+                };
+
+                self.$renderPreview();
+
+                // Customer data
+                self.$customerMail = EntityData.email;
+                self.$onOutputChange();
+
+                self.Loader.hide();
+            }).catch(function (e) {
+                onError(e);
+            });
+        },
+
+        /**
+         * Render preview with selected template
+         */
+        $renderPreview: function () {
+            var self           = this;
+            var PreviewContent = this.getContent().getElement('.quiqqer-erp-outputDialog-preview');
+
+            this.Loader.show();
+
+            var showPreviewError = function () {
+                PreviewContent.set('html', '');
+
+                new Element('div', {
+                    'class': 'quiqqer-erp-outputDialog-nopreview',
+                    html   : QUILocale.get(lg, 'controls.OutputDialog.preview_error')
+                }).inject(PreviewContent);
+            };
+
+            this.$getPreview().then(function (previewHtml) {
+                self.Loader.hide();
+
+                if (!previewHtml) {
+                    showPreviewError();
+                    return;
+                }
+
+                PreviewContent.set('html', '');
+
+                new QUISandbox({
+                    content: previewHtml,
+                    styles : {
+                        height : 1240,
+                        padding: 20,
+                        width  : 874
+                    },
+                    events : {
+                        onLoad: function (Box) {
+                            Box.getElm().addClass('quiqqer-erp-outputDialog-preview');
+                        }
+                    }
+                }).inject(PreviewContent);
+            }).catch(function () {
+                self.Loader.hide();
+                showPreviewError();
+            });
+        },
+
+        /**
+         * 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.$sendMail();
+                    break;
+            }
+
+            Run.then(function () {
+                var Form = self.getContent().getElement('form');
+
+                self.fireEvent('output', [
+                    QUIFormUtils.getFormData(Form),
+                    self
+                ]);
+
+                self.Loader.hide();
+            });
+        },
+
+        /**
+         * Print the document
+         *
+         * @return {Promise}
+         */
+        print: function () {
+            var self     = this,
+                entityId = this.getAttribute('entityId');
+
+            return new Promise(function (resolve) {
+                var id      = 'print-document-' + entityId,
+                    Content = self.getContent(),
+                    Form    = Content.getElement('form');
+
+                self.Loader.show();
+
+                new Element('iframe', {
+                    src   : URL_OPT_DIR + 'quiqqer/erp/bin/output/backend/print.php?' + Object.toQueryString({
+                        id   : entityId,
+                        t    : self.getAttribute('entityType'),
+                        oid  : self.getId(),
+                        tpl  : self.$Template.id,
+                        tplpr: self.$Template.provider
+                    }),
+                    id    : id,
+                    styles: {
+                        position: 'absolute',
+                        top     : -200,
+                        left    : -200,
+                        width   : 50,
+                        height  : 50
+                    }
+                }).inject(document.body);
+
+                self.addEvent('onPrintFinish', function (self, pId) {
+                    if (pId === entityId) {
+                        resolve();
+                    }
+                });
+            });
+        },
+
+        /**
+         * event: on print finish
+         *
+         * @param {String|Number} id
+         */
+        $onPrintFinish: function (id) {
+            this.fireEvent('printFinish', [this, id]);
+
+            (function () {
+                document.getElements('#print-document-' + id).destroy();
+                this.close();
+            }).delay(1000, this);
+        },
+
+        /**
+         * Export the document as PDF
+         *
+         * @return {Promise}
+         */
+        saveAsPdf: function () {
+            var self     = this,
+                entityId = this.getAttribute('entityId');
+
+            return new Promise(function (resolve) {
+                var id      = 'download-document-' + entityId,
+                    Content = self.getContent(),
+                    Form    = Content.getElement('form');
+
+                new Element('iframe', {
+                    src   : URL_OPT_DIR + 'quiqqer/erp/bin/output/backend/download.php?' + Object.toQueryString({
+                        id   : entityId,
+                        t    : self.getAttribute('entityType'),
+                        oid  : self.getId(),
+                        tpl  : self.$Template.id,
+                        tplpr: self.$Template.provider
+                    }),
+                    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);
+            });
+        },
+
+        /**
+         * 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, 'controls.OutputDialog.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, 'controls.OutputDialog.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, 'controls.OutputDialog.data.output.email.btn'));
+            Submit.setAttribute('textimage', 'fa fa-envelope-o');
+
+            if (this.$customerMail && Recipient.value === '') {
+                Recipient.value = this.$customerMail;
+            }
+
+            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('package_quiqqer_erp_ajax_output_getEntityData', resolve, {
+                    'package' : 'quiqqer/erp',
+                    entityId  : self.getAttribute('entityId'),
+                    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('package_quiqqer_erp_ajax_output_getTemplates', resolve, {
+                    'package' : 'quiqqer/erp',
+                    entityType: self.getAttribute('entityType'),
+                    onError   : reject
+                })
+            });
+        },
+
+        /**
+         * Fetch available templates based on entity type
+         *
+         * @return {Promise}
+         */
+        $getPreview: function () {
+            var self = this;
+
+            return new Promise(function (resolve, reject) {
+                QUIAjax.get('package_quiqqer_erp_ajax_output_getPreview', resolve, {
+                    'package': 'quiqqer/erp',
+                    entity   : JSON.encode({
+                        id  : self.getAttribute('entityId'),
+                        type: self.getAttribute('entityType')
+                    }),
+                    template : JSON.encode(self.$Template),
+                    onError  : reject
+                })
+            });
+        },
+
+        /**
+         * Get data of the entity that is outputted
+         *
+         * @return {Promise}
+         */
+        $sendMail: function () {
+            var self = this,
+                Form = this.getContent().getElement('form');
+
+            return new Promise(function (resolve, reject) {
+                QUIAjax.get('package_quiqqer_erp_ajax_output_sendMail', resolve, {
+                    'package'       : 'quiqqer/erp',
+                    entityId        : self.getAttribute('entityId'),
+                    entityType      : self.getAttribute('entityType'),
+                    template        : self.$Template.id,
+                    templateProvider: self.$Template.provider,
+                    mailSubject     : self.$Mail.subject,
+                    mailContent     : self.$Mail.content,
+                    onError         : reject
+                })
+            });
+        }
+    });
+});
\ No newline at end of file
diff --git a/bin/backend/controls/OutputMailEditor.css b/bin/backend/controls/OutputMailEditor.css
new file mode 100644
index 0000000000000000000000000000000000000000..01cb85d474ed0505d30801b95f5cb03c6925048a
--- /dev/null
+++ b/bin/backend/controls/OutputMailEditor.css
@@ -0,0 +1,19 @@
+.quiqqer-erp-outputMailEditor-mailEditor > label {
+    display: block;
+    margin: 10px 0;
+    width: 100%;
+}
+
+.quiqqer-erp-outputMailEditor-mailEditor-subject {
+    display: block;
+    width: 100%;
+}
+
+.quiqqer-erp-outputMailEditor-info {
+    float: left;
+    margin-top: 10px;
+}
+
+.quiqqer-erp-outputMailEditor-mailEditor-content {
+    height: 550px;
+}
\ No newline at end of file
diff --git a/bin/backend/controls/OutputMailEditor.html b/bin/backend/controls/OutputMailEditor.html
new file mode 100644
index 0000000000000000000000000000000000000000..d88070ab731ac10991fcf7a827407863c2217f38
--- /dev/null
+++ b/bin/backend/controls/OutputMailEditor.html
@@ -0,0 +1,13 @@
+<div class="quiqqer-erp-outputMailEditor-mailEditor">
+    <label>
+        <span>{{labelMailSubject}}</span>
+        <input class="quiqqer-erp-outputMailEditor-mailEditor-subject" type="text"/>
+    </label>
+    <label>
+        <span>{{labelMailContent}}</span>
+        <div class="quiqqer-erp-outputMailEditor-mailEditor-content"></div>
+    </label>
+</div>
+<div class="quiqqer-erp-outputMailEditor-info">
+    {{{info}}}
+</div>
\ No newline at end of file
diff --git a/bin/backend/controls/OutputMailEditor.js b/bin/backend/controls/OutputMailEditor.js
new file mode 100644
index 0000000000000000000000000000000000000000..8d8c8eac59d8e3d4c71de79fe12236262b370ce4
--- /dev/null
+++ b/bin/backend/controls/OutputMailEditor.js
@@ -0,0 +1,172 @@
+/**
+ * Edit the e-mail subject and content of an Output document
+ *
+ * @module package/quiqqer/erp/bin/backend/controls/OutputMailEditor
+ * @author www.pcsg.de (Patrick Müller)
+ *
+ * @event onMailSubmit [MailData, this] - Fires if the user submits the mail data
+ */
+define('package/quiqqer/erp/bin/backend/controls/OutputMailEditor', [
+
+    'qui/QUI',
+    'qui/controls/windows/Confirm',
+    'qui/controls/buttons/Select',
+    'qui/controls/elements/Sandbox',
+
+    'qui/utils/Form',
+
+    'Ajax',
+    'Locale',
+    'Mustache',
+    'Users',
+
+    'text!package/quiqqer/erp/bin/backend/controls/OutputMailEditor.html',
+    'css!package/quiqqer/erp/bin/backend/controls/OutputMailEditor.css'
+
+], function (QUI, QUIConfirm, QUISelect, QUISandbox, QUIFormUtils, QUIAjax, QUILocale, Mustache, Users, template) {
+    "use strict";
+
+    var lg = 'quiqqer/erp';
+
+    return new Class({
+
+        Extends: QUIConfirm,
+        type   : 'package/quiqqer/erp/bin/backend/controls/OutputMailEditor',
+
+        Binds: [
+            '$onOpen',
+            '$onSubmit'
+        ],
+
+        options: {
+            entityId  : false,  // Clean entity ID WITHOUT prefix and suffix
+            entityType: false,  // Entity type (e.g. "Invoice")
+
+            mailSubject: false, // Mail subject that is shown initially
+            mailContent: false, // Mail content that is shown initially
+
+            maxHeight: 820,
+            maxWidth : 900
+        },
+
+        initialize: function (options) {
+            this.parent(options);
+
+            this.setAttributes({
+                icon         : 'fa fa-envelope',
+                title        : QUILocale.get(lg, 'controls.OutputMailEditor.title'),
+                autoclose    : false,
+                cancel_button: {
+                    textimage: 'fa fa-close',
+                    text     : QUILocale.get('quiqqer/system', 'close')
+                }
+            });
+
+            this.$Output      = null;
+            this.$Preview     = null;
+            this.$cutomerMail = null;
+            this.$Template    = null;
+
+            this.$subject = null;
+            this.$content = null;
+
+            this.$MailSubjectInput  = null;
+            this.$MailContentEditor = null;
+
+            this.addEvents({
+                onOpen  : this.$onOpen,
+                onSubmit: this.$onSubmit
+            });
+        },
+
+        /**
+         * event: on open
+         */
+        $onOpen: function () {
+            var self    = this,
+                Content = this.getContent();
+
+            this.Loader.show();
+
+            Content.set({
+                html: Mustache.render(template, {
+                    entityId        : self.getAttribute('entityId'),
+                    labelEntityId   : QUILocale.get(lg, 'controls.OutputMailEditor.labelEntityId'),
+                    labelMailSubject: QUILocale.get(lg, 'controls.OutputMailEditor.labelMailSubject'),
+                    labelMailContent: QUILocale.get(lg, 'controls.OutputMailEditor.labelMailContent'),
+                    info            : QUILocale.get(lg, 'controls.OutputMailEditor.info')
+                })
+            });
+
+            Content.addClass('quiqqer-erp-OutputMailEditor');
+
+            this.$MailSubjectInput = Content.getElement('.quiqqer-erp-outputMailEditor-mailEditor-subject');
+            this.$subject          = this.getAttribute('mailSubject');
+            this.$content          = this.getAttribute('mailContent');
+
+            this.$getMailData().then(function (MailData) {
+                require(['Editors'], function (Editors) {
+                    Editors.getEditor().then(function (Editor) {
+                        Editor.addEvent('onLoaded', function () {
+                            self.Loader.hide();
+                            self.fireEvent('load', [self]);
+
+                            Editor.resize();
+                        });
+
+                        Editor.inject(
+                            Content.getElement('.quiqqer-erp-outputMailEditor-mailEditor-content')
+                        );
+
+                        self.$MailContentEditor = Editor;
+
+                        if (self.$subject) {
+                            self.$MailSubjectInput.value = self.$subject;
+                        } else {
+                            self.$MailSubjectInput.value = MailData.subject;
+                        }
+
+                        if (self.$content) {
+                            self.$MailContentEditor.setContent(self.$content);
+                        } else {
+                            self.$MailContentEditor.setContent(MailData.content);
+                        }
+                    });
+                });
+            });
+        },
+
+        /**
+         * Event: onSubmit
+         */
+        $onSubmit: function () {
+            this.fireEvent('mailSubmit', [
+                {
+                    subject: this.$MailSubjectInput.value,
+                    content: this.$MailContentEditor.getContent()
+                },
+                this
+            ]);
+
+            this.close();
+        },
+
+        /**
+         * Get data of the entity that is outputted
+         *
+         * @return {Promise}
+         */
+        $getMailData: function () {
+            var self = this;
+
+            return new Promise(function (resolve, reject) {
+                QUIAjax.get('package_quiqqer_erp_ajax_output_getMailData', resolve, {
+                    'package' : 'quiqqer/erp',
+                    entityId  : self.getAttribute('entityId'),
+                    entityType: self.getAttribute('entityType'),
+                    onError   : reject
+                })
+            });
+        }
+    });
+});
\ No newline at end of file
diff --git a/bin/backend/controls/Panel.js b/bin/backend/controls/Panel.js
index 12bee841db668cdef292e9c4e8cefa609891a250..7f2c306520fbda39addc3e0f13805eeece76c897 100644
--- a/bin/backend/controls/Panel.js
+++ b/bin/backend/controls/Panel.js
@@ -158,8 +158,6 @@ define('package/quiqqer/erp/bin/backend/controls/Panel', [
 
                     return;
                 }
-
-                console.log(typeOf(cls));
             });
         }
     });
diff --git a/bin/backend/controls/settings/OutputTemplates.css b/bin/backend/controls/settings/OutputTemplates.css
new file mode 100644
index 0000000000000000000000000000000000000000..0569430fb4fbfab7b858cd6d12a31c5975c5403e
--- /dev/null
+++ b/bin/backend/controls/settings/OutputTemplates.css
@@ -0,0 +1,17 @@
+.quiqqer-erp-settings-output-templates__hidden {
+    display: none !important;
+}
+
+.quiqqer-erp-settings-output-templates-default-select {
+    width: 100%;
+}
+
+.quiqqer-erp-settings-output-templates-hide-system-default {
+    display: block;
+    float: none !important;
+    width: 100% !important;
+}
+
+.quiqqer-erp-settings-output-templates-hide-system-default > label {
+    float: none !important;
+}
\ No newline at end of file
diff --git a/bin/backend/controls/settings/OutputTemplates.html b/bin/backend/controls/settings/OutputTemplates.html
new file mode 100644
index 0000000000000000000000000000000000000000..b94f79046c9c104521f8eb8ea09aa529aafa6bb6
--- /dev/null
+++ b/bin/backend/controls/settings/OutputTemplates.html
@@ -0,0 +1,31 @@
+<table class="data-table data-table-flexbox">
+    <tbody>
+    {{#templates}}
+    <tr>
+        <td>
+            <div class="field-container">
+                <span class="field-container-item" title="{{entityTypeTitle}}">
+                    {{title}}
+                </span>
+                <div class="field-container-field">
+                    <select class="quiqqer-erp-settings-output-templates-default-select" data-entitytype="{{entityType}}">
+                        {{#outputTemplates}}
+                        <option value="{{id}}--{{provider}}"
+                                data-systemdefault="{{isSystemDefault}}"
+                                data-provider="{{provider}}"
+                        >
+                            {{title}}
+                        </option>
+                        {{/outputTemplates}}
+                    </select>
+                    <label class="quiqqer-erp-settings-output-templates-hide-system-default quiqqer-erp-settings-output-templates__hidden">
+                        <input type="checkbox"/>
+                        <label>{{labelHideSystemDefault}}</label>
+                    </label>
+                </div>
+            </div>
+        </td>
+    </tr>
+    {{/templates}}
+    </tbody>
+</table>
\ No newline at end of file
diff --git a/bin/backend/controls/settings/OutputTemplates.js b/bin/backend/controls/settings/OutputTemplates.js
new file mode 100644
index 0000000000000000000000000000000000000000..613c0dc1224522d09c6bb2ee59de5249bf38805c
--- /dev/null
+++ b/bin/backend/controls/settings/OutputTemplates.js
@@ -0,0 +1,202 @@
+/**
+ * Set default output templates for all output providers
+ *
+ * @module package/quiqqer/erp/bin/backend/controls/settings/OutputTemplates
+ * @author www.pcsg.de (Patrick Müller)
+ */
+define('package/quiqqer/erp/bin/backend/controls/settings/OutputTemplates', [
+
+    'qui/QUI',
+    'qui/controls/loader/Loader',
+    'qui/controls/Control',
+
+    'Locale',
+    'Mustache',
+    'Ajax',
+
+    'text!package/quiqqer/erp/bin/backend/controls/settings/OutputTemplates.html',
+    'css!package/quiqqer/erp/bin/backend/controls/settings/OutputTemplates.css'
+
+], function (QUI, QUILoader, QUIControl, QUILocale, Mustache, QUIAjax, template) {
+    "use strict";
+
+    var lg = 'quiqqer/erp';
+
+    return new Class({
+
+        Extends: QUIControl,
+        Type   : 'package/quiqqer/erp/bin/backend/controls/settings/OutputTemplates',
+
+        Binds: [
+            '$onImport',
+            '$getTemplates',
+            '$onTemplateSelectChange',
+            '$setValue'
+        ],
+
+        initialize: function (options) {
+            this.parent(options);
+
+            this.Loader           = new QUILoader();
+            this.$Input           = null;
+            this.$templateSelects = [];
+
+            this.addEvents({
+                onImport: this.$onImport
+            });
+        },
+
+        /**
+         * @event: on import
+         */
+        $onImport: function () {
+            var self = this;
+
+            this.$Input      = this.getElm();
+            this.$Input.type = 'hidden';
+
+            var Content = new Element('div', {
+                'class': 'quiqqer-erp-settings-output-templates'
+            }).inject(this.$Input, 'before');
+
+            this.Loader.inject(Content);
+            this.Loader.show();
+
+            this.$getTemplates().then(function (templates) {
+                self.Loader.hide();
+
+                var Templates = {};
+                var entityType;
+
+                // Parse templates by entity type
+                for (var i = 0, len = templates.length; i < len; i++) {
+                    var Template = templates[i];
+
+                    if (!(Template.entityType in Templates)) {
+                        Templates[Template.entityType] = {
+                            entityTypeTitle: Template.entityTypeTitle,
+                            outputTemplates: []
+                        };
+                    }
+
+                    Template.isSystemDefault = Template.isSystemDefault ? 1 : 0;
+                    Templates[Template.entityType].outputTemplates.push(Template);
+                }
+
+                var renderTemplates = [];
+
+                for (entityType in Templates) {
+                    renderTemplates.push({
+                        title          : Templates[entityType].entityTypeTitle,
+                        entityType     : entityType,
+                        outputTemplates: Templates[entityType].outputTemplates
+                    });
+                }
+
+                Content.set('html', Mustache.render(template, {
+                    labelHideSystemDefault: QUILocale.get(lg,
+                        'controls.settings.OutputTemplates.tpl.labelHideSystemDefault'
+                    ),
+                    templates             : renderTemplates
+                }));
+
+                self.$templateSelects = Content.getElements('.quiqqer-erp-settings-output-templates-default-select');
+                self.$templateSelects.addEvent('change', self.$onTemplateSelectChange);
+
+                var defaultCheckboxes = Content.getElements('.quiqqer-erp-settings-output-templates-hide-system-default');
+                defaultCheckboxes.addEvent('change', self.$setValue);
+
+                // Set values from setting
+                if (self.$Input.value === '') {
+                    return;
+                }
+
+                var Setting = JSON.decode(self.$Input.value);
+
+                for (entityType in Setting) {
+                    if (!Setting.hasOwnProperty(entityType)) {
+                        continue;
+                    }
+
+                    var EntitySetting = Setting[entityType];
+                    var Select        = Content.getElement('select[data-entitytype="' + entityType + '"]');
+
+                    if (!Select) {
+                        continue;
+                    }
+
+                    Select.value = EntitySetting.id + '--' + EntitySetting.provider;
+
+                    var DefaultCheckbox = Select.getParent().getElement(
+                        '.quiqqer-erp-settings-output-templates-hide-system-default input'
+                    );
+
+                    var Option = Select.getElement('option[value="' + Select.value + '"]');
+
+                    if (!parseInt(Option.get('data-systemdefault'))) {
+                        DefaultCheckbox.getParent().removeClass('quiqqer-erp-settings-output-templates__hidden');
+                    }
+
+                    DefaultCheckbox.checked = EntitySetting.hideSystemDefault;
+                }
+            });
+        },
+
+        /**
+         * Handle change of selected template
+         *
+         * @param {DOMEvent} event
+         */
+        $onTemplateSelectChange: function (event) {
+            var Select          = event.target,
+                DefaultCheckbox = Select.getParent().getElement('.quiqqer-erp-settings-output-templates-hide-system-default'),
+                Selected        = Select.getElement('option[value="' + Select.value + '"]');
+
+            if (!parseInt(Selected.get('data-systemdefault'))) {
+                DefaultCheckbox.removeClass('quiqqer-erp-settings-output-templates__hidden');
+            } else {
+                DefaultCheckbox.addClass('quiqqer-erp-settings-output-templates__hidden');
+            }
+
+            this.$setValue();
+        },
+
+        /**
+         * Set default templates setting value
+         */
+        $setValue: function () {
+            var DefaultTemplates = {};
+
+            for (var i = 0, len = this.$templateSelects.length; i < len; i++) {
+                var Select          = this.$templateSelects[i],
+                    DefaultCheckbox = Select.getParent().getElement('.quiqqer-erp-settings-output-templates-hide-system-default input'),
+                    value           = Select.value.split('--');
+
+                DefaultTemplates[Select.get('data-entitytype')] = {
+                    id               : value[0],
+                    provider         : value[1],
+                    hideSystemDefault: DefaultCheckbox.checked
+                };
+            }
+
+            this.$Input.value = JSON.encode(DefaultTemplates);
+        },
+
+        /**
+         * Fetch available templates for all entity types
+         *
+         * @return {Promise}
+         */
+        $getTemplates: function () {
+            var self = this;
+
+            return new Promise(function (resolve, reject) {
+                QUIAjax.get('package_quiqqer_erp_ajax_output_getTemplates', resolve, {
+                    'package': 'quiqqer/erp',
+                    //entityType: self.getAttribute('entityType'),
+                    onError  : reject
+                })
+            });
+        },
+    });
+});
diff --git a/bin/output/backend/download.php b/bin/output/backend/download.php
new file mode 100644
index 0000000000000000000000000000000000000000..40baf8406539dd08947ad3fb58467a8192e4dfc8
--- /dev/null
+++ b/bin/output/backend/download.php
@@ -0,0 +1,68 @@
+<?php
+
+/**
+ * This file contains the PDF download for an ERP Output document
+ * It opens the native download dialog
+ */
+
+define('QUIQQER_SYSTEM', true);
+define('QUIQQER_AJAX', true);
+
+require_once dirname(__FILE__, 6).'/header.php';
+
+use QUI\ERP\Output\Output;
+use QUI\Utils\Security\Orthos;
+
+$User = QUI::getUserBySession();
+
+if (!$User->canUseBackend()) {
+    exit;
+}
+
+$Request          = QUI::getRequest();
+$entityId         = Orthos::clear($Request->query->get('id'));
+$entityType       = Orthos::clear($Request->query->get('t'));
+$template         = Orthos::clear($Request->query->get('tpl'));
+$templateProvider = Orthos::clear($Request->query->get('tplpr'));
+$quiId            = Orthos::clear($Request->query->get('oid'));
+
+$errorOutput = function ($message) use ($quiId) {
+    echo '
+    <script>
+    var parent = window.parent;
+    
+    if (typeof parent.require !== "undefined") {
+        parent.require(["qui/QUI"], function(QUI) {
+            QUI.getMessageHandler().then(function(MH) {
+                MH.addError("'.$message.'");
+            });
+            
+            var Control = QUI.Controls.getById(\''.$quiId.'\');
+            
+            if (Control) {
+                Control.Loader.hide();     
+            }
+        });
+    }
+    </script>';
+    exit;
+};
+
+try {
+    $HtmlPdfDocument = Output::getDocumentPdf(
+        $entityId,
+        $entityType,
+        null,
+        Output::getOutputTemplateProviderByPackage($templateProvider),
+        $template ?: null
+    );
+
+    $HtmlPdfDocument->download();
+} catch (\Exception $Exception) {
+    QUI\System\Log::writeException($Exception);
+
+    $message = $Exception->getMessage();
+    $message = QUI\Utils\Security\Orthos::clear($message);
+
+    $errorOutput($message);
+}
diff --git a/bin/output/backend/print.php b/bin/output/backend/print.php
new file mode 100644
index 0000000000000000000000000000000000000000..9dd77eaf7b205b8389aea028046ab11ad1207053
--- /dev/null
+++ b/bin/output/backend/print.php
@@ -0,0 +1,83 @@
+<?php
+
+define('QUIQQER_SYSTEM', true);
+define('QUIQQER_AJAX', true);
+
+require_once dirname(__FILE__, 6).'/header.php';
+
+use QUI\ERP\Output\Output;
+use QUI\Utils\Security\Orthos;
+
+$User = QUI::getUserBySession();
+
+if (!$User->canUseBackend()) {
+    exit;
+}
+
+$Request          = QUI::getRequest();
+$entityId         = Orthos::clear($Request->query->get('id'));
+$entityType       = Orthos::clear($Request->query->get('t'));
+$template         = Orthos::clear($Request->query->get('tpl'));
+$templateProvider = Orthos::clear($Request->query->get('tplpr'));
+$quiId            = Orthos::clear($Request->query->get('oid'));
+
+$streamFile = URL_OPT_DIR.'quiqqer/erp/bin/output/backend/printStream.php?';
+$streamFile .= \http_build_query([
+    'id'    => $entityId,
+    't'     => $entityType,
+    'tpl'   => $template,
+    'tplpr' => $templateProvider,
+    'oid'   => $quiId
+]);
+
+echo '
+<html>
+<head>
+    <style>
+        body, html {
+            margin: 0 !important;
+            padding: 0 !important;
+        }
+ 
+        @page {
+            margin: 0 !important;
+        }
+        
+        .container {
+            padding: 2.5em;
+            width: calc(100% - 5em);
+        }
+    </style>
+</head>
+<body>
+    <div class="container">
+        <img 
+            id="pdfDocument" 
+            src="'.$streamFile.'"  
+            style="max-width: 100%;"
+           
+        />
+    </div>
+    <script>
+        var i, len, parts;
+        
+        var search = {};
+        var sData = window.location.search.replace("?", "").split("&");
+        
+        for (i = 0, len = sData.length; i <len; i++) {
+            parts = sData[i].split("=");
+            search[parts[0]] = parts[1];            
+        }
+        
+        var invoiceId = search["invoiceId"];
+        var objectId = search["oid"];
+        var parent = window.parent;
+        
+        window.onload = function() {
+            window.print();
+            parent.QUI.Controls.getById(objectId).$onPrintFinish(invoiceId);
+        }
+    </script>
+</body>
+</html>
+<!--<script>window.print()</script>-->';
diff --git a/bin/output/backend/printStream.php b/bin/output/backend/printStream.php
new file mode 100644
index 0000000000000000000000000000000000000000..2360550adbbf8646f20232bea4137370fe48dbd9
--- /dev/null
+++ b/bin/output/backend/printStream.php
@@ -0,0 +1,51 @@
+<?php
+
+define('QUIQQER_SYSTEM', true);
+define('QUIQQER_AJAX', true);
+
+require_once dirname(__FILE__, 6).'/header.php';
+
+use QUI\ERP\Output\Output;
+use QUI\Utils\Security\Orthos;
+
+$User = QUI::getUserBySession();
+
+if (!$User->canUseBackend()) {
+    exit;
+}
+
+$Request          = QUI::getRequest();
+$entityId         = Orthos::clear($Request->query->get('id'));
+$entityType       = Orthos::clear($Request->query->get('t'));
+$template         = Orthos::clear($Request->query->get('tpl'));
+$templateProvider = Orthos::clear($Request->query->get('tplpr'));
+$quiId            = Orthos::clear($Request->query->get('oid'));
+
+try {
+    $HtmlPdfDocument = Output::getDocumentPdf(
+        $entityId,
+        $entityType,
+        null,
+        Output::getOutputTemplateProviderByPackage($templateProvider),
+        $template ?: null
+    );
+
+    $imageFile = $HtmlPdfDocument->createImage(
+        true,
+        [
+            '-flatten'  // removes background
+        ]
+    );
+} catch (\Exception $Exception) {
+    QUI\System\Log::writeException($Exception);
+    exit;
+}
+
+$Response = QUI::getGlobalResponse();
+$Response->headers->set('Content-Type', 'image/jpg');
+$Response->setContent(file_get_contents($imageFile));
+$Response->send();
+
+if (\file_exists($imageFile)) {
+    \unlink($imageFile);
+}
diff --git a/bin/output/frontend/download.php b/bin/output/frontend/download.php
new file mode 100644
index 0000000000000000000000000000000000000000..7f2d4c610393f10ddf94539ce4cad7db70111cee
--- /dev/null
+++ b/bin/output/frontend/download.php
@@ -0,0 +1,38 @@
+<?php
+
+/**
+ * This file contains the PDF download for an ERP Output document (frontend)
+ * It opens the native download dialog
+ */
+
+define('QUIQQER_SYSTEM', true);
+define('QUIQQER_AJAX', true);
+
+require_once dirname(__FILE__, 6).'/header.php';
+
+use QUI\ERP\Output\Output;
+use QUI\Utils\Security\Orthos;
+
+$User       = QUI::getUserBySession();
+$Request    = QUI::getRequest();
+$entityId   = Orthos::clear($Request->query->get('id'));
+$entityType = Orthos::clear($Request->query->get('t'));
+
+try {
+    $OutputProvider = Output::getOutputProviderByEntityType($entityType);
+
+    if (empty($OutputProvider)) {
+        exit;
+    }
+
+    if (!$OutputProvider::hasDownloadPermission($entityId, $User)) {
+        exit;
+    }
+
+    $HtmlPdfDocument = Output::getDocumentPdf($entityId, $entityType, $OutputProvider);
+    $HtmlPdfDocument->download();
+} catch (\Exception $Exception) {
+    QUI\System\Log::writeException($Exception);
+}
+
+exit;
diff --git a/composer.json b/composer.json
index 7acc68116e6e1cba14f8879d72ecf65f20ff458d..4b5c3f2627b218607b941a626775551666753eb7 100644
--- a/composer.json
+++ b/composer.json
@@ -29,7 +29,8 @@
         "quiqqer\/qui-php": "1.*|>=0.11|dev-master|dev-dev",
         "quiqqer\/smarty3": ">=1.7|dev-master|dev-dev",
         "quiqqer\/countries": ">=1.3|dev-master|dev-dev",
-        "quiqqer\/frontend-users": "1.*|dev-master|dev-dev"
+        "quiqqer\/frontend-users": "1.*|dev-master|dev-dev",
+        "quiqqer\/erp-accounting-templates": "^1|dev-master|dev-dev"
     },
     "autoload": {
         "psr-4": {
diff --git a/locale.xml b/locale.xml
index 1f42692e8d359bdf06c4914a6b23f4f4cbcd4776..88807e8e991cc08b362b21b2be34b03a4a83df43 100644
--- a/locale.xml
+++ b/locale.xml
@@ -108,9 +108,20 @@
             <de><![CDATA[Vorgangsnummer]]></de>
             <en><![CDATA[Processing-ID]]></en>
         </locale>
+        <locale name="output.default_template.suffix">
+            <de><![CDATA[(System-Standard)]]></de>
+            <en><![CDATA[(System default)]]></en>
+        </locale>
+
     </groups>
 
     <groups name="quiqqer/erp" datatype="php">
+
+        <locale name="exception.ajax.output.sendMail.error">
+            <de><![CDATA[Leider ist beim E-Mail-Verand ein unerwarteter Fehler aufgetreten. Bitte wiederholen Sie den Vorgang, prüfen Sie die Fehler-Logs oder kontaktieren Sie einen Administator.]]></de>
+            <en><![CDATA[Unfortunately, an unexpected error has occurred when sending the e-mail. Please repeat the process, check the error logs or contact an administrator.]]></en>
+        </locale>
+
         <locale name="user.settings.title">
             <de><![CDATA[Rechnungsdaten]]></de>
             <en><![CDATA[Billing Details]]></en>
@@ -155,6 +166,15 @@
             <en><![CDATA[Privacy Policy]]></en>
         </locale>
 
+        <locale name="menu.erp.output.title">
+            <de><![CDATA[Beleg-Ausgabe / PDF]]></de>
+            <en><![CDATA[Document output / PDF]]></en>
+        </locale>
+        <locale name="menu.erp.output.default_templates">
+            <de><![CDATA[Vorausgewählte Templates]]></de>
+            <en><![CDATA[Preselected templates]]></en>
+        </locale>
+
         <locale name="shop.settings.general.precision">
             <de><![CDATA[Verwendete Nachkommastellen]]></de>
             <en><![CDATA[Used decimal digits]]></en>
@@ -304,6 +324,16 @@
             We're sorry, but something went wrong. Please contact an administrator.
             ]]></en>
         </locale>
+
+        <locale name="Output.send.success">
+            <de><![CDATA[E-Mail erfolgreich versandt.]]></de>
+            <en><![CDATA[E-Mail sent successfully.]]></en>
+        </locale>
+        <locale name="exception.Output.sendPdfViaMail.missing_recipient">
+            <de><![CDATA[Das Dokument kann nicht per E-Mail versendet werden, da keine gültige Empfänger E-Mail-Adresse angegeben wurde.]]></de>
+            <en><![CDATA[The document cannot be sent by e-mail because no valid recipient e-mail address has been specified.]]></en>
+        </locale>
+
     </groups>
 
     <groups name="quiqqer/erp" datatype="js">
@@ -331,6 +361,95 @@
             <de><![CDATA[Artikelübersichtsrechnung]]></de>
             <en><![CDATA[Article overview calculation]]></en>
         </locale>
+        
+        <!-- Controls -->
+        <locale name="controls.OutputDialog.labelEntityId">
+            <de><![CDATA[Beleg-Nr.]]></de>
+            <en><![CDATA[Document no.]]></en>
+        </locale>
+        <locale name="controls.OutputDialog.labelTemplate">
+            <de><![CDATA[Vorlage (Template)]]></de>
+            <en><![CDATA[Template]]></en>
+        </locale>
+        <locale name="controls.OutputDialog.labelOutputType">
+            <de><![CDATA[Ausgabe]]></de>
+            <en><![CDATA[Output]]></en>
+        </locale>
+        <locale name="controls.OutputDialog.labelMarkAsSent">
+            <de><![CDATA[Als versandt markieren]]></de>
+            <en><![CDATA[Mark as sent]]></en>
+        </locale>
+        <locale name="controls.OutputDialog.labelOpenMailEditor">
+            <de><![CDATA[E-Mail bearbeiten]]></de>
+            <en><![CDATA[Edit e-mail]]></en>
+        </locale>
+        <locale name="controls.OutputDialog.data.output.print">
+            <de><![CDATA[Drucken]]></de>
+            <en><![CDATA[Print]]></en>
+        </locale>
+        <locale name="controls.OutputDialog.data.output.pdf">
+            <de><![CDATA[Als PDF speichern]]></de>
+            <en><![CDATA[Save as PDF]]></en>
+        </locale>
+        <locale name="controls.OutputDialog.data.output.email">
+            <de><![CDATA[Per E-Mail versenden]]></de>
+            <en><![CDATA[Send via e-mail]]></en>
+        </locale>
+        <locale name="controls.OutputDialog.data.output.print.btn">
+            <de><![CDATA[Drucken]]></de>
+            <en><![CDATA[Print]]></en>
+        </locale>
+        <locale name="controls.OutputDialog.data.output.pdf.btn">
+            <de><![CDATA[Herunterladen]]></de>
+            <en><![CDATA[Download]]></en>
+        </locale>
+        <locale name="controls.OutputDialog.data.output.email.btn">
+            <de><![CDATA[Senden]]></de>
+            <en><![CDATA[Send]]></en>
+        </locale>
+        <locale name="controls.OutputDialog.title">
+            <de><![CDATA[Beleg Druck / PDF / Versand]]></de>
+            <en><![CDATA[Document print / PDF / send]]></en>
+        </locale>
+        <locale name="controls.OutputDialog.no_templates_found">
+            <de><![CDATA[Keine Vorlagen gefunden]]></de>
+            <en><![CDATA[No templates found]]></en>
+        </locale>
+        <locale name="controls.OutputDialog.no_preview">
+            <de><![CDATA[Das Dokument ist aktuell nicht verfügbar, da keine Vorlage ausgewählt oder gefunden wurde.]]></de>
+            <en><![CDATA[The document is currently not available because no template was selected or found.]]></en>
+        </locale>
+        <locale name="controls.OutputDialog.preview_error">
+            <de><![CDATA[Die Vorschau konnte leider nicht geladen werden. Bitte prüfen Sie die Fehler-Logs oder kontaktieren Sie einen Administrator.]]></de>
+            <en><![CDATA[The preview could not loaded. Please check the error logs or contact an administrator.]]></en>
+        </locale>
+        <locale name="controls.OutputDialog.descMarkAsSent">
+            <de><![CDATA[Markiert den Beleg als versandt, sofern noch kein Versanddatum gesetzt ist.]]></de>
+            <en><![CDATA[Marks the document as sent, provided that no shipping date has been set.]]></en>
+        </locale>
+
+        <locale name="controls.OutputMailEditor.title">
+            <de><![CDATA[E-Mail für Beleg bearbeiten]]></de>
+            <en><![CDATA[Edit document e-mail]]></en>
+        </locale>
+        <locale name="controls.OutputMailEditor.labelMailSubject">
+            <de><![CDATA[Betreff]]></de>
+            <en><![CDATA[Subject]]></en>
+        </locale>
+        <locale name="controls.OutputMailEditor.labelMailContent">
+            <de><![CDATA[Inhalt (ohne Kopf- und Fußzeile)]]></de>
+            <en><![CDATA[Content (without header and footer)]]></en>
+        </locale>
+        <locale name="controls.OutputMailEditor.info" html="true">
+            <de><![CDATA[<b>Hinweis:</b> Der Beleg wird als PDF-Datei im Anhang der E-Mail mitgesendet.]]></de>
+            <en><![CDATA[<b>Hint:</b> The document is sent as a PDF file attached to the e-mail.]]></en>
+        </locale>
+        
+        <locale name="controls.settings.OutputTemplates.tpl.labelHideSystemDefault">
+            <de><![CDATA[System-Standard Template im Ausgabe-Dialog nicht zur Auswahl anbieten]]></de>
+            <en><![CDATA[Do not offer System Standard Template for selection in output dialog]]></en>
+        </locale>
+        
     </groups>
 
 </locales>
diff --git a/settings.xml b/settings.xml
index e39fce4e8cfd50caf2375ac87600120f51db4b5d..4be8a4ffa37eb305b837103cb25cee4d1feab988 100644
--- a/settings.xml
+++ b/settings.xml
@@ -29,6 +29,12 @@
                 </conf>
             </section>
 
+            <section name="output">
+                <conf name="default_templates">
+                    <type><![CDATA[string]]></type>
+                </conf>
+            </section>
+
             <section name="paymentsChangeable"/>
             <section name="timestampFormat"/>
             <section name="dateFormat"/>
@@ -153,6 +159,20 @@
                             </text>
                         </input>
                     </settings>
+
+                    <settings>
+                        <title>
+                            <locale group="quiqqer/erp" var="menu.erp.output.title"/>
+                        </title>
+
+                        <input conf="output.default_templates"
+                               data-qui="package/quiqqer/erp/bin/backend/controls/settings/OutputTemplates">
+                            <text>
+                                <locale group="quiqqer/erp" var="menu.erp.output.default_templates"/>
+                            </text>
+                        </input>
+
+                    </settings>
                 </category>
 
                 <category name="currencies" index="1">
diff --git a/src/QUI/ERP/Output/Output.php b/src/QUI/ERP/Output/Output.php
new file mode 100644
index 0000000000000000000000000000000000000000..e63a8bbbfbdb3cc94582a77da6955d97aa1f259e
--- /dev/null
+++ b/src/QUI/ERP/Output/Output.php
@@ -0,0 +1,477 @@
+<?php
+
+namespace QUI\ERP\Output;
+
+use QUI;
+use QUI\ERP\Accounting\Invoice\Settings;
+use QUI\ERP\Accounting\Invoice\Utils\Invoice as InvoiceUtils;
+
+/**
+ * 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 none found
+     */
+    public static function getOutputProviderByPackage(string $package)
+    {
+        foreach (self::getAllOutputProviders() as $outputProvider) {
+            if ($outputProvider['package'] === $package) {
+                return $outputProvider['class'];
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Get the OutputProvider for a specific entity type
+     *
+     * @param string $entityType
+     * @return OutputProviderInterface|false - OutputProvider class (static) or false if none found
+     */
+    public static function getOutputProviderByEntityType(string $entityType)
+    {
+        foreach (self::getAllOutputProviders() as $outputProvider) {
+            /** @var OutputProviderInterface $class */
+            $class = $outputProvider['class'];
+
+            if ($class::getEntityType() === $entityType) {
+                return $class;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Get HTML output for a specific document
+     *
+     * @param string|int $entityId
+     * @param string $entityType
+     * @param OutputProviderInterface $OutputProvider (optional)
+     * @param OutputTemplateProviderInterface $TemplateProvider (optional)
+     * @param string $template (optional)
+     * @param bool $preview (optional) - Get preview HTML
+     *
+     * @return string
+     *
+     * @throws QUI\Exception
+     */
+    public static function getDocumentHtml(
+        $entityId,
+        string $entityType,
+        $OutputProvider = null,
+        $TemplateProvider = null,
+        string $template = null,
+        bool $preview = false
+    ) {
+        if (empty($OutputProvider)) {
+            $OutputProvider = self::getOutputProviderByEntityType($entityType);
+        }
+
+        if (empty($OutputProvider)) {
+            throw new QUI\Exception('No output provider found for entity type "'.$entityType.'"');
+        }
+
+        if (empty($TemplateProvider)) {
+            $TemplateProvider = self::getDefaultOutputTemplateProviderForEntityType($entityType);
+        }
+
+        if (empty($TemplateProvider)) {
+            throw new QUI\Exception('No default output template provider found for entity type "'.$entityType.'"');
+        }
+
+        $OutputTemplate = new OutputTemplate(
+            $TemplateProvider,
+            $OutputProvider,
+            $entityId,
+            $entityType,
+            $template
+        );
+
+        return $OutputTemplate->getHTML($preview);
+    }
+
+    /**
+     * Get HTML output for a specific document
+     *
+     * @param string|int $entityId
+     * @param string $entityType
+     * @param OutputProviderInterface $OutputProvider (optional)
+     * @param OutputTemplateProviderInterface $TemplateProvider (optional)
+     * @param string $template (optional)
+     *
+     * @return QUI\HtmlToPdf\Document
+     *
+     * @throws QUI\Exception
+     */
+    public static function getDocumentPdf(
+        $entityId,
+        string $entityType,
+        $OutputProvider = null,
+        $TemplateProvider = null,
+        string $template = null
+    ) {
+        if (empty($OutputProvider)) {
+            $OutputProvider = self::getOutputProviderByEntityType($entityType);
+        }
+
+        if (empty($TemplateProvider)) {
+            $TemplateProvider = self::getDefaultOutputTemplateProviderForEntityType($entityType);
+        }
+
+        $OutputTemplate = new OutputTemplate(
+            $TemplateProvider,
+            $OutputProvider,
+            $entityId,
+            $entityType,
+            $template
+        );
+
+        return $OutputTemplate->getPDFDocument();
+    }
+
+    /**
+     * Get HTML output for a specific document
+     *
+     * @param string|int $entityId
+     * @param string $entityType
+     *
+     * @return string
+     */
+    public static function getDocumentPdfDownloadUrl(
+        $entityId,
+        string $entityType
+    ) {
+        $url = URL_OPT_DIR.'quiqqer/erp/bin/output/frontend/download.php?';
+        $url .= \http_build_query([
+            'id' => $entityId,
+            't'  => $entityType
+        ]);
+
+        return $url;
+    }
+
+    /**
+     * Send document as e-mail with PDF attachment
+     *
+     * @param string|int $entityId
+     * @param string $entityType
+     * @param OutputProviderInterface $OutputProvider (optional)
+     * @param OutputTemplateProviderInterface $TemplateProvider (optional)
+     * @param string $template (optional)
+     * @param string $recipientEmail (optional)
+     * @param string $mailSubject (optional)
+     * @param string $mailContent (optional)
+     *
+     * @return void
+     *
+     * @throws QUI\Exception
+     */
+    public static function sendPdfViaMail(
+        $entityId,
+        string $entityType,
+        $OutputProvider = null,
+        $TemplateProvider = null,
+        string $template = null,
+        string $recipientEmail = null,
+        string $mailSubject = null,
+        string $mailContent = null
+    ) {
+        if (empty($OutputProvider)) {
+            $OutputProvider = self::getOutputProviderByEntityType($entityType);
+        }
+
+        if (empty($TemplateProvider)) {
+            $TemplateProvider = self::getDefaultOutputTemplateProviderForEntityType($entityType);
+        }
+
+        $OutputTemplate = new OutputTemplate(
+            $TemplateProvider,
+            $OutputProvider,
+            $entityId,
+            $entityType,
+            $template
+        );
+
+        $pdfFile = $OutputTemplate->getPDFDocument()->createPDF();
+
+        if (empty($recipientEmail) || !QUI\Utils\Security\Orthos::checkMailSyntax($recipientEmail)) {
+            $recipientEmail = $OutputProvider::getEmailAddress($entityId);
+        }
+
+        if (empty($recipientEmail)) {
+            throw new QUI\ERP\Exception([
+                'quiqqer/erp',
+                'exception.Output.sendPdfViaMail.missing_recipient'
+            ]);
+        }
+
+        // mail send
+        $Mailer = new QUI\Mail\Mailer();
+
+        $Mailer->addRecipient($recipientEmail);
+        $Mailer->addAttachment($pdfFile);
+
+        if (!empty($mailSubject)) {
+            $Mailer->setSubject($mailSubject);
+        } else {
+            $Mailer->setSubject($OutputProvider::getMailSubject($entityId));
+        }
+
+        if (!empty($mailContent)) {
+            $Mailer->setBody($mailContent);
+        } else {
+            $Mailer->setBody($OutputProvider::getMailBody($entityId));
+        }
+
+        $Mailer->send();
+
+        QUI::getEvents()->fireEvent('quiqqerErpOutputSendMail', [$entityId, $entityType, $recipientEmail]);
+
+        // Delete PDF file after send
+        if (\file_exists($pdfFile)) {
+            \unlink($pdfFile);
+        }
+    }
+
+    /**
+     * Get available templates for $entityType (e.g. "Invoice", "InvoiceTemporary" etc.)
+     *
+     * @param string $package
+     * @return OutputTemplateProviderInterface|false - OutputProvider class (static) or false if
+     * @throws QUI\Exception
+     */
+    public static function getOutputTemplateProviderByPackage(string $package)
+    {
+        foreach (self::getAllOutputTemplateProviders() as $provider) {
+            if ($provider['package'] === $package) {
+                return $provider['class'];
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Get available templates for $entityType (e.g. "Invoice", "InvoiceTemporary" etc.)
+     *
+     * @param string $entityType (optional) - Restrict to templates of $entityType [default: fetch templates for all entity types]
+     * @return array
+     */
+    public static function getTemplates(string $entityType = null)
+    {
+        $templates       = [];
+        $outputProviders = [];
+
+        if (empty($entityType)) {
+            $outputProviders = \array_column(self::getAllOutputProviders(), 'class');
+        } else {
+            $OutputProvider = self::getOutputProviderByEntityType($entityType);
+
+            if ($OutputProvider) {
+                $outputProviders[] = $OutputProvider;
+            }
+        }
+
+        foreach (self::getAllOutputTemplateProviders() as $provider) {
+            /** @var OutputTemplateProviderInterface $class */
+            $class   = $provider['class'];
+            $package = $provider['package'];
+
+            /** @var OutputProviderInterface $OutputProvider */
+            foreach ($outputProviders as $OutputProvider) {
+                $entityType            = $OutputProvider::getEntityType();
+                $defaultOutputTemplate = self::getDefaultOutputTemplateForEntityType($entityType);
+
+                foreach ($class::getTemplates($entityType) as $providerTemplateId) {
+                    $templateTitle = $class::getTemplateTitle($providerTemplateId);
+
+                    if ($provider['isSystemDefault']) {
+                        $templateTitle .= ' '.QUI::getLocale()->get('quiqqer/erp', 'output.default_template.suffix');
+                    }
+
+                    $isDefault = $defaultOutputTemplate['provider'] === $provider['package'] &&
+                                 $defaultOutputTemplate['id'] === $providerTemplateId;
+
+                    $providerTemplate = [
+                        'id'              => $providerTemplateId,
+                        'title'           => $templateTitle,
+                        'provider'        => $package,
+                        'isSystemDefault' => $provider['isSystemDefault'],
+                        'isDefault'       => $isDefault,
+                        'entityType'      => $entityType,
+                        'entityTypeTitle' => $OutputProvider::getEntityTypeTitle()
+                    ];
+
+                    $templates[] = $providerTemplate;
+                }
+            }
+        }
+
+        // Sort so that system default is first
+        \usort($templates, function ($a, $b) {
+            if ($a['isSystemDefault']) {
+                return -1;
+            }
+
+            if ($b['isSystemDefault']) {
+                return 1;
+            }
+
+            return 0;
+        });
+
+        return $templates;
+    }
+
+    /**
+     * Return default template id for a specific entity type
+     *
+     * @param string $entityType
+     * @return array - Containting template ID and template provider package
+     */
+    public static function getDefaultOutputTemplateForEntityType(string $entityType)
+    {
+        $fallBackTemplate = [
+            'id'                => 'system_default',
+            'provider'          => 'quiqqer/invoice-accounting-template',
+            'hideSystemDefault' => false
+        ];
+
+        try {
+            $Conf             = QUI::getPackage('quiqqer/erp')->getConfig();
+            $defaultTemplates = $Conf->get('output', 'default_templates');
+
+            if (empty($defaultTemplates)) {
+                return $fallBackTemplate;
+            }
+
+            $defaultTemplates = \json_decode($defaultTemplates, true);
+
+            if (empty($defaultTemplates[$entityType])) {
+                return $fallBackTemplate;
+            }
+
+            return $defaultTemplates[$entityType];
+        } catch (\Exception $Exception) {
+            QUI\System\Log::writeException($Exception);
+            return $fallBackTemplate;
+        }
+    }
+
+    /**
+     * Return default Output Template provider class for a specific entity type
+     *
+     * @param string $entityType
+     * @return OutputTemplateProviderInterface|false
+     */
+    public static function getDefaultOutputTemplateProviderForEntityType(string $entityType)
+    {
+        foreach (self::getAllOutputTemplateProviders() as $provider) {
+            /** @var OutputTemplateProviderInterface $class */
+            $class             = $provider['class'];
+            $providerTemplates = $class::getTemplates($entityType);
+
+            if (!empty($providerTemplates)) {
+                return $class;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Get all available ERP Output provider classes
+     *
+     * @return array - Provider classes
+     */
+    protected static function getAllOutputProviders()
+    {
+        $packages        = QUI::getPackageManager()->getInstalled();
+        $providerClasses = [];
+
+        foreach ($packages as $installedPackage) {
+            try {
+                $Package = QUI::getPackage($installedPackage['name']);
+
+                if (!$Package->isQuiqqerPackage()) {
+                    continue;
+                }
+
+                $packageProvider = $Package->getProvider();
+
+                if (empty($packageProvider['erpOutput'])) {
+                    continue;
+                }
+
+                /** @var OutputProviderInterface $class */
+                foreach ($packageProvider['erpOutput'] as $class) {
+                    if (!\class_exists($class)) {
+                        continue;
+                    }
+
+                    $providerClasses[] = [
+                        'class'   => $class,
+                        'package' => $installedPackage['name']
+                    ];
+                }
+            } catch (QUI\Exception $Exception) {
+                QUI\System\Log::writeException($Exception);
+            }
+        }
+
+        return $providerClasses;
+    }
+
+    /**
+     * Get all available ERP Output Template provider classes
+     *
+     * @return array - Provider classes
+     */
+    protected static function getAllOutputTemplateProviders()
+    {
+        $packages        = QUI::getPackageManager()->getInstalled();
+        $providerClasses = [];
+
+        foreach ($packages as $installedPackage) {
+            try {
+                $Package = QUI::getPackage($installedPackage['name']);
+
+                if (!$Package->isQuiqqerPackage()) {
+                    continue;
+                }
+
+                $packageProvider = $Package->getProvider();
+
+                if (empty($packageProvider['erpOutputTemplate'])) {
+                    continue;
+                }
+
+                /** @var OutputTemplateProviderInterface $class */
+                foreach ($packageProvider['erpOutputTemplate'] as $class) {
+                    if (!\class_exists($class)) {
+                        continue;
+                    }
+
+                    $providerClasses[] = [
+                        'class'           => $class,
+                        'package'         => $installedPackage['name'],
+                        'isSystemDefault' => $installedPackage['name'] === 'quiqqer/invoice-accounting-template'
+                    ];
+                }
+            } catch (QUI\Exception $Exception) {
+                QUI\System\Log::writeException($Exception);
+            }
+        }
+
+        return $providerClasses;
+    }
+}
diff --git a/src/QUI/ERP/Output/OutputProviderInterface.php b/src/QUI/ERP/Output/OutputProviderInterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..5816fe82d8e4b769fe9789f741ebefcc86e2f740
--- /dev/null
+++ b/src/QUI/ERP/Output/OutputProviderInterface.php
@@ -0,0 +1,98 @@
+<?php
+
+namespace QUI\ERP\Output;
+
+use QUI\HtmlToPdf\Document;
+use QUI\Interfaces\Users\User;
+use QUI\Locale;
+
+/**
+ * Interface OutputProviderInterface
+ *
+ * Main interface for all ERP Output providers
+ */
+interface OutputProviderInterface
+{
+    /**
+     * Get output type
+     *
+     * The output type determines the type of templates/providers that are used
+     * to output documents.
+     *
+     * @return string
+     */
+    public static function getEntityType();
+
+    /**
+     * Get title for the output entity
+     *
+     * @param Locale $Locale (optional) - If ommitted use \QUI::getLocale()
+     * @return mixed
+     */
+    public static function getEntityTypeTitle(Locale $Locale = null);
+
+    /**
+     * Get the entity the output is created for
+     *
+     * @param string|int $entityId
+     * @return mixed
+     */
+    public static function getEntity($entityId);
+
+    /**
+     * Get download filename (without file extension)
+     *
+     * @param string|int $entityId
+     * @return string
+     */
+    public static function getDownloadFileName($entityId);
+
+    /**
+     * Get output Locale by entity
+     *
+     * @param string|int $entityId
+     * @return Locale
+     */
+    public static function getLocale($entityId);
+
+    /**
+     * Fill the OutputTemplate with appropriate entity data
+     *
+     * @param string|int $entityId
+     * @return array
+     */
+    public static function getTemplateData($entityId);
+
+    /**
+     * Checks if $User has permission to download the document of $entityId
+     *
+     * @param string|int $entityId
+     * @param User $User
+     * @return bool
+     */
+    public static function hasDownloadPermission($entityId, User $User);
+
+    /**
+     * 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);
+
+    /**
+     * Get e-mail subject when document is sent via mail
+     *
+     * @param string|int $entityId
+     * @return string
+     */
+    public static function getMailSubject($entityId);
+
+    /**
+     * Get e-mail body when document is sent via mail
+     *
+     * @param string|int $entityId
+     * @return string
+     */
+    public static function getMailBody($entityId);
+}
diff --git a/src/QUI/ERP/Output/OutputTemplate.php b/src/QUI/ERP/Output/OutputTemplate.php
new file mode 100644
index 0000000000000000000000000000000000000000..9e2d441f71c67ffe7059bd53dbabcdaf1666b631
--- /dev/null
+++ b/src/QUI/ERP/Output/OutputTemplate.php
@@ -0,0 +1,239 @@
+<?php
+
+namespace QUI\ERP\Output;
+
+use QUI;
+use QUI\Package\Package;
+
+use QUI\ERP\Accounting\Invoice\Exception;
+use QUI\ERP\Accounting\Invoice\Handler;
+use QUI\ERP\Accounting\Invoice\Invoice;
+use QUI\ERP\Accounting\Invoice\InvoiceTemporary;
+
+/**
+ * Class OutputTemplate
+ */
+class OutputTemplate
+{
+    /**
+     * @var OutputTemplateProviderInterface
+     */
+    protected $TemplateProvider;
+
+    /**
+     * @var OutputProviderInterface
+     */
+    protected $OutputProvider;
+
+    /**
+     * @var string
+     */
+    protected $template;
+
+    /**
+     * @var QUI\Interfaces\Template\EngineInterface
+     */
+    protected $Engine;
+
+    /**
+     * @var string
+     */
+    protected $entityType;
+
+    /**
+     * @var string|int
+     */
+    protected $entityId;
+
+    /**
+     * The entity the output is created for
+     *
+     * @var mixed
+     */
+    protected $Entity;
+
+    /**
+     * @var bool
+     */
+    protected $preview = false;
+
+    /**
+     * Template constructor.
+     *
+     * @param OutputTemplateProviderInterface $TemplateProvider - Template provider class
+     * @param OutputProviderInterface $OutputProvider - Output provider class
+     * @param string|int $entityId
+     * @param string $entityType
+     * @param string $template (optional) - Template identifier (from template provider)
+     *
+     * @throws QUI\Exception
+     */
+    public function __construct(
+        $TemplateProvider,
+        $OutputProvider,
+        $entityId,
+        string $entityType,
+        string $template = null
+    ) {
+        $this->Engine           = QUI::getTemplateManager()->getEngine();
+        $this->TemplateProvider = $TemplateProvider;
+        $this->OutputProvider   = $OutputProvider;
+
+        $templates = $this->TemplateProvider::getTemplates($entityType);
+
+        if (empty($template)) {
+            $template = $templates[0];
+        } else {
+            // Check if $template is provided by template provider
+            $templateIsProvided = false;
+
+            foreach ($templates as $providerTemplateId) {
+                if ($providerTemplateId === $template) {
+                    $templateIsProvided = true;
+                    break;
+                }
+            }
+
+            if (!$templateIsProvided) {
+                $template = $templates[0];
+            }
+        }
+
+        $this->template   = $template;
+        $this->entityType = $entityType;
+        $this->entityId   = $entityId;
+        $this->Entity     = $this->OutputProvider::getEntity($entityId);
+    }
+
+    /**
+     * Render the html
+     *
+     * @param bool $preview (optional) -
+     * @return string - HTML content
+     */
+    public function getHTML($preview = false)
+    {
+        $templateData = $this->OutputProvider::getTemplateData($this->entityId);
+        $this->Engine->assign($templateData);
+        $this->preview = $preview;
+
+        return $this->getHTMLHeader().
+               $this->getHTMLBody().
+               $this->getHTMLFooter();
+    }
+
+    /**
+     * Get PDF output
+     *
+     * @return QUI\HtmlToPdf\Document
+     * @throws QUI\Exception
+     * @throws QUI\ExceptionStack
+     */
+    public function getPDFDocument()
+    {
+        $Locale = $this->OutputProvider::getLocale($this->entityId);
+
+        $Document = new QUI\HtmlToPdf\Document([
+            'marginTop'         => 30, // dies ist variabel durch quiqqerInvoicePdfCreate
+            'filename'          => $this->OutputProvider::getDownloadFileName($this->entityId).'.pdf',
+            'marginBottom'      => 80,  // dies ist variabel durch quiqqerInvoicePdfCreate,
+            'pageNumbersPrefix' => $Locale->get('quiqqer/htmltopdf', 'footer.page.prefix')
+        ]);
+
+        QUI::getEvents()->fireEvent(
+            'quiqqerErpOutputPdfCreate',
+            [$this, $Document]
+        );
+
+        try {
+            $templateData = $this->OutputProvider::getTemplateData($this->entityId);
+            $this->Engine->assign($templateData);
+
+            $Document->setHeaderHTML($this->getHTMLHeader());
+            $Document->setContentHTML($this->getHTMLBody());
+            $Document->setFooterHTML($this->getHTMLFooter());
+        } catch (QUI\Exception $Exception) {
+            QUI\System\Log::writeException($Exception);
+        }
+
+        return $Document;
+    }
+
+    /**
+     * @return QUI\Interfaces\Template\EngineInterface
+     */
+    public function getEngine()
+    {
+        return $this->Engine;
+    }
+
+    //region Template Output Helper
+
+    /**
+     * Return the html header
+     *
+     * @return string
+     */
+    public function getHTMLHeader()
+    {
+        return $this->TemplateProvider::getHeaderHtml($this->template, $this->entityType, $this->Engine, $this->Entity);
+    }
+
+    /**
+     * Return the html body
+     *
+     * @return string
+     */
+    public function getHTMLBody()
+    {
+        $Output = new QUI\Output();
+        $Output->setSetting('use-system-image-paths', true);
+
+        return $Output->parse(
+            $this->TemplateProvider::getBodyHtml(
+                $this->template,
+                $this->entityType,
+                $this->Engine,
+                $this->Entity
+            )
+        );
+    }
+
+    /**
+     * Return the html body
+     *
+     * @return string
+     */
+    public function getHTMLFooter()
+    {
+        $footerHtml = '<div class="quiqqer-erp-output-footer">';
+
+        $footerHtml .= $this->TemplateProvider::getFooterHtml(
+            $this->template,
+            $this->entityType,
+            $this->Engine,
+            $this->Entity
+        );
+
+        $footerHtml .= '</div>';
+
+        $css = '';
+
+        if ($this->preview) {
+            $css = '<style>';
+            $css .= '
+            .quiqqer-erp-output-footer {
+                position: absolute;
+                left: 0;
+                bottom: 0;
+                width: 100%;
+            }
+        ';
+            $css .= '</style>';
+        }
+
+        return $css.$footerHtml;
+    }
+
+    //endregion
+}
diff --git a/src/QUI/ERP/Output/OutputTemplateProviderInterface.php b/src/QUI/ERP/Output/OutputTemplateProviderInterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..6a7c7d829a9caf7c246306be5ca8d2d0f08ad26e
--- /dev/null
+++ b/src/QUI/ERP/Output/OutputTemplateProviderInterface.php
@@ -0,0 +1,72 @@
+<?php
+
+namespace QUI\ERP\Output;
+
+use QUI\HtmlToPdf\Document;
+use QUI\Interfaces\Template\EngineInterface;
+use QUI\Locale;
+
+/**
+ * Interface OutputTemplateProviderInterface
+ *
+ * Main interface for all ERP Output template providers
+ */
+interface OutputTemplateProviderInterface
+{
+    /**
+     * Get all entity types the template package provides templates for
+     *
+     * @return string[]
+     */
+    public static function getEntityTypes();
+
+    /**
+     * Get all available templates for $entityType
+     *
+     * @param string $entityType
+     * @return string[]|int[] - Collection of templateIds
+     */
+    public static function getTemplates(string $entityType);
+
+    /**
+     * Get title of Template
+     *
+     * @param string|int $templateId
+     * @param Locale $Locale (optional) - If omitted use \QUI::getLocale()
+     * @return string
+     */
+    public static function getTemplateTitle($templateId, Locale $Locale = null);
+
+    /**
+     * Get HTML for document header area
+     *
+     * @param string|int $templateId
+     * @param string $entityType
+     * @param EngineInterface $Engine
+     * @param mixed $Entity - The entity the output is created for
+     * @return string|false
+     */
+    public static function getHeaderHtml($templateId, string $entityType, EngineInterface $Engine, $Entity);
+
+    /**
+     * Get HTML for document body area
+     *
+     * @param string|int $templateId
+     * @param string $entityType
+     * @param EngineInterface $Engine
+     * @param mixed $Entity - The entity the output is created for
+     * @return string|false
+     */
+    public static function getBodyHtml($templateId, string $entityType, EngineInterface $Engine, $Entity);
+
+    /**
+     * Get HTML for document footer area
+     *
+     * @param string|int $templateId
+     * @param string $entityType
+     * @param EngineInterface $Engine
+     * @param mixed $Entity - The entity the output is created for
+     * @return string|false
+     */
+    public static function getFooterHtml($templateId, string $entityType, EngineInterface $Engine, $Entity);
+}