diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000000000000000000000000000000000000..9ab59b180fb670006ac910a2bc4524a1376dcea0 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,15 @@ + +# Ignore developer files when exporting +.gitattributes export-ignore +.gitignore export-ignore +.gitlab-ci.yml export-ignore +.phive export-ignore +captainhook.json export-ignore +phpcs.xml.dist export-ignore +phpstan-baseline.neon export-ignore +phpstan.dist.neon export-ignore +phpunit.dist.xml export-ignore +tests export-ignore + +# Explicitly set file type and line endings for PHP files, improves git diff output +*.php text eol=lf diff=php \ No newline at end of file diff --git a/.gitignore b/.gitignore index f4da20891502a3992abf2981d8f6c46e33868ab6..e25391c1f85364060ac958b37e8e35c6c51beafe 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,11 @@ tools/ phpstan.neon .phpunit.result.cache -phpunit.xml \ No newline at end of file +phpunit.xml +tools/ + +phpstan.neon + +.phpunit.result.cache + +phpunit.xml diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index b8d20acb6c365ad8036a7b36d47dabd876d08efa..b5a64b401e554341447c74d7cf93a89ac95a3fdb 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,3 +1,17 @@ include: - - project: 'quiqqer/stabilization/semantic-release' - file: '/ci-templates/.gitlab-ci.yml' + - component: dev.quiqqer.com/quiqqer/stabilization/ci-cd-components/quiqqer-package-bundle/quiqqer-package-bundle@main + +# Remove the entire phpunit-php8.1 block, to allow PHPUnit to run on PHP 8.1 in your pipeline +phpunit-php8.1: + rules: + - when: never + +# Remove the entire phpunit-php8.2 block, to allow PHPUnit to run on PHP 8.2 in your pipeline +phpunit-php8.2: + rules: + - when: never + +# Remove the entire phpunit-php8.3 block, to allow PHPUnit to run on PHP 8.3 in your pipeline +phpunit-php8.3: + rules: + - when: never \ No newline at end of file diff --git a/.phive/phars.xml b/.phive/phars.xml index a1315a09b4adad780a9c5e52f74835c708c5c7d5..5bfa092bfad10dad9d23240281a5a2041acb815b 100644 --- a/.phive/phars.xml +++ b/.phive/phars.xml @@ -1,4 +1,8 @@ <?xml version="1.0" encoding="UTF-8"?> <phive xmlns="https://phar.io/phive"> - <phar name="phpstan" version="^1.10.67" installed="1.10.67" location="./tools/phpstan" copy="false"/> + <phar name="phpstan" version="1.11.8" installed="1.11.8" location="./tools/phpstan" copy="false"/> + <phar name="phpunit" version="^10.5.20" installed="10.5.20" location="./tools/phpunit" copy="false"/> + <phar name="phpcs" version="^3.10.1" installed="3.10.1" location="./tools/phpcs" copy="false"/> + <phar name="phpcbf" version="^3.10.1" installed="3.10.1" location="./tools/phpcbf" copy="false"/> + <phar name="captainhook" version="^5.23.3" installed="5.23.3" location="./tools/captainhook" copy="false"/> </phive> diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000000000000000000000000000000000000..4a69a59b440e5beec561eca1e341509bd5a18688 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,3 @@ +# Contributing + +This package follows the [QUIQQER contribution guidelines](https://dev.quiqqer.com/quiqqer/stabilization/documentation/-/wikis/home). \ No newline at end of file diff --git a/ajax/backend/OpenItemsList/getUserOpenItems.php b/ajax/backend/OpenItemsList/getUserOpenItems.php index 28bfbaf8eb2a808ebe2baf18221600bea403ecf0..26f074d5fa0acb68c9193e00001bed7b34417339 100644 --- a/ajax/backend/OpenItemsList/getUserOpenItems.php +++ b/ajax/backend/OpenItemsList/getUserOpenItems.php @@ -16,7 +16,6 @@ 'package_quiqqer_customer_ajax_backend_OpenItemsList_getUserOpenItems', function ($userId, $searchParams, $forceRefresh) { try { - $userId = (int)$userId; $cacheName = 'quiqqer/customer/openitems/' . $userId; $refresh = true; $openItems = []; diff --git a/ajax/backend/create/createCustomer.php b/ajax/backend/create/createCustomer.php index 91f0201342b4473a05c08c3c61a6e9dca6542899..d807f356832358a7264bbf93d0777b4a4763c58b 100644 --- a/ajax/backend/create/createCustomer.php +++ b/ajax/backend/create/createCustomer.php @@ -12,9 +12,10 @@ QUI::$Ajax->registerFunction( 'package_quiqqer_customer_ajax_backend_create_createCustomer', - function ($customerId, $address, $groups) { + function ($customerId, $address, $groups, $attributes) { $address = json_decode($address, true); $groups = json_decode($groups, true); + $attributes = json_decode($attributes, true); $User = QUI\ERP\Customer\Customers::getInstance()->createCustomer( $customerId, @@ -22,8 +23,13 @@ function ($customerId, $address, $groups) { $groups ); + if (is_array($attributes)) { + $User->setAttributes($attributes); + $User->save(); + } + return $User->getUUID(); }, - ['customerId', 'address', 'groups'], + ['customerId', 'address', 'groups', 'attributes'], 'Permission::checkAdminUser' ); diff --git a/ajax/backend/files/downloadEntry/removeFile.php b/ajax/backend/files/downloadEntry/removeFile.php index ac9ceee43427d6f94e7a29ff1b3e21fabc356994..49de65d1b65d835b7ed77204deba99b7e0fdce66 100644 --- a/ajax/backend/files/downloadEntry/removeFile.php +++ b/ajax/backend/files/downloadEntry/removeFile.php @@ -12,7 +12,7 @@ 'package_quiqqer_customer_ajax_backend_files_downloadEntry_removeFile', function ($file, $customerId) { try { - CustomerFiles::removeFileFromDownloadEntry((int)$customerId, $file); + CustomerFiles::removeFileFromDownloadEntry($customerId, $file); } catch (Exception $Exception) { QUI\System\Log::writeException($Exception); diff --git a/ajax/backend/getBusinessType.php b/ajax/backend/getBusinessType.php new file mode 100644 index 0000000000000000000000000000000000000000..eb2a3ffb72c58ae1b921e01d876d770aef3dbc60 --- /dev/null +++ b/ajax/backend/getBusinessType.php @@ -0,0 +1,18 @@ +<?php + +/** + * This file contains package_quiqqer_customer_ajax_backend_getBusinessType + */ + +/** + * Return the shop business type + */ + +QUI::$Ajax->registerFunction( + 'package_quiqqer_customer_ajax_backend_getBusinessType', + function () { + return QUI\ERP\Utils\Shop::getBusinessType(); + }, + false, + 'Permission::checkAdminUser' +); diff --git a/bin/backend/controls/Administration.js b/bin/backend/controls/Administration.js index 84628151c3bf21bb8edf9c29268b60d638adc393..e73747d1c1d24da1f057de852dcb221b94889835 100644 --- a/bin/backend/controls/Administration.js +++ b/bin/backend/controls/Administration.js @@ -131,6 +131,9 @@ define('package/quiqqer/customer/bin/backend/controls/Administration', [ this.$SubmitButton = this.$Elm.getElement('[name="submit"]'); this.$FilterButton = this.$Elm.getElement('button[name="filter"]'); + this.$SearchContainer.setStyle('display', 'none'); + this.$GridContainer.setStyle('display', 'none'); + this.$SearchContainer.getElement('form').addEvent('submit', function(event) { event.stop(); }); @@ -349,17 +352,21 @@ define('package/quiqqer/customer/bin/backend/controls/Administration', [ this.$Grid.disable(); - if (customerId && customerId !== '0') { - this.$openCustomer(this.getAttribute('customerId')); - } else { + CustomerHandler.getCustomerGroupId().then((customerGroup) => { + self.$customerGroup = customerGroup; + + if (customerId && customerId !== '0') { + return this.$openCustomer(this.getAttribute('customerId')); + } + if (this.isInWindow() && this.$SearchInput) { this.$SearchInput.focus(); } - } - CustomerHandler.getCustomerGroupId().then(function(customerGroup) { - self.$customerGroup = customerGroup; - self.refresh().then(function() { + this.$SearchContainer.setStyle('display', null); + this.$GridContainer.setStyle('display', null); + + return self.refresh().then(function() { self.$Grid.enable(); }); }); @@ -687,65 +694,80 @@ define('package/quiqqer/customer/bin/backend/controls/Administration', [ userId ]); + if (!self.isInWindow()) { + require([ + 'utils/Panels', + 'package/quiqqer/customer/bin/backend/controls/customer/Panel' + ], (PanelUtils, Panel) => { + PanelUtils.openPanelInTasks( + new Panel({ + userId: userId + }) + ); + }); + + return; + } + + moofx([ + this.$SearchContainer, + this.$GridContainer + ]).animate({ + opacity: 0 + }, { + duration: 200, + callback: () => { + this.$SearchContainer.setStyle('display', 'none'); + this.$GridContainer.setStyle('display', 'none'); + } + }); + + require([ - 'package/quiqqer/customer/bin/backend/controls/customer/Panel', - 'utils/Panels' - ], function(Panel, PanelUtils) { - if (self.isInWindow()) { - const Container = new Element('div', { - 'class': 'quiqqer-customer-administration-customer', - styles: { - left: -50, - opacity: 0 - } - }).inject(self.getElm()); + 'package/quiqqer/customer/bin/backend/controls/customer/Panel' + ], function(Panel) { + const Container = new Element('div', { + 'class': 'quiqqer-customer-administration-customer', + styles: { + opacity: 0 + } + }).inject(self.getElm()); - self.$CustomerPanel = new Panel({ - header: false, - userId: userId, - showUserButton: true, - showDeleteButton: false, - events: { - onLoaded: () => { - self.fireEvent('customerOpenEnd', [ - this, - userId, - self.$CustomerPanel - ]); - }, - onError: (Instance) => { - if (!Instance.$User) { - self.setAttribute('customerId', false); - } + self.$CustomerPanel = new Panel({ + header: false, + userId: userId, + showUserButton: true, + showDeleteButton: false, + 'hide-loader': true, + events: { + onLoaded: () => { + self.fireEvent('customerOpenEnd', [ + this, + userId, + self.$CustomerPanel + ]); + + moofx(Container).animate({ + opacity: 1 + }); + }, + onError: (Instance) => { + if (!Instance.$User) { + self.setAttribute('customerId', false); } } - }); - - self.$CustomerPanel.inject(Container); - - self.fireEvent('customerOpen', [ - this, - userId, - self.$CustomerPanel - ]); - - moofx(Container).animate({ - left: 0, - opacity: 1 - }, { - callback: function() { - self.$CustomerPanel.fireEvent('show'); - } - }); + } + }); - return; - } + self.$CustomerPanel.inject(Container); - PanelUtils.openPanelInTasks( - new Panel({ - userId: userId - }) - ); + self.fireEvent('customerOpen', [ + this, + userId, + self.$CustomerPanel + ]); + + self.$CustomerPanel.fireEvent('show'); }); }, @@ -757,17 +779,29 @@ define('package/quiqqer/customer/bin/backend/controls/Administration', [ return; } - const self = this, - Container = this.$CustomerPanel.getElm().getParent(); + const Container = this.$CustomerPanel.getElm().getParent(); + const hiedCustomer = new Promise((resolve) => { + moofx(Container).animate({ + opacity: 0 + }, { + callback: resolve + }); + }); - moofx(Container).animate({ - left: 50, - opacity: 0 - }, { - callback: function() { - self.$CustomerPanel = null; - Container.destroy(); - } + this.$SearchContainer.setStyle('display', null); + this.$SearchContainer.setStyle('opacity', null); + + this.$GridContainer.setStyle('display', null); + this.$GridContainer.setStyle('opacity', null); + + Promise.all([ + this.refresh(), + hiedCustomer + ]).then(() => { + this.$Grid.enable(); + + this.$CustomerPanel = null; + Container.destroy(); }); }, diff --git a/bin/backend/controls/OpenItems/OpenItems.js b/bin/backend/controls/OpenItems/OpenItems.js index 20a905d2eabc624f167a3cb9beba7f16e54a607e..e81e8ef1b8bcc16cc17c3b06c11e9fba2f4a7ea1 100644 --- a/bin/backend/controls/OpenItems/OpenItems.js +++ b/bin/backend/controls/OpenItems/OpenItems.js @@ -992,44 +992,9 @@ define('package/quiqqer/customer/bin/backend/controls/OpenItems/OpenItems', [ const submitTransaction = function(Win, Data) { Win.Loader.show(); - switch (erpEntity) { - case 'Invoice': - require(['package/quiqqer/invoice/bin/Invoices'], function(Invoices) { - Invoices.addPaymentToInvoice( - Row.documentNo, - Data.amount, - Data.payment_method - ).then(function() { - Win.close(); - - self.$refreshUserEntry(self.$currentRecordsUserId).then(function() { - self.$refreshUserRecords(self.$GridDetails, true); - }); - }).catch(function(err) { - Win.Loader.hide(); - }); - }); - break; - - case 'Order': - require(['package/quiqqer/order/bin/backend/Orders'], function(Orders) { - Orders.addPaymentToOrder( - Row.documentId, - Data.amount, - Data.payment_method, - Data.date - ).then(function() { - Win.close(); - - self.$refreshUserEntry(self.$currentRecordsUserId).then(function() { - self.$refreshUserRecords(self.$GridDetails, true); - }); - }).catch(function(err) { - Win.Loader.hide(); - }); - }); - break; - } + self.$refreshUserEntry(self.$currentRecordsUserId).then(function() { + self.$refreshUserRecords(self.$GridDetails, true); + }); }; const linkTransaction = (txId, Win) => { @@ -1070,7 +1035,7 @@ define('package/quiqqer/customer/bin/backend/controls/OpenItems/OpenItems', [ }; new AddPaymentWindow({ - entityId: Row.documentNo, + entityId: Row.hash, entityType: erpEntity, events: { onSubmit: submitTransaction, diff --git a/bin/backend/controls/create/Customer.html b/bin/backend/controls/create/Customer.html index 91fd442de5c276a263083bed0bf5986e852f2906..388ff10be476f8041175168321027d84d570b006 100644 --- a/bin/backend/controls/create/Customer.html +++ b/bin/backend/controls/create/Customer.html @@ -34,6 +34,20 @@ </label> </td> </tr> + <tr> + <td> + <label class="field-container"> + <span class="field-container-item"> + {{textIsNettoBruttoUser}} + </span> + <select name="quiqqer.erp.isNettoUser" class="field-container-field"> + <option value=""></option> + <option value="2">{{textBrutto}}</option> + <option value="1">{{textNetto}}</option> + </select> + </label> + </td> + </tr> <tr> <td> <label class="field-container"> diff --git a/bin/backend/controls/create/Customer.js b/bin/backend/controls/create/Customer.js index 101a268a2725b053f464ed79a8d302b6b68f6ecb..8233f75430c4d9b43bcbb868d5f3fd53ea0f3ada 100644 --- a/bin/backend/controls/create/Customer.js +++ b/bin/backend/controls/create/Customer.js @@ -21,7 +21,7 @@ define('package/quiqqer/customer/bin/backend/controls/create/Customer', [ ], function(QUI, QUIControl, Countries, Handler, QUILocale, QUIAjax, Mustache, template) { 'use strict'; - var lg = 'quiqqer/customer'; + const lg = 'quiqqer/customer'; return new Class({ @@ -66,6 +66,9 @@ define('package/quiqqer/customer/bin/backend/controls/create/Customer', [ customerGroupsHeader: QUILocale.get(lg, 'window.customer.creation.groups.title'), customerGroupsText: QUILocale.get(lg, 'window.customer.creation.groups.text'), labelPrefix: QUILocale.get(lg, 'window.customer.creation.customerNo.labelPrefix'), + textIsNettoBruttoUser: QUILocale.get(lg, 'customer.user.information.textBruttoNetto'), + textNetto: QUILocale.get('quiqqer/erp', 'user.settings.userNettoStatus.netto'), + textBrutto: QUILocale.get('quiqqer/erp', 'user.settings.userNettoStatus.brutto'), textAddressCompany: QUILocale.get('quiqqer/core', 'company'), textAddressSalutation: QUILocale.get('quiqqer/core', 'salutation'), @@ -86,10 +89,10 @@ define('package/quiqqer/customer/bin/backend/controls/create/Customer', [ this.$Form = this.$Elm.getElement('form'); // key events - var self = this; - var CustomerId = this.$Elm.getElement('[name="customerId"]'); - var Company = this.$Elm.getElement('[name="address-company"]'); - var Country = this.$Elm.getElement('[name="address-country"]'); + const self = this; + const CustomerId = this.$Elm.getElement('[name="customerId"]'); + const Company = this.$Elm.getElement('[name="address-company"]'); + const Country = this.$Elm.getElement('[name="address-country"]'); CustomerId.addEvent('keydown', function(event) { if (event.key === 'tab') { @@ -140,13 +143,13 @@ define('package/quiqqer/customer/bin/backend/controls/create/Customer', [ * event: on inject */ $onInject: function() { - var self = this; - var Group = this.$Elm.getElement('[name="group"]'); + const self = this; + const Group = this.$Elm.getElement('[name="group"]'); Countries.getCountries().then(function(countries) { - var CountrySelect = self.$Elm.getElement('[name="address-country"]'); + const CountrySelect = self.$Elm.getElement('[name="address-country"]'); - for (var code in countries) { + for (let code in countries) { if (!countries.hasOwnProperty(code)) { continue; } @@ -160,10 +163,32 @@ define('package/quiqqer/customer/bin/backend/controls/create/Customer', [ if (QUIQQER_CONFIG.globals.country) { CountrySelect.value = QUIQQER_CONFIG.globals.country; } + }).then(() => { + const Select = this.$Elm.getElement('[name="quiqqer.erp.isNettoUser"]') + + return new Promise((resolve) => { + QUIAjax.get('package_quiqqer_customer_ajax_backend_getBusinessType', (businessType) => { + switch (businessType.toLowerCase()) { + case 'b2b': + case 'b2b-b2c': + Select.value = '1'; + break; + + case 'b2c': + case 'b2c-b2b': + Select.value = '2'; + break; + } + + resolve(); + }, { + 'package': 'quiqqer/customer', + }); + }); }).then(function() { return QUI.parse(self.$Elm); }).then(function() { - var GroupControl = QUI.Controls.getById(Group.get('data-quiid')); + const GroupControl = QUI.Controls.getById(Group.get('data-quiid')); GroupControl.disable(); self.showCustomerNumber(); @@ -174,12 +199,12 @@ define('package/quiqqer/customer/bin/backend/controls/create/Customer', [ * Create the customer */ createCustomer: function() { - var self = this; - var elements = this.$Form.elements; - var customerId = elements.customerId.value; - var groups = elements.groups.value.split(','); + const self = this; + const elements = this.$Form.elements; + const customerId = elements.customerId.value; + const groups = elements.groups.value.split(','); - var address = { + const address = { 'salutation': elements['address-salutation'].value, 'firstname': elements['address-firstname'].value, 'lastname': elements['address-lastname'].value, @@ -200,7 +225,10 @@ define('package/quiqqer/customer/bin/backend/controls/create/Customer', [ 'package': 'quiqqer/customer', customerId: customerId, address: JSON.encode(address), - groups: JSON.encode(groups) + groups: JSON.encode(groups), + attributes: JSON.encode({ + 'quiqqer.erp.isNettoUser': this.$Elm.getElement('[name="quiqqer.erp.isNettoUser"]').value + }) }); }, @@ -212,16 +240,16 @@ define('package/quiqqer/customer/bin/backend/controls/create/Customer', [ return this.createCustomer(); } - var self = this; - var steps = this.$List.getElements('li'); - var pos = this.$List.getPosition(this.$Container); - var top = pos.y; + const self = this; + const steps = this.$List.getElements('li'); + const pos = this.$List.getPosition(this.$Container); + const top = pos.y; - var height = this.$Container.getSize().y; - var scrollHeight = this.$Container.getScrollSize().y; - var newTop = this.$roundToStepPos(top - height); + const height = this.$Container.getSize().y; + const scrollHeight = this.$Container.getScrollSize().y; + const newTop = this.$roundToStepPos(top - height); - var step = 1; + let step = 1; if ((top * -1) / height) { step = Math.round(((top * -1) / height) + 1); @@ -239,11 +267,11 @@ define('package/quiqqer/customer/bin/backend/controls/create/Customer', [ } return new Promise(function(resolve) { - var checkPromises = []; + const checkPromises = []; if (step === 1) { - var elements = self.$Form.elements; - var customerId = elements.customerId.value; + const elements = self.$Form.elements; + const customerId = elements.customerId.value; checkPromises.push(Handler.validateCustomerNo(customerId)); } @@ -267,12 +295,12 @@ define('package/quiqqer/customer/bin/backend/controls/create/Customer', [ * Previous next step */ previous: function() { - var self = this; - var pos = this.$List.getPosition(this.$Container); - var top = pos.y; + const self = this; + const pos = this.$List.getPosition(this.$Container); + const top = pos.y; - var height = this.$Container.getSize().y; - var newTop = this.$roundToStepPos(top + height); + const height = this.$Container.getSize().y; + let newTop = this.$roundToStepPos(top + height); this.$Next.set('html', QUILocale.get(lg, 'window.customer.creation.next')); this.$Next.set('data-last', null); @@ -297,11 +325,11 @@ define('package/quiqqer/customer/bin/backend/controls/create/Customer', [ * refresh the step display */ refreshStepDisplay: function() { - var step = 1; - var steps = this.$List.getElements('li'); - var pos = this.$List.getPosition(this.$Container); - var top = pos.y; - var height = this.$Container.getSize().y; + let step = 1; + const steps = this.$List.getElements('li'); + const pos = this.$List.getPosition(this.$Container); + const top = pos.y; + const height = this.$Container.getSize().y; if ((top * -1) / height) { step = Math.round(((top * -1) / height) + 1); @@ -329,8 +357,8 @@ define('package/quiqqer/customer/bin/backend/controls/create/Customer', [ * @return {number} */ $roundToStepPos: function(currentPos) { - var height = this.$Container.getSize().y; - var pos = Math.round(currentPos / height) * -1; + const height = this.$Container.getSize().y; + const pos = Math.round(currentPos / height) * -1; return pos * height * -1; }, @@ -339,7 +367,7 @@ define('package/quiqqer/customer/bin/backend/controls/create/Customer', [ * Show the customer number step */ showCustomerNumber: function() { - var self = this; + const self = this; this.$Next.disabled = true; @@ -347,8 +375,8 @@ define('package/quiqqer/customer/bin/backend/controls/create/Customer', [ Handler.getNewCustomerNo(), Handler.getCustomerIdPrefix() ]).then(function(result) { - var Input = self.$Elm.getElement('input[name="customerId"]'); - var InputPrefix = self.$Elm.getElement('input[name="prefix"]'); + const Input = self.$Elm.getElement('input[name="customerId"]'); + const InputPrefix = self.$Elm.getElement('input[name="prefix"]'); if (result[1]) { InputPrefix.value = result[1]; diff --git a/bin/backend/controls/create/CustomerWindow.js b/bin/backend/controls/create/CustomerWindow.js index c05f30e40c8c00f8fa86b54f9217c26e6a98bd6b..e48837162ead532919b8744cae5457ad876d4f71 100644 --- a/bin/backend/controls/create/CustomerWindow.js +++ b/bin/backend/controls/create/CustomerWindow.js @@ -24,7 +24,7 @@ define('package/quiqqer/customer/bin/backend/controls/create/CustomerWindow', [ ], options: { - maxHeight: 700, + maxHeight: 750, maxWidth: 600, buttons: false }, @@ -50,6 +50,7 @@ define('package/quiqqer/customer/bin/backend/controls/create/CustomerWindow', [ this.getContent().set('html', ''); this.getContent().setStyle('padding', 0); + this.Loader.show(); new CreateCustomer({ events: { diff --git a/bin/backend/controls/customer/AddressCreateWindow.js b/bin/backend/controls/customer/AddressCreateWindow.js index df57d5eaafab5ad33ed1f3ad5a78cceb0f66d319..b5854d7ddeb76e422ae45122a24bd30a317fffcc 100644 --- a/bin/backend/controls/customer/AddressCreateWindow.js +++ b/bin/backend/controls/customer/AddressCreateWindow.js @@ -83,6 +83,9 @@ define('package/quiqqer/customer/bin/backend/controls/customer/AddressCreateWind self.addEmail(); }); + this.addPhone(); + this.addEmail(); + this.Loader.hide(); }, diff --git a/bin/backend/controls/customer/Panel.css b/bin/backend/controls/customer/Panel.css index e5734638ded30a36f1343b298fe191f8d237a671..dcfe74beac720288ca44b90d0d97c123242182b8 100644 --- a/bin/backend/controls/customer/Panel.css +++ b/bin/backend/controls/customer/Panel.css @@ -47,6 +47,11 @@ float: none; } +.quiqqer-customer-panel form[name="customer-information"] { + display: inline-block; + width: 100%; +} + /** comments ===================================== */ @@ -164,7 +169,7 @@ form[name="customer-information"] .comments { height: 45px; bottom: 0; float: left; - width: 50%; + width: 100%; } .comments-pagination .quiqqer-sheets-desktop, diff --git a/bin/backend/controls/customer/Panel.js b/bin/backend/controls/customer/Panel.js index 5a29a0dc35a17096e3e23de226532a61a6966c97..453856491616279626a102fd290865e25993b2ad 100644 --- a/bin/backend/controls/customer/Panel.js +++ b/bin/backend/controls/customer/Panel.js @@ -10,6 +10,7 @@ define('package/quiqqer/customer/bin/backend/controls/customer/Panel', [ 'qui/controls/desktop/Panel', 'qui/controls/buttons/Button', 'qui/controls/buttons/ButtonMultiple', + 'qui/controls/loader/Loader', 'qui/controls/windows/Confirm', 'package/quiqqer/countries/bin/Countries', 'package/quiqqer/customer/bin/backend/controls/customer/AddressEditWindow', @@ -27,7 +28,7 @@ define('package/quiqqer/customer/bin/backend/controls/customer/Panel', [ 'text!package/quiqqer/customer/bin/backend/controls/customer/Panel.EditId.html', 'css!package/quiqqer/customer/bin/backend/controls/customer/Panel.css' -], function(QUI, QUIPanel, QUIButton, QUIButtonMultiple, QUIConfirm, Countries, AddressEditWindow, Handler, +], function(QUI, QUIPanel, QUIButton, QUIButtonMultiple, QUILoader, QUIConfirm, Countries, AddressEditWindow, Handler, FormUtils, Users, QUILocale, QUIAjax, Packages, Mustache, templateInformation, templateEditId ) { 'use strict'; @@ -67,7 +68,8 @@ define('package/quiqqer/customer/bin/backend/controls/customer/Panel', [ icon: 'fa fa-user', userId: false, showUserButton: true, - showDeleteButton: true + showDeleteButton: true, + 'hide-loader': false }, initialize: function(options) { @@ -179,6 +181,10 @@ define('package/quiqqer/customer/bin/backend/controls/customer/Panel', [ $onCreate: function() { const self = this; + if (this.getAttribute('hide-loader')) { + this.Loader = new QUILoader(); + } + this.getElm().addClass('quiqqer-customer-panel'); // buttons @@ -942,6 +948,12 @@ define('package/quiqqer/customer/bin/backend/controls/customer/Panel', [ PaginationContainer.set('html', paginationHtml); + if (paginationHtml === '') { + PaginationContainer.setStyle('display', 'none'); + } else { + PaginationContainer.setStyle('display', null); + } + return QUI.parse(PaginationContainer).then(function() { if (paginationHtml) { self.$CommentsPagination = QUI.Controls.getById( @@ -985,6 +997,24 @@ define('package/quiqqer/customer/bin/backend/controls/customer/Panel', [ Payments.getPayments().then(function(payments) { let i, len, text; + // payment sort + let current = QUILocale.getCurrent(); + + payments.sort((a, b) => { + let titleA = a.title[current] ? a.title[current].toLowerCase() : ''; + let titleB = b.title[current] ? b.title[current].toLowerCase() : ''; + + if (titleA < titleB) { + return -1; + } + + if (titleA > titleB) { + return 1; + } + + return 0; + }); + for (i = 0, len = payments.length; i < len; i++) { text = ''; diff --git a/bin/backend/controls/customer/Select.js b/bin/backend/controls/customer/Select.js index 14a7af9e29f0b7edf1a4f5588dbc117d1ebdb8ed..3783a3f5c717e74c726cfcdd7f54eaa2eb908d42 100644 --- a/bin/backend/controls/customer/Select.js +++ b/bin/backend/controls/customer/Select.js @@ -33,7 +33,11 @@ define('package/quiqqer/customer/bin/backend/controls/customer/Select', [ Type: 'package/quiqqer/customer/bin/backend/controls/customer/Select', Binds: [ + '$onCreate', '$onSearchButtonClick', + 'openCustomerSearch', + 'editCustomer', + 'createCustomer', 'userSearch' ], @@ -54,11 +58,67 @@ define('package/quiqqer/customer/bin/backend/controls/customer/Select', [ ); this.addEvents({ - onSearchButtonClick: this.$onSearchButtonClick, - onCreate: () => { - this.getElm().addClass('quiqqer-customer-select'); + onCreate: this.$onCreate + }); + }, + + $onCreate: function() { + this.getElm().addClass('quiqqer-customer-select'); + this.getElm().set('data-qui', 'package/quiqqer/customer/bin/backend/controls/customer/Select'); + + this.$SearchButton.setAttribute('menuCorner', 'topRight'); + + this.$SearchButton.appendChild({ + text: QUILocale.get(lg, 'customer.select.button.search'), + name: 'search', + icon: 'fa fa-search', + events: { + click: this.openCustomerSearch + } + }); + + this.$SearchButton.appendChild({ + text: QUILocale.get(lg, 'customer.select.button.create'), + name: 'create', + icon: 'fa fa-plus', + events: { + click: this.createCustomer } }); + + this.$SearchButton.appendChild({ + text: QUILocale.get(lg, 'customer.select.button.edit'), + name: 'edit', + icon: 'fa fa-edit', + disabled: true, + events: { + click: this.editCustomer + } + }); + + const Search = this.$SearchButton.getChildren().filter((Instance) => { + return Instance.getAttribute('name') === 'search'; + })[0]; + + + const Edit = this.$SearchButton.getChildren().filter((Instance) => { + return Instance.getAttribute('name') === 'edit'; + })[0]; + + this.$SearchButton.getContextMenu((Menu) => { + Menu.setAttribute('menuCorner', 'topRight'); + Menu.addEvent('show', () => { + if (this.getValue()) { + Edit.enable(); + Search.setAttribute('text', QUILocale.get(lg, 'customer.select.button.replace')); + } else { + Edit.disable(); + Search.setAttribute('text', QUILocale.get(lg, 'customer.select.button.search')); + } + + Menu.getElm().setStyle('left', Menu.getElm().getPosition().x + 15); + }); + }); }, /** @@ -95,6 +155,14 @@ define('package/quiqqer/customer/bin/backend/controls/customer/Select', [ }); }, + addItem: function(id) { + if (id === '0' || id === 0) { + return this; + } + + return this.parent(id); + }, + /** * event : on search click * @@ -110,7 +178,7 @@ define('package/quiqqer/customer/bin/backend/controls/customer/Select', [ require([ 'package/quiqqer/customer/bin/backend/controls/AdministrationWindow' - ], function(Window) { + ], (Window) => { new Window({ autoclose: true, multiple: self.getAttribute('multiple'), @@ -129,6 +197,84 @@ define('package/quiqqer/customer/bin/backend/controls/customer/Select', [ Btn.setAttribute('icon', oldIcon); Btn.enable(); }); + }, + + /** + * Opens the customer search window + */ + openCustomerSearch: function() { + const oldIcon = this.$SearchButton.getAttribute('icon'); + + this.$SearchButton.setAttribute('icon', 'fa fa-spinner fa-spin'); + this.$SearchButton.disable(); + + require([ + 'package/quiqqer/customer/bin/backend/controls/AdministrationWindow' + ], (Window) => { + new Window({ + autoclose: true, + multiple: this.getAttribute('multiple'), + search: this.getAttribute('search'), + searchSettings: this.getAttribute('searchSettings'), + events: { + onSubmit: (Win, userIds) => { + for (let i = 0, len = userIds.length; i < len; i++) { + this.addItem(userIds[i]); + } + } + } + }).open(); + + this.$SearchButton.setAttribute('icon', oldIcon); + this.$SearchButton.enable(); + }); + }, + + /** + * opens the customer creation dialog + */ + createCustomer: function() { + const oldIcon = this.$SearchButton.getAttribute('icon'); + + this.$SearchButton.setAttribute('icon', 'fa fa-spinner fa-spin'); + this.$SearchButton.disable(); + + require([ + 'package/quiqqer/customer/bin/backend/controls/create/CustomerWindow' + ], (CustomerWindow) => { + new CustomerWindow({ + events: { + submit: (Instance, customerId) => { + this.addItem(customerId); + } + } + }).open(); + + this.$SearchButton.setAttribute('icon', oldIcon); + this.$SearchButton.enable(); + }); + }, + + editCustomer: function() { + if (this.getValue() === '') { + return; + } + + if (!this.getValue()) { + return; + } + + const Item = this.getElm().getElement('.qui-elements-selectItem'); + + if (Item) { + Item.dispatchEvent( + new MouseEvent('dblclick', { + 'view': window, + 'bubbles': true, + 'cancelable': true + }) + ); + } } }); }); diff --git a/bin/backend/controls/customer/SelectItem.js b/bin/backend/controls/customer/SelectItem.js index 945ea440230080605cea396ec173d790223d5721..83dfcb2515cdbf9a9c2f98c4f77b11c21ebe45c3 100644 --- a/bin/backend/controls/customer/SelectItem.js +++ b/bin/backend/controls/customer/SelectItem.js @@ -17,12 +17,17 @@ define('package/quiqqer/customer/bin/backend/controls/customer/SelectItem', [ Type: 'package/quiqqer/customer/bin/backend/controls/customer/SelectItem', Binds: [ - 'refresh' + 'refresh', + '$onCustomerInject' ], initialize: function(options) { this.parent(options); this.setAttribute('icon', 'fa fa-user-o'); + + this.addEvents({ + onInject: this.$onCustomerInject + }); }, /** @@ -33,6 +38,11 @@ define('package/quiqqer/customer/bin/backend/controls/customer/SelectItem', [ refresh: function() { const id = this.getAttribute('id'); + if (!id || id === '' || id === '0' || id === 0) { + this.destroy(); + return Promise.resolve(); + } + // user this.setAttribute('icon', 'fa fa-user-o'); @@ -55,6 +65,34 @@ define('package/quiqqer/customer/bin/backend/controls/customer/SelectItem', [ } }); }); + }, + + $onCustomerInject: function() { + this.getElm().setStyle('cursor', 'pointer'); + this.getElm().addEvent('dblclick', () => { + if (!this.getAttribute('id')) { + return; + } + + require([ + 'package/quiqqer/customer/bin/backend/controls/AdministrationWindow' + ], (AdministrationWindow) => { + new AdministrationWindow({ + customerId: this.getAttribute('id'), + events: { + onSubmit: (Win, userIds) => { + const Parent = QUI.Controls.getById( + this.getElm().getParent('.quiqqer-customer-select').get('data-quiid') + ); + + for (let i = 0, len = userIds.length; i < len; i++) { + Parent.addItem(userIds[i]); + } + } + } + }).open(); + }); + }); } }); }); diff --git a/bin/backend/download.php b/bin/backend/download.php index 707b617779466c26a6a7de742eefe0c4fafb6278..53c7afe39b37c19731b2c938ee2c4b4dd958f618 100644 --- a/bin/backend/download.php +++ b/bin/backend/download.php @@ -8,10 +8,13 @@ define('QUIQQER_SYSTEM', true); define('QUIQQER_AJAX', true); +if (!isset($_REQUEST['customerId'])) { + exit; +} + if ( - !isset($_REQUEST['file']) - || !isset($_REQUEST['customerId']) - || !isset($_REQUEST['extension']) + !isset($_REQUEST['hash']) + && (!isset($_REQUEST['file']) || !isset($_REQUEST['extension'])) ) { exit; } @@ -25,6 +28,7 @@ $Request = QUI::getRequest(); $file = Orthos::clear($Request->query->get('file')); +$hash = Orthos::clear($Request->query->get('hash')); $extension = Orthos::clear($Request->query->get('extension')); $customerId = $Request->query->get('customerId'); @@ -54,17 +58,26 @@ } try { - $Customer = QUI::getUsers()->get($customerId); - $customerDir = QUI\ERP\Customer\CustomerFiles::getFolderPath($Customer); + if (!empty($hash)) { + $file = QUI\ERP\Customer\CustomerFiles::getFileByHash($customerId, $hash); + $filePath = $file['dirname'] . DIRECTORY_SEPARATOR . $file['basename']; - if (!empty($extension) && $extension !== 'false') { - $file .= '.' . $extension; - } + if (file_exists($filePath)) { + QUI\Utils\System\File::send($filePath, 0, $file['basename']); + } + } else { + $Customer = QUI::getUsers()->get($customerId); + $customerDir = QUI\ERP\Customer\CustomerFiles::getFolderPath($Customer); + + if (!empty($extension) && $extension !== 'false') { + $file .= '.' . $extension; + } - $filePath = $customerDir . DIRECTORY_SEPARATOR . $file; + $filePath = $customerDir . DIRECTORY_SEPARATOR . $file; - if (file_exists($filePath)) { - QUI\Utils\System\File::send($filePath, 0, $file); + if (file_exists($filePath)) { + QUI\Utils\System\File::send($filePath, 0, $file); + } } } catch (\Exception $Exception) { QUI\System\Log::addDebug($Exception->getMessage()); diff --git a/captainhook.json b/captainhook.json new file mode 100644 index 0000000000000000000000000000000000000000..3702e1a358868bedd5ff4c7eae40bb1abb589267 --- /dev/null +++ b/captainhook.json @@ -0,0 +1,13 @@ +{ + "pre-commit": { + "enabled": true, + "actions": [ + { + "action": "\\CaptainHook\\App\\Hook\\PHP\\Action\\Linting" + }, + { + "action": "composer test" + } + ] + } +} \ No newline at end of file diff --git a/composer.json b/composer.json index 88a0e7c5ebad53fe8792af322c16948d93968c40..447c39f2a32fd0103685e6d032b5d721bb0b6e9b 100644 --- a/composer.json +++ b/composer.json @@ -1,31 +1,73 @@ { - "name": "quiqqer/customer", - "type": "quiqqer-module", - "description": "With the customer administration you extend QUIQQER by a simpler administration of your customers.", - "license": "GPL-3.0+", - "authors": [ - { - "name": "Henning Leutz", - "email": "leutz@pcsg.de", - "homepage": "https://www.pcsg.de", - "role": "Developer" + "name": "quiqqer/customer", + "type": "quiqqer-module", + "description": "With the customer administration you extend QUIQQER by a simpler administration of your customers.", + "license": "GPL-3.0+", + "authors": [ + { + "name": "Henning Leutz", + "email": "leutz@pcsg.de", + "homepage": "https://www.pcsg.de", + "role": "Developer" + } + ], + "support": { + "email": "support@pcsg.de", + "url": "https://www.pcsg.de" + }, + "require": { + "php": "^8", + "quiqqer/core": "^2", + "quiqqer/erp": "^3.2" + }, + "suggest": { + "quiqqer/user-downloads": "^1" + }, + "autoload": { + "psr-4": { + "QUI\\ERP\\Customer\\": "src/QUI/ERP/Customer" + } + }, + "scripts": { + "test": [ + "@dev:lint", + "@dev:phpunit" + ], + "dev:phpunit": "./tools/phpunit", + "dev:lint": [ + "@dev:lint:phpstan", + "@dev:lint:style" + ], + "dev:lint:phpstan": "./tools/phpstan", + "dev:lint:style": "./tools/phpcs", + "dev:lint:style:fix": "./tools/phpcbf", + "dev:init": [ + "@dev:init:check-requirements", + "@dev:init:tools", + "@dev:init:git-hooks" + ], + "dev:init:check-requirements": [ + "which composer > /dev/null || (echo 'Error: composer has to be globally installed'; exit 1)", + "which phive > /dev/null || (echo 'Error: PHIVE has to be globally installed'; exit 1)" + ], + "dev:init:tools": "phive install --temporary", + "dev:init:git-hooks": "./tools/captainhook install --only-enabled --force" + }, + "scripts-aliases": { + "test": [ + "dev:test" + ] + }, + "scripts-descriptions": { + "test": "Runs linting, static analysis, and unit tests.", + "dev:phpunit": "Run PHPUnit test suites", + "dev:lint": "Run PHPStan and code style check", + "dev:lint:phpstan": "Run PHPStan", + "dev:lint:style": "Run code style check (PHP_CodeSniffer)", + "dev:lint:style:fix": "Try to fix code style errors automatically", + "dev:init": "Initialize the developer tooling (tools and git hooks)", + "dev:init:check-requirements": "Check if the necessary requirements are met", + "dev:init:tools": "Install all developer tools (requires PHIVE)", + "dev:init:git-hooks": "Install all git hooks (may require tools to be installed)" } - ], - "support": { - "email": "support@pcsg.de", - "url": "https://www.pcsg.de" - }, - "require": { - "php": "^8", - "quiqqer/core": "^2", - "quiqqer/erp": "^3.2" - }, - "suggest": { - "quiqqer/user-downloads": "^1" - }, - "autoload": { - "psr-4": { - "QUI\\ERP\\Customer\\": "src/QUI/ERP/Customer" - } - } -} +} \ No newline at end of file diff --git a/locale.xml b/locale.xml index f4f1d6b6155281fb33d26d388f391c78a0f326ab..1b8283714188b97718664af05f44ffcb8433400c 100644 --- a/locale.xml +++ b/locale.xml @@ -76,6 +76,21 @@ <en><![CDATA[Prefix that is prependend to all customer numbers.]]></en> </locale> + <locale name="customer.settings.setCustomerNoAtOrder"> + <de><![CDATA[Automatische Kundennummer-Zuordnung bei Bestellung]]></de> + <en><![CDATA[Automatic customer number assignment when ordering]]></en> + </locale> + <locale name="customer.settings.setCustomerNoAtOrder.description"> + <de><![CDATA[ + Diese Einstellung ermöglicht es, dass bei jeder Bestellung automatisch die Kundennummer des Kunden hinzugefügt wird. + Das Bestellungsmodul (quiqqer/orders) muss dafür installiert sein. + ]]></de> + <en><![CDATA[ + This setting allows the customer's customer number to be added automatically with every order. + The order module (quiqqer/orders) must be installed for this. + ]]></en> + </locale> + <locale name="exception.customer.group.not.exists"> <de><![CDATA[Kundengruppe existiert nicht.]]></de> <en><![CDATA[Customer group does not exist.]]></en> @@ -1165,6 +1180,22 @@ Best regards <de><![CDATA[Neue Adresse anlegen]]></de> <en><![CDATA[Create new address]]></en> </locale> + <locale name="customer.select.button.create"> + <de><![CDATA[Neuen Kunden anlegen]]></de> + <en><![CDATA[Create new customer]]></en> + </locale> + <locale name="customer.select.button.search"> + <de><![CDATA[Kunden suchen]]></de> + <en><![CDATA[Search customer]]></en> + </locale> + <locale name="customer.select.button.replace"> + <de><![CDATA[Kunden ersetzen]]></de> + <en><![CDATA[Replace customer]]></en> + </locale> + <locale name="customer.select.button.edit"> + <de><![CDATA[Kunden editieren]]></de> + <en><![CDATA[Edit customer]]></en> + </locale> </groups> </locales> diff --git a/phpcs.xml.dist b/phpcs.xml.dist new file mode 100644 index 0000000000000000000000000000000000000000..d48084fbdc86a2dbfb78dd89ab2e0b0ff2322ba7 --- /dev/null +++ b/phpcs.xml.dist @@ -0,0 +1,23 @@ +<?xml version="1.0"?> +<ruleset> + <!-- Use PSR-12 ruleset --> + <rule ref="PSR12"/> + + <!-- Only scan *.php files --> + <arg name="extensions" value="php"/> + + <!-- Ignore warnings --> + <arg name="warning-severity" value="0"/> + + <!-- Process 64 (or number of CPU cores) files in parallel --> + <arg name="parallel" value="64"/> + + <!-- Output relative file paths, by setting the current folder as the basepath --> + <arg name="basepath" value="."/> + + <!-- Show colored output --> + <arg name="colors"/> + + <!-- Scan everything in the current folder --> + <file>.</file> +</ruleset> diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..a2480f2b38bc0182387d66c7e66c0f08cda2a4a4 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -0,0 +1,326 @@ +parameters: + ignoreErrors: + - + message: "#^Parameter \\#1 \\$User of static method QUI\\\\ERP\\\\Comments\\:\\:getHistoryByUser\\(\\) expects QUI\\\\Users\\\\User, QUI\\\\Interfaces\\\\Users\\\\User given\\.$#" + count: 1 + path: ajax/backend/customer/getCommentsAndHistory.php + + - + message: "#^Parameter \\#1 \\$User of static method QUI\\\\ERP\\\\Comments\\:\\:getHistoryByUser\\(\\) expects QUI\\\\Users\\\\User, QUI\\\\Interfaces\\\\Users\\\\User given\\.$#" + count: 1 + path: ajax/backend/customer/getHistory.php + + - + message: "#^Parameter \\#1 \\$User of static method QUI\\\\ERP\\\\Comments\\:\\:getHistoryByUser\\(\\) expects QUI\\\\Users\\\\User, QUI\\\\Interfaces\\\\Users\\\\User given\\.$#" + count: 1 + path: ajax/backend/customer/getPagination.php + + - + message: "#^Parameter \\#1 \\$User of method QUI\\\\Users\\\\Auth\\\\Handler\\:\\:sendPasswordResetVerificationMail\\(\\) expects QUI\\\\Users\\\\User, QUI\\\\Interfaces\\\\Users\\\\User given\\.$#" + count: 1 + path: ajax/backend/customer/passwordMail.php + + - + message: "#^Call to method addUrl\\(\\) on an unknown class QUI\\\\UserDownloads\\\\DownloadEntry\\.$#" + count: 1 + path: src/QUI/ERP/Customer/CustomerFiles.php + + - + message: "#^Call to method delete\\(\\) on an unknown class QUI\\\\UserDownloads\\\\DownloadEntry\\.$#" + count: 1 + path: src/QUI/ERP/Customer/CustomerFiles.php + + - + message: "#^Call to method getUrls\\(\\) on an unknown class QUI\\\\UserDownloads\\\\DownloadEntry\\.$#" + count: 1 + path: src/QUI/ERP/Customer/CustomerFiles.php + + - + message: "#^Call to method update\\(\\) on an unknown class QUI\\\\UserDownloads\\\\DownloadEntry\\.$#" + count: 1 + path: src/QUI/ERP/Customer/CustomerFiles.php + + - + message: "#^Cannot call method getQuiqqerMediaUrls\\(\\) on QUI\\\\UserDownloads\\\\DownloadEntry\\|false\\.$#" + count: 1 + path: src/QUI/ERP/Customer/CustomerFiles.php + + - + message: "#^Cannot call method getUrls\\(\\) on QUI\\\\UserDownloads\\\\DownloadEntry\\|false\\.$#" + count: 1 + path: src/QUI/ERP/Customer/CustomerFiles.php + + - + message: "#^Cannot call method removeUrl\\(\\) on QUI\\\\UserDownloads\\\\DownloadEntry\\|false\\.$#" + count: 1 + path: src/QUI/ERP/Customer/CustomerFiles.php + + - + message: "#^Cannot call method update\\(\\) on QUI\\\\UserDownloads\\\\DownloadEntry\\|false\\.$#" + count: 1 + path: src/QUI/ERP/Customer/CustomerFiles.php + + - + message: "#^Method QUI\\\\ERP\\\\Customer\\\\CustomerFiles\\:\\:createDownloadEntry\\(\\) has invalid return type QUI\\\\UserDownloads\\\\DownloadEntry\\.$#" + count: 2 + path: src/QUI/ERP/Customer/CustomerFiles.php + + - + message: "#^Method QUI\\\\ERP\\\\Customer\\\\CustomerFiles\\:\\:getDownloadEntry\\(\\) has invalid return type QUI\\\\UserDownloads\\\\DownloadEntry\\.$#" + count: 2 + path: src/QUI/ERP/Customer/CustomerFiles.php + + - + message: "#^PHPDoc tag @throws with type QUI\\\\Exception\\|QUI\\\\UserDownloads\\\\Exception is not subtype of Throwable$#" + count: 1 + path: src/QUI/ERP/Customer/CustomerFiles.php + + - + message: "#^Parameter \\#1 \\$code of method QUI\\\\Interfaces\\\\Users\\\\User\\:\\:activate\\(\\) expects string, false given\\.$#" + count: 2 + path: src/QUI/ERP/Customer/Customers.php + + - + message: "#^Result of && is always false\\.$#" + count: 1 + path: src/QUI/ERP/Customer/Customers.php + + - + message: "#^Strict comparison using \\=\\=\\= between mixed and '' will always evaluate to false\\.$#" + count: 2 + path: src/QUI/ERP/Customer/Customers.php + + - + message: "#^Strict comparison using \\=\\=\\= between mixed and null will always evaluate to false\\.$#" + count: 1 + path: src/QUI/ERP/Customer/Customers.php + + - + message: "#^Call to method getOrder\\(\\) on an unknown class QUI\\\\ERP\\\\Order\\\\Controls\\\\OrderProcess\\\\CustomerData\\.$#" + count: 1 + path: src/QUI/ERP/Customer/EventHandler.php + + - + message: "#^Parameter \\$Step of method QUI\\\\ERP\\\\Customer\\\\EventHandler\\:\\:onQuiqqerOrderCustomerDataSaveEnd\\(\\) has invalid type QUI\\\\ERP\\\\Order\\\\Controls\\\\OrderProcess\\\\CustomerData\\.$#" + count: 2 + path: src/QUI/ERP/Customer/EventHandler.php + + - + message: "#^Call to method getAttribute\\(\\) on an unknown class QUI\\\\ERP\\\\Order\\\\AbstractOrder\\.$#" + count: 1 + path: src/QUI/ERP/Customer/OpenItemsList/Events.php + + - + message: "#^Call to method getCustomer\\(\\) on an unknown class QUI\\\\ERP\\\\Accounting\\\\Invoice\\\\Invoice\\.$#" + count: 2 + path: src/QUI/ERP/Customer/OpenItemsList/Events.php + + - + message: "#^Call to method getCustomer\\(\\) on an unknown class QUI\\\\ERP\\\\Order\\\\AbstractOrder\\.$#" + count: 1 + path: src/QUI/ERP/Customer/OpenItemsList/Events.php + + - + message: "#^Call to method getCustomer\\(\\) on an unknown class QUI\\\\ERP\\\\Order\\\\Order\\.$#" + count: 1 + path: src/QUI/ERP/Customer/OpenItemsList/Events.php + + - + message: "#^Call to method getHash\\(\\) on an unknown class QUI\\\\ERP\\\\Accounting\\\\Payments\\\\Transactions\\\\Transaction\\.$#" + count: 2 + path: src/QUI/ERP/Customer/OpenItemsList/Events.php + + - + message: "#^Call to static method getInstance\\(\\) on an unknown class QUI\\\\ERP\\\\Accounting\\\\Invoice\\\\Handler\\.$#" + count: 1 + path: src/QUI/ERP/Customer/OpenItemsList/Events.php + + - + message: "#^Call to static method getInstance\\(\\) on an unknown class QUI\\\\ERP\\\\Order\\\\Handler\\.$#" + count: 1 + path: src/QUI/ERP/Customer/OpenItemsList/Events.php + + - + message: "#^Call to static method getInstance\\(\\) on an unknown class QUI\\\\ERP\\\\Order\\\\Settings\\.$#" + count: 2 + path: src/QUI/ERP/Customer/OpenItemsList/Events.php + + - + message: "#^Parameter \\$Invoice of method QUI\\\\ERP\\\\Customer\\\\OpenItemsList\\\\Events\\:\\:onQuiqqerInvoicePaymentStatusChanged\\(\\) has invalid type QUI\\\\ERP\\\\Accounting\\\\Invoice\\\\Invoice\\.$#" + count: 2 + path: src/QUI/ERP/Customer/OpenItemsList/Events.php + + - + message: "#^Parameter \\$Invoice of method QUI\\\\ERP\\\\Customer\\\\OpenItemsList\\\\Events\\:\\:onQuiqqerInvoiceTemporaryInvoicePostEnd\\(\\) has invalid type QUI\\\\ERP\\\\Accounting\\\\Invoice\\\\Invoice\\.$#" + count: 2 + path: src/QUI/ERP/Customer/OpenItemsList/Events.php + + - + message: "#^Parameter \\$Order of method QUI\\\\ERP\\\\Customer\\\\OpenItemsList\\\\Events\\:\\:onQuiqqerOrderCreated\\(\\) has invalid type QUI\\\\ERP\\\\Order\\\\AbstractOrder\\.$#" + count: 2 + path: src/QUI/ERP/Customer/OpenItemsList/Events.php + + - + message: "#^Parameter \\$Order of method QUI\\\\ERP\\\\Customer\\\\OpenItemsList\\\\Events\\:\\:onQuiqqerOrderPaidStatusChanged\\(\\) has invalid type QUI\\\\ERP\\\\Order\\\\Order\\.$#" + count: 2 + path: src/QUI/ERP/Customer/OpenItemsList/Events.php + + - + message: "#^Parameter \\$TempInvoice of method QUI\\\\ERP\\\\Customer\\\\OpenItemsList\\\\Events\\:\\:onQuiqqerInvoiceTemporaryInvoicePostEnd\\(\\) has invalid type QUI\\\\ERP\\\\Accounting\\\\Invoice\\\\InvoiceTemporary\\.$#" + count: 2 + path: src/QUI/ERP/Customer/OpenItemsList/Events.php + + - + message: "#^Parameter \\$Transaction of method QUI\\\\ERP\\\\Customer\\\\OpenItemsList\\\\Events\\:\\:onTransactionCreate\\(\\) has invalid type QUI\\\\ERP\\\\Accounting\\\\Payments\\\\Transactions\\\\Transaction\\.$#" + count: 2 + path: src/QUI/ERP/Customer/OpenItemsList/Events.php + + - + message: "#^Call to method getArticles\\(\\) on an unknown class QUI\\\\ERP\\\\Order\\\\Order\\.$#" + count: 1 + path: src/QUI/ERP/Customer/OpenItemsList/Handler.php + + - + message: "#^Call to method getAttribute\\(\\) on an unknown class QUI\\\\ERP\\\\Accounting\\\\Invoice\\\\Invoice\\.$#" + count: 6 + path: src/QUI/ERP/Customer/OpenItemsList/Handler.php + + - + message: "#^Call to method getAttribute\\(\\) on an unknown class QUI\\\\ERP\\\\Order\\\\Order\\.$#" + count: 3 + path: src/QUI/ERP/Customer/OpenItemsList/Handler.php + + - + message: "#^Call to method getCancelledStatus\\(\\) on an unknown class QUI\\\\ERP\\\\Order\\\\ProcessingStatus\\\\Handler\\.$#" + count: 1 + path: src/QUI/ERP/Customer/OpenItemsList/Handler.php + + - + message: "#^Call to method getCleanId\\(\\) on an unknown class QUI\\\\ERP\\\\Order\\\\Order\\.$#" + count: 1 + path: src/QUI/ERP/Customer/OpenItemsList/Handler.php + + - + message: "#^Call to method getCurrency\\(\\) on an unknown class QUI\\\\ERP\\\\Accounting\\\\Invoice\\\\Invoice\\.$#" + count: 1 + path: src/QUI/ERP/Customer/OpenItemsList/Handler.php + + - + message: "#^Call to method getCurrency\\(\\) on an unknown class QUI\\\\ERP\\\\Order\\\\Order\\.$#" + count: 1 + path: src/QUI/ERP/Customer/OpenItemsList/Handler.php + + - + message: "#^Call to method getDate\\(\\) on an unknown class QUI\\\\ERP\\\\Accounting\\\\Payments\\\\Transactions\\\\Transaction\\.$#" + count: 4 + path: src/QUI/ERP/Customer/OpenItemsList/Handler.php + + - + message: "#^Call to method getGlobalProcessId\\(\\) on an unknown class QUI\\\\ERP\\\\Accounting\\\\Invoice\\\\Invoice\\.$#" + count: 2 + path: src/QUI/ERP/Customer/OpenItemsList/Handler.php + + - + message: "#^Call to method getGlobalProcessId\\(\\) on an unknown class QUI\\\\ERP\\\\Order\\\\Order\\.$#" + count: 2 + path: src/QUI/ERP/Customer/OpenItemsList/Handler.php + + - + message: "#^Call to method getId\\(\\) on an unknown class QUI\\\\ERP\\\\Accounting\\\\Invoice\\\\Invoice\\.$#" + count: 2 + path: src/QUI/ERP/Customer/OpenItemsList/Handler.php + + - + message: "#^Call to method getPaidStatusInformation\\(\\) on an unknown class QUI\\\\ERP\\\\Accounting\\\\Invoice\\\\Invoice\\.$#" + count: 1 + path: src/QUI/ERP/Customer/OpenItemsList/Handler.php + + - + message: "#^Call to method getPaidStatusInformation\\(\\) on an unknown class QUI\\\\ERP\\\\Order\\\\Order\\.$#" + count: 1 + path: src/QUI/ERP/Customer/OpenItemsList/Handler.php + + - + message: "#^Call to method getPrefixedNumber\\(\\) on an unknown class QUI\\\\ERP\\\\Accounting\\\\Invoice\\\\Invoice\\.$#" + count: 1 + path: src/QUI/ERP/Customer/OpenItemsList/Handler.php + + - + message: "#^Call to method getPrefixedNumber\\(\\) on an unknown class QUI\\\\ERP\\\\Order\\\\Order\\.$#" + count: 1 + path: src/QUI/ERP/Customer/OpenItemsList/Handler.php + + - + message: "#^Call to method getUUID\\(\\) on an unknown class QUI\\\\ERP\\\\Accounting\\\\Invoice\\\\Invoice\\.$#" + count: 1 + path: src/QUI/ERP/Customer/OpenItemsList/Handler.php + + - + message: "#^Call to method getUUID\\(\\) on an unknown class QUI\\\\ERP\\\\Order\\\\Order\\.$#" + count: 2 + path: src/QUI/ERP/Customer/OpenItemsList/Handler.php + + - + message: "#^Call to static method getInstance\\(\\) on an unknown class QUI\\\\ERP\\\\Accounting\\\\Dunning\\\\Handler\\.$#" + count: 1 + path: src/QUI/ERP/Customer/OpenItemsList/Handler.php + + - + message: "#^Call to static method getInstance\\(\\) on an unknown class QUI\\\\ERP\\\\Accounting\\\\Invoice\\\\Handler\\.$#" + count: 1 + path: src/QUI/ERP/Customer/OpenItemsList/Handler.php + + - + message: "#^Call to static method getInstance\\(\\) on an unknown class QUI\\\\ERP\\\\Accounting\\\\Payments\\\\Transactions\\\\Handler\\.$#" + count: 1 + path: src/QUI/ERP/Customer/OpenItemsList/Handler.php + + - + message: "#^Call to static method getInstance\\(\\) on an unknown class QUI\\\\ERP\\\\Order\\\\Handler\\.$#" + count: 1 + path: src/QUI/ERP/Customer/OpenItemsList/Handler.php + + - + message: "#^Call to static method getTransactionsByInvoice\\(\\) on an unknown class QUI\\\\ERP\\\\Accounting\\\\Invoice\\\\Utils\\\\Invoice\\.$#" + count: 1 + path: src/QUI/ERP/Customer/OpenItemsList/Handler.php + + - + message: "#^Instantiated class QUI\\\\ERP\\\\Order\\\\ProcessingStatus\\\\Handler not found\\.$#" + count: 1 + path: src/QUI/ERP/Customer/OpenItemsList/Handler.php + + - + message: "#^Method QUI\\\\ERP\\\\Customer\\\\OpenItemsList\\\\Handler\\:\\:getOpenInvoices\\(\\) has invalid return type QUI\\\\ERP\\\\Accounting\\\\Invoice\\\\Invoice\\.$#" + count: 1 + path: src/QUI/ERP/Customer/OpenItemsList/Handler.php + + - + message: "#^Method QUI\\\\ERP\\\\Customer\\\\OpenItemsList\\\\Handler\\:\\:getOpenOrders\\(\\) has invalid return type QUI\\\\ERP\\\\Order\\\\Order\\.$#" + count: 1 + path: src/QUI/ERP/Customer/OpenItemsList/Handler.php + + - + message: "#^PHPDoc tag @var for variable \\$TransactionA contains unknown class QUI\\\\ERP\\\\Accounting\\\\Payments\\\\Transactions\\\\Transaction\\.$#" + count: 2 + path: src/QUI/ERP/Customer/OpenItemsList/Handler.php + + - + message: "#^PHPDoc tag @var for variable \\$TransactionB contains unknown class QUI\\\\ERP\\\\Accounting\\\\Payments\\\\Transactions\\\\Transaction\\.$#" + count: 2 + path: src/QUI/ERP/Customer/OpenItemsList/Handler.php + + - + message: "#^Parameter \\$Invoice of method QUI\\\\ERP\\\\Customer\\\\OpenItemsList\\\\Handler\\:\\:parseInvoiceToOpenItem\\(\\) has invalid type QUI\\\\ERP\\\\Accounting\\\\Invoice\\\\Invoice\\.$#" + count: 2 + path: src/QUI/ERP/Customer/OpenItemsList/Handler.php + + - + message: "#^Parameter \\$Order of method QUI\\\\ERP\\\\Customer\\\\OpenItemsList\\\\Handler\\:\\:parseOrderToOpenItem\\(\\) has invalid type QUI\\\\ERP\\\\Order\\\\Order\\.$#" + count: 2 + path: src/QUI/ERP/Customer/OpenItemsList/Handler.php + + - + message: "#^Strict comparison using \\=\\=\\= between 'ad\\.firstname'\\|'ad\\.lastname'\\|'company'\\|'customerId'\\|'email'\\|'firstname'\\|'lastname'\\|'usergroup'\\|'userId'\\|'username' and 'users\\.customerId' will always evaluate to false\\.$#" + count: 1 + path: src/QUI/ERP/Customer/Search.php diff --git a/phpstan.dist.neon b/phpstan.dist.neon index 28b833b0c7f267b8fd9421e8d34bc372bec7f20e..56d8beab0e0ae895340157b07790b8ad830f8d48 100644 --- a/phpstan.dist.neon +++ b/phpstan.dist.neon @@ -2,7 +2,7 @@ includes: - phpstan-baseline.neon parameters: - level: 1 + level: 5 paths: - src - ajax diff --git a/phpunit.dist.xml b/phpunit.dist.xml new file mode 100644 index 0000000000000000000000000000000000000000..f6c7becf0c12757beb871a9333e2d81e02aa7cae --- /dev/null +++ b/phpunit.dist.xml @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="UTF-8"?> +<phpunit bootstrap="tests/phpunit-bootstrap.php"> + <testsuites> + <testsuite name="Tests"> + <directory>tests/</directory> + </testsuite> + </testsuites> +</phpunit> diff --git a/settings.xml b/settings.xml index 7702e5b80459e3f4e3082248438e09d9ddac39c6..34348236a9b95cedea415d7cb1d4801d5287190c 100644 --- a/settings.xml +++ b/settings.xml @@ -5,7 +5,7 @@ <config> <section name="customer"> <conf name="groupId"> - <type><![CDATA[integer]]></type> + <type><![CDATA[string]]></type> </conf> <conf name="customerLogin"> <type>integer</type> @@ -17,6 +17,10 @@ <conf name="customerNoPrefix"> <type><![CDATA[string]]></type> </conf> + <conf name="setCustomerNoAtOrder"> + <type><![CDATA[boolean]]></type> + <defaultvalue>1</defaultvalue> + </conf> </section> <section name="openItems"> @@ -74,6 +78,15 @@ </description> </input> + <input conf="customer.setCustomerNoAtOrder" type="checkbox"> + <text> + <locale group="quiqqer/customer" var="customer.settings.setCustomerNoAtOrder"/> + </text> + <description> + <locale group="quiqqer/customer" var="customer.settings.setCustomerNoAtOrder.description"/> + </description> + </input> + </settings> <settings title="openItems" name="openItems"> diff --git a/src/QUI/ERP/Customer/CustomerFiles.php b/src/QUI/ERP/Customer/CustomerFiles.php index 70cc46aa6f244c8d4801f9da7674bb2418e1ebc5..67933316e48d06b89dd6d4bb1ab28de004490276 100644 --- a/src/QUI/ERP/Customer/CustomerFiles.php +++ b/src/QUI/ERP/Customer/CustomerFiles.php @@ -58,12 +58,40 @@ public static function getFolderPath(QUI\Interfaces\Users\User $User): string $fileDir = $varDir . $User->getId(); - // if dir with id exists, rename it to hash folder + // dir migration if (is_dir($fileDir)) { $fileUuidDir = $varDir . $User->getUUID(); - rename($fileDir, $fileUuidDir); + if (!is_dir($fileUuidDir)) { + rename($fileDir, $fileUuidDir); + } else { + if (!($dh = opendir($fileDir))) { + throw new QUI\Exception('Users without ID cannot have a customer file folder.'); + } + + while (($file = readdir($dh)) !== false) { + if ($file == "." || $file == "..") { + continue; + } + + if (file_exists($fileUuidDir . $file)) { + unlink($fileUuidDir . $file); + continue; + } + + rename( + $fileDir . $file, + $fileUuidDir . $file + ); + } + + closedir($dh); + rmdir($fileDir); + } + $fileDir = $fileUuidDir; + } else { + $fileDir = $varDir . $User->getUUID(); } QUI\Utils\System\File::mkdir($fileDir); diff --git a/src/QUI/ERP/Customer/Customers.php b/src/QUI/ERP/Customer/Customers.php index 462b0e9dcb37d7ae74cafe55248ffe3a0be55c0c..04398f7b8d6f19f929f61b7f9878be83a7539ce8 100644 --- a/src/QUI/ERP/Customer/Customers.php +++ b/src/QUI/ERP/Customer/Customers.php @@ -67,11 +67,7 @@ public function createCustomer($customerId, array $address = [], array $groupIds $User->save(); if (!empty($address)) { - try { - $Address = $User->getStandardAddress(); - } catch (QUI\Exception) { - $Address = $User->addAddress(); - } + $Address = $User->getStandardAddress(); $needles = [ 'salutation', @@ -345,17 +341,14 @@ public function setAttributesToCustomer(bool|int|string $userId, array $attribut $groups = []; if (isset($attributes['group'])) { - $groups[] = (int)$attributes['group']; - $User->setAttribute('mainGroup', (int)$attributes['group']); + $groups[] = $attributes['group']; + $User->setAttribute('mainGroup', $attributes['group']); } elseif (isset($attributes['group']) && $attributes['group'] === null) { $User->setAttribute('mainGroup', false); } - if (isset($attributes['groups'])) { - if (str_contains($attributes['groups'], ',')) { - $attributes['groups'] = explode(',', $attributes['groups']); - } - + if (!empty($attributes['groups'])) { + $attributes['groups'] = explode(',', $attributes['groups']); $groups = array_merge($groups, $attributes['groups']); } diff --git a/src/QUI/ERP/Customer/EventHandler.php b/src/QUI/ERP/Customer/EventHandler.php index 90bd411e22ec55cd1a0fe5023afc71924dc7a272..b150c1aa159fa5b6688ccf03f7ec27ce9cca8631 100644 --- a/src/QUI/ERP/Customer/EventHandler.php +++ b/src/QUI/ERP/Customer/EventHandler.php @@ -16,11 +16,13 @@ use function array_merge; use function array_values; +use function count; use function dirname; use function file_exists; use function is_array; use function is_numeric; use function json_decode; +use function json_encode; use function md5; use function trim; @@ -241,7 +243,7 @@ public static function onUserSaveEnd(QUI\Users\User $User): void if (isset($attributes['mainGroup'])) { try { - $mainGroup = (int)$attributes['mainGroup']; + $mainGroup = $attributes['mainGroup']; QUI::getGroups()->get($mainGroup); $data['mainGroup'] = $mainGroup; @@ -297,14 +299,14 @@ public static function onUserSaveEnd(QUI\Users\User $User): void /** * @param QUI\Users\User $User * @param bool|string $code - * @param null|QUI\Interfaces\Users\User $ParentUser + * @param null|QUI\Interfaces\Users\User $PermissionUser * * @throws QUI\Users\Exception|QUI\Exception */ public static function onUserActivateBegin( QUI\Users\User $User, bool|string $code, - ?QUI\Interfaces\Users\User $ParentUser + ?QUI\Interfaces\Users\User $PermissionUser ): void { $Group = Utils::getInstance()->getCustomerGroup(); @@ -352,6 +354,23 @@ public static function onQuiqqerOrderCustomerDataSaveEnd( QUI\ERP\Customer\Customers::getInstance()->addUserToCustomerGroup($User->getUUID()); } catch (QUI\Exception $Exception) { QUI\System\Log::addDebug($Exception->getMessage()); + return; + } + + // setting: automatically add customer number when ordering + $Config = QUI::getPackage('quiqqer/customer')->getConfig(); + + if ($Config->get('customer', 'setCustomerNoAtOrder') && !$User->getAttribute('customerId')) { + $NumberRange = new NumberRange(); + $nextCustomerNo = $NumberRange->getNextCustomerNo(); + + try { + $User->setAttribute('customerId', $nextCustomerNo); + $User->save(QUI::getUsers()->getSystemUser()); + + $NumberRange->setRange($nextCustomerNo + 1); + } catch (QUI\Exception) { + } } } @@ -369,14 +388,7 @@ public static function onFrontendUserDataMiddle( QUI\Users\User $User, $Address ): void { - try { - $Engine = QUI::getTemplateManager()->getEngine(); - } catch (QUI\Exception $Exception) { - QUI\System\Log::writeException($Exception); - - return; - } - + $Engine = QUI::getTemplateManager()->getEngine(); $canEdit = QUI\Permissions\Permission::hasPermission('quiqqer.customer.FrontendUsers.contactPerson.edit'); $canView = QUI\Permissions\Permission::hasPermission('quiqqer.customer.FrontendUsers.contactPerson.view'); @@ -429,6 +441,70 @@ public static function onQuiqqerMigrationV2(MigrationV2 $Console): void 'userId' ); + // users extra fields + $Console->writeLn('- Migrate customer attributes'); + + $userTable = QUI::getUsers()->table(); + $tableAddresses = QUI::getUsers()->tableAddress(); + + $result = QUI::getDataBase()->fetch([ + 'from' => $userTable + ]); + + foreach ($result as $entry) { + $extra = json_decode($entry['extra'], true); + + if (!empty($extra['quiqqer.erp.customer.contact.person'])) { + if (is_numeric($extra['quiqqer.erp.customer.contact.person'])) { + try { + $extra['quiqqer.erp.customer.contact.person'] = QUI::getUsers()->get( + $extra['quiqqer.erp.customer.contact.person'] + )->getUUID(); + } catch (QUI\Exception) { + } + } + } + + if (!empty($extra['quiqqer.erp.address'])) { + if (is_numeric($extra['quiqqer.erp.address'])) { + try { + $addressData = QUI::getDataBase()->fetch([ + 'from' => $tableAddresses, + 'where' => [ + 'id' => $extra['quiqqer.erp.address'] + ] + ]); + + if (count($addressData)) { + $extra['quiqqer.erp.address'] = $addressData[0]['uuid']; + } + } catch (QUI\Exception) { + } + } + } + + if (!empty($extra['quiqqer.erp.supplier.contact.person'])) { + if (is_numeric($extra['quiqqer.erp.supplier.contact.person'])) { + try { + $extra['quiqqer.erp.supplier.contact.person'] = QUI::getUsers()->get( + $extra['quiqqer.erp.supplier.contact.person'] + )->getUUID(); + } catch (QUI\Exception) { + } + } + } + + try { + QUI::getDataBase()->update( + $userTable, + ['extra' => json_encode($extra)], + ['id' => $entry['id']] + ); + } catch (QUI\Exception) { + } + } + + // migrate settings $Console->writeLn('- Migrate customer settings'); diff --git a/src/QUI/ERP/Customer/OpenItemsList/Handler.php b/src/QUI/ERP/Customer/OpenItemsList/Handler.php index f179b524e2e8f05c50559eadc501f93e57a04aac..f4b2a01e7daaa59307d9383107bdbcf412442ca2 100644 --- a/src/QUI/ERP/Customer/OpenItemsList/Handler.php +++ b/src/QUI/ERP/Customer/OpenItemsList/Handler.php @@ -195,7 +195,7 @@ protected static function parseInvoiceToOpenItem(Invoice $Invoice): Item $Item = new Item($Invoice->getId(), self::DOCUMENT_TYPE_INVOICE); // Basic data - $Item->setDocumentNo($Invoice->getId()); + $Item->setDocumentNo($Invoice->getPrefixedNumber()); $Item->setDate(date_create($Invoice->getAttribute('c_date'))); $Item->setDueDate(date_create($Invoice->getAttribute('time_for_payment'))); $Item->setGlobalProcessId($Invoice->getGlobalProcessId()); diff --git a/src/QUI/ERP/Customer/OpenItemsList/OutputProvider.php b/src/QUI/ERP/Customer/OpenItemsList/OutputProvider.php index ec2d85adeafd644ffdb6faab33cf7ec4a998159c..8a78f318b1f3360a2599a809c72e38cf97ff9e06 100644 --- a/src/QUI/ERP/Customer/OpenItemsList/OutputProvider.php +++ b/src/QUI/ERP/Customer/OpenItemsList/OutputProvider.php @@ -76,7 +76,7 @@ public static function getDownloadFileName(int|string $entityId): string return $Locale->get('quiqqer/customer', 'OutputProvider.download_filename', [ 'date' => $Date->format('Y-m-d'), - 'uid' => $ERPUser->getUUID() + 'uid' => $ERPUser->getCustomerNo() ]); } diff --git a/src/QUI/ERP/Customer/Search.php b/src/QUI/ERP/Customer/Search.php index bec083a3576ffc61d1fdc05b945658465a9474e1..5c507cabbf46a91eb1a23aed5b1ca7062d0c375b 100644 --- a/src/QUI/ERP/Customer/Search.php +++ b/src/QUI/ERP/Customer/Search.php @@ -200,8 +200,25 @@ protected function parseListForGrid($data): array $Address = null; $uuid = ''; + if (!empty($entry['user_uuid'])) { + $entry['uuid'] = $entry['user_uuid']; + $entry['user_id'] = $entry['user_uuid']; + } + + if (!empty($entry['uuid'])) { + $entry['user_id'] = $entry['uuid']; + } + + if (empty($entry['user_id']) && !empty($entry['id'])) { + $entry['user_id'] = $entry['id']; + } + + if (empty($entry['user_id'])) { + continue; + } + try { - $User = $Users->get((int)$entry['user_id']); + $User = $Users->get($entry['user_id']); $uuid = $User->getUUID(); $Address = $User->getStandardAddress(); } catch (QUI\Exception) { @@ -257,7 +274,7 @@ protected function parseListForGrid($data): array 'id' => (int)$entry['id'], 'customerId' => $entry['customerId'], 'status' => !!$entry['active'], - 'user_id' => (int)$entry['user_id'], + 'user_id' => $entry['user_id'], 'user_uuid' => $uuid, 'username' => $entry['username'], 'firstname' => $entry['firstname'], @@ -558,7 +575,7 @@ protected function getQuery(bool $count = false): array users.`email` as user_email FROM $table as users LEFT JOIN users_address AS ad ON users.id = ad.uid - AND users.address = ad.id + AND users.address = ad.uuid {$whereQuery} ) as search_query ", @@ -568,14 +585,17 @@ protected function getQuery(bool $count = false): array return [ "query" => " - SELECT users.`id` as user_id, - users.`firstname` as user_firstname, - users.`lastname` as user_lastname, - users.`email` as user_email, - users.*, ad.* + SELECT + users.`id` as user_id, + users.`firstname` as user_firstname, + users.`lastname` as user_lastname, + users.`email` as user_email, + users.`uuid` as user_uuid, + users.*, + ad.* FROM $table as users LEFT JOIN users_address AS ad ON users.id = ad.uid - AND users.address = ad.id + AND users.address = ad.uuid {$whereQuery} ORDER BY {$order} {$limit} diff --git a/tests/phpunit-bootstrap.php b/tests/phpunit-bootstrap.php new file mode 100644 index 0000000000000000000000000000000000000000..eca92fd67bed8ae4ec424ed82d300119d792f042 --- /dev/null +++ b/tests/phpunit-bootstrap.php @@ -0,0 +1,11 @@ +<?php + +if (!defined('QUIQQER_SYSTEM')) { + define('QUIQQER_SYSTEM', true); +} + +if (!defined('QUIQQER_AJAX')) { + define('QUIQQER_AJAX', true); +} + +require_once __DIR__ . '/../../../../bootstrap.php';