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