/** * @module package/quiqqer/invoice/bin/backend/controls/panels/TemporaryInvoice * @author www.pcsg.de (Henning Leutz) * * Edit a Temporary Invoice and created a posted invoice */ define('package/quiqqer/invoice/bin/backend/controls/panels/TemporaryInvoice', [ 'qui/QUI', 'qui/controls/desktop/Panel', 'qui/controls/buttons/Button', 'qui/controls/buttons/ButtonMultiple', 'qui/controls/buttons/Separator', 'qui/controls/windows/Confirm', 'qui/utils/Form', 'controls/users/address/Select', 'package/quiqqer/invoice/bin/Invoices', 'package/quiqqer/invoice/bin/backend/utils/Dialogs', 'package/quiqqer/erp/bin/backend/controls/Comments', 'package/quiqqer/erp/bin/backend/controls/articles/Text', 'package/quiqqer/payments/bin/backend/Payments', 'package/quiqqer/customer/bin/backend/controls/customer/address/Window', 'package/quiqqer/customer/bin/backend/controls/customer/userFiles/Select', 'utils/Lock', 'Locale', 'Ajax', 'Mustache', 'Users', 'Editors', 'text!package/quiqqer/invoice/bin/backend/controls/panels/TemporaryInvoice.Data.html', 'text!package/quiqqer/invoice/bin/backend/controls/panels/TemporaryInvoice.Post.html', 'text!package/quiqqer/invoice/bin/backend/controls/panels/TemporaryInvoice.Missing.html', 'css!package/quiqqer/invoice/bin/backend/controls/panels/TemporaryInvoice.css' ], function (QUI, QUIPanel, QUIButton, QUIButtonMultiple, QUISeparator, QUIConfirm, QUIFormUtils, AddressSelect, Invoices, Dialogs, Comments, TextArticle, Payments, AddressWindow, CustomerFileSelect, Locker, QUILocale, QUIAjax, Mustache, Users, Editors, templateData, templatePost, templateMissing) { "use strict"; const lg = 'quiqqer/invoice'; return new Class({ Extends: QUIPanel, Type : 'package/quiqqer/invoice/bin/backend/controls/panels/TemporaryInvoice', Binds: [ 'save', 'post', 'openData', 'openArticles', 'openComments', 'openAddCommentDialog', 'openVerification', '$openCategory', '$closeCategory', '$onCreate', '$onInject', '$onKeyUp', '$onDestroy', '$onDeleteInvoice', '$onArticleReplaceClick', '$clickDelete', 'toggleSort', '$showLockMessage', 'print', 'openInvoiceFiles', '$openXmlCategory' ], options: { invoiceId : false, customer_id : false, invoice_address : false, invoice_address_id: false, project_name : '', date : '', time_for_payment : '', data : {}, articles : [] }, initialize: function (options) { this.setAttributes({ icon: 'fa fa-money' }); this.parent(options); this.$AdditionalText = null; this.$ArticleList = null; this.$ArticleListSummary = null; this.$AddProduct = null; this.$ArticleSort = null; this.$AddressDelivery = null; this.$AddSeparator = null; this.$SortSeparator = null; this.$locked = false; this.$serializedList = {}; this.addEvents({ onCreate : this.$onCreate, onInject : this.$onInject, onDestroy: this.$onDestroy }); Invoices.addEvents({ onDeleteInvoice: this.$onDeleteInvoice }); }, /** * Return the lock key * * @return {string} */ $getLockKey: function () { return 'lock-invoice-temporary-' + this.getAttribute('invoiceId'); }, /** * Return the lock group * @return {string} */ $getLockGroups: function () { return 'quiqqer/invoice'; }, /** * Panel refresh */ refresh: function () { let title = this.getAttribute('invoiceId'); if (this.getAttribute('id')) { title = this.getAttribute('id'); } title = title + ' ('; if (this.getAttribute('isbrutto')) { title = title + QUILocale.get(lg, 'brutto.panel.title'); } else { title = title + QUILocale.get(lg, 'netto.panel.title'); } title = title + ')'; this.setAttribute('title', title); this.parent(); }, /** * Refresh the invoice data */ doRefresh: function () { const self = this; let invoiceId = this.getAttribute('invoiceId'); return Invoices.getTemporaryInvoice(invoiceId).then(function (data) { self.setAttributes(data); if (data.articles.articles && data.articles.articles.length) { self.$serializedList = { articles : data.articles.articles, priceFactors: data.articles.priceFactors }; self.setAttribute('articles', data.articles.articles); self.setAttribute('priceFactors', data.articles.priceFactors); } if (data.invoice_address) { self.setAttribute('invoice_address', data.invoice_address); } self.refresh(); }); }, /** * Saves the current data * * @return {Promise} */ save: function () { if (this.$locked) { return Promise.resolve(); } this.Loader.show(); this.$unloadCategory(false); return Invoices.saveInvoice( this.getAttribute('invoiceId'), this.getCurrentData() ).then(function () { this.Loader.hide(); this.showSavedIconAnimation(); }.bind(this)).catch(function (err) { console.error(err); console.error(err.getMessage()); this.Loader.hide(); }.bind(this)); }, /** * Post the temporary invoice * * @return {Promise} */ post: function () { const self = this; this.Loader.show(); this.$unloadCategory(false); return Invoices.saveInvoice( this.getAttribute('invoiceId'), this.getCurrentData() ).then(function (Data) { return Promise.all([ Invoices.postInvoice(self.getAttribute('invoiceId')), Invoices.getSetting('temporaryInvoice', 'openPrintDialogAfterPost'), Data ]); }).then(function (result) { let newInvoiceHash = result[0], openPrintDialogAfterPost = result[1], Data = result[2]; if (!openPrintDialogAfterPost) { self.destroy(); return; } let entityType; switch (parseInt(Data.type)) { case 3: entityType = 'CreditNote'; break; case 4: entityType = 'Canceled'; break; default: entityType = 'Invoice'; } // open print dialog Dialogs.openPrintDialog(newInvoiceHash, entityType).then(function () { self.destroy(); }); }).catch(function (err) { console.error(err); console.error(err.getMessage()); this.Loader.hide(); }.bind(this)); }, /** * @returns {{customer_id, invoice_address_id, project_name, articles, date, time_for_payment}} */ getCurrentData: function () { let deliveryAddress = this.getAttribute('addressDelivery'); if (!deliveryAddress) { deliveryAddress = this.getAttribute('delivery_address'); } return { customer_id : this.getAttribute('customer_id'), invoice_address_id : this.getAttribute('invoice_address_id'), project_name : this.getAttribute('project_name'), articles : this.getAttribute('articles'), priceFactors : this.getAttribute('priceFactors'), date : this.getAttribute('date'), editor_id : this.getAttribute('editor_id'), ordered_by : this.getAttribute('ordered_by'), contact_person : this.getAttribute('contact_person'), contactEmail : this.getAttribute('contactEmail'), time_for_payment : this.getAttribute('time_for_payment'), payment_method : this.getAttribute('payment_method'), additional_invoice_text: this.getAttribute('additional_invoice_text'), currency : this.getAttribute('currency'), currencyRate : this.getAttribute('currencyRate'), addressDelivery : deliveryAddress, processing_status : this.getAttribute('processing_status'), attached_customer_files: this.getAttribute('attached_customer_files') }; }, /** * Return the current user data */ getUserData: function () { return { uid: this.getAttribute('customer_id'), aid: this.getAttribute('invoice_address_id') }; }, /** * Categories */ /** * Open the data category * * @returns {Promise} */ openData: function () { const self = this; this.renderDataDone = false; this.Loader.show(); return this.$closeCategory().then(function () { const Container = self.getContent().getElement('.container'); Container.setStyle('height', null); Container.set({ html: Mustache.render(templateData, { textInvoiceData : QUILocale.get(lg, 'erp.panel.temporary.invoice.category.data.textInvoiceData'), textInvoiceDate : QUILocale.get(lg, 'erp.panel.temporary.invoice.category.data.textInvoiceDate'), textTermOfPayment : QUILocale.get(lg, 'erp.panel.temporary.invoice.category.data.textTermOfPayment'), textProjectName : QUILocale.get(lg, 'erp.panel.temporary.invoice.category.data.textProjectName'), textOrderedBy : QUILocale.get(lg, 'erp.panel.temporary.invoice.category.data.textOrderedBy'), textEditor : QUILocale.get(lg, 'erp.panel.temporary.invoice.category.data.textEditor'), textInvoicePayment: QUILocale.get(lg, 'erp.panel.temporary.invoice.category.data.textInvoicePayment'), textPaymentMethod : QUILocale.get(lg, 'erp.panel.temporary.invoice.category.data.textPaymentMethod'), textInvoiceText : QUILocale.get(lg, 'erp.panel.temporary.invoice.category.data.textInvoiceText'), textStatus : QUILocale.get(lg, 'erp.panel.temporary.invoice.category.data.textStatus'), textContactPerson : QUILocale.get(lg, 'erp.panel.temporary.invoice.category.data.textContactPerson'), textCurrency : QUILocale.get(lg, 'erp.panel.temporary.invoice.category.data.textCurrency'), textCurrencyRate: QUILocale.get(lg, 'erp.panel.temporary.invoice.category.data.textCurrencyRate'), textInvoiceDeliveryAddress: QUILocale.get(lg, 'deliveryAddress') }) }); const Form = Container.getElement('form'); QUIFormUtils.setDataToForm(self.getAttribute('data'), Form); // set invoice date to today // quiqqer/invoice#46 let local = new Date(); local.setMinutes(local.getMinutes() - local.getTimezoneOffset()); let dateDate = local.toJSON().slice(0, 10); QUIFormUtils.setDataToForm({ date : dateDate, time_for_payment : self.getAttribute('time_for_payment'), project_name : self.getAttribute('project_name'), editor_id : self.getAttribute('editor_id'), processing_status: self.getAttribute('processing_status'), contact_person : self.getAttribute('contact_person'), currency : self.getAttribute('currency'), currencyRate : self.getAttribute('currencyRate') }, Form); Form.elements.date.set('disabled', true); Form.elements.date.set('title', QUILocale.get(lg, 'permissions.set.invoice.date')); require(['Permissions'], function (Permissions) { Permissions.hasPermission('quiqqer.invoice.changeDate').then(function (has) { if (has) { Form.elements.date.set('disabled', false); Form.elements.date.set('title', ''); } }); }); return QUI.parse(Container); }).then(function () { return new Promise(function (resolve, reject) { const Form = self.getContent().getElement('form'); require([ 'Packages', 'utils/Controls' ], function (Packages, ControlUtils) { ControlUtils.parse(Form).then(function () { return Packages.getConfig('quiqqer/currency'); }).then(resolve); }, reject); }); }).then(function (config) { const Content = self.getContent(); const quiId = Content.getElement( '[data-qui="package/quiqqer/erp/bin/backend/controls/userData/UserData"]' ).get('data-quiid'); let editorIdQUIId = Content.getElement('[name="editorId"]').get('data-quiid'); let orderedByIdQUIId = Content.getElement('[name="orderedBy"]').get('data-quiid'); let currencyIdQUIId = Content.getElement('[name="currency"]').get('data-quiid'); let CurrencyRate = Content.getElement('[name="currencyRate"]'); let Data = QUI.Controls.getById(quiId); let EditorId = QUI.Controls.getById(editorIdQUIId); let OrderedBy = QUI.Controls.getById(orderedByIdQUIId); let Currency = QUI.Controls.getById(currencyIdQUIId); if (parseInt(config.currency.differentAccountingCurrencies) === 0) { Content.getElements('table.invoice-currency').setStyle('display', 'none'); } OrderedBy.setAttribute('showAddressName', false); Data.addEvent('onChange', function () { const Customer = Data.getValue(); let userId = Customer.userId; self.setAttribute('customer_id', parseInt(userId)); self.setAttribute('invoice_address_id', Customer.addressId); self.setAttribute('contact_person', Customer.contactPerson); self.setAttribute('contactEmail', Customer.contactEmail); // reset deliver address if (self.$AddressDelivery) { self.$AddressDelivery.reset(); self.$AddressDelivery.setAttribute('userId', userId); } Promise.all([ Invoices.getPaymentTime(userId), Invoices.isNetto(userId) ]).then(function (result) { let paymentTime = result[0]; let isNetto = result[1]; Content.getElement('[name="time_for_payment"]').value = paymentTime; self.setAttribute('isbrutto', !isNetto); self.setAttribute('time_for_payment', paymentTime); self.refresh(); }); }); // currency Currency.addEvent('change', function (Instance, value) { if (self.renderDataDone === false) { return; } self.Loader.show(); self.setAttribute('currency', value); require(['package/quiqqer/currency/bin/Currency'], function (Currencies) { Currencies.getCurrency(value).then(function (data) { if ("rate" in data) { self.setAttribute('currencyRate', data.rate); CurrencyRate.value = data.rate; } self.Loader.hide(); }).catch(function (err) { console.error(err); self.Loader.hide(); }); }); }); // editor EditorId.addEvent('onChange', function () { self.setAttribute('editor_id', EditorId.getValue()); }); if (typeof window.QUIQQER_EMPLOYEE_GROUP !== 'undefined') { EditorId.setAttribute('search', true); EditorId.setAttribute('searchSettings', { filter: { filter_group: window.QUIQQER_EMPLOYEE_GROUP } }); } if (parseInt(self.getAttribute('editor_id'))) { EditorId.addItem(self.getAttribute('editor_id')); } else { EditorId.addItem(USER.id); } // ordered by OrderedBy.addEvent('onChange', function () { self.setAttribute('ordered_by', OrderedBy.getValue()); }); if (typeof window.QUIQQER_CUSTOMER_GROUP !== 'undefined') { OrderedBy.setAttribute('search', true); OrderedBy.setAttribute('searchSettings', { filter: { filter_group: window.QUIQQER_CUSTOMER_GROUP } }); } if (parseInt(self.getAttribute('ordered_by'))) { OrderedBy.addItem(parseInt(self.getAttribute('ordered_by'))); } // invoice address let address = self.getAttribute('invoice_address'); if (!address) { address = {}; } address.userId = self.getAttribute('customer_id'); address.addressId = self.getAttribute('invoice_address_id'); address.contactPerson = self.getAttribute('contact_person') ? self.getAttribute('contact_person') : ''; address.contactEmail = self.getAttribute('contactEmail') ? self.getAttribute('contactEmail') : ''; if (self.getAttribute('contactEmail')) { address.contactEmail = self.getAttribute('contactEmail'); } return Data.setValue(address); }).then(function () { // delivery address self.$AddressDelivery = QUI.Controls.getById( self.getContent().getElement( '[data-qui="package/quiqqer/erp/bin/backend/controls/DeliveryAddress"]' ).get('data-quiid') ); let deliveryAddress = self.getAttribute('addressDelivery'); if (!deliveryAddress) { deliveryAddress = self.getAttribute('delivery_address'); if (deliveryAddress) { deliveryAddress = JSON.decode(deliveryAddress); } } if (deliveryAddress) { self.$AddressDelivery.setAttribute('userId', self.getAttribute('customer_id')); self.$AddressDelivery.setValue(deliveryAddress); } }).then(function () { let Container = self.getContent().getElement('.container'); new QUIButton({ text : QUILocale.get(lg, 'erp.panel.temporary.invoice.button.nextToArticles') + ' <span class="fa fa-angle-right"></span>', styles: { display: 'block', 'float': 'right', margin : '0 0 20px' }, events: { onClick: function () { self.openArticles().catch(function (e) { console.error(e); }); } } }).inject(Container); }).then(function () { return Payments.getPayments(); }).then(function (payments) { // load payments let Payments = self.getContent().getElement('[name="payment_method"]'); new Element('option', { html : '', value: '' }).inject(Payments); let i, len, title; let current = QUILocale.getCurrent(); for (i = 0, len = payments.length; i < len; i++) { title = payments[i].title; if (typeOf(title) === 'object' && typeof title[current] !== 'undefined') { title = title[current]; } new Element('option', { html : title, value: payments[i].id }).inject(Payments); } Payments.value = self.getAttribute('payment_method'); }).then(function () { // additional-invoice-text -> wysiwyg return self.$loadAdditionalInvoiceText(); }).then(function () { self.getCategory('data').setActive(); return self.Loader.hide(); }).then(function () { return self.$openCategory(); }).then(function () { self.renderDataDone = true; }); }, /** * Open the product category * * @returns {Promise} */ openArticles: function () { const self = this; this.Loader.show(); return self.$closeCategory().then(function (Container) { return new Promise(function (resolve) { require([ 'package/quiqqer/erp/bin/backend/controls/articles/ArticleList', 'package/quiqqer/erp/bin/backend/controls/articles/ArticleSummary' ], function (List, Summary) { self.$ArticleList = new List({ nettoinput: !self.getAttribute('isbrutto'), currency : self.getAttribute('currency'), events : { onArticleReplaceClick: self.$onArticleReplaceClick }, styles : { height: 'calc(100% - 110px)' } }).inject(Container); Container.setStyle('height', '100%'); self.$ArticleListSummary = new Summary({ currency: self.getAttribute('currency'), List : self.$ArticleList, styles : { bottom : -20, left : 0, opacity : 0, position: 'absolute' } }).inject(Container.getParent()); moofx(self.$ArticleListSummary.getElm()).animate({ bottom : 0, opacity: 1 }); self.$ArticleList.setUser(self.getUserData()); if (self.$serializedList) { self.$ArticleList.unserialize(self.$serializedList); } self.$AddProduct.show(); self.$AddSeparator.show(); self.$SortSeparator.show(); self.$ArticleSort.show(); self.getCategory('articles').setActive(); new QUIButton({ text : '<span class="fa fa-angle-left"></span> ' + QUILocale.get(lg, 'erp.panel.temporary.invoice.button.data'), styles: { 'float': 'left', margin : '20px 0 0' }, events: { onClick: self.openData } }).inject(Container); new QUIButton({ text : QUILocale.get(lg, 'erp.panel.temporary.invoice.category.review.btnGoto') + ' <span class="fa fa-angle-right"></span>', styles: { 'float': 'right', margin : '20px 0 0' }, events: { onClick: self.openVerification } }).inject(Container); self.Loader.hide().then(resolve); }); }); }).then(function () { return self.$openCategory(); }); }, /** * open the comments * * @return {Promise<Promise>} */ openComments: function () { const self = this; this.Loader.show(); this.getCategory('comments').setActive(); return this.$closeCategory().then(function () { self.refreshComments(); }).then(function () { return self.$openCategory(); }).then(function () { self.Loader.hide(); }); }, /** * Refresh the comment display */ refreshComments: function () { const Container = this.getContent().getElement('.container'); Container.set('html', ''); new QUIButton({ textimage: 'fa fa-comments', text : QUILocale.get(lg, 'invoice.panel.comment.add'), styles : { 'float' : 'right', marginBottom: 10 }, events : { onClick: this.openAddCommentDialog } }).inject(Container); new Comments({ comments: this.getAttribute('comments') }).inject(Container); }, /** * Open invoice file management * * @returns {Promise} */ openInvoiceFiles: function () { this.Loader.show(); this.getCategory('invoiceFiles').setActive(); return this.$closeCategory().then((Container) => { Container.setStyle('overflow', 'hidden'); Container.setStyle('padding', 20); Container.setStyle('height', '100%'); const customerId = this.getAttribute('customer_id'); if (!customerId) { new Element('p', { html: QUILocale.get(lg, 'controls.panels.TemporaryInvoice.invoice_files_no_customer') }).inject(Container); return; } const FileControl = new CustomerFileSelect({ userId : this.getAttribute('customer_id'), confirmItemDelete: true, events : { onChange: (FileSelectControl) => { FileSelectControl.getFiles().then((customerFiles) => { this.setAttribute('attached_customer_files', customerFiles); }); } } }).inject(Container); FileControl.getElm().addClass('quiqqer-invoice-files'); FileControl.importValue(this.getAttribute('attached_customer_files')); }).then(() => { return this.$openCategory(); }).catch((err) => { console.error('ERROR'); console.error(err); return this.$openCategory(); }); }, /** * Open the verification category * * @returns {Promise} */ openVerification: function () { const self = this; let ParentContainer = null, FrameContainer = null; this.Loader.show(); return this.$closeCategory().then((Container) => { FrameContainer = new Element('div', { 'class': 'quiqqer-invoice-backend-temporaryInvoice-previewContainer' }).inject(Container); Container.setStyle('overflow', 'hidden'); Container.setStyle('padding', 0); Container.setStyle('height', '100%'); ParentContainer = Container; return Invoices.getTemporaryInvoicePreview( self.getAttribute('hash'), self.getCurrentData() ).then(function (html) { return new Promise(function (resolve) { require(['qui/controls/elements/Sandbox'], function (Sandbox) { new Sandbox({ content: html, styles : { height : '100%', padding: 20, width : '95%' }, events : { onLoad: function (Box) { Box.getElm().addClass('quiqqer-invoice-backend-temporaryInvoice-preview'); } } }).inject(FrameContainer); resolve(); }); }); }); }).then(function () { // check invoice date const Now = new Date(); Now.setHours(0, 0, 0, 0); let InvoiceDate = new Date(self.getAttribute('date')); if (InvoiceDate < Now) { new QUIConfirm({ title : QUILocale.get(lg, 'window.invoice.date.past.title'), text : QUILocale.get(lg, 'window.invoice.date.past.title'), information : QUILocale.get(lg, 'window.invoice.date.past.content'), icon : 'fa fa-clock-o', texticon : 'fa fa-clock-o', maxHeight : 400, maxWidth : 600, autoclose : false, cancel_button: { text : QUILocale.get(lg, 'window.invoice.date.past.cancel.text'), textimage: 'fa fa-close' }, ok_button : { text : QUILocale.get(lg, 'window.invoice.date.past.ok.text'), textimage: 'fa fa-check' }, events : { onSubmit: function (Win) { Win.Loader.show(); let Today = new Date(); let today = Today.toISOString().split('T')[0]; self.setAttribute('date', today + ' 00:00:00'); self.save().then(function () { self.openVerification(); Win.close(); }); } } }).open(); } }).then(function () { return Invoices.getMissingAttributes(self.getAttribute('invoiceId')); }).then(function (missing) { const Missing = new Element('div', { 'class': 'quiqqer-invoice-backend-temporaryInvoice-missing', styles : { opacity: 0, bottom : -20 } }).inject(ParentContainer); if (Object.getLength(missing)) { Missing.set('html', Mustache.render(templateMissing, { message: QUILocale.get(lg, 'message.invoice.missing') })); const Info = new Element('info', { 'class': 'quiqqer-invoice-backend-temporaryInvoice-missing-miss-message', styles : { display: 'none', opacity: 0 } }).inject(ParentContainer); Missing.getElement( '.quiqqer-invoice-backend-temporaryInvoice-missing-miss-button' ).addEvent('click', function () { let isShow = parseInt(Info.getStyle('opacity')); if (isShow) { moofx(Info).animate({ bottom : 60, opacity: 0 }, { callback: function () { Info.setStyle('display', 'none'); } }); } else { Info.setStyle('display', null); moofx(Info).animate({ bottom : 80, opacity: 1 }); } }); for (let missed in missing) { if (!missing.hasOwnProperty(missed)) { continue; } new Element('div', { 'class': 'messages-message message-error', html : missing[missed] }).inject(Info); } Missing.getElement( '.quiqqer-invoice-backend-temporaryInvoice-missing-miss-button' ).click(); } else { // post available Missing.set('html', Mustache.render(templatePost, { message: QUILocale.get(lg, 'message.invoice.ok') })); new QUIButton({ text : QUILocale.get(lg, 'journal.btn.post'), 'class' : 'btn-green', events : { onClick: self.post }, disabled: self.$locked }).inject( Missing.getElement('.quiqqer-invoice-backend-temporaryInvoice-missing-button') ); } self.getCategory('verification').setActive(); self.Loader.hide().then(function () { return new Promise(function (resolve) { moofx(Missing).animate({ opacity: 1, bottom : 0 }, { callback: function () { self.Loader.hide().then(resolve); } }); }); }); }).then(function () { return self.$openCategory(); }).catch(function (err) { console.error('ERROR'); console.error(err); return self.$openCategory(); }); }, /** * Opens the product search * * @todo only if products are installed */ openProductSearch: function () { const self = this; this.$AddProduct.setAttribute('textimage', 'fa fa-spinner fa-spin'); return new Promise(function (resolve) { require([ 'package/quiqqer/erp/bin/backend/controls/articles/product/AddProductWindow', 'package/quiqqer/invoice/bin/backend/controls/articles/Article' ], function (Win, Article) { let productDescriptionSource = false; new Win({ user : self.getUserData(), fields: false, events: { onLoad: function (Instance, ProductSearch) { ProductSearch.Loader.show(); require(['package/quiqqer/invoice/bin/Invoices'], function (Invoices) { Invoices.getSetting('invoice', 'productDescriptionSource').then(function (src) { if (parseInt(src)) { productDescriptionSource = parseInt(src); Instance.setAttribute('fields', [productDescriptionSource]); } ProductSearch.Loader.hide(); }); }); }, onSubmit: function (Win, article) { const Instance = new Article(article); if ("calculated_vatArray" in article) { Instance.setVat(article.calculated_vatArray.vat); } if (productDescriptionSource && typeof article.fields !== 'undefined' && typeof article.fields[productDescriptionSource] !== 'undefined') { let field = article.fields[productDescriptionSource]; let current = QUILocale.getCurrent(); if (field && typeof field[current] !== 'undefined') { Instance.setAttribute('description', field[current]); } else if (field) { Instance.setAttribute('description', field); } } self.$ArticleList.addArticle(Instance); resolve(Instance); } } }).open(); self.$AddProduct.setAttribute('textimage', 'fa fa-plus'); }); }); }, /** * * @return {Promise} */ $loadAdditionalInvoiceText: function () { const self = this; return new Promise(function (resolve) { const EditorParent = new Element('div').inject( self.getContent().getElement('.additional-invoice-text') ); Editors.getEditor(null).then(function (Editor) { self.$AdditionalText = Editor; // minimal toolbar self.$AdditionalText.setAttribute('buttons', { lines: [ [ [ { type : "button", button: "Bold" }, { type : "button", button: "Italic" }, { type : "button", button: "Underline" }, { type: "separator" }, { type : "button", button: "RemoveFormat" }, { type: "separator" }, { type : "button", button: "NumberedList" }, { type : "button", button: "BulletedList" } ] ] ] }); self.$AdditionalText.addEvent('onLoaded', function () { self.$AdditionalText.switchToWYSIWYG(); self.$AdditionalText.showToolbar(); self.$AdditionalText.setContent(self.getAttribute('additional_invoice_text')); resolve(); }); self.$AdditionalText.inject(EditorParent); self.$AdditionalText.setHeight(200); }); }); }, /** * Close the current category and save it * * @returns {Promise} */ $closeCategory: function () { const self = this; if (self.$AddProduct) { self.$AddProduct.hide(); self.$AddSeparator.hide(); self.$SortSeparator.hide(); self.$ArticleSort.hide(); } if (self.$ArticleListSummary) { moofx(self.$ArticleListSummary.getElm()).animate({ bottom : -20, opacity: 0 }, { duration: 250, callback: function () { self.$ArticleListSummary.destroy(); self.$ArticleListSummary = null; } }); } self.getContent().setStyle('padding', 0); return new Promise(function (resolve) { let Container = self.getContent().getElement('.container'); if (!Container) { Container = new Element('div', { 'class': 'container', styles : { opacity : 0, position: 'relative', top : -50 } }).inject(self.getContent()); } moofx(Container).animate({ opacity: 0, top : -50 }, { duration: 200, callback: function () { self.$unloadCategory(); if (self.$AddressDelivery) { self.$AddressDelivery.destroy(); self.$AddressDelivery = null; } Container.set('html', ''); Container.setStyle('padding', 20); self.save().then(function () { resolve(Container); }).catch(function () { resolve(Container); }); } }); }); }, /** * Open the current category * * @returns {Promise} */ $openCategory: function () { const self = this; return new Promise(function (resolve) { const Container = self.getContent().getElement('.container'); if (!Container) { resolve(); return; } moofx(Container).animate({ opacity: 1, top : 0 }, { duration: 200, callback: resolve }); }); }, /** * Unload the category and reserve the data * * @param {Boolean} [destroyList] - destroy the article list, default = true */ $unloadCategory: function (destroyList) { const Container = this.getContent().getElement('.container'); destroyList = typeof destroyList === 'undefined' ? true : destroyList; if (this.$ArticleList) { this.setAttribute('articles', this.$ArticleList.save()); this.setAttribute('priceFactors', this.$ArticleList.getPriceFactors()); this.$serializedList = this.$ArticleList.serialize(); if (destroyList) { this.$ArticleList.destroy(); this.$ArticleList = null; } } if (this.$AddressDelivery) { this.setAttribute('addressDelivery', this.$AddressDelivery.getValue()); this.setAttribute('delivery_address', this.$AddressDelivery.getValue()); } if (this.$AdditionalText) { this.setAttribute( 'additional_invoice_text', this.$AdditionalText.getContent() ); } const Form = Container.getElement('form'); if (!Form) { return; } let formData = QUIFormUtils.getFormData(Form); let data = this.getAttribute('data') || {}; // timefields if ("date" in formData) { this.setAttribute('date', formData.date + ' 00:00:00'); } [ 'processing_status', 'time_for_payment', 'project_name', 'payment_method', 'editor_id', 'ordered_by', 'currencyRate', 'currency' ].each(function (entry) { if (!formData.hasOwnProperty(entry)) { return; } if (entry === 'time_for_payment') { formData[entry] = parseInt(formData[entry]); } this.setAttribute(entry, formData[entry]); delete formData[entry]; }.bind(this)); this.setAttribute('data', Object.merge(data, formData)); }, /** * Event Handling */ /** * event: on create */ $onCreate: function () { const self = this; this.$AddProduct = new QUIButtonMultiple({ textimage: 'fa fa-plus', text : QUILocale.get(lg, 'erp.panel.temporary.invoice.buttonAdd'), events : { onClick: function () { if (self.$ArticleList) { self.openProductSearch(); } } } }); this.$AddProduct.hide(); this.$AddProduct.appendChild({ text : QUILocale.get(lg, 'erp.panel.temporary.invoice.buttonAdd.custom'), events: { onClick: function () { if (self.$ArticleList) { self.$ArticleList.insertNewProduct(); } } } }); this.$AddProduct.appendChild({ text : QUILocale.get(lg, 'erp.panel.temporary.invoice.buttonAdd.text'), events: { onClick: function () { if (self.$ArticleList) { self.$ArticleList.addArticle(new TextArticle()); } } } }); this.$AddSeparator = new QUISeparator(); this.$SortSeparator = new QUISeparator(); // buttons this.addButton({ name : 'save', text : QUILocale.get('quiqqer/system', 'save'), textimage: 'fa fa-save', events : { onClick: function () { //quiqqer/invoice', 'message.invoice.save.successfully' self.save().then(function () { QUI.getMessageHandler().then(function (MH) { MH.addSuccess( QUILocale.get('quiqqer/invoice', 'message.invoice.save.successfully') ); }); }); } } }); this.$ArticleSort = new QUIButton({ name : 'sort', textimage: 'fa fa-sort', text : QUILocale.get(lg, 'erp.panel.temporary.invoice.button.article.sort.text'), events : { onClick: this.toggleSort } }); this.$ArticleSort.hide(); this.addButton(this.$AddSeparator); this.addButton(this.$AddProduct); this.addButton(this.$SortSeparator); this.addButton(this.$ArticleSort); this.addButton({ name : 'lock', icon : 'fa fa-warning', styles: { background: '#fcf3cf', color : '#7d6608', 'float' : 'right' }, events: { onClick: this.$showLockMessage } }); this.getButtons('lock').hide(); this.addButton({ name : 'delete', icon : 'fa fa-trash', title : QUILocale.get(lg, 'erp.panel.temporary.invoice.deleteButton.title'), styles: { 'float': 'right' }, events: { onClick: this.$clickDelete } }); this.addButton({ name : 'output', textimage: 'fa fa-print', text : QUILocale.get(lg, 'journal.btn.pdf'), styles : { 'float': 'right' }, events : { onClick: this.print } }); // categories this.addCategory({ name : 'data', icon : 'fa fa-info', text : QUILocale.get(lg, 'erp.panel.temporary.invoice.category.data'), events: { onClick: this.openData } }); this.addCategory({ name : 'articles', icon : 'fa fa-list', text : QUILocale.get(lg, 'erp.panel.temporary.invoice.category.pos'), events: { onClick: this.openArticles } }); this.addCategory({ name : 'comments', icon : 'fa fa-comments', text : QUILocale.get(lg, 'erp.panel.temporary.invoice.category.comments'), events: { onClick: this.openComments } }); this.addCategory({ name : 'invoiceFiles', icon : 'fa fa-file-text-o', text : QUILocale.get(lg, 'erp.panel.temporary.invoice.category.invoiceFiles'), events: { onClick: this.openInvoiceFiles } }); this.addCategory({ name : 'verification', icon : 'fa fa-check', text : QUILocale.get(lg, 'erp.panel.temporary.invoice.category.review'), events: { onClick: this.openVerification } }); // invoice.xml panel api QUIAjax.get('package_quiqqer_invoice_ajax_invoices_panel_getCategories', (categories) => { let cat, title; if (typeOf(categories) === 'array' && !categories.length) { return; } for (let category in categories) { if (!categories.hasOwnProperty(category)) { continue; } cat = categories[category]; title = cat.title; this.addCategory({ icon : cat.icon, name : cat.name, title : QUILocale.get(title[0], title[1]), text : QUILocale.get(title[0], title[1]), events: { onClick: this.$openXmlCategory } }); } }, { 'package': 'quiqqer/invoice' }); }, /** * event: on inject */ $onInject: function () { this.Loader.show(); if (!this.getAttribute('invoiceId')) { this.destroy(); return; } document.addEvent('keyup', this.$onKeyUp); const self = this; let invoiceId = this.getAttribute('invoiceId'); Locker.isLocked( this.$getLockKey(), this.$getLockGroups() ).then(function (isLocked) { if (isLocked) { self.$locked = isLocked; self.lockPanel(); return; } return Locker.lock( self.$getLockKey(), self.$getLockGroups() ); }).then(function () { return self.doRefresh(); }).then(function () { return Invoices.getMissingAttributes(invoiceId); }).then(function (missing) { if (Object.getLength(missing)) { self.getCategoryBar().firstChild().click(); return; } self.getCategoryBar().getChildren('verification').click(); }).catch(function (Exception) { QUI.getMessageHandler().then(function (MH) { console.error(Exception); if (typeof Exception.getMessage === 'function') { MH.addError(Exception.getMessage()); } }); self.destroy(); }); }, /** * event: on panel destroy */ $onDestroy: function () { Invoices.removeEvents({ onDeleteInvoice: this.$onDeleteInvoice }); document.removeEvent('keyup', this.$onKeyUp); Locker.unlock( this.$getLockKey(), this.$getLockGroups() ); }, /** * lock the complete panel */ lockPanel: function () { this.getButtons('save').disable(); this.getButtons('delete').disable(); this.getButtons('lock').show(); }, /** * unlock the lock * * @return {Promise<T>} */ unlockPanel: function () { const self = this; this.Loader.show(); return Locker.unlock( this.$getLockKey(), this.$getLockGroups() ).then(function () { return Locker.isLocked( self.$getLockKey(), self.$getLockGroups() ); }).then(function (isLocked) { if (isLocked) { return; } self.$locked = isLocked; self.getButtons('lock').hide(); return self.refresh(); }).then(function () { return self.openData(); }); }, /** * show the lock message window */ $showLockMessage: function () { const self = this; let btnText = QUILocale.get('quiqqer/quiqqer', 'submit'); if (window.USER.isSU) { btnText = QUILocale.get(lg, 'button.unlock.invoice.is.locked'); } new QUIConfirm({ title : QUILocale.get(lg, 'window.unlock.invoice.title'), icon : 'fa fa-warning', texticon : 'fa fa-warning', text : QUILocale.get(lg, 'window.unlock.invoice.text', this.$locked), information: QUILocale.get(lg, 'message.invoice.is.locked', this.$locked), autoclose : false, maxHeight : 400, maxWidth : 600, ok_button : { text: btnText }, events: { onSubmit: function (Win) { if (!window.USER.isSU) { Win.close(); return; } Win.Loader.show(); self.unlockPanel().then(function () { Win.close(); }); } } }).open(); }, /** * event: on key up * * @param event */ $onKeyUp: function (event) { if (this.$ArticleList && (event.event.code === 'NumpadAdd' || event.code === 107)) { this.$AddProduct.click(); } }, /** * opens the delete dialog */ $clickDelete: function () { const self = this; const invoiceId = this.getAttribute('invoiceId'); new QUIConfirm({ title : QUILocale.get(lg, 'dialog.ti.delete.title'), text : QUILocale.get(lg, 'dialog.ti.delete.text'), information: QUILocale.get(lg, 'dialog.ti.delete.information', { invoices: '<ul><li>' + invoiceId + '</li></ul>' }), icon : 'fa fa-trash', texticon : 'fa fa-trash', maxHeight : 400, maxWidth : 600, autoclose : false, ok_button : { text : QUILocale.get('quiqqer/system', 'delete'), textimage: 'fa fa-trash' }, events : { onSubmit: function (Win) { Win.Loader.show(); Invoices.deleteInvoice(self.getAttribute('invoiceId')).then(function () { Win.close(); }).then(function () { Win.Loader.show(); }); } } }).open(); }, /** * event : on invoice deletion */ $onDeleteInvoice: function (Invoices, invoiceId) { if (invoiceId === this.getAttribute('invoiceId')) { this.destroy(); } }, /** * * @param List * @param Article */ $onArticleReplaceClick: function (List, Article) { const self = this; const replaceArticle = function (NewArticle) { List.replaceArticle( NewArticle, Article.getAttribute('position') ); NewArticle.select(); }; new QUIConfirm({ title : QUILocale.get(lg, 'erp.panel.temporary.invoice.replace.article.title'), maxHeight: 400, maxWidth : 600, icon : 'fa fa-retweet', events : { onOpen: function (Win) { Win.getContent().setStyles({ textAlign: 'center' }); Win.getContent().set( 'html', QUILocale.get(lg, 'erp.panel.temporary.invoice.replace.article.text') ); const Select = new Element('select', { styles: { margin: '20px auto 0' } }).inject(Win.getContent()); new Element('option', { html : QUILocale.get(lg, 'erp.panel.temporary.invoice.replace.article.withProduct'), value: 'product' }).inject(Select); new Element('option', { html : QUILocale.get(lg, 'erp.panel.temporary.invoice.buttonAdd.custom'), value: 'custom' }).inject(Select); new Element('option', { html : QUILocale.get(lg, 'erp.panel.temporary.invoice.buttonAdd.text'), value: 'text' }).inject(Select); }, onSubmit: function (Win) { const Select = Win.getContent().getElement('select'); if (Select.value === 'product') { self.openProductSearch().then(replaceArticle); return; } if (Select.value === 'text') { replaceArticle(new TextArticle()); return; } require([ 'package/quiqqer/invoice/bin/backend/controls/articles/Article' ], function (Article) { replaceArticle(new Article()); }); } } }).open(); }, /** * Toggle the article sorting */ toggleSort: function () { this.$ArticleList.toggleSorting(); if (this.$ArticleList.isSortingEnabled()) { this.$ArticleSort.setActive(); return; } this.$ArticleSort.setNormal(); }, //region comments /** * Open the add dialog window */ openAddCommentDialog: function () { const self = this; new QUIConfirm({ title : QUILocale.get(lg, 'dialog.add.comment.title'), icon : 'fa fa-edit', maxHeight: 600, maxWidth : 800, events : { onOpen: function (Win) { Win.getContent().set('html', ''); Win.Loader.show(); require([ 'Editors' ], function (Editors) { Editors.getEditor(null).then(function (Editor) { Win.$Editor = Editor; Win.$Editor.addEvent('onLoaded', function () { Win.$Editor.switchToWYSIWYG(); Win.$Editor.showToolbar(); Win.$Editor.setContent(self.getAttribute('content')); Win.Loader.hide(); }); Win.$Editor.inject(Win.getContent()); Win.$Editor.setHeight(200); }); }); }, onSubmit: function (Win) { Win.Loader.show(); self.addComment(Win.$Editor.getContent()).then(function () { return self.doRefresh(); }).then(function () { Win.$Editor.destroy(); Win.close(); self.refreshComments(); }); } } }).open(); }, /** * add a comment to the order * * @param {String} message */ addComment: function (message) { return Invoices.addComment(this.getAttribute('invoiceId'), message); }, //endregion /** * set the contact person by an address data object to the contact person input field * * @param address */ $setContactPersonByAddress: function (address) { const Content = this.getContent(), PersonInput = Content.getElement('[name="contact_person"]'); if (!PersonInput) { return; } let value = (address.salutation + ' ' + address.firstname + ' ' + address.lastname).trim(); PersonInput.set('value', value); }, /** * Opens the print dialog * * @return {Promise} */ print: function () { const self = this; let type = self.getAttribute('type'), entityType; switch (parseInt(type)) { case 3: entityType = 'CreditNote'; break; case 4: entityType = 'Canceled'; break; default: entityType = 'Invoice'; } return new Promise(function (resolve) { require([ 'package/quiqqer/invoice/bin/backend/utils/Dialogs' ], function (Dialogs) { Dialogs.openPrintDialog(self.getAttribute('hash'), entityType).then(resolve); }); }); }, //region category stuff $openXmlCategory: function (Category) { this.Loader.show(); QUIAjax.get('package_quiqqer_invoice_ajax_invoices_panel_getCategory', (html) => { this.$closeCategory().then((Container) => { Container.set('html', html); return QUI.parse(Container); }).then(() => { return this.$openCategory(); }).then(() => { this.Loader.hide(); }); }, { 'package': 'quiqqer/invoice', category : Category.getAttribute('name') }); } //endregion }); });