diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 68fe9de68d6..d7463d62172 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -26,8 +26,9 @@ Default **language here is english**. So please prepare your contributions in en 1. [Fork](https://help.github.com/articles/fork-a-repo) the [GitHub repository](https://github.com/Dolibarr/dolibarr). 2. Clone your fork. 3. Choose a branch(See the [Branches](#branches) section below). -4. Commit and push your changes. -5. [Make a pull request](https://help.github.com/articles/creating-a-pull-request). +4. Read our developer documentation on the [Dolibarr Wiki](https://wiki.dolibarr.org/index.php?title=Developer_documentation). +5. Commit and push your changes. +6. [Make a pull request](https://help.github.com/articles/creating-a-pull-request). ### Branches diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md deleted file mode 100644 index 432f30f2332..00000000000 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ /dev/null @@ -1,35 +0,0 @@ ---- -name: Bug report -about: Create a report to help us fix something that is broken -title: '' -labels: Bug -assignees: '' - ---- - -# Instructions -*This is a template to help you report good issues. You may use [Github Markdown](https://help.github.com/articles/getting-started-with-writing-and-formatting-on-github/) syntax to format your issue report.* -*Please:* -- *replace the bracket enclosed texts with meaningful information* -- *remove any unused sub-section* - - -# Bug -[*Short description*] - -## Environment -- **Version**: [*Affected Dolibarr version(s)*] -- **OS**: [*Server OS type and version*] -- **Web server**: [*Webserver type and version*] -- **PHP**: [*PHP version*] -- **Database**: [*Database type and version*] -- **URL(s)**: [*Affected URL(s)*] - -## Expected and actual behavior -[*Verbose description*] - -## Steps to reproduce the behavior -[*Verbose description*] - -## [Attached files](https://help.github.com/articles/issue-attachments) (Screenshots, screencasts, dolibarr.log, debugging informations…) -[*Files*] diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 00000000000..d7dc3d584e3 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,71 @@ +name: Bug report +description: Create a report to help us fix something that is broken +labels: ["Bug"] + +body: + - type: markdown + attributes: + value: | + This is a template to help you report good issues. You may use [Github Markdown](https://help.github.com/articles/getting-started-with-writing-and-formatting-on-github/) syntax to format your issue report. + + - type: textarea + id: bug + attributes: + label: Bug + description: Please give a short description of the bug + validations: + required: true + + - type: input + id: environment-version + attributes: + label: Environment Version + description: Affected Dolibarr version(s) + + - type: input + id: environment-os + attributes: + label: Environment OS + description: Server OS type and version + + - type: input + id: environment-webserver + attributes: + label: Environment Web server + description: Webserver type and version + + - type: input + id: environment-php + attributes: + label: Environment PHP + description: PHP version + + - type: input + id: environment-database + attributes: + label: Environment Database + description: Database type and version + + - type: input + id: environment-urls + attributes: + label: Environment URL(s) + description: Affected URL(s) + + - type: textarea + id: expected-behaviour + attributes: + label: Expected and actual behavior + description: Verbose description + + - type: textarea + id: reproduce + attributes: + label: Steps to reproduce the behavior + description: Verbose description + + - type: textarea + id: files + attributes: + label: Attached files + description: Screenshots, screencasts, dolibarr.log, debugging informations diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md deleted file mode 100644 index 32e2deff2c1..00000000000 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ /dev/null @@ -1,27 +0,0 @@ ---- -name: Feature request -about: Suggest a new idea for this project -title: '' -labels: Feature request -assignees: '' - ---- - -# Instructions -*This is a template to help you report good issues. You may use [Github Markdown](https://help.github.com/articles/getting-started-with-writing-and-formatting-on-github/) syntax to format your issue report.* -*Please:* -- *replace the bracket enclosed texts with meaningful information* -- *remove any unused sub-section* - - -# Feature Request -[*Short description*] - -## Use case -[*Verbose description*] - -## Suggested implementation -[*Verbose description*] - -## Suggested steps -[*List of tasks to achieve goal*] diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 00000000000..885f3472d18 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,35 @@ +name: Feature request +description: Suggest a new idea for this project +labels: ["Feature request"] + +body: + - type: markdown + attributes: + value: | + This is a template to help you report good issues. You may use [Github Markdown](https://help.github.com/articles/getting-started-with-writing-and-formatting-on-github/) syntax to format your issue report. + + - type: textarea + id: feature-request + attributes: + label: Feature Request + description: Short description + validations: + required: true + + - type: textarea + id: use-case + attributes: + label: Use case + description: Verbose description + + - type: textarea + id: suggested-implementation + attributes: + label: Suggested implementation + description: Verbose description + + - type: textarea + id: suggested-steps + attributes: + label: Suggested steps + description: List of tasks to achieve goal diff --git a/.gitignore b/.gitignore index 371c8be2f5e..e4790fe7b4e 100644 --- a/.gitignore +++ b/.gitignore @@ -55,3 +55,4 @@ yarn.lock package-lock.json doc/install.lock +/factory/ diff --git a/.travis.yml b/.travis.yml index b48a3667bb0..5d7eb7a1678 100644 --- a/.travis.yml +++ b/.travis.yml @@ -57,7 +57,7 @@ jobs: php: nightly env: DB=mysql - stage: PHP Dev - if: type = push AND branch = 14.0 + if: type = push AND branch = 15.0 php: nightly env: DB=mysql @@ -411,6 +411,12 @@ script: php upgrade.php 13.0.0 14.0.0 ignoredbversion > $TRAVIS_BUILD_DIR/upgrade13001400.log php upgrade2.php 13.0.0 14.0.0 > $TRAVIS_BUILD_DIR/upgrade13001400-2.log php step5.php 13.0.0 14.0.0 > $TRAVIS_BUILD_DIR/upgrade13001400-3.log + php upgrade.php 14.0.0 15.0.0 ignoredbversion > $TRAVIS_BUILD_DIR/upgrade14001500.log + php upgrade2.php 14.0.0 15.0.0 > $TRAVIS_BUILD_DIR/upgrade14001500-2.log + php step5.php 14.0.0 15.0.0 > $TRAVIS_BUILD_DIR/upgrade14001500-3.log + php upgrade.php 15.0.0 16.0.0 ignoredbversion > $TRAVIS_BUILD_DIR/upgrade15001600.log + php upgrade2.php 15.0.0 16.0.0 > $TRAVIS_BUILD_DIR/upgrade15001600-2.log + php step5.php 15.0.0 16.0.0 > $TRAVIS_BUILD_DIR/upgrade15001600-3.log ls -alrt $TRAVIS_BUILD_DIR/ - | diff --git a/ChangeLog b/ChangeLog index 1c0e96fc27c..030650d5863 100644 --- a/ChangeLog +++ b/ChangeLog @@ -15,7 +15,7 @@ NEW: Add option to disable globaly some notifications emails. NEW: #18326 Workflow: Close order on shipment closing. NEW: #18401 Add __NEWREF__ subtitute to get new object reference. NEW: #18403 Add __URL_SHIPMENT__ substitute to get the URL of a shipment -NEW: #18689 REST API module: add api key generate / modify right. +NEW: #18689 REST API module: add api key generate / modify pemrission. NEW: #18663 Make "L'Annuaire des Entreprises" the default provider for SIREN verification for French thirdparties. NEW: #18046 Add tags on ticket/categories NEW: #18326 Workflow: Close order on shipment closing. @@ -105,6 +105,15 @@ NEW: Use an ajax call for the clicktodial feature instead of href link. NEW: when multiple order linked to facture, show list into note. NEW: when we delete several objects with massaction, if somes object has child we must see which objects are concerned and nevertheless delete objects which can be deleted NEW: Editing a page in website module keep old page with name .back +NEW: External backups can be downloaded from the "About info page". +NEW: Add massaction to switch status on sale / on purchase of a product. + + + Modules +NEW: Stable module Knowledge Management +NEW: Experimental module Event Organization Management +NEW: Experimental module Workstations Management +NEW: Development of module Partnership Management For developers: @@ -114,6 +123,7 @@ NEW: Introduce method hasRight NEW: Can use textarea field into a confirm popup. NEW: Can use the result_mode of mysqli driver. Save memory for list count NEW: #18319 REST API - Shipment: Add 'close' action / endpoint / POST method. +NEW: Add API /approve and /makeOrder for purchase orders. NEW: add action trigger for member excluded NEW: add option MAIN_IBAN_IS_NEVER_MANDATORY, MAIN_IBAN_NOT_MANDATORY, PROPAL_NOT_BILLABLE, PROPAL_REOPEN_UNSIGNED_ONLY, PROPOSAL_ARE_NOT_BILLABLE, TICKETS_MESSAGE_FORCE_MAIL NEW: Add code codebar column on serial/lot structure @@ -131,17 +141,179 @@ NEW: printFieldListFrom hook call on several lists NEW: Use lang selector when using a field key 'lang' in modulebuilder NEW: we need to be able to put more filters on deleteByParentField() function NEW: make it easier to set the `keyword`, `keywords` and `description` attributes of an ecm file object - - +NEW: Experimental feature to manage user sessions in database +NEW: Hidden option API_DISABLE_COMPRESSION is now visible in API setup page. +NEW: Add hook printUnderHeaderPDFline on invoice PDF templates (can be used for example to add a barcode or more information on header of invoices). Following changes may create regressions for some external modules, but were necessary to make Dolibarr better: +* ALL EXTERNAL MODULES THAT WERE NOT CORRECTLY DEVELOPPED WILL NOT WORK ON V15 (All modules that forgot to manage the security token field + into forms will be broken. The security token field is expected since Dolibarr v9 but a lot of external modules did not implement it). * Update hook 'printOriginObjectLine', removed check on product type and special code. Need now reshook. * Old deprecated module "SimplePOS" has been completely removed. Use module "TakePOS" is you need a Point Of Sale. * The method static ActionComm::getActions($db, ...) is no more static. Use $actioncomm->getActions(...) instead (without $db param). * The 'action=delete&file=...' has been replaced with 'action=deletefile&file=...' to avoid confusion with deletion of object lines. * Method getDictvalue has been renamed into getDictionaryValue to match camel case rule. +* To execute shell or command line command, your code must never use method like exec, shell_exec, popen, .. but must use the built-in + method executeCLI() available into core/class/utils.class.php +* Class file expeditionbatch.class.php renamed to expeditionlinebatch.class.php +* ExpeditionLineBatch::fetchAll is not static anymore and first parameter $db is removed +* ExtraFields->showOutputField parameter 4 'extrafieldsobjectkey' is now required +* CommonObject method add_object_linked now sets targettype to 'mymodule_myobject' instead of 'myobject', + you can use hook 'setLinkedObjectSourceTargetType' to set your usual targettype +***** ChangeLog for 14.0.5 compared to 14.0.4 ***** + +FIX: 13.0: printFieldListWhere called twice on same query +FIX: 14.0.4 fatal error on cron list. +FIX: #19476 +FIX: #19564 +FIX: #19651 +FIX: Accountancy - SQL error on subledger account search in journal +FIX: apply eldy's suggestion to not overwrite existing extrafields of $line +FIX: Can't close a down payment if paid with credit notes. +FIX: better compatibility with multicompany +FIX: contact card: bad colspan value for separator extrafield in creation/modification form +FIX: discounts are applied both when fetching the best supplier price and when displaying it +FIX: double display for contact categorie on societe create card +FIX: fatal error on cron list. +FIX: holiday list: only mass delete if leave request is not in draft, canceled or refused, like in card +FIX: holiday mass deletion: correct return of record deleted +FIX: Holiday month report +FIX: info tab on customer invoice record not found +FIX: line extrafields are inoperative in dispatch cards even when they exist +FIX: list of categories in stats of supplier invoices +FIX: missing default value for more comprehensive +FIX: multicurrency: fields in discount unitialized when creating deposit +FIX: Navigation on bank transaction list +FIX: Can't edit a bank transaction due to bad permission check. +FIX: Option MAIN_DIRECT_STATUS_UPDATE broken. Ajax on/off not saving value in DB after updating to version >=12 +FIX: postgresql compatibility, "" as is not authorized +FIX: printFieldListWhere called twice (at different locations) for the same SQL query, can result in syntax errors +FIX: select too large into addrights (pb of missing parenthesis) +FIX: set optional from post, we can't untick boolean field on product card +FIX: Take into consideration work leave over serveral months +FIX: test if method exist on wrong object +FIX: title for nature of third party in company list +FIX: Urgent onglet contact inaccessible depuis une facture +FIX: wrong syntax of sql request + +***** ChangeLog for 14.0.4 compared to 14.0.3 ***** + +FIX: $totalarray is overwritten, totals were lost +FIX: 13.0 - due to a typo in the 'mode' parameter, the "first name" column of the list of members displays the full name +FIX: 13.0: end date required to edit a ticket message +FIX: 13.0 feedback of PR #18993: make ticket messages punctual events with attr percentage = -1 +FIX: 13.0 PR #18993: add comment on modified part +FIX: 13.0: sometimes firstname was mistyped as fistname +FIX: 14.0 - civility field of private third party creation form has inadequate width +FIX: 14.0 - civility field width inadequate due to select2 calculating the width while the field has no width (display: none) +FIX: 14.0 - due to a typo in the 'mode' parameter, the "first name" co… +FIX: #18634 : Problem of virtual stock with reception module enabled +FIX: #18695 Added ref_ext to supplier invoice +FIX: #18698 Supplier invoice list - "alert" checkbox not working +FIX: #18735 +FIX: #18767 : Adherent delete +FIX: #18797 +FIX: #18854 +FIX: #18875 in v14 +FIX: #18910 +FIX: #18910 : MRP List SQL query syntax error with more than one extrafileds. +FIX: #18912 Accountancy - SQL error when custom group is added without country defined +FIX: #18934 on-registration in the extrafieldsline database for deliveries +FIX: #18968 +FIX: #19008 +FIX: #19014 - the properties of some fields are not updated when you submit the form +FIX: #19210 +FIX: #19214 : PostgreSQL error on admin/limits.php +FIX: #19241 Project - Fix display salary in overview +FIX: #19305 +FIX: 2 columns for total labels +FIX: Accountancy - Format Quadra export - Missing line type C to create automaticly a subledger account with label +FIX: Accountancy - If deposit invoice is used, force binding in deposit accounting account to solve transaction +FIX: Accountancy - Missing specific filename for export on format FEC2, Ciel & repare it +FIX: Accountancy - Option of export popup are inverted +FIX: Accountancy - PHP8 +FIX: Accountancy - Product admin - SQL error when we affect accounting account with product_perentity activated +FIX: Accountancy simplified - Salaries are not present in report +FIX: Accountancy - Some correction on export name +FIX: Accountancy - Trunc code_journal to 2 in format XIMPORT (Ciel, Sage50) +FIX: add warehouse in projects' overview count +FIX: also on customer index for automatic binding +FIX: Attachment of pdf into shipment when sending email +FIX: autocalculation of the supplier price in main currency. +FIX: avoid warning if $categories is an id +FIX: bad sign of amount stored for multicurrency columns on credit notes +FIX: Bad use of a forced contact of another company on PDF/ODT documents +FIX: Bad use of dol_concatdesc() +FIX: Button text on proposal card for create a invoice +FIX: calculateCosts of BOM must not be included into fetch +FIX: calculation of balance in conciliation page on desc sorting. +FIX: card.php +FIX: Change date format of the inventorycode to be equal as mass stock transfert +FIX: check if greater 0 +FIX: close cash with some terminals in TakePOS +FIX: compatibility with Multicompany +FIX: consistent UX when calling a tab from the invoice card with empty ref/id +FIX: default language defined for IN country +FIX: Expense report - In edit mode, field qty doesn't accept decimal unlike the create mode +FIX: fetch of product with modulebuilder load too much data +FIX: filter for export of accounting documents +FIX: Filter on categories +FIX: generate documents with PDF options +FIX: indentation +FIX: init hookmanager after loading $conf values +FIX: invoice: inpossible to create an invoice because of very bad check + warnings when trying to print tabs for invoice with no ID +FIX: legal issue on expense report pdf (must also show price without tax) +FIX: list of categories in stats of supplier invoices +FIX: load tranlate array after setting lang +FIX: lost superadmin grade after edit user card +FIX: missing filter status=1 on rss feeds +FIX: missing permission check reported by me@lainwir3d.net on product api +FIX: missing return status +FIX: missing sql filter by entity +FIX: move fetch_optionnal into $ac_static->fetch() +FIX: only a superadmin can modify entity +FIX: only ones value is return for dictionaries +FIX: optional visibility on create card +FIX: payment style and html5 tags +FIX: payment using wrong type in takepos when too many payment mode +FIX: PR#18931 Remove useless explicit call to dol_shutdown +FIX: Product accountancy affectation with product_perentity activated (PR #18620) +FIX: products/services card: hidden extrafields were overridden +FIX: project task list: extrafields could not be displayed +FIX: Propal list - Problem of pagination on date +FIX: reload user lang +FIX: Remove not complete order from the virtual stock +FIX: Replenish: SQL error when no warehouse has been created + Warning when there are no warehouses +FIX: resource list : Use standard code to handle list filters +FIX: restrictedArea for payment delete +FIX: Ret PR +FIX: second approval back in stable feature as is the setting for minimum amount (last part from PR#14286) +FIX: selected lines on supplier invoice create +FIX: Selection of type "people" for membership must hide the company +FIX: select list of orders not complete when field type of company is on +FIX: show end hours in events linked to objects +FIX: support of localtax on expense report +FIX: task time: can't filter by user with pgsql + show error message +FIX: task time: keep on using natural_search +FIX: tcpdf vulnerability to roman numeral bomb, cf. tecnickom/TCPDF issue #315 +FIX: Test when date of invoie is in future (pb with TZ and offset) +FIX: Ticket - Card - Wrong font awesome library +FIX: Ticket - Duplicate field project when we create ticket from project +FIX: translation into email for member at membership validation. +FIX: Travis Sanitize SQL +FIX: unprivileged user can see task associated with a not allowed project +FIX: URGENT: impossible to create an invoice +FIX: Use of accent into filename of GED +FIX: user date timezone offset +FIX: User salary card - translation problem +FIX: user without permission can set ticket subject +FIX: We need a default price base type in variant creation case with multiprices when parent has been created with only one level price +FIX: wrong array key value +FIX: wrong check +FIX: wrong position of error message +Sync transifex. ***** ChangeLog for 14.0.3 compared to 14.0.2 ***** @@ -497,7 +669,8 @@ Following changes may create regressions for some external modules, but were nec * Removed deprecated substitution key __REFCLIENT__ (replaced with __REF_CLIENT__) * Removed constant MAIN_COUNTRIES_IN_EEC. You can now set if country is in Europe or not from the dictionary of countries. * v14 seems to work correctly on PHP v8 but it generates a lot of verbose warnings. Currently, v14 i snot yet officialy supported with PHP 8. - +* To execute shell or command line command, your code must never use method like exec, shell_exec, popen, .. but must use the built-in + method executeCLI() available into core/class/utils.class.php ***** ChangeLog for 13.0.5 compared to 13.0.4 ***** diff --git a/README.md b/README.md index b0f7a2c3761..4e120a4cb91 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,7 @@ ![Build status](https://img.shields.io/travis/Dolibarr/dolibarr/develop.svg) [![Minimum PHP Version](https://img.shields.io/badge/php-%3E%3D%205.6-8892BF.svg?style=flat-square)](https://php.net/) [![GitHub release](https://img.shields.io/github/v/release/Dolibarr/dolibarr)](https://github.com/Dolibarr/dolibarr) +[![CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/5521/badge)](https://bestpractices.coreinfrastructure.org/projects/5521) Dolibarr ERP & CRM is a modern software package that helps manage your organization's activity (contacts, suppliers, invoices, orders, stocks, agenda…). diff --git a/SECURITY.md b/SECURITY.md index 427b1cc7ae2..cadd4a23791 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -6,14 +6,14 @@ This file contains some policies about the security reports on Dolibarr ERP CRM | Version | Supported | | ---------- | ---------------------- | -| <= 14.0.1 | :x: | -| >= 14.0.2+ | :white_check_mark: except CSRF attacks| +| <= 14.0.4 | :x: | +| >= 14.0.5+ | :white_check_mark: except CSRF attacks| | >= develop | :white_check_mark: | ## Reporting a Vulnerability -To report a vulnerability, please use GitHub security advisory at https://github.com/Dolibarr/dolibarr/security/advisories/new (if you have permissions) or alternatively send an email to security@dolibarr.org (for everybody) - +To report a vulnerability, for a private report, please use GitHub security advisory at [https://github.com/Dolibarr/dolibarr/security/advisories/new](https://github.com/Dolibarr/dolibarr/security/advisories/new) (if you have permissions). +Alternatively send an email to security@dolibarr.org (for everybody) ## Hunting vulnerabilities on Dolibarr @@ -23,7 +23,7 @@ If you believe you've found a security bug in our service, we are happy to work Any type of denial of service attacks is strictly forbidden, as well as any interference with network equipment and Dolibarr infrastructure. -We recommand to install Dolibarr ERP CRM on your own server (as most Open Source software, download and use is free: https://www.dolibarr.org/download) to get access on every side of application. +We recommand to install Dolibarr ERP CRM on your own server (as most Open Source software, download and use is free: [https://www.dolibarr.org/download](https://www.dolibarr.org/download)) to get access on every side of application. ### User Agent @@ -31,8 +31,7 @@ If you try to find bug on Dolibarr, we recommend to append to your user-agent he ### Account access -You can install the web application yourself on your own platform/server so you get full access to application and sources. Download the zip of the files to put into your own web server virtual host from https://www.dolibarr.org/download - +You can install the web application yourself on your own platform/server so you get full access to application and sources. Download the zip of the files to put into your own web server virtual host from [https://www.dolibarr.org/download](https://www.dolibarr.org/download) ## Eligibility and Responsible Disclosure @@ -46,7 +45,6 @@ You must avoid tests that could cause degradation or interruption of our service You must not leak, manipulate, or destroy any user data of third parties to find your vulnerability. - ## Scope for qualified vulnerabilities ONLY vulnerabilities discovered, when the following setup on test platform is used, are "valid": @@ -64,13 +62,12 @@ ONLY vulnerabilities discovered, when the following setup on test platform is us Scope is the web application (back office) and the APIs. - ## Qualifying vulnerabilities for reporting * Remote code execution (RCE) * Local files access and manipulation (LFI, RFI, XXE, SSRF, XSPA) * Code injections (HTML, JS, SQL, PHP, ...) -* Cross-Site Scripting (XSS), except from setup page of module "External web site" (allowing any content here, editable by admin user only, is accepted on purpose or into module "Web site" when permission to edit website content is allowed). +* Cross-Site Scripting (XSS), except from setup page of module "External web site" (allowing any content here, editable by admin user only, is accepted on purpose) and except into module "Web site" when permission to edit website content is allowed (injecting any data in this case is allowed too). * Cross-Site Requests Forgery (CSRF) with real security impact (when using GET URLs, CSRF are qualified only for creating, updating or deleting data from pages restricted to admin users) * Open redirect * Broken authentication & session management @@ -81,7 +78,6 @@ Scope is the web application (back office) and the APIs. * Software version disclosure (for non admin users only) * Stack traces or path disclosure (for non admin users only) - ## Non-qualifying vulnerabilities for reporting * "Self" XSS @@ -99,4 +95,3 @@ Scope is the web application (back office) and the APIs. * Software version or private IP disclosure when logged user is admin * Stack traces or path disclosure when logged user is admin * Any vulnerabilities due to a configuration different than the one defined into chapter "Scope for qualified vulnerabilities". - diff --git a/build/docker/Dockerfile b/build/docker/Dockerfile index eb8e3ade6dc..ead2a8af1c5 100644 --- a/build/docker/Dockerfile +++ b/build/docker/Dockerfile @@ -39,15 +39,13 @@ RUN chmod +x /usr/local/bin/docker-run.sh RUN pecl install xdebug && docker-php-ext-enable xdebug RUN echo 'zend_extension="/usr/local/lib/php/extensions/no-debug-non-zts-20180731/xdebug.so"' >> ${PHP_INI_DIR}/php.ini -RUN echo 'xdebug.remote_autostart=1' >> ${PHP_INI_DIR}/php.ini -RUN echo 'xdebug.remote_enable=1' >> ${PHP_INI_DIR}/php.ini -RUN echo 'xdebug.default_enable=1' >> ${PHP_INI_DIR}/php.ini -#RUN echo 'xdebug.remote_host=docker.host' >> ${PHP_INI_DIR}/php.ini -RUN echo 'xdebug.remote_port=9000' >> ${PHP_INI_DIR}/php.ini -RUN echo 'xdebug.remote_connect_back=1' >> ${PHP_INI_DIR}/php.ini -RUN echo 'xdebug.profiler_enable=0' >> ${PHP_INI_DIR}/php.ini -RUN echo 'xdebug.remote_log="/tmp/xdebug.log"' >> ${PHP_INI_DIR}/php.ini -#RUN echo 'localhost docker.host' >> /etc/hosts +RUN echo 'xdebug.mode=debug' >> ${PHP_INI_DIR}/php.ini +RUN echo 'xdebug.start_with_request=yes' >> ${PHP_INI_DIR}/php.ini +RUN echo 'xdebug.client_host=host.docker.internal' >> ${PHP_INI_DIR}/php.ini +RUN echo 'xdebug.client_port=9003' >> ${PHP_INI_DIR}/php.ini +RUN echo 'xdebug.discover_client_host=true' >> ${PHP_INI_DIR}/php.ini +#RUN echo 'xdebug.log="/tmp/xdebug.log"' >> ${PHP_INI_DIR}/php.ini +RUN echo 'xdebug.idekey="netbeans-xdebug"' >> ${PHP_INI_DIR}/php.ini # set up sendmail config, to use maildev RUN echo "account default" > /etc/msmtprc diff --git a/build/docker/docker-compose.yml b/build/docker/docker-compose.yml index b72118de5fb..8994043cd8a 100644 --- a/build/docker/docker-compose.yml +++ b/build/docker/docker-compose.yml @@ -48,6 +48,7 @@ services: - external-pod extra_hosts: - "localhost.localdomain:127.0.0.1" + - "host.docker.internal:host-gateway" mail: image: maildev/maildev diff --git a/dev/initdemo/sftpget_and_loaddump.php b/dev/initdemo/sftpget_and_loaddump.php index 7d781fe5b0c..63b5ac65054 100755 --- a/dev/initdemo/sftpget_and_loaddump.php +++ b/dev/initdemo/sftpget_and_loaddump.php @@ -4,7 +4,7 @@ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or + * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, diff --git a/dev/initdemo/updatedemo.php b/dev/initdemo/updatedemo.php index 4dd98451823..4ee2032c7cf 100755 --- a/dev/initdemo/updatedemo.php +++ b/dev/initdemo/updatedemo.php @@ -4,7 +4,7 @@ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or + * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, diff --git a/dev/resources/iso-normes/Intracommreport-ManuelDebXml.pdf b/dev/resources/iso-normes/Intracommreport-ManuelDebXml.pdf deleted file mode 100644 index fef9f48f53e..00000000000 Binary files a/dev/resources/iso-normes/Intracommreport-ManuelDebXml.pdf and /dev/null differ diff --git a/dev/resources/iso-normes/QR code for invoices.txt b/dev/resources/iso-normes/QR code for invoices.txt new file mode 100644 index 00000000000..a55c9569297 --- /dev/null +++ b/dev/resources/iso-normes/QR code for invoices.txt @@ -0,0 +1,13 @@ +List of QR Code format we found on some invoices +------------------------------------------------ + + +* For SEPA QR payment Code format (Europe) +------------------------------------------ +https://en.wikipedia.org/wiki/EPC_QR_code#Generators + + + +* For ZATCA QR Code format (Saudi Arabia) +----------------------------------------- +https://www.pwc.com/m1/en/services/tax/me-tax-legal-news/2021/saudi-arabia-guide-to-develop-compliant-qr-code-for-simplified-einvoices.html diff --git a/dev/resources/iso-normes/address_format.txt b/dev/resources/iso-normes/address_format.txt index d87e90e79b5..18069cd89fa 100644 --- a/dev/resources/iso-normes/address_format.txt +++ b/dev/resources/iso-normes/address_format.txt @@ -1,3 +1,5 @@ +Address format + https://bitboost.com/ref/international-address-formats.html#Formats https://www.upu.int/en/Postal-Solutions/Programmes-Services/Addressing-Solutions diff --git a/dev/resources/facturx-zugferd/README.txt b/dev/resources/iso-normes/facturx-zugferd/README.txt similarity index 100% rename from dev/resources/facturx-zugferd/README.txt rename to dev/resources/iso-normes/facturx-zugferd/README.txt diff --git a/dev/resources/iso-normes/format PDF - PDF A.pdf b/dev/resources/iso-normes/format PDF - PDF A.pdf new file mode 100644 index 00000000000..0ceeb5230de Binary files /dev/null and b/dev/resources/iso-normes/format PDF - PDF A.pdf differ diff --git a/dev/resources/iso-normes/format_FEC-Lien_outil_de_test_agréé.pdf b/dev/resources/iso-normes/format_FEC - Lien_outil_de_test_agréé.pdf similarity index 100% rename from dev/resources/iso-normes/format_FEC-Lien_outil_de_test_agréé.pdf rename to dev/resources/iso-normes/format_FEC - Lien_outil_de_test_agréé.pdf diff --git a/dev/resources/iso-normes/sample_FEC_file.txt b/dev/resources/iso-normes/format_FEC - fie example.txt similarity index 100% rename from dev/resources/iso-normes/sample_FEC_file.txt rename to dev/resources/iso-normes/format_FEC - fie example.txt diff --git a/dev/resources/intracommreport/manuelDebXml1.2.pdf b/dev/resources/iso-normes/intracommreport/Intracommreport-ManuelDebXml.pdf similarity index 100% rename from dev/resources/intracommreport/manuelDebXml1.2.pdf rename to dev/resources/iso-normes/intracommreport/Intracommreport-ManuelDebXml.pdf diff --git a/dev/resources/iso-normes/Intracommreport-ManuelDesXML.pdf b/dev/resources/iso-normes/intracommreport/Intracommreport-ManuelDesXML.pdf similarity index 100% rename from dev/resources/iso-normes/Intracommreport-ManuelDesXML.pdf rename to dev/resources/iso-normes/intracommreport/Intracommreport-ManuelDesXML.pdf diff --git a/dev/resources/intracommreport/schema_deb.xsd b/dev/resources/iso-normes/intracommreport/schema_deb.xsd similarity index 100% rename from dev/resources/intracommreport/schema_deb.xsd rename to dev/resources/iso-normes/intracommreport/schema_deb.xsd diff --git a/dev/resources/iso-normes/locales.txt b/dev/resources/iso-normes/locales.txt index 67ea5280342..a4459d2a8b1 100644 --- a/dev/resources/iso-normes/locales.txt +++ b/dev/resources/iso-normes/locales.txt @@ -1,3 +1,5 @@ +Date and number format +---------------------- For languages: https://icu4c-demos.unicode.org/icu-bin/icudemos - Locale Explorer -> Error 404 diff --git a/dev/resources/sepa/pain.001.001.03.xsd b/dev/resources/iso-normes/sepa/pain.001.001.03.xsd similarity index 100% rename from dev/resources/sepa/pain.001.001.03.xsd rename to dev/resources/iso-normes/sepa/pain.001.001.03.xsd diff --git a/dev/resources/sepa/pain.008.001.02.xsd b/dev/resources/iso-normes/sepa/pain.008.001.02.xsd similarity index 100% rename from dev/resources/sepa/pain.008.001.02.xsd rename to dev/resources/iso-normes/sepa/pain.008.001.02.xsd diff --git a/dev/resources/sepa/sample-credit-transfer.xml b/dev/resources/iso-normes/sepa/sample-credit-transfer.xml similarity index 100% rename from dev/resources/sepa/sample-credit-transfer.xml rename to dev/resources/iso-normes/sepa/sample-credit-transfer.xml diff --git a/dev/resources/sepa/sample-direct-debit.xml b/dev/resources/iso-normes/sepa/sample-direct-debit.xml similarity index 100% rename from dev/resources/sepa/sample-direct-debit.xml rename to dev/resources/iso-normes/sepa/sample-direct-debit.xml diff --git a/dev/resources/sepa/text.txt b/dev/resources/iso-normes/sepa/text.txt similarity index 100% rename from dev/resources/sepa/text.txt rename to dev/resources/iso-normes/sepa/text.txt diff --git a/dev/resources/iso-normes/world_tax_rates.txt b/dev/resources/iso-normes/world_tax_rates.txt index c007474e5fd..508446b618a 100644 --- a/dev/resources/iso-normes/world_tax_rates.txt +++ b/dev/resources/iso-normes/world_tax_rates.txt @@ -1,3 +1,6 @@ +VAT Rates +--------- + http://www.taxrates.cc/index.html https://en.wikipedia.org/wiki/List_of_countries_by_tax_rates diff --git a/dev/tools/fixdosfiles.sh b/dev/tools/fixdosfiles.sh index 6fd152127fb..4be867aea98 100755 --- a/dev/tools/fixdosfiles.sh +++ b/dev/tools/fixdosfiles.sh @@ -17,14 +17,14 @@ fi # To detec if [ "x$1" = "xlist" ] then - find . \( -iname "functions" -o -iname "*.md" -o -iname "*.html" -o -iname "*.htm" -o -iname "*.php" -o -iname "*.sh" -o -iname "*.cml" -o -iname "*.css" -o -iname "*.js" -o -iname "*.lang" -o -iname "*.pl" -o -iname "*.sql" -o -iname "*.txt" -o -iname "*.xml" -o -iname "*.pml" \) -exec file "{}" + | grep -v 'documents\/website' | grep -v 'documents\/mdedias' | grep CRLF -# find . \( -iname "*.md" -o -iname "*.html" -o -iname "*.htm" -o -iname "*.php" -o -iname "*.sh" -o -iname "*.cml" -o -iname "*.css" -o -iname "*.js" -o -iname "*.lang" -o -iname "*.pl" -o -iname "*.sql" -o -iname "*.txt" -o -iname "*.xml" \) -exec file "{}" + | grep -v 'documents\/website' | grep -v 'documents\/mdedias' | grep -v 'htdocs\/includes' | grep CRLF + find . \( -iname "functions" -o -iname "*.md" -o -iname "*.html" -o -iname "*.htm" -o -iname "*.php" -o -iname "*.sh" -o -iname "*.cml" -o -iname "*.css" -o -iname "*.js" -o -iname "*.lang" -o -iname "*.pl" -o -iname "*.sql" -o -iname "*.txt" -o -iname "*.xml" -o -iname "*.pml" \) -exec file "{}" + | grep -v 'custom\/' | grep -v 'documents\/website' | grep -v 'documents\/medias' | grep -v 'documents\/sellyoursaas' | grep CRLF +# find . \( -iname "*.md" -o -iname "*.html" -o -iname "*.htm" -o -iname "*.php" -o -iname "*.sh" -o -iname "*.cml" -o -iname "*.css" -o -iname "*.js" -o -iname "*.lang" -o -iname "*.pl" -o -iname "*.sql" -o -iname "*.txt" -o -iname "*.xml" \) -exec file "{}" + | grep -v 'custom\/' | grep -v 'documents\/website' | grep -v 'documents\/medias' | grep -v 'documents\/sellyoursaas' | grep -v 'htdocs\/includes' | grep CRLF fi # To convert if [ "x$1" = "xfix" ] then - for fic in `find . \( -iname "functions" -o -iname "*.md" -o -iname "*.html" -o -iname "*.htm" -o -iname "*.php" -o -iname "*.sh" -o -iname "*.cml" -o -iname "*.css" -o -iname "*.js" -o -iname "*.lang" -o -iname "*.pl" -o -iname "*.sql" -o -iname "*.txt" -o -iname "*.xml" -o -iname "*.pml" \) -exec file "{}" + | grep -v 'documents\/website' | grep -v 'documents\/mdedias' | grep CRLF | awk -F':' '{ print $1 }' ` + for fic in `find . \( -iname "functions" -o -iname "*.md" -o -iname "*.html" -o -iname "*.htm" -o -iname "*.php" -o -iname "*.sh" -o -iname "*.cml" -o -iname "*.css" -o -iname "*.js" -o -iname "*.lang" -o -iname "*.pl" -o -iname "*.sql" -o -iname "*.txt" -o -iname "*.xml" -o -iname "*.pml" \) -exec file "{}" + | grep -v 'custom\/' | grep -v 'documents\/website' | grep -v 'documents\/medias' | grep -v 'documents\/sellyoursaas' | grep CRLF | awk -F':' '{ print $1 }' ` do echo "Fix file $fic" dos2unix "$fic" diff --git a/dev/tools/optimize_images.sh b/dev/tools/optimize_images.sh index 2f8a84c57e7..dd538c5e1aa 100755 --- a/dev/tools/optimize_images.sh +++ b/dev/tools/optimize_images.sh @@ -14,7 +14,8 @@ max_output_size=0 usage() { cat <trans("New"), $langs->trans("Addanaccount"), 'fa fa-plus-circle', './card.php?action=create'); include DOL_DOCUMENT_ROOT.'/core/tpl/massactions_pre.tpl.php'; - print_barre_liste($langs->trans('ListAccounts'), $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, $massactionbutton, $num, $nbtotalofrecords, 'title_accountancy', 0, $newcardbutton, '', $limit, 0, 0, 1); + print_barre_liste($langs->trans('ListAccounts'), $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, $massactionbutton, $num, $nbtotalofrecords, 'accounting_account', 0, $newcardbutton, '', $limit, 0, 0, 1); // Box to select active chart of account print $langs->trans("Selectchartofaccounts")." : "; @@ -404,6 +404,11 @@ if ($resql) { $moreforfilter = ''; + $accountstatic = new AccountingAccount($db); + $accountparent = new AccountingAccount($db); + $totalarray = array(); + $totalarray['nbfield'] = 0; + print '
'; print ''."\n"; @@ -466,11 +471,6 @@ if ($resql) { print_liste_field_titre($selectedfields, $_SERVER["PHP_SELF"], "", '', '', '', $sortfield, $sortorder, 'center maxwidthsearch '); print "\n"; - $accountstatic = new AccountingAccount($db); - $accountparent = new AccountingAccount($db); - $totalarray = array(); - $totalarray['nbfield'] = 0; - $i = 0; while ($i < min($num, $limit)) { $obj = $db->fetch_object($resql); @@ -615,8 +615,13 @@ if ($resql) { } if ($num == 0) { - $totalarray['nbfield']++; - print ''; + $colspan = 1; + foreach ($arrayfields as $key => $val) { + if (!empty($val['checked'])) { + $colspan++; + } + } + print ''; } print "
'.$langs->trans("None").'
'.$langs->trans("None").'
"; diff --git a/htdocs/accountancy/admin/accountmodel.php b/htdocs/accountancy/admin/accountmodel.php index 97a460beaac..29c2b7d5510 100644 --- a/htdocs/accountancy/admin/accountmodel.php +++ b/htdocs/accountancy/admin/accountmodel.php @@ -495,7 +495,7 @@ if ($id) { if ($valuetoshow != '') { print ''; if (!empty($tabhelp[$id][$value]) && preg_match('/^http(s*):/i', $tabhelp[$id][$value])) { - print ''.$valuetoshow.' '.img_help(1, $valuetoshow).''; + print ''.$valuetoshow.' '.img_help(1, $valuetoshow).''; } elseif (!empty($tabhelp[$id][$value])) { print $form->textwithpicto($valuetoshow, $tabhelp[$id][$value]); } else { diff --git a/htdocs/accountancy/admin/card.php b/htdocs/accountancy/admin/card.php index 9430bf33439..91d8257ea7f 100644 --- a/htdocs/accountancy/admin/card.php +++ b/htdocs/accountancy/admin/card.php @@ -417,13 +417,13 @@ if ($action == 'create') { print '
'; if (!empty($user->rights->accounting->chartofaccount)) { - print ''.$langs->trans('Modify').''; + print 'id.'">'.$langs->trans('Modify').''; } else { print ''.$langs->trans('Modify').''; } if (!empty($user->rights->accounting->chartofaccount)) { - print ''.$langs->trans('Delete').''; + print 'id.'">'.$langs->trans('Delete').''; } else { print ''.$langs->trans('Delete').''; } diff --git a/htdocs/accountancy/admin/categories_list.php b/htdocs/accountancy/admin/categories_list.php index d914b825b7f..bb629577ab6 100644 --- a/htdocs/accountancy/admin/categories_list.php +++ b/htdocs/accountancy/admin/categories_list.php @@ -520,7 +520,7 @@ if ($tabname[$id]) { if ($valuetoshow != '') { print ''; if (!empty($tabhelp[$id][$value]) && preg_match('/^http(s*):/i', $tabhelp[$id][$value])) { - print ''.$valuetoshow.' '.img_help(1, $valuetoshow).''; + print ''.$valuetoshow.' '.img_help(1, $valuetoshow).''; } elseif (!empty($tabhelp[$id][$value])) { print $form->textwithpicto($valuetoshow, $tabhelp[$id][$value]); } else { diff --git a/htdocs/accountancy/admin/defaultaccounts.php b/htdocs/accountancy/admin/defaultaccounts.php index 5d78036159a..c99503f95f6 100644 --- a/htdocs/accountancy/admin/defaultaccounts.php +++ b/htdocs/accountancy/admin/defaultaccounts.php @@ -52,6 +52,7 @@ $list_account_main = array( ); $list_account = array(); + $list_account[] = '---Product---'; $list_account[] = 'ACCOUNTING_PRODUCT_SOLD_ACCOUNT'; if ($mysoc->isInEEC()) { @@ -63,6 +64,7 @@ if ($mysoc->isInEEC()) { $list_account[] = 'ACCOUNTING_PRODUCT_BUY_INTRA_ACCOUNT'; } $list_account[] = 'ACCOUNTING_PRODUCT_BUY_EXPORT_ACCOUNT'; + $list_account[] = '---Service---'; $list_account[] = 'ACCOUNTING_SERVICE_SOLD_ACCOUNT'; if ($mysoc->isInEEC()) { @@ -74,11 +76,11 @@ if ($mysoc->isInEEC()) { $list_account[] = 'ACCOUNTING_SERVICE_BUY_INTRA_ACCOUNT'; } $list_account[] = 'ACCOUNTING_SERVICE_BUY_EXPORT_ACCOUNT'; + $list_account[] = '---Others---'; $list_account[] = 'ACCOUNTING_VAT_BUY_ACCOUNT'; $list_account[] = 'ACCOUNTING_VAT_SOLD_ACCOUNT'; $list_account[] = 'ACCOUNTING_VAT_PAY_ACCOUNT'; -$list_account[] = 'ACCOUNTING_ACCOUNT_SUSPENSE'; if ($conf->banque->enabled) { $list_account[] = 'ACCOUNTING_ACCOUNT_TRANSFER_CASH'; } @@ -96,6 +98,7 @@ if ($conf->loan->enabled) { if ($conf->societe->enabled) { $list_account[] = 'ACCOUNTING_ACCOUNT_CUSTOMER_DEPOSIT'; } +$list_account[] = 'ACCOUNTING_ACCOUNT_SUSPENSE'; /* * Actions @@ -154,6 +157,7 @@ print ''; // Define main accounts for thirdparty +print '
'; print ''; print ''; @@ -164,17 +168,29 @@ foreach ($list_account_main as $key) { $keydesc = $key.'_Desc'; $htmltext = $langs->trans($keydesc); - print ''; // Value - print ''; print ''; } +print "
'.$langs->trans("ThirdParties").' | '.$langs->trans("Users").'
'; + print ''; + if ($key == 'ACCOUNTING_ACCOUNT_CUSTOMER') { + print img_picto('', 'company', 'class="pictofixedwidth"'); + } elseif ($key == 'ACCOUNTING_ACCOUNT_SUPPLIER') { + print img_picto('', 'company', 'class="pictofixedwidth"'); + } else { + print img_picto('', 'user', 'class="pictofixedwidth"'); + } print $form->textwithpicto($label, $htmltext); print ''; // Do not force class=right, or it align also the content of the select box + print ''; // Do not force class=right, or it align also the content of the select box print $formaccounting->select_account($conf->global->$key, $key, 1, '', 1, 1, 'minwidth100 maxwidth300 maxwidthonsmartphone', 'accountsmain'); print '
\n"; +print "
\n"; +print '
'; +print ''; + foreach ($list_account as $key) { $reg = array(); if (preg_match('/---(.*)---/', $key, $reg)) { @@ -183,9 +199,32 @@ foreach ($list_account as $key) { print ''; // Param $label = $langs->trans($key); - print ''; + print ''; // Value - print ''; print ''; @@ -194,7 +233,7 @@ foreach ($list_account as $key) { print "
'.$label.''; + if (preg_match('/^ACCOUNTING_PRODUCT/', $key)) { + print img_picto('', 'product', 'class="pictofixedwidth"'); + } elseif (preg_match('/^ACCOUNTING_SERVICE/', $key)) { + print img_picto('', 'service', 'class="pictofixedwidth"'); + } elseif (preg_match('/^ACCOUNTING_VAT_PAY_ACCOUNT/', $key)) { + print img_picto('', 'payment_vat', 'class="pictofixedwidth"'); + } elseif (preg_match('/^ACCOUNTING_VAT/', $key)) { + print img_picto('', 'vat', 'class="pictofixedwidth"'); + } elseif (preg_match('/^ACCOUNTING_ACCOUNT_CUSTOMER/', $key)) { + print img_picto('', 'bill', 'class="pictofixedwidth"'); + } elseif (preg_match('/^LOAN_ACCOUNTING_ACCOUNT/', $key)) { + print img_picto('', 'loan', 'class="pictofixedwidth"'); + } elseif (preg_match('/^DONATION_ACCOUNTING/', $key)) { + print img_picto('', 'donation', 'class="pictofixedwidth"'); + } elseif (preg_match('/^ADHERENT_SUBSCRIPTION/', $key)) { + print img_picto('', 'member', 'class="pictofixedwidth"'); + } elseif (preg_match('/^ACCOUNTING_ACCOUNT_TRANSFER/', $key)) { + print img_picto('', 'bank_account', 'class="pictofixedwidth"'); + } elseif (preg_match('/^ACCOUNTING_ACCOUNT_SUSPENSE/', $key)) { + print img_picto('', 'question', 'class="pictofixedwidth"'); + } + print $label; + print ''; // Do not force class=right, or it align also the content of the select box + print ''; // Do not force class=right, or it align also the content of the select box print $formaccounting->select_account(getDolGlobalString($key), $key, 1, '', 1, 1, 'minwidth100 maxwidth300 maxwidthonsmartphone', 'accounts'); print '
\n"; - +print "
\n"; print '
'; diff --git a/htdocs/accountancy/admin/export.php b/htdocs/accountancy/admin/export.php index 9e393beeacd..8cef3a05cf4 100644 --- a/htdocs/accountancy/admin/export.php +++ b/htdocs/accountancy/admin/export.php @@ -142,7 +142,7 @@ $linkback = ''; print load_fiche_titre($langs->trans('ExportOptions'), $linkback, 'accountancy'); -print "\n".''; print ''; } - print ''; + print ''; print ' '; print ''; print ''; } } - - if ($sortfield == 'b.datev,b.dateo,b.rowid' && $sortorder == 'desc,desc,desc') { + if ($sortfield == 'b.datev,b.dateo,b.rowid' && ($sortorder == 'desc' || $sortorder == 'desc,desc' || $sortorder == 'desc,desc,desc')) { $balance = price2num($balancebefore, 'MT'); // balance = balancebefore of previous line (sort is desc) $balancebefore = price2num($balancebefore - ($sign * $objp->amount), 'MT'); } else { @@ -1340,14 +1339,15 @@ if ($resql) { $labeltoshow = $langs->trans($reg[1]); } else { if ($objp->label == '(payment_salary)') { - $labeltoshow = dol_trunc($langs->trans("SalaryPayment", 40)); + $labeltoshow = $langs->trans("SalaryPayment"); } else { $labeltoshow = dol_escape_htmltag($objp->label); $titletoshow = $objp->label; } } - print ''; - print $labeltoshow; // Already escaped + + + print ''; // Add info about links after description $cachebankaccount = array(); @@ -1356,70 +1356,70 @@ if ($resql) { if ($links[$key]['type'] == 'withdraw') { $banktransferstatic->id = $links[$key]['url_id']; $banktransferstatic->ref = $links[$key]['label']; - print ' '.$banktransferstatic->getNomUrl(0); + print $banktransferstatic->getNomUrl(0).' '; } elseif ($links[$key]['type'] == 'payment') { $paymentstatic->id = $links[$key]['url_id']; $paymentstatic->ref = $links[$key]['url_id']; // FIXME This is id, not ref of payment $paymentstatic->date = $db->jdate($objp->do); - print ' '.$paymentstatic->getNomUrl(2); + print $paymentstatic->getNomUrl(2).' '; } elseif ($links[$key]['type'] == 'payment_supplier') { $paymentsupplierstatic->id = $links[$key]['url_id']; $paymentsupplierstatic->ref = $links[$key]['url_id']; // FIXME This is id, not ref of payment - print ' '.$paymentsupplierstatic->getNomUrl(2); + print $paymentsupplierstatic->getNomUrl(2).' '; } elseif ($links[$key]['type'] == 'payment_sc') { $paymentscstatic->id = $links[$key]['url_id']; $paymentscstatic->ref = $links[$key]['url_id']; $paymentscstatic->label = $links[$key]['label']; - print ' '.$paymentscstatic->getNomUrl(2); + print $paymentscstatic->getNomUrl(2).' '; } elseif ($links[$key]['type'] == 'payment_vat') { $paymentvatstatic->id = $links[$key]['url_id']; $paymentvatstatic->ref = $links[$key]['url_id']; - print ' '.$paymentvatstatic->getNomUrl(2); + print $paymentvatstatic->getNomUrl(2).' '; } elseif ($links[$key]['type'] == 'payment_salary') { $paymentsalstatic->id = $links[$key]['url_id']; $paymentsalstatic->ref = $links[$key]['url_id']; $paymentsalstatic->label = $links[$key]['label']; - print ' '.$paymentsalstatic->getNomUrl(2); + print $paymentsalstatic->getNomUrl(2).' '; } elseif ($links[$key]['type'] == 'payment_loan') { print ''; print ' '.img_object($langs->trans('ShowPayment'), 'payment').' '; - print ''; + print ' '; } elseif ($links[$key]['type'] == 'payment_donation') { $paymentdonationstatic->id = $links[$key]['url_id']; $paymentdonationstatic->ref = $links[$key]['url_id']; - print ' '.$paymentdonationstatic->getNomUrl(2); + print $paymentdonationstatic->getNomUrl(2).' '; } elseif ($links[$key]['type'] == 'payment_expensereport') { $paymentexpensereportstatic->id = $links[$key]['url_id']; $paymentexpensereportstatic->ref = $links[$key]['url_id']; - print ' '.$paymentexpensereportstatic->getNomUrl(2); + print $paymentexpensereportstatic->getNomUrl(2).' '; } elseif ($links[$key]['type'] == 'payment_various') { $paymentvariousstatic->id = $links[$key]['url_id']; $paymentvariousstatic->ref = $links[$key]['url_id']; - print ' '.$paymentvariousstatic->getNomUrl(2); + print $paymentvariousstatic->getNomUrl(2).' '; } elseif ($links[$key]['type'] == 'banktransfert') { // Do not show link to transfer since there is no transfer card (avoid confusion). Can already be accessed from transaction detail. if ($objp->amount > 0) { $banklinestatic->fetch($links[$key]['url_id']); $bankstatic->id = $banklinestatic->fk_account; $bankstatic->label = $banklinestatic->bank_account_ref; - print ' ('.$langs->trans("TransferFrom").' '; + print $langs->trans("TransferFrom").' '; print $bankstatic->getNomUrl(1, 'transactions'); print ' '.$langs->trans("toward").' '; $bankstatic->id = $objp->bankid; $bankstatic->label = $objp->bankref; print $bankstatic->getNomUrl(1, ''); - print ')'; + print ' - '; } else { $bankstatic->id = $objp->bankid; $bankstatic->label = $objp->bankref; - print ' ('.$langs->trans("TransferFrom").' '; + print $langs->trans("TransferFrom").' '; print $bankstatic->getNomUrl(1, ''); print ' '.$langs->trans("toward").' '; $banklinestatic->fetch($links[$key]['url_id']); $bankstatic->id = $banklinestatic->fk_account; $bankstatic->label = $banklinestatic->bank_account_ref; print $bankstatic->getNomUrl(1, 'transactions'); - print ')'; + print ' - '; } //var_dump($links); } elseif ($links[$key]['type'] == 'company') { @@ -1431,22 +1431,22 @@ if ($resql) { // Information is already shown using the payment_salary link. No need of this link. } else { // Show link with label $links[$key]['label'] - if (!empty($objp->label) && !empty($links[$key]['label'])) { - print ' - '; - } print ''; if (preg_match('/^\((.*)\)$/i', $links[$key]['label'], $reg)) { // Label generique car entre parentheses. On l'affiche en le traduisant if ($reg[1] == 'paiement') { $reg[1] = 'Payment'; } - print ' '.$langs->trans($reg[1]); + print $langs->trans($reg[1]); } else { - print ' '.$links[$key]['label']; + print $links[$key]['label']; } - print ''; + print ''.($labeltoshow ? ' - ' : ''); } } + + print $labeltoshow; // Already escaped + print ''; if (!$i) { $totalarray['nbfield']++; @@ -1489,7 +1489,7 @@ if ($resql) { // Payment type if (!empty($arrayfields['type']['checked'])) { - print ''; + print ''; $labeltype = ($langs->trans("PaymentTypeShort".$objp->fk_type) != "PaymentTypeShort".$objp->fk_type) ? $langs->trans("PaymentTypeShort".$objp->fk_type) : $langs->getLabelFromKey($db, $objp->fk_type, 'c_paiement', 'code', 'libelle', '', 1); if ($labeltype == 'SOLD') { print ' '; //$langs->trans("InitialBankBalance"); @@ -1515,9 +1515,9 @@ if ($resql) { print ''; $companylinked_id = 0; - $userlinked_id = 0; + $userlinked_id = 0; - //payment line type to define user display and user or company linked + //payment line type to define user display and user or company linked foreach ($links as $key => $value) { if ($links[$key]['type'] == 'payment_sc') { $type_link = 'payment_sc'; @@ -1664,20 +1664,20 @@ if ($resql) { } } - // Action edit/delete + // Action edit/delete and select print ''; // Transaction reconciliated or edit link if ($objp->conciliated && $bankaccount->canBeConciliated() > 0) { // If line not conciliated and account can be conciliated - print ''; + print ''; print img_edit(); print ''; } else { if ($user->rights->banque->modifier || $user->rights->banque->consolidate) { - print ''; + print ''; print img_edit(); print ''; } else { - print ''; + print ''; print img_view(); print ''; } @@ -1687,24 +1687,19 @@ if ($resql) { } } if ($user->rights->banque->modifier) { - print 'rowid.'&id='.$objp->bankid.'&page='.$page.'">'; + print 'rowid.'&page='.$page.$param.($sortfield ? '&sortfield='.$sortfield : '').($sortorder ? '&sortorder='.$sortorder : '').'">'; print img_delete('', 'class="marginleftonly"'); print ''; } } - print ''; - if (!$i) { - $totalarray['nbfield']++; - } // Action column - print ''; if ($massactionbutton || $massaction) { // If we are in select mode (massactionbutton defined) or if we have already selected and sent an action ($massaction) defined $selected = 0; if (in_array($obj->rowid, $arrayofselected)) { $selected = 1; } - print ''; + print ''; } print ''; if (!$i) { diff --git a/htdocs/compta/bank/card.php b/htdocs/compta/bank/card.php index c543b9edd9b..5d2bb71109b 100644 --- a/htdocs/compta/bank/card.php +++ b/htdocs/compta/bank/card.php @@ -175,7 +175,7 @@ if (empty($reshook)) { } // Fill array 'array_options' with data from add form - $ret = $extrafields->setOptionalsFromPost(null, $object); + $ret = $extrafields->setOptionalsFromPost(null, $object, '@GETPOSTISSET'); if (!$error) { $id = $object->create($user); @@ -343,7 +343,7 @@ if ($action == 'create') { print load_fiche_titre($langs->trans("NewFinancialAccount"), '', 'bank_account'); if ($conf->use_javascript_ajax) { - print "\n".''; // This ajax service is called only when a directory $selecteddir is opened but not when closed. - //print ''; } @@ -169,7 +169,7 @@ if (!empty($conf->use_javascript_ajax) && empty($conf->global->MAIN_ECM_DISABLE_ if (empty($conf->use_javascript_ajax) || !empty($conf->global->MAIN_ECM_DISABLE_JS)) { print '
    '; - // Load full tree from database. We will use it to define nbofsubdir and nboffilesinsubdir + // Load full manual tree from database. We will use it to define nbofsubdir and nboffilesinsubdir if (empty($sqltree)) { $sqltree = $ecmdirstatic->get_full_arbo(0); // Slow } diff --git a/htdocs/core/ajax/check_notifications.php b/htdocs/core/ajax/check_notifications.php index bbbc95a19c5..a5efb52e787 100644 --- a/htdocs/core/ajax/check_notifications.php +++ b/htdocs/core/ajax/check_notifications.php @@ -170,8 +170,10 @@ if (empty($_SESSION['auto_check_events_not_before']) || $time >= $_SESSION['auto $event['code'] = $obj->code; $event['label'] = $obj->label; $event['location'] = $obj->location; - $event['reminder_date_formated'] = dol_print_date($db->jdate($obj->dateremind), 'standard'); - $event['event_date_start_formated'] = dol_print_date($db->jdate($obj->datep), 'standard'); + $event['reminder_date_formated_tzserver'] = dol_print_date($db->jdate($obj->dateremind), 'standard', 'tzserver'); + $event['event_date_start_formated_tzserver'] = dol_print_date($db->jdate($obj->datep), 'standard', 'tzserver'); + $event['reminder_date_formated'] = dol_print_date($db->jdate($obj->dateremind), 'standard', 'tzuser'); + $event['event_date_start_formated'] = dol_print_date($db->jdate($obj->datep), 'standard', 'tzuser'); $eventfound[$obj->id_agenda] = $event; } diff --git a/htdocs/core/ajax/objectonoff.php b/htdocs/core/ajax/objectonoff.php index 05843abed9d..5bac89345ce 100644 --- a/htdocs/core/ajax/objectonoff.php +++ b/htdocs/core/ajax/objectonoff.php @@ -65,9 +65,14 @@ if (!empty($user->socid)) { $socid = $user->socid; } -/*if (empty($conf->global->MAIN_DIRECT_STATUS_UPDATE)) { - accessforbidden('Calling this file is allowed only when MAIN_DIRECT_STATUS_UPDATE is set'); -}*/ +if (in_array($field, array('status'))) { + restrictedArea($user, $element, $id); +} elseif ($element == 'product' && in_array($field, array('tosell', 'tobuy', 'tobatch'))) { // Special case for products + restrictedArea($user, 'produit|service', $id, 'product&product', '', '', 'rowid'); +} else { + accessforbidden("Bad value for combination of parameters element/field.", 0, 0, 1); + exit; +} /* @@ -78,15 +83,6 @@ top_httphead(); print ''."\n"; -if (in_array($field, array('status'))) { - restrictedArea($user, $element, $id); -} elseif ($element == 'product' && in_array($field, array('tosell', 'tobuy', 'tobatch'))) { // Special case for products - restrictedArea($user, 'produit|service', $id, 'product&product', '', '', 'rowid'); -} else { - accessforbidden("Bad value for combination of parameters element/field.", 0, 0, 1); - exit; -} - // Registering new values if (($action == 'set') && !empty($id)) { $triggerkey = strtoupper($element).'_UPDATE'; diff --git a/htdocs/core/ajax/onlineSign.php b/htdocs/core/ajax/onlineSign.php index 488b5051ea5..5110918541f 100644 --- a/htdocs/core/ajax/onlineSign.php +++ b/htdocs/core/ajax/onlineSign.php @@ -51,11 +51,30 @@ if (!defined('NOBROWSERNOTIF')) { include '../../main.inc.php'; $action = GETPOST('action', 'aZ09'); + $signature = GETPOST('signaturebase64'); $ref = GETPOST('ref', 'aZ09'); $mode = GETPOST('mode', 'aZ09'); +$SECUREKEY = GETPOST("securekey"); // Secure key + $error = 0; $response = ""; + +$type = $mode; + +// Check securitykey +$securekeyseed = ''; +if ($type == 'proposal') { + $securekeyseed = $conf->global->PROPOSAL_ONLINE_SIGNATURE_SECURITY_TOKEN; +} + +if (!dol_verifyHash($securekeyseed.$type.$ref, $SECUREKEY, '0')) { + http_response_code(403); + print 'Bad value for securitykey. Value provided '.dol_escape_htmltag($SECUREKEY).' does not match expected value for ref='.dol_escape_htmltag($ref); + exit(-1); +} + + /* * Actions */ @@ -71,62 +90,91 @@ if ($action == "importSignature") { if (!empty($signature) && $signature[0] == "image/png;base64") { $signature = $signature[1]; $data = base64_decode($signature); - $upload_dir = DOL_DATA_ROOT."/".$mode."/".$ref."/"; - $date = dol_print_date(dol_now(), "%Y%m%d%H%M%S"); - $filename = "signatures/".$date."_signature.png"; - if (!is_dir($upload_dir."signatures/")) { - if (!mkdir($upload_dir."signatures/")) { - $response ="error mkdir"; - $error++; + + if ($mode == "propale" || $mode == 'proposal') { + require_once DOL_DOCUMENT_ROOT.'/comm/propal/class/propal.class.php'; + require_once DOL_DOCUMENT_ROOT.'/core/lib/pdf.lib.php'; + $object = new Propal($db); + $object->fetch(0, $ref); + + $upload_dir = !empty($conf->propal->multidir_output[$object->entity])?$conf->propal->multidir_output[$object->entity]:$conf->propal->dir_output; + $upload_dir .= '/'.dol_sanitizeFileName($object->ref).'/'; + + $date = dol_print_date(dol_now(), "%Y%m%d%H%M%S"); + $filename = "signatures/".$date."_signature.png"; + if (!is_dir($upload_dir."signatures/")) { + if (!dol_mkdir($upload_dir."signatures/")) { + $response ="Error mkdir. Failed to create dir ".$upload_dir."signatures/"; + $error++; + } } - } - if (!$error) { - $return = file_put_contents($upload_dir.$filename, $data); - if ($return == false) { - $response = 'error file_put_content'; - } else { - if ($mode == "propale") { - require_once DOL_DOCUMENT_ROOT.'/comm/propal/class/propal.class.php'; - require_once DOL_DOCUMENT_ROOT.'/core/lib/pdf.lib.php'; - $object = new Propal($db); - $object->fetch(0, $ref); - $pdf = pdf_getInstance(); - $pdf->Open(); - $pdf->AddPage(); - $pagecount = $pdf->setSourceFile($upload_dir.$ref.".pdf"); + if (!$error) { + $return = file_put_contents($upload_dir.$filename, $data); + if ($return == false) { + $error++; + $response = 'Error file_put_content: failed to create signature file.'; + } + } - $tppl = $pdf->importPage(1); - $pdf->useTemplate($tppl); - $pdf->Image($upload_dir.$filename, 129, 239.6, 60, 15); - $pdf->Close(); - $pdf->Output($upload_dir.$ref."_signed-".$date.".pdf", "F"); + if (!$error) { + $newpdffilename = $upload_dir.$ref."_signed-".$date.".pdf"; - $sql = "UPDATE ".MAIN_DB_PREFIX."propal"; - $sql .= " SET fk_statut = ".((int) $object::STATUS_SIGNED).", note_private = '".$object->note_private."', date_signature='".$db->idate(dol_now())."'"; - $sql .= " WHERE rowid = ".((int) $object->id); + $pdf = pdf_getInstance(); + $pdf->Open(); + $pdf->AddPage(); + $pagecount = $pdf->setSourceFile($upload_dir.$ref.".pdf"); // original PDF - dol_syslog(__METHOD__, LOG_DEBUG); - $resql = $db->query($sql); - if (!$resql) { - $error++; - } else { - $num = $db->affected_rows($resql); - } + $tppl = $pdf->importPage(1); + $pdf->useTemplate($tppl); + $pdf->Image($upload_dir.$filename, 129, 239.6, 60, 15); // FIXME Position will be wrong with non A4 format. Use a value from width and height of page minus relative offset. + $pdf->Close(); + $pdf->Output($newpdffilename, "F"); - if (!$error) { - $db->commit(); - $response = "success"; - setEventMessage("PropalSigned"); - } else { - $db->rollback(); - $response = "error sql"; - } + $db->begin(); + + // Index the new file and update the last_main_doc property of object. + $object->indexFile($newpdffilename, 1); + + $online_sign_ip = getUserRemoteIP(); + $online_sign_name = ''; // TODO Ask name on form to sign + + $sql = "UPDATE ".MAIN_DB_PREFIX."propal"; + $sql .= " SET fk_statut = ".((int) $object::STATUS_SIGNED).", note_private = '".$db->escape($object->note_private)."',"; + $sql .= " date_signature = '".$db->idate(dol_now())."',"; + $sql .= " online_sign_ip = '".$db->escape($online_sign_ip)."'"; + if ($online_sign_name) { + $sql .= ", online_sign_name = '".$db->escape($online_sign_name)."'"; + } + $sql .= " WHERE rowid = ".((int) $object->id); + + dol_syslog(__METHOD__, LOG_DEBUG); + $resql = $db->query($sql); + if (!$resql) { + $error++; + } else { + $num = $db->affected_rows($resql); + } + + if (!$error) { + $db->commit(); + $response = "success"; + setEventMessages("PropalSigned", null, 'warnings'); + } else { + $db->rollback(); + $error++; + $response = "error sql"; } } } } else { + $error++; $response = 'error signature_not_found'; } } + +if ($error) { + http_response_code(501); +} + echo $response; diff --git a/htdocs/core/bookmarks_page.php b/htdocs/core/bookmarks_page.php index 46c4b38988e..dde8df5f82e 100644 --- a/htdocs/core/bookmarks_page.php +++ b/htdocs/core/bookmarks_page.php @@ -55,10 +55,22 @@ $left = ($langs->trans("DIRECTION") == 'rtl' ? 'right' : 'left'); * View */ +// Important: Following code is to avoid page request by browser and PHP CPU at each Dolibarr page access. +if (empty($dolibarr_nocache) && GETPOST('cache', 'int')) { + header('Cache-Control: max-age='.GETPOST('cache', 'int').', public'); + // For a .php, we must set an Expires to avoid to have it forced to an expired value by the web server + header('Expires: '.gmdate('D, d M Y H:i:s', dol_now('gmt') + GETPOST('cache', 'int')).' GMT'); + // HTTP/1.0 + header('Pragma: token=public'); +} else { + // HTTP/1.0 + header('Cache-Control: no-cache'); +} + $title = $langs->trans("Bookmarks"); // URL http://mydolibarr/core/bookmarks_page?dol_use_jmobile=1 can be used for tests -$head = ''."\n"; +$head = ''."\n"; // This is used by DoliDroid to know page is a bookmark selection page $arrayofjs = array(); $arrayofcss = array(); top_htmlhead($head, $title, 0, 0, $arrayofjs, $arrayofcss); @@ -77,7 +89,7 @@ $bookmarkList = ''; $searchForm = ''; -if (empty($conf->bookmarks->enabled)) { +if (empty($conf->bookmark->enabled)) { $langs->load("admin"); $bookmarkList .= '
    '.$langs->trans("WarningModuleNotActive", $langs->transnoentitiesnoconv("Bookmarks")).''; $bookmarkList .= '

    '; @@ -91,7 +103,7 @@ if (empty($conf->bookmarks->enabled)) { $bookmarkList = ''; diff --git a/htdocs/core/boxes/box_accountancy_last_manual_entries.php b/htdocs/core/boxes/box_accountancy_last_manual_entries.php index b1e4a637046..96abd8699f3 100644 --- a/htdocs/core/boxes/box_accountancy_last_manual_entries.php +++ b/htdocs/core/boxes/box_accountancy_last_manual_entries.php @@ -134,7 +134,7 @@ class box_accountancy_last_manual_entries extends ModeleBoxes ); $this->info_box_contents[$line][] = array( - 'td' => 'class="nowraponall right"', + 'td' => 'class="nowraponall right amount"', 'text' => price($amount, 0, $langs, 0, -1, -1, $conf->currency), ); diff --git a/htdocs/core/boxes/box_activity.php b/htdocs/core/boxes/box_activity.php index 487c2170a4a..371a7a0dbed 100644 --- a/htdocs/core/boxes/box_activity.php +++ b/htdocs/core/boxes/box_activity.php @@ -174,7 +174,7 @@ class box_activity extends ModeleBoxes $totalnb += $data[$j]->nb; $this->info_box_contents[$line][3] = array( - 'td' => 'class="nowraponall right"', + 'td' => 'class="nowraponall right amount"', 'text' => price($data[$j]->Mnttot, 1, $langs, 0, 0, -1, $conf->currency), ); $this->info_box_contents[$line][4] = array( @@ -262,7 +262,7 @@ class box_activity extends ModeleBoxes $totalnb += $data[$j]->nb; $this->info_box_contents[$line][3] = array( - 'td' => 'class="nowraponall right"', + 'td' => 'class="nowraponall right amount"', 'text' => price($data[$j]->Mnttot, 1, $langs, 0, 0, -1, $conf->currency), ); $this->info_box_contents[$line][4] = array( @@ -350,7 +350,7 @@ class box_activity extends ModeleBoxes ); $this->info_box_contents[$line][3] = array( - 'td' => 'class="nowraponall right"', + 'td' => 'class="nowraponall right amount"', 'text' => price($data[$j]->Mnttot, 1, $langs, 0, 0, -1, $conf->currency) ); @@ -433,7 +433,7 @@ class box_activity extends ModeleBoxes ); $totalnb += $data[$j]->nb; $this->info_box_contents[$line][3] = array( - 'td' => 'class="nowraponall right"', + 'td' => 'class="nowraponall right amount"', 'text' => price($data[$j]->Mnttot, 1, $langs, 0, 0, -1, $conf->currency), ); $this->info_box_contents[$line][4] = array( diff --git a/htdocs/core/boxes/box_commandes.php b/htdocs/core/boxes/box_commandes.php index abf9baf06f3..ae939afe25c 100644 --- a/htdocs/core/boxes/box_commandes.php +++ b/htdocs/core/boxes/box_commandes.php @@ -163,7 +163,7 @@ class box_commandes extends ModeleBoxes ); $this->info_box_contents[$line][] = array( - 'td' => 'class="nowraponall right"', + 'td' => 'class="nowraponall right amount"', 'text' => price($objp->total_ht, 0, $langs, 0, -1, -1, $conf->currency), ); diff --git a/htdocs/core/boxes/box_comptes.php b/htdocs/core/boxes/box_comptes.php index f42b7a2ef7c..5570051a065 100644 --- a/htdocs/core/boxes/box_comptes.php +++ b/htdocs/core/boxes/box_comptes.php @@ -141,8 +141,11 @@ class box_comptes extends ModeleBoxes ); $this->info_box_contents[$line][] = array( - 'td' => 'class="right nowraponall"', - 'text' => price($solde, 0, $langs, 1, -1, -1, $objp->currency_code) + 'td' => 'class="nowraponall right amount"', + 'text' => '' + .price($solde, 0, $langs, 1, -1, -1, $objp->currency_code) + .'', + 'asis' => 1, ); $line++; @@ -161,7 +164,7 @@ class box_comptes extends ModeleBoxes ); $this->info_box_contents[$line][] = array( - 'td' => 'class="liste_total right nowraponall"', + 'td' => 'class="liste_total nowraponall right amount"', 'text' => price($solde, 0, $langs, 0, -1, -1, $key) ); $line++; diff --git a/htdocs/core/boxes/box_dolibarr_state_board.php b/htdocs/core/boxes/box_dolibarr_state_board.php index 21bf58b859f..0381b493448 100644 --- a/htdocs/core/boxes/box_dolibarr_state_board.php +++ b/htdocs/core/boxes/box_dolibarr_state_board.php @@ -105,7 +105,8 @@ class box_dolibarr_state_board extends ModeleBoxes 'supplier_invoices', 'contracts', 'interventions', - 'ticket' + 'ticket', + 'dolresource' ); $conditions = array( 'users' => $user->rights->user->user->lire, @@ -132,7 +133,8 @@ class box_dolibarr_state_board extends ModeleBoxes 'projects' => !empty($conf->projet->enabled) && $user->rights->projet->lire, 'expensereports' => !empty($conf->expensereport->enabled) && $user->rights->expensereport->lire, 'holidays' => !empty($conf->holiday->enabled) && $user->rights->holiday->read, - 'ticket' => !empty($conf->ticket->enabled) && $user->rights->ticket->read + 'ticket' => !empty($conf->ticket->enabled) && $user->rights->ticket->read, + 'dolresource' => !empty($conf->resource->enabled) && $user->rights->resource->read ); $classes = array( 'users' => 'User', @@ -156,6 +158,7 @@ class box_dolibarr_state_board extends ModeleBoxes 'expensereports' => 'ExpenseReport', 'holidays' => 'Holiday', 'ticket' => 'Ticket', + 'dolresource' => 'Dolresource' ); $includes = array( 'users' => DOL_DOCUMENT_ROOT . "/user/class/user.class.php", @@ -178,7 +181,8 @@ class box_dolibarr_state_board extends ModeleBoxes 'projects' => DOL_DOCUMENT_ROOT . "/projet/class/project.class.php", 'expensereports' => DOL_DOCUMENT_ROOT . "/expensereport/class/expensereport.class.php", 'holidays' => DOL_DOCUMENT_ROOT . "/holiday/class/holiday.class.php", - 'ticket' => DOL_DOCUMENT_ROOT . "/ticket/class/ticket.class.php" + 'ticket' => DOL_DOCUMENT_ROOT . "/ticket/class/ticket.class.php", + 'dolresource' => DOL_DOCUMENT_ROOT . "/resource/class/dolresource.class.php" ); $links = array( 'users' => DOL_URL_ROOT . '/user/list.php', @@ -201,7 +205,8 @@ class box_dolibarr_state_board extends ModeleBoxes 'projects' => DOL_URL_ROOT . '/projet/list.php?mainmenu=project', 'expensereports' => DOL_URL_ROOT . '/expensereport/list.php?mainmenu=hrm&leftmenu=expensereport', 'holidays' => DOL_URL_ROOT . '/holiday/list.php?mainmenu=hrm&leftmenu=holiday', - 'ticket' => DOL_URL_ROOT . '/ticket/list.php?leftmenu=ticket' + 'ticket' => DOL_URL_ROOT . '/ticket/list.php?leftmenu=ticket', + 'dolresource' => DOL_URL_ROOT . '/resource/list.php?mainmenu=tools', ); $titres = array( 'users' => "Users", @@ -225,6 +230,7 @@ class box_dolibarr_state_board extends ModeleBoxes 'expensereports' => "ExpenseReports", 'holidays' => "Holidays", 'ticket' => "Ticket", + 'dolresource' => "Resources", ); $langfile = array( 'customers' => "companies", diff --git a/htdocs/core/boxes/box_factures_fourn.php b/htdocs/core/boxes/box_factures_fourn.php index ae905d5d468..42a945b9289 100644 --- a/htdocs/core/boxes/box_factures_fourn.php +++ b/htdocs/core/boxes/box_factures_fourn.php @@ -183,7 +183,7 @@ class box_factures_fourn extends ModeleBoxes ); $this->info_box_contents[$line][] = array( - 'td' => 'class="right nowraponall"', + 'td' => 'class="nowraponall right amount"', 'text' => price($objp->total_ht, 0, $langs, 0, -1, -1, $conf->currency), ); diff --git a/htdocs/core/boxes/box_factures_fourn_imp.php b/htdocs/core/boxes/box_factures_fourn_imp.php index cfef8801414..a421706e855 100644 --- a/htdocs/core/boxes/box_factures_fourn_imp.php +++ b/htdocs/core/boxes/box_factures_fourn_imp.php @@ -170,7 +170,7 @@ class box_factures_fourn_imp extends ModeleBoxes ); $this->info_box_contents[$line][] = array( - 'td' => 'class="nowraponall right"', + 'td' => 'class="nowraponall right amount"', 'text' => price($objp->total_ht, 0, $langs, 0, -1, -1, $conf->currency), ); diff --git a/htdocs/core/boxes/box_funnel_of_prospection.php b/htdocs/core/boxes/box_funnel_of_prospection.php index fd89e732937..cdaa9298f63 100644 --- a/htdocs/core/boxes/box_funnel_of_prospection.php +++ b/htdocs/core/boxes/box_funnel_of_prospection.php @@ -219,7 +219,7 @@ class box_funnel_of_prospection extends ModeleBoxes if (!$conf->use_javascript_ajax) { $stringtoprint .= ''; $stringtoprint .= ''.$labelStatus.''; - $stringtoprint .= ''.price((isset($valsamount[$status]) ? (float) $valsamount[$status] : 0), 0, '', 1, -1, -1, $conf->currency).''; + $stringtoprint .= ''.price((isset($valsamount[$status]) ? (float) $valsamount[$status] : 0), 0, '', 1, -1, -1, $conf->currency).''; $stringtoprint .= "\n"; } } @@ -237,7 +237,7 @@ class box_funnel_of_prospection extends ModeleBoxes $dolgraph->setBorderColor(array_values($bordercolorseries)); $dolgraph->setShowLegend(2); if (!empty($conf->dol_optimize_smallscreen)) { - $px1->SetWidth(320); + $dolgraph->SetWidth(320); } $dolgraph->setShowPercent(1); $dolgraph->setMirrorGraphValues(true); @@ -277,7 +277,7 @@ class box_funnel_of_prospection extends ModeleBoxes ); $this->info_box_contents[$line][] = array( 'tr' => 'class="oddeven"', - 'td' => 'class="right "', + 'td' => 'class="nowraponall right amount"', 'maxlength' => 500, 'text' => price($totalamount, 0, '', 1, -1, -1, $conf->currency) ); @@ -290,7 +290,7 @@ class box_funnel_of_prospection extends ModeleBoxes ); $this->info_box_contents[$line][] = array( - 'td' => 'class="right "', + 'td' => 'class="nowraponall right amount"', 'maxlength' => 500, 'text' => price(price2num($ponderated_opp_amount, 'MT'), 0, '', 1, -1, -1, $conf->currency) ); diff --git a/htdocs/core/boxes/box_graph_invoices_permonth.php b/htdocs/core/boxes/box_graph_invoices_permonth.php index 18ffe022610..8179e134bcf 100644 --- a/htdocs/core/boxes/box_graph_invoices_permonth.php +++ b/htdocs/core/boxes/box_graph_invoices_permonth.php @@ -146,9 +146,6 @@ class box_graph_invoices_permonth extends ModeleBoxes $filenamenb = $dir."/".$prefix."invoicesnbinyear-".$endyear.".png"; // default value for customer mode $fileurlnb = DOL_URL_ROOT.'/viewimage.php?modulepart=billstats&file=invoicesnbinyear-'.$endyear.'.png'; - if ($mode == 'supplier') { - $fileurlnb = DOL_URL_ROOT.'/viewimage.php?modulepart=billstatssupplier&file=invoicessuppliernbinyear-'.$endyear.'.png'; - } $px1 = new DolGraph(); $mesg = $px1->isGraphKo(); @@ -189,10 +186,7 @@ class box_graph_invoices_permonth extends ModeleBoxes $filenamenb = $dir."/".$prefix."invoicesamountinyear-".$endyear.".png"; // default value for customer mode - $fileurlnb = DOL_URL_ROOT.'/viewimage.php?modulepart=billstats&file=invoicesamountinyear-'.$endyear.'.png'; - if ($mode == 'supplier') { - $fileurlnb = DOL_URL_ROOT.'/viewimage.php?modulepart=billstatssupplier&file=invoicessupplieramountinyear-'.$endyear.'.png'; - } + $fileurlnb = DOL_URL_ROOT.'/viewimage.php?modulepart=billstats&file=invoicesamountinyear-'.$endyear.'.png'; $px2 = new DolGraph(); $mesg = $px2->isGraphKo(); @@ -233,7 +227,7 @@ class box_graph_invoices_permonth extends ModeleBoxes if (!$mesg) { $stringtoshow = ''; - $stringtoshow .= ''."\n"; } } @@ -244,7 +254,7 @@ class DolEditor $out .= '
    '.$titlecontent; $out .= '   -   '.dol_escape_htmltag($langs->trans("ShowMoreLines")).'     '; $out .= '
    '; - $out .= ''; - if (!empty($hidemargininfos)) { - print ''; + if (!empty($hidemargininfos)) { + print ''; + } } - } - print '
    '; - print ''."\n"; + print '
    '; + print '' . "\n"; - print ''; - print ''; - print ''; - print ''; - if ($conf->global->MARGIN_TYPE == "1") { - print ''; - } else { - print ''; - } - print ''; - if (!empty($conf->global->DISPLAY_MARGIN_RATES)) { - print ''; - } - if (!empty($conf->global->DISPLAY_MARK_RATES)) { - print ''; - } - print ''; - - if (!empty($conf->product->enabled)) { - //if ($marginInfo['margin_on_products'] != 0 && $marginInfo['margin_on_services'] != 0) { - print ''; - print ''; - print ''; - print ''; - print ''; + print '
    '.$langs->trans('Margins').''.$langs->trans('SellingPrice').''.$langs->trans('BuyingPrice').''.$langs->trans('CostPrice').''.$langs->trans('Margin').''.$langs->trans('MarginRate').''.$langs->trans('MarkRate').'
    '.$langs->trans('MarginOnProducts').''.price($marginInfo['pv_products']).''.price($marginInfo['pa_products']).''.price($marginInfo['margin_on_products']).'
    '; + print ''; + print ''; + print ''; + if ($conf->global->MARGIN_TYPE == "1") { + print ''; + } else { + print ''; + } + print ''; if (!empty($conf->global->DISPLAY_MARGIN_RATES)) { - print ''; + print ''; } if (!empty($conf->global->DISPLAY_MARK_RATES)) { - print ''; + print ''; } print ''; - } - if (!empty($conf->service->enabled)) { - print ''; - print ''; - print ''; - print ''; - print ''; - if (!empty($conf->global->DISPLAY_MARGIN_RATES)) { - print ''; + if (!empty($conf->product->enabled)) { + //if ($marginInfo['margin_on_products'] != 0 && $marginInfo['margin_on_services'] != 0) { + print ''; + print ''; + print ''; + print ''; + print ''; + if (!empty($conf->global->DISPLAY_MARGIN_RATES)) { + print ''; + } + if (!empty($conf->global->DISPLAY_MARK_RATES)) { + print ''; + } + print ''; } - if (!empty($conf->global->DISPLAY_MARK_RATES)) { - print ''; - } - print ''; - } - if (!empty($conf->product->enabled) && !empty($conf->service->enabled)) { - print ''; - print ''; - print ''; - print ''; - print ''; - if (!empty($conf->global->DISPLAY_MARGIN_RATES)) { - print ''; + if (!empty($conf->service->enabled)) { + print ''; + print ''; + print ''; + print ''; + print ''; + if (!empty($conf->global->DISPLAY_MARGIN_RATES)) { + print ''; + } + if (!empty($conf->global->DISPLAY_MARK_RATES)) { + print ''; + } + print ''; } - if (!empty($conf->global->DISPLAY_MARK_RATES)) { - print ''; + + if (!empty($conf->product->enabled) && !empty($conf->service->enabled)) { + print ''; + print ''; + print ''; + print ''; + print ''; + if (!empty($conf->global->DISPLAY_MARGIN_RATES)) { + print ''; + } + if (!empty($conf->global->DISPLAY_MARK_RATES)) { + print ''; + } + print ''; } - print ''; + print $hookmanager->resPrint; + print '
    ' . $langs->trans('Margins') . '' . $langs->trans('SellingPrice') . '' . $langs->trans('BuyingPrice') . '' . $langs->trans('CostPrice') . '' . $langs->trans('Margin') . ''.(($marginInfo['margin_rate_products'] == '') ? '' : price($marginInfo['margin_rate_products'], null, null, null, null, 2).'%').'' . $langs->trans('MarginRate') . ''.(($marginInfo['mark_rate_products'] == '') ? '' : price($marginInfo['mark_rate_products'], null, null, null, null, 2).'%').'' . $langs->trans('MarkRate') . '
    '.$langs->trans('MarginOnServices').''.price($marginInfo['pv_services']).''.price($marginInfo['pa_services']).''.price($marginInfo['margin_on_services']).''.(($marginInfo['margin_rate_services'] == '') ? '' : price($marginInfo['margin_rate_services'], null, null, null, null, 2).'%').'
    ' . $langs->trans('MarginOnProducts') . '' . price($marginInfo['pv_products']) . '' . price($marginInfo['pa_products']) . '' . price($marginInfo['margin_on_products']) . '' . (($marginInfo['margin_rate_products'] == '') ? '' : price($marginInfo['margin_rate_products'], null, null, null, null, 2) . '%') . '' . (($marginInfo['mark_rate_products'] == '') ? '' : price($marginInfo['mark_rate_products'], null, null, null, null, 2) . '%') . '
    '.(($marginInfo['mark_rate_services'] == '') ? '' : price($marginInfo['mark_rate_services'], null, null, null, null, 2).'%').'
    '.$langs->trans('TotalMargin').''.price($marginInfo['pv_total']).''.price($marginInfo['pa_total']).''.price($marginInfo['total_margin']).''.(($marginInfo['total_margin_rate'] == '') ? '' : price($marginInfo['total_margin_rate'], null, null, null, null, 2).'%').'
    ' . $langs->trans('MarginOnServices') . '' . price($marginInfo['pv_services']) . '' . price($marginInfo['pa_services']) . '' . price($marginInfo['margin_on_services']) . '' . (($marginInfo['margin_rate_services'] == '') ? '' : price($marginInfo['margin_rate_services'], null, null, null, null, 2) . '%') . '' . (($marginInfo['mark_rate_services'] == '') ? '' : price($marginInfo['mark_rate_services'], null, null, null, null, 2) . '%') . '
    '.(($marginInfo['total_mark_rate'] == '') ? '' : price($marginInfo['total_mark_rate'], null, null, null, null, 2).'%').'
    ' . $langs->trans('TotalMargin') . '' . price($marginInfo['pv_total']) . '' . price($marginInfo['pa_total']) . '' . price($marginInfo['total_margin']) . '' . (($marginInfo['total_margin_rate'] == '') ? '' : price($marginInfo['total_margin_rate'], null, null, null, null, 2) . '%') . '' . (($marginInfo['total_mark_rate'] == '') ? '' : price($marginInfo['total_mark_rate'], null, null, null, null, 2) . '%') . '
    '; + print '
    '; + } elseif ($reshook > 0) { + print $hookmanager->resPrint; } - print ''; - print '
    '; } } diff --git a/htdocs/core/class/html.formother.class.php b/htdocs/core/class/html.formother.class.php index 99ee1b6af9b..2b7466628a5 100644 --- a/htdocs/core/class/html.formother.class.php +++ b/htdocs/core/class/html.formother.class.php @@ -57,13 +57,14 @@ class FormOther } /** - * Return HTML code for scanner tool. + * Return the HTML code for scanner tool. * This must be called into an existing
    * - * @param string $jstoexecuteonadd Name of javascript function to call + * @param string $jstoexecuteonadd Name of javascript function to call once the barcode scanning session is complete and user has click on "Add". + * @param string $mode 'all' (both product and lot barcode) or 'product' (product barcode only) or 'lot' (lot number only) * @return string HTML component */ - public function getHTMLScannerForm($jstoexecuteonadd = 'barcodescannerjs') + public function getHTMLScannerForm($jstoexecuteonadd = 'barcodescannerjs', $mode = 'all') { global $langs; @@ -71,14 +72,23 @@ class FormOther $out .= ''."\n"; $out .= '
    '; - $out .= '
    Barcode scanner tool...

    '; + $out .= '
    '.img_picto('', 'barcode', 'class="pictofixedwidth"').'Barcode scanner tool...

    '; - $out .= ' Autodetect if we scan a product barcode or a lot/serial barcode
    '; - $out .= ' Scan a product barcode
    '; - $out .= ' Scan a product lot or serial number
    '; - - $out .= $langs->trans("QtyToAddAfterBarcodeScan").'
    '; - $out .= ''; + if ($mode == 'product') { + $out .= ''; + } elseif ($mode == 'lot') { + $out .= ''; + } else { // $mode = 'all' + $out .= '
    '; + $out .= '
    '; + $out .= '
    '; + } + $stringaddbarcode = $langs->trans("QtyToAddAfterBarcodeScan", "tmphtml"); + $htmltoreplaceby = ''; + $stringaddbarcode = str_replace("tmphtml", $htmltoreplaceby, $stringaddbarcode); + $out .= $stringaddbarcode.'
    '; + $out .= '
    '; + $out .= ''; /*print '
    '.$langs->trans("or").'
    '; @@ -88,14 +98,21 @@ class FormOther */ $out .= '
    '; $out .= '
    '; - $out .= ''; - $out .= ''; + $out .= ''; + $out .= ''; + $out .= '
    '; $out .= '
    '; + $out .= '
    '; $out .= ''; @@ -542,7 +559,6 @@ class FormOther $resql_usr = $this->db->query($sql_usr); if ($resql_usr) { $userstatic = new User($this->db); - $showstatus = 1; while ($obj_usr = $this->db->fetch_object($resql_usr)) { $userstatic->id = $obj_usr->rowid; @@ -828,6 +844,7 @@ class FormOther $out .= ''; $out .= ''; @@ -906,11 +938,11 @@ class FormOther // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps /** - * Creation d'un icone de couleur + * Creae an image for color * - * @param string $color Couleur de l'image - * @param string $module Nom du module - * @param string $name Nom de l'image + * @param string $color Color of image + * @param string $module Name of module + * @param string $name Name of image * @param int $x Largeur de l'image en pixels * @param int $y Hauteur de l'image en pixels * @return void @@ -1195,7 +1227,7 @@ class FormOther // Javascript code for dynamic actions if (!empty($conf->use_javascript_ajax)) { - $selectboxlist .= ''; + } + return $out; + } + + /** + * get the type : used for old module builder setup conf style conversion and tests + * because this two class will quickly evolve it's important to not set or get directly $this->type (will be protected) so this method exist + * to be sure we can manage evolution easily + * + * @return string + */ + public function getType() + { + return $this->type; + } + + /** + * set the type from string : used for old module builder setup conf style conversion and tests + * because this two class will quickly evolve it's important to not set directly $this->type (will be protected) so this method exist + * to be sure we can manage evolution easily + * @param string $type possible values based on old module builder setup : 'string', 'textarea', 'category:'.Categorie::TYPE_CUSTOMER', 'emailtemplate', 'thirdparty_type' + * @deprecated yes this setTypeFromTypeString came deprecated because it exists only for manage setup convertion + * @return bool + */ + public function setTypeFromTypeString($type) + { + $this->type = $type; + return true; + } + + /** + * Add error + * @param array|string $errors the error text + * @return null + */ + public function setErrors($errors) + { + if (is_array($errors)) { + if (!empty($errors)) { + foreach ($errors as $error) { + $this->setErrors($error); + } + } + } elseif (!empty($errors)) { + $this->errors[] = $errors; + } + } + + /** + * @return bool|string Generate the output html for this item + */ + public function generateOutputField() + { + global $conf, $user; + + if (!empty($this->fieldOverride)) { + return $this->fieldOverride; + } + + if (!empty($this->fieldOutputOverride)) { + return $this->fieldOutputOverride; + } + + $out = ''; + + if ($this->type == 'title') { + // nothing to do + } elseif ($this->type == 'textarea') { + $out.= dol_nl2br($this->fieldValue); + } elseif ($this->type== 'html') { + $out.= $this->fieldValue; + } elseif ($this->type == 'yesno') { + $out.= ajax_constantonoff($this->confKey); + } elseif (preg_match('/emailtemplate:/', $this->type)) { + include_once DOL_DOCUMENT_ROOT . '/core/class/html.formmail.class.php'; + $formmail = new FormMail($this->db); + + $tmp = explode(':', $this->type); + + $template = $formmail->getEMailTemplate($this->db, $tmp[1], $user, $this->langs, $this->fieldValue); + if ($template<0) { + $this->setErrors($formmail->errors); + } + $out.= $this->langs->trans($template->label); + } elseif (preg_match('/category:/', $this->type)) { + $c = new Categorie($this->db); + $result = $c->fetch($this->fieldValue); + if ($result < 0) { + $this->setErrors($c->errors); + } + $ways = $c->print_all_ways(' >> ', 'none', 0, 1); // $ways[0] = "ccc2 >> ccc2a >> ccc2a1" with html formated text + $toprint = array(); + foreach ($ways as $way) { + $toprint[] = '
  • color ? ' style="background: #' . $c->color . ';"' : ' style="background: #bbb"') . '>' . $way . '
  • '; + } + $out.='
      ' . implode(' ', $toprint) . '
    '; + } elseif (preg_match('/thirdparty_type/', $this->type)) { + if ($this->fieldValue==2) { + $out.= $this->langs->trans("Prospect"); + } elseif ($this->fieldValue==3) { + $out.= $this->langs->trans("ProspectCustomer"); + } elseif ($this->fieldValue==1) { + $out.= $this->langs->trans("Customer"); + } elseif ($this->fieldValue==0) { + $out.= $this->langs->trans("NorProspectNorCustomer"); + } + } elseif ($this->type == 'product') { + $product = new Product($this->db); + $resprod = $product->fetch($this->fieldValue); + if ($resprod > 0) { + $out.= $product->ref; + } elseif ($resprod < 0) { + $this->setErrors($product->errors); + } + } else { + $out.= $this->fieldValue; + } + + return $out; + } + + + /* + * METHODS FOR SETTING DISPLAY TYPE + */ + + /** + * Set type of input as string + * @return self + */ + public function setAsString() + { + $this->type = 'string'; + return $this; + } + + /** + * Set type of input as textarea + * @return self + */ + public function setAsTextarea() + { + $this->type = 'textarea'; + return $this; + } + + /** + * Set type of input as html editor + * @return self + */ + public function setAsHtml() + { + $this->type = 'html'; + return $this; + } + + /** + * Set type of input as emailtemplate selector + * @param string $templateType email template type + * @return self + */ + public function setAsEmailTemplate($templateType) + { + $this->type = 'emailtemplate:'.$templateType; + return $this; + } + + /** + * Set type of input as thirdparty_type selector + * @return self + */ + public function setAsThirdpartyType() + { + $this->type = 'thirdparty_type'; + return $this; + } + + /** + * Set type of input as Yes + * @return self + */ + public function setAsYesNo() + { + $this->type = 'yesno'; + return $this; + } + + /** + * Set type of input as secure key + * @return self + */ + public function setAsSecureKey() + { + $this->type = 'securekey'; + return $this; + } + + /** + * Set type of input as product + * @return self + */ + public function setAsProduct() + { + $this->type = 'product'; + return $this; + } + + /** + * Set type of input as a category selector + * TODO add default value + * @param int $catType Type of category ('customer', 'supplier', 'contact', 'product', 'member'). Old mode (0, 1, 2, ...) is deprecated. + * @return self + */ + public function setAsCategory($catType) + { + $this->type = 'category:'.$catType; + return $this; + } + + /** + * Set type of input as a simple title + * no data to store + * @return self + */ + public function setAsTitle() + { + $this->type = 'title'; + return $this; + } +} diff --git a/htdocs/core/class/html.formsms.class.php b/htdocs/core/class/html.formsms.class.php index 60811956c32..68b8410e647 100644 --- a/htdocs/core/class/html.formsms.class.php +++ b/htdocs/core/class/html.formsms.class.php @@ -127,7 +127,7 @@ class FormSms print "\n\n"; print ' -'; } else { // Default Header Redirect diff --git a/htdocs/core/class/translate.class.php b/htdocs/core/class/translate.class.php index e741adc84b5..f881447cd67 100644 --- a/htdocs/core/class/translate.class.php +++ b/htdocs/core/class/translate.class.php @@ -555,9 +555,9 @@ class Translate * Return translated value of key for special keys ("Currency...", "Civility...", ...). * Search in lang file, then into database. Key must be any complete entry into lang file: CurrencyEUR, ... * If not found, return key. - * The string return is not formated (translated with transnoentitiesnoconv) - * NOTE: To avoid infinite loop (getLabelFromKey->transnoentities->getTradFromKey), if you modify this function, - * check that getLabelFromKey is not called with same value than input. + * The string return is not formated (translated with transnoentitiesnoconv). + * NOTE: To avoid infinite loop (getLabelFromKey->transnoentities->getTradFromKey->getLabelFromKey), if you modify this function, + * check that getLabelFromKey is never called with the same value than $key. * * @param string $key Key to translate * @return string Translated string (translated with transnoentitiesnoconv) @@ -579,13 +579,13 @@ class Translate $newstr = $this->getLabelFromKey($db, $reg[1], 'c_currencies', 'code_iso', 'label'); } elseif (preg_match('/^SendingMethod([0-9A-Z]+)$/i', $key, $reg)) { $newstr = $this->getLabelFromKey($db, $reg[1], 'c_shipment_mode', 'code', 'libelle'); - } elseif (preg_match('/^PaymentTypeShort([0-9A-Z]+)$/i', $key, $reg)) { + } elseif (preg_match('/^PaymentType(?:Short)?([0-9A-Z]+)$/i', $key, $reg)) { $newstr = $this->getLabelFromKey($db, $reg[1], 'c_paiement', 'code', 'libelle', '', 1); } elseif (preg_match('/^OppStatus([0-9A-Z]+)$/i', $key, $reg)) { $newstr = $this->getLabelFromKey($db, $reg[1], 'c_lead_status', 'code', 'label'); } elseif (preg_match('/^OrderSource([0-9A-Z]+)$/i', $key, $reg)) { // TODO OrderSourceX must be replaced with content of table llx_c_input_reason or llx_c_input_method - //$newstr=$this->getLabelFromKey($db,$reg[1],'c_ordersource','code','label'); + //$newstr=$this->getLabelFromKey($db,$reg[1],'llx_c_input_reason','code','label'); } /* Disabled. There is too many cases where translation of $newstr is not defined is normal (like when output with setEventMessage an already translated string) @@ -945,9 +945,9 @@ class Translate * * @param DoliDB $db Database handler * @param string $key Translation key to get label (key in language file) - * @param string $tablename Table name without prefix - * @param string $fieldkey Field for key - * @param string $fieldlabel Field for label + * @param string $tablename Table name without prefix. This value must always be a hardcoded string and not a value coming from user input. + * @param string $fieldkey Field for key. This value must always be a hardcoded string and not a value coming from user input. + * @param string $fieldlabel Field for label. This value must always be a hardcoded string and not a value coming from user input. * @param string $keyforselect Use another value than the translation key for the where into select * @param int $filteronentity Use a filter on entity * @return string Label in UTF8 (but without entities) @@ -959,10 +959,15 @@ class Translate if ($key == '') { return ''; } + // Test should be useless because the 3 variables are never set from user input but we keep it in case of. + if (preg_match('/[^0-9A-Z_]/i', $tablename) || preg_match('/[^0-9A-Z_]/i', $fieldkey) || preg_match('/[^0-9A-Z_]/i', $fieldlabel)) { + $this->error = 'Bad value for parameter tablename, fieldkey or fieldlabel'; + return -1; + } //print 'param: '.$key.'-'.$keydatabase.'-'.$this->trans($key); exit; - // Check if a translation is available (this can call getTradFromKey) + // Check if a translation is available (Note: this can call getTradFromKey that can call getLabelFromKey) $tmp = $this->transnoentitiesnoconv($key); if ($tmp != $key && $tmp != 'ErrorBadValueForParamNotAString') { return $tmp; // Found in language array @@ -973,6 +978,7 @@ class Translate return $this->cache_labels[$tablename][$key]; // Found in cache } + // Not found in loaded language file nor in cache. So we will take the label into database. $sql = "SELECT ".$fieldlabel." as label"; $sql .= " FROM ".MAIN_DB_PREFIX.$tablename; $sql .= " WHERE ".$fieldkey." = '".$db->escape($keyforselect ? $keyforselect : $key)."'"; diff --git a/htdocs/core/class/utils.class.php b/htdocs/core/class/utils.class.php index d56fcddafa7..b408985a92d 100644 --- a/htdocs/core/class/utils.class.php +++ b/htdocs/core/class/utils.class.php @@ -358,10 +358,19 @@ class Utils dol_syslog("Utils::dumpDatabase execmethod=".$execmethod." command:".$fullcommandcrypted, LOG_INFO); + + /* If value has been forced with a php_admin_value, this has no effect. Example of value: '512M' */ + $MemoryLimit = getDolGlobalString('MAIN_MEMORY_LIMIT_DUMP'); + if (!empty($MemoryLimit)) { + @ini_set('memory_limit', $MemoryLimit); + } + + // TODO Replace with executeCLI function if ($execmethod == 1) { $output_arr = array(); $retval = null; + exec($fullcommandclear, $output_arr, $retval); if ($retval != 0) { @@ -390,21 +399,23 @@ class Utils if ($execmethod == 2) { // With this method, there is no way to get the return code, only output $handlein = popen($fullcommandclear, 'r'); $i = 0; - while (!feof($handlein)) { - $i++; // output line number - $read = fgets($handlein); - // Exclude warning line we don't want - if ($i == 1 && preg_match('/Warning.*Using a password/i', $read)) { - continue; - } - fwrite($handle, $read); - if (preg_match('/'.preg_quote('-- Dump completed').'/i', $read)) { - $ok = 1; - } elseif (preg_match('/'.preg_quote('SET SQL_NOTES=@OLD_SQL_NOTES').'/i', $read)) { - $ok = 1; + if ($handlein) { + while (!feof($handlein)) { + $i++; // output line number + $read = fgets($handlein); + // Exclude warning line we don't want + if ($i == 1 && preg_match('/Warning.*Using a password/i', $read)) { + continue; + } + fwrite($handle, $read); + if (preg_match('/'.preg_quote('-- Dump completed').'/i', $read)) { + $ok = 1; + } elseif (preg_match('/'.preg_quote('SET SQL_NOTES=@OLD_SQL_NOTES').'/i', $read)) { + $ok = 1; + } } + pclose($handlein); } - pclose($handlein); } @@ -627,7 +638,6 @@ class Utils $execmethod = 1; } //$execmethod=1; - dol_syslog("Utils::executeCLI execmethod=".$execmethod." system:".$command, LOG_DEBUG); $output_arr = array(); diff --git a/htdocs/core/class/validate.class.php b/htdocs/core/class/validate.class.php index 1b2447dc23a..61251167c21 100644 --- a/htdocs/core/class/validate.class.php +++ b/htdocs/core/class/validate.class.php @@ -55,13 +55,17 @@ class Validate { global $langs; - if ($outputLang) { + if (empty($outputLang)) { $this->outputLang = $langs; } else { $this->outputLang = $outputLang; } - $outputLang->load('validate'); + if (!is_object($this->outputLang) || !method_exists($this->outputLang, 'load')) { + return false; + } + + $this->outputLang->loadLangs(array('validate', 'errors')); $this->db = $db; } @@ -212,7 +216,7 @@ class Validate /** * Check Duration validity * - * @param string $duration to validate + * @param mixed $duration to validate * @return boolean Validity is ok or not */ public function isDuration($duration) @@ -224,6 +228,21 @@ class Validate return true; } + /** + * Check numeric validity + * + * @param mixed $string to validate + * @return boolean Validity is ok or not + */ + public function isNumeric($string) + { + if (!is_numeric($string)) { + $this->error = $this->outputLang->trans('RequireValidNumeric'); + return false; + } + return true; + } + /** * Check for boolean validity * diff --git a/htdocs/core/customreports.php b/htdocs/core/customreports.php index b7f01de0aeb..fafd1c015e8 100644 --- a/htdocs/core/customreports.php +++ b/htdocs/core/customreports.php @@ -40,7 +40,6 @@ if (!defined('USE_CUSTOM_REPORT_AS_INCLUDE')) { $objecttype = 'thirdparty'; } - $search_filters = GETPOST('search_filters', 'array'); $search_measures = GETPOST('search_measures', 'array'); //$search_xaxis = GETPOST('search_xaxis', 'array'); @@ -57,7 +56,7 @@ if (!defined('USE_CUSTOM_REPORT_AS_INCLUDE')) { } $search_yaxis = GETPOST('search_yaxis', 'array'); - $search_graph = GETPOST('search_graph', 'none'); + $search_graph = GETPOST('search_graph', 'restricthtml'); // Load variable for pagination $limit = GETPOST('limit', 'int') ? GETPOST('limit', 'int') : $conf->liste_limit; @@ -158,6 +157,7 @@ $extrafields->fetch_name_optionals_label($object->table_element); $search_array_options = $extrafields->getOptionalsFromPost($object->table_element, '', 'search_'); $search_component_params = array(''); +$search_component_params_hidden = GETPOST('search_component_params_hidden', 'alphanohtml'); $MAXUNIQUEVALFORGROUP = 20; $MAXMEASURESINBARGRAPH = 20; @@ -175,14 +175,16 @@ $arrayofgroupby = array(); $arrayofyaxis = array(); $arrayofvaluesforgroupby = array(); -$result = restrictedArea($user, $object->element, 0, ''); +restrictedArea($user, $object->element, 0, ''); + +$error = 0; /* * Actions */ - +// None @@ -245,8 +247,19 @@ if (is_array($search_groupby) && count($search_groupby)) { } else { $sql .= ' FROM '.MAIN_DB_PREFIX.$object->table_element.' as t'; } - // TODO Add the where here - // ... + + // Add the where here + /* + $sqlfilters = GETPOST('search_component_params_hidden', 'alphanohtml'); + if ($sqlfilters) { + $errormessage = ''; + if (dolCheckFilters($sqlfilters, $errormessage)) { + $regexstring = '\(([^:\'\(\)]+:[^:\'\(\)]+:[^\(\)]+)\)'; + $sql .= " WHERE (".preg_replace_callback('/'.$regexstring.'/', 'dolForgeCriteriaCallback', $sqlfilters).")"; + } else { + print $errormessage; + } + }*/ $sql .= ' LIMIT '.($MAXUNIQUEVALFORGROUP + 1); @@ -333,6 +346,24 @@ print ''; print ''; print ''; +$viewmode = ''; + +$viewmode .= '
    '; +$arrayofgraphs = array('bars' => 'Bars', 'lines' => 'Lines'); // also 'pies' +$viewmode .= '
    '.$langs->trans("Graph").'
    '; +$viewmode .= $form->selectarray('search_graph', $arrayofgraphs, $search_graph, 0, 0, 0, 'minwidth100', 1); +$viewmode .= '
    '; + +$num = 0; +$massactionbutton = ''; +$nav = ''; +$newcardbutton = ''; +$limit = 0; + +print_barre_liste('', $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, $massactionbutton, $num, -1, 'object_action', 0, $nav.''.$newcardbutton, '', $limit, 1, 0, 1, $viewmode); + + + print '
    '; // Select object @@ -349,22 +380,23 @@ foreach ($arrayoftype as $key => $val) { } print $form->selectarray('objecttype', $newarrayoftype, $objecttype, 0, 0, 0, '', 1, 0, 0, '', 'minwidth200', 1); if (empty($conf->use_javascript_ajax)) { - print ''; + print ''; } else { - print ''; } print '
    '; -// Add Filter +// Add Filter (you can use param &show_search_component_params_hidden=1 for debug) print '
    '; -print $form->searchComponent(array($object->element => $object->fields), $search_component_params); +print $form->searchComponent(array($object->element => $object->fields), $search_component_params, array(), $search_component_params_hidden); print '
    '; // Add measures into array @@ -393,13 +425,6 @@ print $form->multiselectarray('search_measures', $arrayofmesures, $search_measur print '
    '; -// Group by -print '
    '; -print '
    '.$langs->trans("GroupBy").'
    '; -print $formother->selectGroupByField($object, $search_groupby, $arrayofgroupby); -print '
    '; - - // XAxis print '
    '; print '
    '.$langs->trans("XAxis").'
    '; @@ -407,6 +432,13 @@ print $formother->selectXAxisField($object, $search_xaxis, $arrayofxaxis); print '
    '; +// Group by +print '
    '; +print '
    '.$langs->trans("GroupBy").'
    '; +print $formother->selectGroupByField($object, $search_groupby, $arrayofgroupby); +print '
    '; + + if ($mode == 'grid') { // YAxis print '
    '; @@ -449,14 +481,11 @@ if ($mode == 'grid') { } if ($mode == 'graph') { - print '
    '; - $arrayofgraphs = array('bars' => 'Bars', 'lines' => 'Lines'); // also 'pies' - print '
    '.$langs->trans("Graph").'
    '; - print $form->selectarray('search_graph', $arrayofgraphs, $search_graph, 0, 0, 0, 'minwidth100', 1); - print '
    '; + // } + print '
    '; -print ''; +print ''; print '
    '; print '
    '; print '
    '; @@ -531,8 +560,16 @@ if (!empty($search_measures) && !empty($search_xaxis)) { if ($object->ismultientitymanaged == 1) { $sql .= ' AND entity IN ('.getEntity($object->element).')'; } - foreach ($search_filters as $key => $val) { - // TODO Add the where here + // Add the where here + $sqlfilters = GETPOST('search_component_params_hidden', 'alphanohtml'); + if ($sqlfilters) { + $errormessage = ''; + if (dolCheckFilters($sqlfilters, $errormessage)) { + $regexstring = '\(([^:\'\(\)]+:[^:\'\(\)]+:[^\(\)]+)\)'; + $sql .= " AND (".preg_replace_callback('/'.$regexstring.'/', 'dolForgeCriteriaCallback', $sqlfilters).")"; + } else { + print $errormessage; + } } $sql .= ' GROUP BY '; foreach ($search_xaxis as $key => $val) { diff --git a/htdocs/core/db/Database.interface.php b/htdocs/core/db/Database.interface.php index c6769ad0619..e349072ddec 100644 --- a/htdocs/core/db/Database.interface.php +++ b/htdocs/core/db/Database.interface.php @@ -227,7 +227,7 @@ interface Database * Note that with Mysql, this parameter is not used as Myssql can already commit a transaction even if one request is in error, without using savepoints. * @param string $type Type of SQL order ('ddl' for insert, update, select, delete or 'dml' for create, alter...) * @param int $result_mode Result mode - * @return resource Resultset of answer + * @return bool|resource Resultset of answer or false */ public function query($query, $usesavepoint = 0, $type = 'auto', $result_mode = 0); diff --git a/htdocs/core/db/DoliDB.class.php b/htdocs/core/db/DoliDB.class.php index 1aecd205da3..4476228d509 100644 --- a/htdocs/core/db/DoliDB.class.php +++ b/htdocs/core/db/DoliDB.class.php @@ -62,11 +62,26 @@ abstract class DoliDB implements Database /** @var string Last error number. For example: 'DB_ERROR_RECORD_ALREADY_EXISTS', '12345', ... */ public $lasterrno; + /** @var string If we need to set a prefix specific to the database so it can be reused (when defined instead of MAIN_DB_PREFIX) to forge requests */ + public $prefix_db; + /** @var bool Status */ public $ok; /** @var string */ public $error; + + + /** + * Return the DB prefix + * + * @return string The DB prefix + */ + public function prefix() + { + return (empty($this->prefix_db) ? MAIN_DB_PREFIX : $this->prefix_db); + } + /** * Format a SQL IF * @@ -77,7 +92,8 @@ abstract class DoliDB implements Database */ public function ifsql($test, $resok, $resko) { - return 'IF('.$test.','.$resok.','.$resko.')'; + //return 'IF('.$test.','.$resok.','.$resko.')'; // Not sql standard + return '(CASE WHEN '.$test.' THEN '.$resok.' ELSE '.$resko.' END)'; } /** @@ -233,7 +249,7 @@ abstract class DoliDB implements Database * Define sort criteria of request * * @param string $sortfield List of sort fields, separated by comma. Example: 't1.fielda,t2.fieldb' - * @param string $sortorder Sort order, separated by comma. Example: 'ASC,DESC'; + * @param string $sortorder Sort order, separated by comma. Example: 'ASC,DESC'. Note: If the quantity fo sortorder values is lower than sortfield, we used the last value for missing values. * @return string String to provide syntax of a sort sql string */ public function order($sortfield = null, $sortorder = null) diff --git a/htdocs/core/db/mysqli.class.php b/htdocs/core/db/mysqli.class.php index 0db4e16a897..4dd71f0e351 100644 --- a/htdocs/core/db/mysqli.class.php +++ b/htdocs/core/db/mysqli.class.php @@ -663,9 +663,13 @@ class DoliDBMysqli extends DoliDB $like = ''; if ($table) { - $like = "LIKE '".$table."'"; + $tmptable = preg_replace('/[^a-z0-9\.\-\_%]/i', '', $table); + + $like = "LIKE '".$this->escape($tmptable)."'"; } - $sql = "SHOW TABLES FROM ".$database." ".$like.";"; + $tmpdatabase = preg_replace('/[^a-z0-9\.\-\_]/i', '', $database); + + $sql = "SHOW TABLES FROM ".$tmpdatabase." ".$like.";"; //print $sql; $result = $this->query($sql); if ($result) { @@ -688,7 +692,9 @@ class DoliDBMysqli extends DoliDB // phpcs:enable $infotables = array(); - $sql = "SHOW FULL COLUMNS FROM ".$table.";"; + $tmptable = preg_replace('/[^a-z0-9\.\-\_]/i', '', $table); + + $sql = "SHOW FULL COLUMNS FROM ".$tmptable.";"; dol_syslog($sql, LOG_DEBUG); $result = $this->query($sql); @@ -794,7 +800,9 @@ class DoliDBMysqli extends DoliDB public function DDLDropTable($table) { // phpcs:enable - $sql = "DROP TABLE ".$table; + $tmptable = preg_replace('/[^a-z0-9\.\-\_]/i', '', $table); + + $sql = "DROP TABLE ".$tmptable; if (!$this->query($sql)) { return -1; @@ -925,8 +933,9 @@ class DoliDBMysqli extends DoliDB public function DDLDropField($table, $field_name) { // phpcs:enable - $sql = "ALTER TABLE ".$table." DROP COLUMN `".$field_name."`"; - dol_syslog(get_class($this)."::DDLDropField ".$sql, LOG_DEBUG); + $tmp_field_name = preg_replace('/[^a-z0-9\.\-\_]/i', '', $field_name); + + $sql = "ALTER TABLE ".$table." DROP COLUMN `".$tmp_field_name."`"; if ($this->query($sql)) { return 1; } diff --git a/htdocs/core/db/pgsql.class.php b/htdocs/core/db/pgsql.class.php index 5997349d0c5..74abf7a1e36 100644 --- a/htdocs/core/db/pgsql.class.php +++ b/htdocs/core/db/pgsql.class.php @@ -495,7 +495,7 @@ class DoliDBPgsql extends DoliDB * @param int $usesavepoint 0=Default mode, 1=Run a savepoint before and a rollback to savepoint if error (this allow to have some request with errors inside global transactions). * @param string $type Type of SQL order ('ddl' for insert, update, select, delete or 'dml' for create, alter...) * @param int $result_mode Result mode (not used with pgsql) - * @return false|resource Resultset of answer + * @return bool|resource Resultset of answer */ public function query($query, $usesavepoint = 0, $type = 'auto', $result_mode = 0) { @@ -937,7 +937,9 @@ class DoliDBPgsql extends DoliDB $escapedlike = ''; if ($table) { - $escapedlike = " AND table_name LIKE '".$this->escape($table)."'"; + $tmptable = preg_replace('/[^a-z0-9\.\-\_%]/i', '', $table); + + $escapedlike = " AND table_name LIKE '".$this->escape($tmptable)."'"; } $result = pg_query($this->db, "SELECT table_name FROM information_schema.tables WHERE table_schema = 'public'".$escapedlike." ORDER BY table_name"); if ($result) { @@ -973,8 +975,8 @@ class DoliDBPgsql extends DoliDB $sql .= " '' as \"Extra\","; $sql .= " '' as \"Privileges\""; $sql .= " FROM information_schema.columns infcol"; - $sql .= " WHERE table_schema='public' "; - $sql .= " AND table_name='".$this->escape($table)."'"; + $sql .= " WHERE table_schema = 'public' "; + $sql .= " AND table_name = '".$this->escape($table)."'"; $sql .= " ORDER BY ordinal_position;"; dol_syslog($sql, LOG_DEBUG); @@ -1078,7 +1080,9 @@ class DoliDBPgsql extends DoliDB public function DDLDropTable($table) { // phpcs:enable - $sql = "DROP TABLE ".$table; + $tmptable = preg_replace('/[^a-z0-9\.\-\_]/i', '', $table); + + $sql = "DROP TABLE ".$tmptable; if (!$this->query($sql)) { return -1; @@ -1236,8 +1240,9 @@ class DoliDBPgsql extends DoliDB public function DDLDropField($table, $field_name) { // phpcs:enable - $sql = "ALTER TABLE ".$table." DROP COLUMN ".$field_name; - dol_syslog($sql, LOG_DEBUG); + $tmp_field_name = preg_replace('/[^a-z0-9\.\-\_]/i', '', $field_name); + + $sql = "ALTER TABLE ".$table." DROP COLUMN ".$tmp_field_name; if (!$this->query($sql)) { $this->error = $this->lasterror(); return -1; diff --git a/htdocs/core/db/sqlite3.class.php b/htdocs/core/db/sqlite3.class.php index c03d2a5ee04..10f9c021c0d 100644 --- a/htdocs/core/db/sqlite3.class.php +++ b/htdocs/core/db/sqlite3.class.php @@ -394,7 +394,7 @@ class DoliDBSqlite3 extends DoliDB * Note that with Mysql, this parameter is not used as Myssql can already commit a transaction even if one request is in error, without using savepoints. * @param string $type Type of SQL order ('ddl' for insert, update, select, delete or 'dml' for create, alter...) * @param int $result_mode Result mode (not used with sqlite) - * @return SQLite3Result Resultset of answer + * @return bool|SQLite3Result Resultset of answer */ public function query($query, $usesavepoint = 0, $type = 'auto', $result_mode = 0) { @@ -407,6 +407,7 @@ class DoliDBSqlite3 extends DoliDB $this->error = ''; // Convert MySQL syntax to SQLite syntax + $reg = array(); if (preg_match('/ALTER\s+TABLE\s*(.*)\s*ADD\s+CONSTRAINT\s+(.*)\s*FOREIGN\s+KEY\s*\(([\w,\s]+)\)\s*REFERENCES\s+(\w+)\s*\(([\w,\s]+)\)/i', $query, $reg)) { // Ajout d'une clef étrangère à la table // procédure de remplacement de la table pour ajouter la contrainte @@ -875,9 +876,13 @@ class DoliDBSqlite3 extends DoliDB $like = ''; if ($table) { - $like = "LIKE '".$table."'"; + $tmptable = preg_replace('/[^a-z0-9\.\-\_%]/i', '', $table); + + $like = "LIKE '".$this->escape($tmptable)."'"; } - $sql = "SHOW TABLES FROM ".$database." ".$like.";"; + $tmpdatabase = preg_replace('/[^a-z0-9\.\-\_]/i', '', $database); + + $sql = "SHOW TABLES FROM ".$tmpdatabase." ".$like.";"; //print $sql; $result = $this->query($sql); if ($result) { @@ -901,7 +906,9 @@ class DoliDBSqlite3 extends DoliDB // phpcs:enable $infotables = array(); - $sql = "SHOW FULL COLUMNS FROM ".$table.";"; + $tmptable = preg_replace('/[^a-z0-9\.\-\_]/i', '', $table); + + $sql = "SHOW FULL COLUMNS FROM ".$tmptable.";"; dol_syslog($sql, LOG_DEBUG); $result = $this->query($sql); @@ -1002,7 +1009,9 @@ class DoliDBSqlite3 extends DoliDB public function DDLDropTable($table) { // phpcs:enable - $sql = "DROP TABLE ".$table; + $tmptable = preg_replace('/[^a-z0-9\.\-\_]/i', '', $table); + + $sql = "DROP TABLE ".$tmptable; if (!$this->query($sql)) { return -1; @@ -1112,8 +1121,9 @@ class DoliDBSqlite3 extends DoliDB public function DDLDropField($table, $field_name) { // phpcs:enable - $sql = "ALTER TABLE ".$table." DROP COLUMN `".$field_name."`"; - dol_syslog(get_class($this)."::DDLDropField ".$sql, LOG_DEBUG); + $tmp_field_name = preg_replace('/[^a-z0-9\.\-\_]/i', '', $field_name); + + $sql = "ALTER TABLE ".$table." DROP COLUMN `".$tmp_field_name."`"; if (!$this->query($sql)) { $this->error = $this->lasterror(); return -1; diff --git a/htdocs/core/get_info.php b/htdocs/core/get_info.php index ee21aac0de3..067372c26ff 100644 --- a/htdocs/core/get_info.php +++ b/htdocs/core/get_info.php @@ -33,7 +33,7 @@ if (!defined('NOCSRFCHECK')) { if (!defined('NOTOKENRENEWAL')) { define('NOTOKENRENEWAL', 1); } -//if (! defined('NOLOGIN')) define('NOLOGIN',1); // Not disabled cause need to load personalized language +//if (! defined('NOLOGIN')) define('NOLOGIN',1); // Not disabled cause need to load personalized language and need security layer if (!defined('NOREQUIREMENU')) { define('NOREQUIREMENU', 1); } @@ -56,8 +56,8 @@ $left = ($langs->trans("DIRECTION") == 'rtl' ? 'right' : 'left'); $title = $langs->trans("Info"); -// URL http://mydolibarr/core/search_page?dol_use_jmobile=1 can be used for tests -$head = ''."\n"; +// URL http://mydolibarr/core/get_info.php?dol_use_jmobile=1 can be used for tests +$head = ''."\n"; $arrayofjs = array(); $arrayofcss = array(); top_htmlhead($head, $title, 0, 0, $arrayofjs, $arrayofcss); @@ -93,7 +93,7 @@ if (empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) { if ($_SESSION["dol_authmode"] != 'forceuser' && $_SESSION["dol_authmode"] != 'http') { $logouthtmltext .= $langs->trans("Logout").'
    '; - $logouttext .= ''; + $logouttext .= ''; //$logouttext .= img_picto($langs->trans('Logout').":".$langs->trans('Logout'), 'logout_top.png', 'class="login"', 0, 0, 1); $logouttext .= ''; $logouttext .= ''; @@ -158,7 +158,7 @@ if (empty($conf->global->MAIN_PRINT_DISABLELINK) && empty($conf->global->MAIN_OP } } $qs.=(($qs && $morequerystring)?'&':'').$morequerystring; - $text =''; + $text =''; //$text.= img_picto(":".$langs->trans("PrintContentArea"), 'printer_top.png', 'class="printer"'); $text.=''; $text.=''; @@ -191,7 +191,7 @@ if (empty($conf->global->MAIN_HELP_DISABLELINK) && empty($conf->global->MAIN_OPT $title=$appli.'
    '; $title.=$langs->trans($mode == 'wiki' ? 'GoToWikiHelpPage': 'GoToHelpPage'); if ($mode == 'wiki') $title.=' - '.$langs->trans("PageWiki").' "'.dol_escape_htmltag(strtr($helppage,'_',' ')).'"'; - $text.=''; diff --git a/htdocs/core/get_menudiv.php b/htdocs/core/get_menudiv.php index 84be9ff8e0f..597031945f1 100644 --- a/htdocs/core/get_menudiv.php +++ b/htdocs/core/get_menudiv.php @@ -81,10 +81,22 @@ $left = ($langs->trans("DIRECTION") == 'rtl' ? 'right' : 'left'); * View */ +// Important: Following code is to avoid page request by browser and PHP CPU at each Dolibarr page access. +if (empty($dolibarr_nocache) && GETPOST('cache', 'int')) { + header('Cache-Control: max-age='.GETPOST('cache', 'int').', public, must-revalidate'); + // For a .php, we must set an Expires to avoid to have it forced to an expired value by the web server + header('Expires: '.gmdate('D, d M Y H:i:s', dol_now('gmt') + GETPOST('cache', 'int')).' GMT'); + // HTTP/1.0 + header('Pragma: token=public'); +} else { + // HTTP/1.0 + header('Cache-Control: no-cache'); +} + $title = $langs->trans("Menu"); // URL http://mydolibarr/core/get_menudiv.php?dol_use_jmobile=1 can be used for tests -$head = ''."\n"; +$head = ''."\n"; // This is used by DoliDroid to know page is a menu page $arrayofjs = array(); $arrayofcss = array(); top_htmlhead($head, $title, 0, 0, $arrayofjs, $arrayofcss); @@ -109,12 +121,13 @@ print ' display: none; } - a.alilevel0 { + a.alilevel0, span.spanlilevel0 { background-image: url(\''.DOL_URL_ROOT.'/theme/'.urlencode($conf->theme).'/img/next.png\') !important; background-repeat: no-repeat !important; background-position-x: 10px; background-position-y: 16px; padding: 1em 15px 1em 40px; + display: block; } li.lilevel0 font.vsmenudisabled { background-repeat: no-repeat !important; diff --git a/htdocs/core/js/lib_notification.js.php b/htdocs/core/js/lib_notification.js.php index ee41555119a..af5952f6026 100644 --- a/htdocs/core/js/lib_notification.js.php +++ b/htdocs/core/js/lib_notification.js.php @@ -64,26 +64,31 @@ print "jQuery(document).ready(function () {\n"; //print " console.log('referrer=".dol_escape_js($_SERVER['HTTP_REFERER'])."');\n"; print ' var nowtime = Date.now();'; -print ' var time_auto_update = '.$conf->global->MAIN_BROWSER_NOTIFICATION_FREQUENCY.';'."\n"; // Always defined +print ' var time_auto_update = '.max(1, getDolGlobalInt('MAIN_BROWSER_NOTIFICATION_FREQUENCY')).';'."\n"; // Always defined print ' var time_js_next_test;'."\n"; ?> -/* Check if permission ok */ -if (Notification.permission !== "granted") { - console.log("Ask Notification.permission"); - Notification.requestPermission() +/* Check if Notification is supported */ +if ("Notification" in window) { + /* Check if permission ok */ + if (Notification.permission !== "granted") { + console.log("Ask Notification.permission"); + Notification.requestPermission() + } + + /* Launch timer */ + + // We set a delay before launching first test so next check will arrive after the time_auto_update compared to previous one. + //var time_first_execution = (time_auto_update + (time_js_next_test - nowtime)) * 1000; //need milliseconds + var time_first_execution = global->MAIN_BROWSER_NOTIFICATION_CHECK_FIRST_EXECUTION) ? 0 : $conf->global->MAIN_BROWSER_NOTIFICATION_CHECK_FIRST_EXECUTION); ?>; + + setTimeout(first_execution, time_first_execution * 1000); + time_js_next_test = nowtime + time_first_execution; + console.log("Launch browser notif check: setTimeout is set to launch 'first_execution' function after a wait of time_first_execution="+time_first_execution+". nowtime (time php page generation) = "+nowtime+" time_js_next_check = "+time_js_next_test); +} else { + console.log("This browser in this context does not support Notification."); } -/* Launch timer */ - -// We set a delay before launching first test so next check will arrive after the time_auto_update compared to previous one. -//var time_first_execution = (time_auto_update + (time_js_next_test - nowtime)) * 1000; //need milliseconds -var time_first_execution = global->MAIN_BROWSER_NOTIFICATION_CHECK_FIRST_EXECUTION) ? 0 : $conf->global->MAIN_BROWSER_NOTIFICATION_CHECK_FIRST_EXECUTION); ?>; - -setTimeout(first_execution, time_first_execution * 1000); -time_js_next_test = nowtime + time_first_execution; -console.log("Launch browser notif check: setTimeout is set to launch 'first_execution' function after a wait of time_first_execution="+time_first_execution+". nowtime (time php page generation) = "+nowtime+" time_js_next_check = "+time_js_next_test); - function first_execution() { console.log("Call first_execution then set repeat time to time_auto_update = MAIN_BROWSER_NOTIFICATION_FREQUENCY = "+time_auto_update); diff --git a/htdocs/core/js/lib_photosresize.js b/htdocs/core/js/lib_photosresize.js index 99c7c36c29e..38c508f90b3 100644 --- a/htdocs/core/js/lib_photosresize.js +++ b/htdocs/core/js/lib_photosresize.js @@ -32,12 +32,40 @@ function updateCoords(c) { //alert(parseInt(jQuery("#ratioforcrop").val())); ratio=1; - if (parseInt(jQuery("#ratioforcrop").val()) > 0) ratio = parseInt(jQuery("#ratioforcrop").val()); + imagewidth=0; + imageheight=0; + + console.log(c); + + if (parseInt(jQuery("#ratioforcrop").val()) > 1) { + ratio = parseInt(jQuery("#ratioforcrop").val()); + if (parseInt(jQuery("#imagewidth").val()) > 0) imagewidth = parseInt(jQuery("#imagewidth").val()); + if (parseInt(jQuery("#imageheight").val()) > 0) imageheight = parseInt(jQuery("#imageheight").val()); + } + + x = Math.floor(c.x * ratio); + y = Math.floor(c.y * ratio); + x2 = Math.ceil(c.x2 * ratio); + y2 = Math.ceil(c.y2 * ratio); + console.log("x="+x+" y="+y+" x2="+x2+" y2="+y2+" imageheight="+imageheight+" ratio="+ratio); + if (imagewidth > 0 && x > imagewidth) { + x = imagewidth; + } + if (imageheight > 0 && y > imageheight) { + y = imageheight; + } + if (imagewidth > 0 && x2 > imagewidth) { + x2 = imagewidth; + } + if (imageheight > 0 && y2 > imageheight) { + y2 = imageheight; + } + //console.log(ratio); - jQuery('#x').val(Math.ceil(c.x * ratio)); - jQuery('#y').val(Math.ceil(c.y * ratio)); - jQuery('#x2').val(Math.ceil(c.x2 * ratio)); - jQuery('#y2').val(Math.ceil(c.y2 * ratio)); - jQuery('#w').val(Math.ceil(c.w * ratio)); - jQuery('#h').val(Math.ceil(c.h * ratio)); + jQuery('#x').val(x); + jQuery('#y').val(y); + jQuery('#x2').val(x2); + jQuery('#y2').val(y2); + jQuery('#w').val(x2-x); + jQuery('#h').val(y2-y); }; diff --git a/htdocs/core/lib/admin.lib.php b/htdocs/core/lib/admin.lib.php index 842d7333bbd..448677002df 100644 --- a/htdocs/core/lib/admin.lib.php +++ b/htdocs/core/lib/admin.lib.php @@ -240,6 +240,7 @@ function run_sql($sqlfile, $silent = 1, $entity = '', $usesavepoint = 1, $handle if (empty($nocommentremoval)) { $buf = preg_replace('/([,;ERLT\)])\s*--.*$/i', '\1', $buf); //remove comment from a line that not start with -- before add it to the buffer } + if ($buffer) $buffer .= ' '; $buffer .= trim($buf); } @@ -451,7 +452,7 @@ function run_sql($sqlfile, $silent = 1, $entity = '', $usesavepoint = 1, $handle } //if (!empty($conf->use_javascript_ajax)) { // use_javascript_ajax is not defined - print ''; if ($addlink) { if ($textonlink === 'image') { - $out .= ' '.img_picto('', 'globe').''; + $out .= ' '.img_picto('', 'globe').''; } else { - $out .= ' '.$langs->trans("Link").''; + $out .= ' '.$langs->trans("Link").''; } } return $out; @@ -10568,3 +10666,90 @@ function jsonOrUnserialize($stringtodecode) return $result; } + + + +/** + * Return if a $sqlfilters parameter is valid and will pass the preg_replace_callback() to replace Generic filter string with SQL filter string + * Example of usage: + * if ($sqlfilters) { + * $errormessage = ''; + * if (dolCheckFilters($sqlfilters, $errormessage)) { + * $regexstring = '\(([^:\'\(\)]+:[^:\'\(\)]+:[^\(\)]+)\)'; + * $sql .= " AND (".preg_replace_callback('/'.$regexstring.'/', 'dolForgeCriteriaCallback', $sqlfilters).")"; + * } + * } + * + * @param string $sqlfilters sqlfilter string + * @param string $error Error message + * @return boolean True if valid, False if not valid ($error is filled with the reason in such a case) + */ +function dolCheckFilters($sqlfilters, &$error = '') +{ + //$regexstring='\(([^:\'\(\)]+:[^:\'\(\)]+:[^:\(\)]+)\)'; + //$tmp=preg_replace_all('/'.$regexstring.'/', '', $sqlfilters); + $tmp = $sqlfilters; + $i = 0; $nb = strlen($tmp); + $counter = 0; + while ($i < $nb) { + if ($tmp[$i] == '(') { + $counter++; + } + if ($tmp[$i] == ')') { + $counter--; + } + if ($counter < 0) { + $error = "Bad sqlfilters=".$sqlfilters; + dol_syslog($error, LOG_WARNING); + return false; + } + $i++; + } + return true; +} + +/** + * Function to forge a SQL criteria from a Generic filter string. + * Example of usage: + * if ($sqlfilters) { + * $errormessage = ''; + * if (dolCheckFilters($sqlfilters, $errormessage)) { + * $regexstring = '\(([^:\'\(\)]+:[^:\'\(\)]+:[^\(\)]+)\)'; + * $sql .= " AND (".preg_replace_callback('/'.$regexstring.'/', 'dolForgeCriteriaCallback', $sqlfilters).")"; + * } + * } + * + * @param array $matches Array of found string by regex search. + * Example: "t.ref:like:'SO-%'" or "t.date_creation:<:'20160101'" or "t.date_creation:<:'2016-01-01 12:30:00'" or "t.nature:is:NULL" + * @return string Forged criteria. Example: "t.field like 'abc%'" + */ +function dolForgeCriteriaCallback($matches) +{ + global $db; + + //dol_syslog("Convert matches ".$matches[1]); + if (empty($matches[1])) { + return ''; + } + $tmp = explode(':', $matches[1], 3); + + if (count($tmp) < 3) { + return ''; + } + + $operand = preg_replace('/[^a-z0-9\._]/i', '', trim($tmp[0])); + + $operator = strtoupper(preg_replace('/[^a-z<>=]/i', '', trim($tmp[1]))); + + $tmpescaped = trim($tmp[2]); + $regbis = array(); + if ($operator == 'IN') { + $tmpescaped = "(".$db->sanitize($tmpescaped, 1).")"; + } elseif (preg_match('/^\'(.*)\'$/', $tmpescaped, $regbis)) { + $tmpescaped = "'".$db->escape($regbis[1])."'"; + } else { + $tmpescaped = $db->sanitize($db->escape($tmpescaped)); + } + + return $db->escape($operand).' '.$db->escape($operator)." ".$tmpescaped; +} diff --git a/htdocs/core/lib/functions2.lib.php b/htdocs/core/lib/functions2.lib.php index 26c56d45463..fe6e8e0fa40 100644 --- a/htdocs/core/lib/functions2.lib.php +++ b/htdocs/core/lib/functions2.lib.php @@ -2131,30 +2131,38 @@ function dolGetElementUrl($objectid, $objecttype, $withpicto = 0, $option = '') // Special cases, to work with non standard path if ($objecttype == 'facture' || $objecttype == 'invoice') { + $langs->load('bills'); $classpath = 'compta/facture/class'; $module = 'facture'; $myobject = 'facture'; } elseif ($objecttype == 'commande' || $objecttype == 'order') { + $langs->load('orders'); $classpath = 'commande/class'; $module = 'commande'; $myobject = 'commande'; } elseif ($objecttype == 'propal') { + $langs->load('propal'); $classpath = 'comm/propal/class'; } elseif ($objecttype == 'supplier_proposal') { + $langs->load('supplier_proposal'); $classpath = 'supplier_proposal/class'; } elseif ($objecttype == 'shipping') { + $langs->load('sendings'); $classpath = 'expedition/class'; $myobject = 'expedition'; $module = 'expedition_bon'; } elseif ($objecttype == 'delivery') { + $langs->load('deliveries'); $classpath = 'delivery/class'; $myobject = 'delivery'; $module = 'delivery_note'; } elseif ($objecttype == 'contract') { + $langs->load('contracts'); $classpath = 'contrat/class'; $module = 'contrat'; $myobject = 'contrat'; } elseif ($objecttype == 'member') { + $langs->load('members'); $classpath = 'adherents/class'; $module = 'adherent'; $myobject = 'adherent'; @@ -2163,13 +2171,16 @@ function dolGetElementUrl($objectid, $objecttype, $withpicto = 0, $option = '') $module = 'cabinetmed'; $myobject = 'cabinetmedcons'; } elseif ($objecttype == 'fichinter') { + $langs->load('interventions'); $classpath = 'fichinter/class'; $module = 'ficheinter'; $myobject = 'fichinter'; } elseif ($objecttype == 'project') { + $langs->load('projects'); $classpath = 'projet/class'; $module = 'projet'; } elseif ($objecttype == 'task') { + $langs->load('projects'); $classpath = 'projet/class'; $module = 'projet'; $myobject = 'task'; diff --git a/htdocs/core/lib/geturl.lib.php b/htdocs/core/lib/geturl.lib.php index be7e1ffa80b..8f9942d8108 100644 --- a/htdocs/core/lib/geturl.lib.php +++ b/htdocs/core/lib/geturl.lib.php @@ -35,9 +35,10 @@ * @param string[] $addheaders Array of string to add into header. Example: ('Accept: application/xrds+xml', ....) * @param string[] $allowedschemes List of schemes that are allowed ('http' + 'https' only by default) * @param int $localurl 0=Only external URL are possible, 1=Only local URL, 2=Both external and local URL are allowed. + * @param int $ssl_verifypeer -1=Auto (no ssl check on dev, check on prod), 0=No ssl check, 1=Always ssl check * @return array Returns an associative array containing the response from the server array('content'=>response, 'curl_error_no'=>errno, 'curl_error_msg'=>errmsg...) */ -function getURLContent($url, $postorget = 'GET', $param = '', $followlocation = 1, $addheaders = array(), $allowedschemes = array('http', 'https'), $localurl = 0) +function getURLContent($url, $postorget = 'GET', $param = '', $followlocation = 1, $addheaders = array(), $allowedschemes = array('http', 'https'), $localurl = 0, $ssl_verifypeer = -1) { //declaring of global variables global $conf; @@ -75,8 +76,17 @@ function getURLContent($url, $postorget = 'GET', $param = '', $followlocation = } //curl_setopt($ch, CURLOPT_SSLVERSION, 6); for tls 1.2 + // Turning on or off the ssl target certificate + if ($ssl_verifypeer < 0) { + global $dolibarr_main_prod; + $ssl_verifypeer = ($dolibarr_main_prod ? true : false); + } + if (!empty($conf->global->MAIN_CURL_DISABLE_VERIFYPEER)) { + $ssl_verifypeer = 0; + } + // Turning off the server and peer verification(TrustManager Concept). - curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, ($ssl_verifypeer ? true : false)); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); // Restrict use to some protocols only @@ -214,11 +224,14 @@ function getURLContent($url, $postorget = 'GET', $param = '', $followlocation = } } - // Common check (local and external) - if (in_array($iptocheck, array('100.100.100.200'))) { - $info['http_code'] = 400; - $info['content'] = 'Error bad hostname IP (Used by Alibaba metadata). Must be an external URL.'; - break; + // Common check on ip (local and external) + $arrayofmetadataserver = array('100.100.100.200' => 'Alibaba', '192.0.0.192'=> 'Oracle', '192.80.8.124'=>'Packet'); + foreach ($arrayofmetadataserver as $ipofmetadataserver => $nameofmetadataserver) { + if ($iptocheck == $ipofmetadataserver) { + $info['http_code'] = 400; + $info['content'] = 'Error bad hostname IP (Used by '.$nameofmetadataserver.' metadata server). This IP is forbidden.'; + break 2; // exit the foreach and the do... + } } // Set CURLOPT_CONNECT_TO so curl will not try another resolution that may give a different result. Possible only on PHP v7+ diff --git a/htdocs/core/lib/images.lib.php b/htdocs/core/lib/images.lib.php index 0ccb6a415fc..6b973d89887 100644 --- a/htdocs/core/lib/images.lib.php +++ b/htdocs/core/lib/images.lib.php @@ -388,7 +388,7 @@ function dolRotateImage($file_path) * Add exif orientation correction for image * * @param string $fileSource Full path to source image to rotate - * @param string $fileDest string : Full path to image to rotate | false return gd img | null the raw image stream will be outputted directly + * @param string|bool $fileDest string : Full path to image to rotate | false return gd img | null the raw image stream will be outputted directly * @param int $quality output image quality * @return bool : true on success or false on failure or gd img if $fileDest is false. */ @@ -630,7 +630,7 @@ function vignette($file, $maxWidth = 160, $maxHeight = 120, $extName = '_small', } // replace image with good orientation - if (!empty($rotated)) { + if (!empty($rotated) && isset($trueImgWidth) && isset($trueImgHeight)) { $img = $rotated; $imgWidth = $trueImgWidth; $imgHeight = $trueImgHeight; diff --git a/htdocs/core/lib/invoice.lib.php b/htdocs/core/lib/invoice.lib.php index 8b61a690910..c64c63fc0dd 100644 --- a/htdocs/core/lib/invoice.lib.php +++ b/htdocs/core/lib/invoice.lib.php @@ -509,12 +509,14 @@ function getNumberInvoicesPieChart($mode) while ($i < $num) { $obj = $db->fetch_object($resql); - /*$dataseries = array(array($langs->trans('InvoiceLate30Days'), $obj->nblate30) + /* + $dataseries = array(array($langs->trans('InvoiceLate30Days'), $obj->nblate30) ,array($langs->trans('InvoiceLate15Days'), $obj->nblate15 - $obj->nblate30) ,array($langs->trans('InvoiceLateMinus15Days'), $obj->nblatenow - $obj->nblate15) ,array($langs->trans('InvoiceNotLate'), $obj->nbnotlatenow - $obj->nbnotlate15) ,array($langs->trans('InvoiceNotLate15Days'), $obj->nbnotlate15 - $obj->nbnotlate30) - ,array($langs->trans('InvoiceNotLate30Days'), $obj->nbnotlate30));*/ + ,array($langs->trans('InvoiceNotLate30Days'), $obj->nbnotlate30)); + */ $dataseries[$i]=array($langs->trans('NbOfOpenInvoices'), $obj->nblate30, $obj->nblate15 - $obj->nblate30, $obj->nblatenow - $obj->nblate15, $obj->nbnotlatenow - $obj->nbnotlate15, $obj->nbnotlate15 - $obj->nbnotlate30, $obj->nbnotlate30); $i++; } @@ -561,8 +563,9 @@ function getNumberInvoicesPieChart($mode) $dolgraph->setShowLegend(2); $dolgraph->setShowPercent(1); $dolgraph->SetType(array('bars', 'bars', 'bars', 'bars', 'bars', 'bars')); - $dolgraph->setHeight('160'); - $dolgraph->setWidth('400'); + //$dolgraph->SetType(array('pie')); + $dolgraph->setHeight('160'); /* 160 min is required to show the 6 lines of legend */ + $dolgraph->setWidth('450'); $dolgraph->setHideXValues(true); if ($mode == 'customers') { $dolgraph->draw('idgraphcustomerinvoices'); diff --git a/htdocs/core/lib/memory.lib.php b/htdocs/core/lib/memory.lib.php index 557bbe6dcf5..0c056ddbc2e 100644 --- a/htdocs/core/lib/memory.lib.php +++ b/htdocs/core/lib/memory.lib.php @@ -93,7 +93,7 @@ function dol_setcache($memoryid, $data, $expire = 0) $dolmemcache->add($memoryid, $data, $expire); // This fails if key already exists $rescode = $dolmemcache->getResultCode(); if ($rescode == 0) { - return is_countable($data) ? count($data) : 0; + return is_array($data) ? count($data) : (is_scalar($data) ? strlen($data) : 0); } else { return -$rescode; } @@ -113,7 +113,7 @@ function dol_setcache($memoryid, $data, $expire = 0) //$dolmemcache->setOption(Memcached::OPT_COMPRESSION, false); $result = $dolmemcache->add($memoryid, $data, false, $expire); // This fails if key already exists if ($result) { - return is_countable($data) ? count($data) : 0; + return is_array($data) ? count($data) : (is_scalar($data) ? strlen($data) : 0); } else { return -1; } diff --git a/htdocs/core/lib/modulebuilder.lib.php b/htdocs/core/lib/modulebuilder.lib.php index d44da05fa38..7c32b377bd4 100644 --- a/htdocs/core/lib/modulebuilder.lib.php +++ b/htdocs/core/lib/modulebuilder.lib.php @@ -305,6 +305,8 @@ function rebuildObjectSql($destdir, $module, $objectname, $newmask, $readdir = ' $texttoinsert .= "\t".$key." ".$type; if ($key == 'rowid') { $texttoinsert .= ' AUTO_INCREMENT PRIMARY KEY'; + } elseif ($type == 'timestamp') { + $texttoinsert .= ' DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP'; } if ($key == 'entity') { $texttoinsert .= ' DEFAULT 1'; diff --git a/htdocs/core/lib/oauth.lib.php b/htdocs/core/lib/oauth.lib.php index 48356868143..ab1b5a217b8 100644 --- a/htdocs/core/lib/oauth.lib.php +++ b/htdocs/core/lib/oauth.lib.php @@ -25,13 +25,13 @@ // Supported OAUTH (a provider is supported when a file xxx_oauthcallback.php is available into htdocs/core/modules/oauth) $supportedoauth2array = array( - 'OAUTH_GOOGLE_NAME'=>'google', + 'OAUTH_GOOGLE_NAME'=>array('callbackfile' => 'google', 'picto' => 'google', 'urlforapp' => 'OAUTH_GOOGLE_DESC', 'name'=>'Google'), ); -if ($conf->global->MAIN_FEATURES_LEVEL >= 2) { - $supportedoauth2array['OAUTH_STRIPE_TEST_NAME'] = 'stripetest'; - $supportedoauth2array['OAUTH_STRIPE_LIVE_NAME'] = 'stripelive'; +if (!empty($conf->stripe->enabled)) { + $supportedoauth2array['OAUTH_STRIPE_TEST_NAME'] = array('callbackfile' => 'stripetest', 'picto' => 'stripe', 'urlforapp' => '', 'name'=>'StripeTest'); + $supportedoauth2array['OAUTH_STRIPE_LIVE_NAME'] = array('callbackfile' => 'stripelive', 'picto' => 'stripe', 'urlforapp' => '', 'name'=>'StripeLive'); } -$supportedoauth2array['OAUTH_GITHUB_NAME'] = 'github'; +$supportedoauth2array['OAUTH_GITHUB_NAME'] = array('callbackfile' => 'github', 'picto' => 'github', 'urlforapp' => 'OAUTH_GITHUB_DESC', 'name'=>'GitHub'); diff --git a/htdocs/core/lib/payments.lib.php b/htdocs/core/lib/payments.lib.php index 2bb1cf1d0cf..2911564adef 100644 --- a/htdocs/core/lib/payments.lib.php +++ b/htdocs/core/lib/payments.lib.php @@ -187,7 +187,7 @@ function showOnlinePaymentUrl($type, $ref) $out = img_picto('', 'globe').' '.$langs->trans("ToOfferALinkForOnlinePayment", $servicename).'
    '; $url = getOnlinePaymentUrl(0, $type, $ref); $out .= ''; $out .= ajax_autoselect("onlinepaymenturl", 0); return $out; @@ -205,7 +205,7 @@ function getHtmlOnlinePaymentLink($type, $ref, $label = '') { $url = getOnlinePaymentUrl(0, $type, $ref); $label = $label ? $label : $url; - return''.$label.''; + return ''.$label.''; } @@ -213,14 +213,14 @@ function getHtmlOnlinePaymentLink($type, $ref, $label = '') * Return string with full Url * * @param int $mode 0=True url, 1=Url formated with colors - * @param string $type Type of URL ('free', 'order', 'invoice', 'contractline', 'member' ...) + * @param string $type Type of URL ('free', 'order', 'invoice', 'contractline', 'member', 'boothlocation', ...) * @param string $ref Ref of object - * @param int $amount Amount (required for $type='free' only) - * @param string $freetag Free tag + * @param int $amount Amount (required and used for $type='free' only) + * @param string $freetag Free tag (required and used for $type='free' only) * @param string $localorexternal 0=Url for browser, 1=Url for external access * @return string Url string */ -function getOnlinePaymentUrl($mode, $type, $ref = '', $amount = '9.99', $freetag = 'your_tag', $localorexternal = 0) +function getOnlinePaymentUrl($mode, $type, $ref = '', $amount = '9.99', $freetag = 'your_tag', $localorexternal = 1) { global $conf, $dolibarr_main_url_root; @@ -248,7 +248,7 @@ function getOnlinePaymentUrl($mode, $type, $ref = '', $amount = '9.99', $freetag } //if ($mode) $out.='&noidempotency=1'; } elseif ($type == 'order') { - $out = $urltouse.'/public/payment/newpayment.php?source=order&ref='.($mode ? '' : ''); + $out = $urltouse.'/public/payment/newpayment.php?source='.$type.'&ref='.($mode ? '' : ''); if ($mode == 1) { $out .= 'order_ref'; } @@ -271,7 +271,7 @@ function getOnlinePaymentUrl($mode, $type, $ref = '', $amount = '9.99', $freetag } } } elseif ($type == 'invoice') { - $out = $urltouse.'/public/payment/newpayment.php?source=invoice&ref='.($mode ? '' : ''); + $out = $urltouse.'/public/payment/newpayment.php?source='.$type.'&ref='.($mode ? '' : ''); if ($mode == 1) { $out .= 'invoice_ref'; } @@ -294,7 +294,7 @@ function getOnlinePaymentUrl($mode, $type, $ref = '', $amount = '9.99', $freetag } } } elseif ($type == 'contractline') { - $out = $urltouse.'/public/payment/newpayment.php?source=contractline&ref='.($mode ? '' : ''); + $out = $urltouse.'/public/payment/newpayment.php?source='.$type.'&ref='.($mode ? '' : ''); if ($mode == 1) { $out .= 'contractline_ref'; } @@ -340,9 +340,8 @@ function getOnlinePaymentUrl($mode, $type, $ref = '', $amount = '9.99', $freetag $out .= ($mode ? '' : ''); } } - } - if ($type == 'donation') { - $out = $urltouse.'/public/payment/newpayment.php?source=donation&ref='.($mode ? '' : ''); + } elseif ($type == 'donation') { + $out = $urltouse.'/public/payment/newpayment.php?source='.$type.'&ref='.($mode ? '' : ''); if ($mode == 1) { $out .= 'donation_ref'; } @@ -364,6 +363,29 @@ function getOnlinePaymentUrl($mode, $type, $ref = '', $amount = '9.99', $freetag $out .= ($mode ? '' : ''); } } + } elseif ($type == 'boothlocation') { + $out = $urltouse.'/public/payment/newpayment.php?source='.$type.'&ref='.($mode ? '' : ''); + if ($mode == 1) { + $out .= 'invoice_ref'; + } + if ($mode == 0) { + $out .= urlencode($ref); + } + $out .= ($mode ? '' : ''); + if (!empty($conf->global->PAYMENT_SECURITY_TOKEN)) { + if (empty($conf->global->PAYMENT_SECURITY_TOKEN_UNIQUE)) { + $out .= '&securekey='.urlencode($conf->global->PAYMENT_SECURITY_TOKEN); + } else { + $out .= '&securekey='.($mode ? '' : ''); + if ($mode == 1) { + $out .= "hash('".$conf->global->PAYMENT_SECURITY_TOKEN."' + '".$type."' + invoice_ref)"; + } + if ($mode == 0) { + $out .= dol_hash($conf->global->PAYMENT_SECURITY_TOKEN.$type.$ref, 2); + } + $out .= ($mode ? '' : ''); + } + } } // For multicompany @@ -439,9 +461,11 @@ function htmlPrintOnlinePaymentFooter($fromcompany, $langs, $addformmessage = 0, $line2 .= ($line2 ? " - " : "").$langs->transnoentities("VATIntraShort").": ".$fromcompany->tva_intra; } + print ''."\n"; + print '
    '; - print '
    '."\n"; + print '
    '."\n"; if ($addformmessage) { print ''; print '
    '; diff --git a/htdocs/core/lib/phpsessionindb.lib.php b/htdocs/core/lib/phpsessionindb.lib.php index 132036ba23d..8001b3ece60 100644 --- a/htdocs/core/lib/phpsessionindb.lib.php +++ b/htdocs/core/lib/phpsessionindb.lib.php @@ -29,15 +29,29 @@ /** * The session open handler called by PHP whenever a session is initialized. * - * @param string $database_name Database NamedConstraint - * @param string $table_name Table name + * @param string $save_path Value of session.save_path into php.ini + * @param string $session_name Session name (Example: 'DOLSESSID_xxxxxx') * @return boolean Always true */ -function dolSessionOpen($database_name, $table_name) +function dolSessionOpen($save_path, $session_name) { - global $conf, $dbsession; + global $dbsession; - $dbsession = getDoliDBInstance($conf->db->type, $conf->db->host, $conf->db->user, $conf->db->pass, $conf->db->name, $conf->db->port); + global $dolibarr_main_db_type, $dolibarr_main_db_host; + global $dolibarr_main_db_user, $dolibarr_main_db_pass, $dolibarr_main_db_name, $dolibarr_main_db_port; + + global $dolibarr_session_db_type, $dolibarr_session_db_host; + global $dolibarr_session_db_user, $dolibarr_session_db_pass, $dolibarr_session_db_name, $dolibarr_session_db_port; + + if (empty($dolibarr_session_db_type)) { $dolibarr_session_db_type = $dolibarr_main_db_type; } + if (empty($dolibarr_session_db_host)) { $dolibarr_session_db_host = $dolibarr_main_db_host; } + if (empty($dolibarr_session_db_user)) { $dolibarr_session_db_user = $dolibarr_main_db_user; } + if (empty($dolibarr_session_db_pass)) { $dolibarr_session_db_pass = $dolibarr_main_db_pass; } + if (empty($dolibarr_session_db_name)) { $dolibarr_session_db_name = $dolibarr_main_db_name; } + if (empty($dolibarr_session_db_port)) { $dolibarr_session_db_port = $dolibarr_main_db_port; } + //var_dump('open '.$database_name.' '.$table_name); + + $dbsession = getDoliDBInstance($dolibarr_session_db_type, $dolibarr_session_db_host, $dolibarr_session_db_user, $dolibarr_session_db_pass, $dolibarr_session_db_name, $dolibarr_session_db_port); return true; } @@ -51,8 +65,10 @@ function dolSessionOpen($database_name, $table_name) function dolSessionRead($sess_id) { global $dbsession; + global $sessionlastvalueread; + global $sessionidfound; - $sql = "SELECT session_variable FROM ".MAIN_DB_PREFIX."session"; + $sql = "SELECT session_id, session_variable FROM ".MAIN_DB_PREFIX."session"; $sql .= " WHERE session_id = '".$dbsession->escape($sess_id)."'"; // Execute the query @@ -60,10 +76,16 @@ function dolSessionRead($sess_id) $num_rows = $dbsession->num_rows($resql); if ($num_rows == 0) { // No session found - return an empty string + $sessionlastvalueread = ''; + $sessionidfound = ''; return ''; } else { // Found a session - return the serialized string $obj = $dbsession->fetch_object($resql); + $sessionlastvalueread = $obj->session_variable; + $sessionidfound = $obj->session_id; + //var_dump($sessionlastvalueread); + //var_dump($sessionidfound); return $obj->session_variable; } } @@ -79,42 +101,84 @@ function dolSessionRead($sess_id) function dolSessionWrite($sess_id, $val) { global $dbsession; + global $sessionlastvalueread; + global $sessionidfound; - $time_stamp = dol_now(); + /*var_dump('write '.$sess_id); + var_dump($val); + var_dump('sessionlastvalueread='.$sessionlastvalueread.' sessionidfound='.$sessionidfound); + */ - $sql = "SELECT session_id FROM ".MAIN_DB_PREFIX."session"; - $sql .= " WHERE session_id = '".$dbsession->escape($sess_id)."'"; + //$sessionlastvalueread=''; + if ($sessionlastvalueread != $val) { + $time_stamp = dol_now(); - // Execute the query - $resql = $dbsession->query($sql); - $num_rows = $dbsession->num_rows($resql); - if ($num_rows == 0) { - // No session found, insert a new one - $insert_query = "INSERT INTO ".MAIN_DB_PREFIX."session"; - $insert_query .= "(session_id, session_variable, last_accessed)"; - $insert_query .= " VALUES ('".$dbsession->escape($sess_id)."', '".$dbsession->escape($val)."', '".$dbsession->idate($time_stamp)."')"; - $dbsession->query($insert_query); - } else { - // Existing session found - Update the session variables - $update_query = "UPDATE ".MAIN_DB_PREFIX."session"; - $update_query .= "SET session_variable = '".$dbsession->escape($val)."',"; - $update_query .= " last_accessed = '".$dbsession->idate($time_stamp)."'"; - $update_query .= " WHERE session_id = '".$dbsession->escape($sess_id)."'"; - $dbsession->query($update_query); + if (empty($sessionidfound)) { + // No session found, insert a new one + $insert_query = "INSERT INTO ".MAIN_DB_PREFIX."session"; + $insert_query .= "(session_id, session_variable, last_accessed, fk_user, remote_ip, user_agent)"; + $insert_query .= " VALUES ('".$dbsession->escape($sess_id)."', '".$dbsession->escape($val)."', '".$dbsession->idate($time_stamp)."', 0, '".$dbsession->escape(getUserRemoteIP())."', '".$dbsession->escape(substr($_SERVER['HTTP_USER_AGENT'], 0, 255))."')"; + + $result = $dbsession->query($insert_query); + if (!$result) { + dol_print_error($dbsession); + return false; + } + } else { + if ($sessionidfound != $sess_id) { + // oops. How can this happen ? + dol_print_error($dbsession, 'Oops sess_id received in dolSessionWrite differs from the cache value $sessionidfound. How can this happen ?'); + return false; + } + /*$sql = "SELECT session_id, session_variable FROM ".MAIN_DB_PREFIX."session"; + $sql .= " WHERE session_id = '".$dbsession->escape($sess_id)."'"; + + // Execute the query + $resql = $dbsession->query($sql); + $num_rows = $dbsession->num_rows($resql); + if ($num_rows == 0) { + // No session found, insert a new one + $insert_query = "INSERT INTO ".MAIN_DB_PREFIX."session"; + $insert_query .= "(session_id, session_variable, last_accessed, fk_user, remote_ip, user_agent)"; + $insert_query .= " VALUES ('".$dbsession->escape($sess_id)."', '".$dbsession->escape($val)."', '".$dbsession->idate($time_stamp)."', 0, '".$dbsession->escape(getUserRemoteIP())."', '".$dbsession->escape(substr($_SERVER['HTTP_USER_AGENT'], 0, 255)."')"; + var_dump($insert_query); + $result = $dbsession->query($insert_query); + if (!$result) { + dol_print_error($dbsession); + return false; + } + } else { + */ + // Existing session found - Update the session variables + $update_query = "UPDATE ".MAIN_DB_PREFIX."session"; + $update_query .= " SET session_variable = '".$dbsession->escape($val)."',"; + $update_query .= " last_accessed = '".$dbsession->idate($time_stamp)."',"; + $update_query .= " remote_ip = '".$dbsession->escape(getUserRemoteIP())."',"; + $update_query .= " user_agent = '".$dbsession->escape($_SERVER['HTTP_USER_AGENT'])."'"; + $update_query .= " WHERE session_id = '".$dbsession->escape($sess_id)."'"; + + $result = $dbsession->query($update_query); + if (!$result) { + dol_print_error($dbsession); + return false; + } + } } + return true; } /** * This function is executed on shutdown of the session. * - * @param string $sess_id Session ID * @return boolean Always returns true. */ -function dolSessionClose($sess_id) +function dolSessionClose() { global $dbsession; + //var_dump('close'); + $dbsession->close(); return true; @@ -130,6 +194,8 @@ function dolSessionDestroy($sess_id) { global $dbsession; + //var_dump('destroy'); + $delete_query = "DELETE FROM ".MAIN_DB_PREFIX."session"; $delete_query .= " WHERE session_id = '".$dbsession->escape($sess_id)."'"; $dbsession->query($delete_query); diff --git a/htdocs/core/lib/price.lib.php b/htdocs/core/lib/price.lib.php index 28723ab87db..4375d14430b 100644 --- a/htdocs/core/lib/price.lib.php +++ b/htdocs/core/lib/price.lib.php @@ -404,7 +404,7 @@ function calcul_price_total($qty, $pu, $remise_percent_ligne, $txtva, $uselocalt } } - // Recal function using the multicurrency price as reference price. We must set param $multicurrency_tx to 1 to avoid infinite loop. + // Recall function using the multicurrency price as reference price. We must set param $multicurrency_tx to 1 to avoid infinite loop. $newresult = calcul_price_total($qty, $pu_devise, $remise_percent_ligne, $txtva, $uselocaltax1_rate, $uselocaltax2_rate, $remise_percent_global, $price_base_type, $info_bits, $type, $seller, $localtaxes_array, $progress, 1, 0, ''); if ($multicurrency_code) { diff --git a/htdocs/core/lib/project.lib.php b/htdocs/core/lib/project.lib.php index e9e06708f7c..90a67991413 100644 --- a/htdocs/core/lib/project.lib.php +++ b/htdocs/core/lib/project.lib.php @@ -679,22 +679,23 @@ function projectLinesa(&$inc, $parent, &$lines, &$level, $var, $showproject, &$t // Title of task if (count($arrayfields) > 0 && !empty($arrayfields['t.label']['checked'])) { - print ''; + $labeltoshow = ''; if ($showlineingray) { - print ''; + $labeltoshow .= ''; } //else print ''; for ($k = 0; $k < $level; $k++) { - print '
    '; + $labeltoshow .= '
    '; } - print $lines[$i]->label; + $labeltoshow .= dol_escape_htmltag($lines[$i]->label); for ($k = 0; $k < $level; $k++) { - print '
    '; + $labeltoshow .= '
    '; } if ($showlineingray) { - print '
    '; + $labeltoshow .= '
    '; } - //else print ''; + print ''; + print $labeltoshow; print "\n"; } @@ -822,29 +823,7 @@ function projectLinesa(&$inc, $parent, &$lines, &$level, $var, $showproject, &$t } } - // Contacts of tasks. Disabled, because available by default just after - /* - if (!empty($conf->global->PROJECT_SHOW_CONTACTS_IN_LIST)) { - print ''; - foreach (array('internal', 'external') as $source) { - $tab = $lines[$i]->liste_contact(-1, $source); - $num = count($tab); - if (!empty($num)) { - foreach ($tab as $contacttask) { - //var_dump($contacttask); - if ($source == 'internal') { - $c = new User($db); - } else { - $c = new Contact($db); - } - $c->fetch($contacttask['id']); - print $c->getNomUrl(1).' ('.$contacttask['libelle'].')
    '; - } - } - } - print ''; - }*/ - if (count($arrayfields) > 0 && !empty($arrayfields['c.assigned']['checked'])) { + if (count($arrayfields) > 0 && !empty($arrayfields['t.budget_amount']['checked'])) { print ''; print price($lines[$i]->budget_amount, 0, $langs, 1, 0, 0, $conf->currency); $total_budget_amount += $lines[$i]->budget_amount; @@ -854,10 +833,11 @@ function projectLinesa(&$inc, $parent, &$lines, &$level, $var, $showproject, &$t // Contacts of task if (count($arrayfields) > 0 && !empty($arrayfields['c.assigned']['checked'])) { print ''; + $ifisrt = 1; foreach (array('internal', 'external') as $source) { $tab = $lines[$i]->liste_contact(-1, $source); - $num = count($tab); - if (!empty($num)) { + $numcontact = count($tab); + if (!empty($numcontact)) { foreach ($tab as $contacttask) { //var_dump($contacttask); if ($source == 'internal') { @@ -867,14 +847,19 @@ function projectLinesa(&$inc, $parent, &$lines, &$level, $var, $showproject, &$t } $c->fetch($contacttask['id']); if (!empty($c->photo)) { - print $c->getNomUrl(-2).' '; + if (get_class($c) == 'User') { + print $c->getNomUrl(-2, '', 0, 0, 24, 1, '', ($ifisrt ? '' : 'notfirst')); + } else { + print $c->getNomUrl(-2, '', 0, '', -1, 0, ($ifisrt ? '' : 'notfirst')); + } } else { if (get_class($c) == 'User') { - print $c->getNomUrl(2, '', 0, 0, 24, 1);//.' '; + print $c->getNomUrl(2, '', 0, 0, 24, 1, '', ($ifisrt ? '' : 'notfirst')); } else { - print $c->getNomUrl(2);//.' '; + print $c->getNomUrl(2, '', 0, '', -1, 0, ($ifisrt ? '' : 'notfirst')); } } + $ifisrt = 0; } } } diff --git a/htdocs/core/lib/reception.lib.php b/htdocs/core/lib/reception.lib.php index 42355497286..26f6817ed24 100644 --- a/htdocs/core/lib/reception.lib.php +++ b/htdocs/core/lib/reception.lib.php @@ -36,8 +36,7 @@ function reception_prepare_head(Reception $object) { global $db, $langs, $conf, $user; - $langs->load("sendings"); - $langs->load("deliveries"); + $langs->loadLangs(array("sendings", "deliveries")); $h = 0; $head = array(); @@ -49,8 +48,8 @@ function reception_prepare_head(Reception $object) if (empty($conf->global->MAIN_DISABLE_CONTACTS_TAB)) { $objectsrc = $object; - if ($object->origin == 'commande' && $object->origin_id > 0) { - $objectsrc = new Commande($db); + if ($object->origin == 'supplier_order' && $object->origin_id > 0) { + $objectsrc = new CommandeFournisseur($db); $objectsrc->fetch($object->origin_id); } $nbContact = count($objectsrc->liste_contact(-1, 'internal')) + count($objectsrc->liste_contact(-1, 'external')); @@ -69,6 +68,19 @@ function reception_prepare_head(Reception $object) // $this->tabs = array('entity:-tabname); to remove a tab complete_head_from_modules($conf, $langs, $object, $head, $h, 'reception'); + require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php'; + require_once DOL_DOCUMENT_ROOT.'/core/class/link.class.php'; + $upload_dir = $conf->reception->dir_output."/".dol_sanitizeFileName($object->ref); + $nbFiles = count(dol_dir_list($upload_dir, 'files', 0, '', '(\.meta|_preview.*\.png)$')); + $nbLinks = Link::count($db, $object->element, $object->id); + $head[$h][0] = DOL_URL_ROOT.'/reception/document.php?id='.$object->id; + $head[$h][1] = $langs->trans('Documents'); + if (($nbFiles + $nbLinks) > 0) { + $head[$h][1] .= ''.($nbFiles + $nbLinks).''; + } + $head[$h][2] = 'documents'; + $h++; + $nbNote = 0; if (!empty($object->note_private)) { $nbNote++; diff --git a/htdocs/core/lib/security.lib.php b/htdocs/core/lib/security.lib.php index ede899d7761..4a20fbf96f2 100644 --- a/htdocs/core/lib/security.lib.php +++ b/htdocs/core/lib/security.lib.php @@ -97,7 +97,7 @@ function dol_decode($chain, $key = '1') * If constant MAIN_SECURITY_SALT is defined, we use it as a salt (used only if hashing algorightm is something else than 'password_hash'). * * @param string $chain String to hash - * @param string $type Type of hash ('0':auto will use MAIN_SECURITY_HASH_ALGO else md5, '1':sha1, '2':sha1+md5, '3':md5, '4':md5 for OpenLdap with no salt, '5':sha256, '6':password_hash). Use '3' here, if hash is not needed for security purpose, for security need, prefer '0'. + * @param string $type Type of hash ('0':auto will use MAIN_SECURITY_HASH_ALGO else md5, '1':sha1, '2':sha1+md5, '3':md5, '4': for OpenLdap, '5':sha256, '6':password_hash). Use '3' here, if hash is not needed for security purpose, for security need, prefer '0'. * @return string Hash of string * @see getRandomPassword() */ @@ -111,7 +111,7 @@ function dol_hash($chain, $type = '0') } // Salt value - if (!empty($conf->global->MAIN_SECURITY_SALT) && $type != '4' && $type !== 'md5openldap') { + if (!empty($conf->global->MAIN_SECURITY_SALT) && $type != '4' && $type !== 'openldap') { $chain = $conf->global->MAIN_SECURITY_SALT.$chain; } @@ -121,8 +121,8 @@ function dol_hash($chain, $type = '0') return sha1(md5($chain)); } elseif ($type == '3' || $type == 'md5') { return md5($chain); - } elseif ($type == '4' || $type == 'md5openldap') { - return '{md5}'.base64_encode(pack("H*", md5($chain))); // For OpenLdap with md5 (based on an unencrypted password in base) + } elseif ($type == '4' || $type == 'openldap') { + return dolGetLdapPasswordHash($chain, getDolGlobalString('LDAP_PASSWORD_HASH_TYPE', 'md5')); } elseif ($type == '5' || $type == 'sha256') { return hash('sha256', $chain); } elseif ($type == '6' || $type == 'password_hash') { @@ -145,7 +145,7 @@ function dol_hash($chain, $type = '0') * * @param string $chain String to hash (not hashed string) * @param string $hash hash to compare - * @param string $type Type of hash ('0':auto, '1':sha1, '2':sha1+md5, '3':md5, '4':md5 for OpenLdap, '5':sha256). Use '3' here, if hash is not needed for security purpose, for security need, prefer '0'. + * @param string $type Type of hash ('0':auto, '1':sha1, '2':sha1+md5, '3':md5, '4': for OpenLdap, '5':sha256). Use '3' here, if hash is not needed for security purpose, for security need, prefer '0'. * @return bool True if the computed hash is the same as the given one */ function dol_verifyHash($chain, $hash, $type = '0') @@ -167,6 +167,50 @@ function dol_verifyHash($chain, $hash, $type = '0') return dol_hash($chain, $type) == $hash; } +/** + * Returns a specific ldap hash of a password. + * + * @param string $password Password to hash + * @param string $type Type of hash + * @return string Hash of password + */ +function dolGetLdapPasswordHash($password, $type = 'md5') +{ + if (empty($type)) { + $type = 'md5'; + } + + $salt = substr(sha1(time()), 0, 8); + + if ($type === 'md5') { + return '{MD5}' . base64_encode(hash("md5", $password, true)); //For OpenLdap with md5 (based on an unencrypted password in base) + } elseif ($type === 'md5frommd5') { + return '{MD5}' . base64_encode(hex2bin($password)); // Create OpenLDAP MD5 password from Dolibarr MD5 password + } elseif ($type === 'smd5') { + return "{SMD5}" . base64_encode(hash("md5", $password . $salt, true) . $salt); + } elseif ($type === 'sha') { + return '{SHA}' . base64_encode(hash("sha1", $password, true)); + } elseif ($type === 'ssha') { + return "{SSHA}" . base64_encode(hash("sha1", $password . $salt, true) . $salt); + } elseif ($type === 'sha256') { + return "{SHA256}" . base64_encode(hash("sha256", $password, true)); + } elseif ($type === 'ssha256') { + return "{SSHA256}" . base64_encode(hash("sha256", $password . $salt, true) . $salt); + } elseif ($type === 'sha384') { + return "{SHA384}" . base64_encode(hash("sha384", $password, true)); + } elseif ($type === 'ssha384') { + return "{SSHA384}" . base64_encode(hash("sha384", $password . $salt, true) . $salt); + } elseif ($type === 'sha512') { + return "{SHA512}" . base64_encode(hash("sha512", $password, true)); + } elseif ($type === 'ssha512') { + return "{SSHA512}" . base64_encode(hash("sha512", $password . $salt, true) . $salt); + } elseif ($type === 'crypt') { + return '{CRYPT}' . crypt($password, $salt); + } elseif ($type === 'clear') { + return '{CLEAR}' . $password; // Just for test, plain text password is not secured ! + } +} + /** * Check permissions of a user to show a page and an object. Check read permission. * If GETPOST('action','aZ09') defined, we also check write and delete permission. @@ -226,6 +270,7 @@ function restrictedArea($user, $features, $objectid = 0, $tableandshare = '', $f $features = 'produit'; } + // Get more permissions checks from hooks $parameters = array('features'=>$features, 'originalfeatures'=>$originalfeatures, 'objectid'=>$objectid, 'dbt_select'=>$dbt_select, 'idtype'=>$dbt_select, 'isdraft'=>$isdraft); $reshook = $hookmanager->executeHooks('restrictedArea', $parameters); @@ -522,6 +567,14 @@ function restrictedArea($user, $features, $objectid = 0, $tableandshare = '', $f if (empty($user->rights->adherent->supprimer)) { $deleteok = 0; } + } elseif ($feature == 'paymentbybanktransfer') { + if (empty($user->rights->paymentbybanktransfer->create)) { // There is no delete permission + $deleteok = 0; + } + } elseif ($feature == 'prelevement') { + if (empty($user->rights->prelevement->bons->creer)) { // There is no delete permission + $deleteok = 0; + } } elseif (!empty($feature2)) { // This is for permissions on 2 levels foreach ($feature2 as $subfeature) { if (empty($user->rights->$feature->$subfeature->supprimer) && empty($user->rights->$feature->$subfeature->delete)) { @@ -573,24 +626,30 @@ function restrictedArea($user, $features, $objectid = 0, $tableandshare = '', $f } /** - * Check access by user to object is ok. - * This function is also called by restrictedArea that check before if module is enabled and if permission of user for $action is ok. + * Check that access by a given user to an object is ok. + * This function is also called by restrictedArea() that check before if module is enabled and if permission of user for $action is ok. * - * @param User $user User to check - * @param array $featuresarray Features/modules to check. Example: ('user','service','member','project','task',...) - * @param int|string $objectid Object ID if we want to check a particular record (optional) is linked to a owned thirdparty (optional). - * @param string $tableandshare 'TableName&SharedElement' with Tablename is table where object is stored. SharedElement is an optional key to define where to check entity for multicompany modume. Param not used if objectid is null (optional). - * @param string $feature2 Feature to check, second level of permission (optional). Can be or check with 'level1|level2'. - * @param string $dbt_keyfield Field name for socid foreign key if not fk_soc. Not used if objectid is null (optional) - * @param string $dbt_select Field name for select if not rowid. Not used if objectid is null (optional) - * @param string $parenttableforentity Parent table for entity. Example 'fk_website@website' - * @return bool True if user has access, False otherwise + * @param User $user User to check + * @param array $featuresarray Features/modules to check. Example: ('user','service','member','project','task',...) + * @param int|string|Object $object Full object or object ID or list of object id. For example if we want to check a particular record (optional) is linked to a owned thirdparty (optional). + * @param string $tableandshare 'TableName&SharedElement' with Tablename is table where object is stored. SharedElement is an optional key to define where to check entity for multicompany modume. Param not used if objectid is null (optional). + * @param string $feature2 Feature to check, second level of permission (optional). Can be or check with 'level1|level2'. + * @param string $dbt_keyfield Field name for socid foreign key if not fk_soc. Not used if objectid is null (optional) + * @param string $dbt_select Field name for select if not rowid. Not used if objectid is null (optional) + * @param string $parenttableforentity Parent table for entity. Example 'fk_website@website' + * @return bool True if user has access, False otherwise * @see restrictedArea() */ -function checkUserAccessToObject($user, array $featuresarray, $objectid = 0, $tableandshare = '', $feature2 = '', $dbt_keyfield = '', $dbt_select = 'rowid', $parenttableforentity = '') +function checkUserAccessToObject($user, array $featuresarray, $object = 0, $tableandshare = '', $feature2 = '', $dbt_keyfield = '', $dbt_select = 'rowid', $parenttableforentity = '') { global $db, $conf; + if (is_object($object)) { + $objectid = $object->id; + } else { + $objectid = $object; // $objectid can be X or 'X,Y,Z' + } + //dol_syslog("functions.lib:restrictedArea $feature, $objectid, $dbtablename, $feature2, $dbt_socfield, $dbt_select, $isdraft"); //print "user_id=".$user->id.", features=".join(',', $featuresarray).", feature2=".$feature2.", objectid=".$objectid; //print ", tableandshare=".$tableandshare.", dbt_socfield=".$dbt_keyfield.", dbt_select=".$dbt_select."
    "; @@ -603,7 +662,7 @@ function checkUserAccessToObject($user, array $featuresarray, $objectid = 0, $ta foreach ($featuresarray as $feature) { $sql = ''; - //var_dump($feature); + //var_dump($feature);exit; // For backward compatibility if ($feature == 'member') { @@ -616,11 +675,15 @@ function checkUserAccessToObject($user, array $featuresarray, $objectid = 0, $ta $feature = 'projet_task'; } + $checkonentitydone = 0; + + // Array to define rules of checks to do $check = array('adherent', 'banque', 'bom', 'don', 'mrp', 'user', 'usergroup', 'payment', 'payment_supplier', 'product', 'produit', 'service', 'produit|service', 'categorie', 'resource', 'expensereport', 'holiday', 'salaries', 'website'); // Test on entity only (Objects with no link to company) $checksoc = array('societe'); // Test for societe object $checkother = array('contact', 'agenda'); // Test on entity + link to third party on field $dbt_keyfield. Allowed if link is empty (Ex: contacts...). $checkproject = array('projet', 'project'); // Test for project object $checktask = array('projet_task'); // Test for task object + $checkhierarchy = array('expensereport', 'holiday'); $nocheck = array('barcode', 'stock'); // No test //$checkdefault = 'all other not already defined'; // Test on entity + link to third party on field $dbt_keyfield. Not allowed if link is empty (Ex: invoice, orders...). @@ -661,10 +724,12 @@ function checkUserAccessToObject($user, array $featuresarray, $objectid = 0, $ta $sql .= " AND dbt.entity IN (".getEntity($sharedelement, 1).")"; } } - } elseif (in_array($feature, $checksoc)) { // We check feature = checksoc + $checkonentitydone = 1; + } + if (in_array($feature, $checksoc)) { // We check feature = checksoc // If external user: Check permission for external users if ($user->socid > 0) { - if ($user->socid <> $objectid) { + if ($user->socid != $objectid) { return false; } } elseif (!empty($conf->societe->enabled) && ($user->rights->societe->lire && empty($user->rights->societe->client->voir))) { @@ -683,7 +748,10 @@ function checkUserAccessToObject($user, array $featuresarray, $objectid = 0, $ta $sql .= " WHERE s.rowid IN (".$db->sanitize($objectid, 1).")"; $sql .= " AND s.entity IN (".getEntity($sharedelement, 1).")"; } - } elseif (in_array($feature, $checkother)) { // Test on entity + link to thirdparty. Allowed if link is empty (Ex: contacts...). + + $checkonentitydone = 1; + } + if (in_array($feature, $checkother)) { // Test on entity + link to thirdparty. Allowed if link is empty (Ex: contacts...). // If external user: Check permission for external users if ($user->socid > 0) { $sql = "SELECT COUNT(dbt.".$dbt_select.") as nb"; @@ -705,25 +773,19 @@ function checkUserAccessToObject($user, array $featuresarray, $objectid = 0, $ta $sql .= " WHERE dbt.".$dbt_select." IN (".$db->sanitize($objectid, 1).")"; $sql .= " AND dbt.entity IN (".getEntity($sharedelement, 1).")"; } - if ($feature == 'agenda') { - // Also check owner or attendee for users without allactions->read - if ($objectid > 0 && empty($user->rights->agenda->allactions->read)) { - require_once DOL_DOCUMENT_ROOT.'/comm/action/class/actioncomm.class.php'; - $action = new ActionComm($db); - $action->fetch($objectid); - if ($action->authorid != $user->id && $action->userownerid != $user->id && !(array_key_exists($user->id, $action->userassigned))) { - return false; - } - } - } - } elseif (in_array($feature, $checkproject)) { + + $checkonentitydone = 1; + } + if (in_array($feature, $checkproject)) { if (!empty($conf->projet->enabled) && empty($user->rights->projet->all->lire)) { + $projectid = $objectid; + include_once DOL_DOCUMENT_ROOT.'/projet/class/project.class.php'; $projectstatic = new Project($db); $tmps = $projectstatic->getProjectsAuthorizedForUser($user, 0, 1, 0); $tmparray = explode(',', $tmps); - if (!in_array($objectid, $tmparray)) { + if (!in_array($projectid, $tmparray)) { return false; } } else { @@ -732,16 +794,21 @@ function checkUserAccessToObject($user, array $featuresarray, $objectid = 0, $ta $sql .= " WHERE dbt.".$dbt_select." IN (".$db->sanitize($objectid, 1).")"; $sql .= " AND dbt.entity IN (".getEntity($sharedelement, 1).")"; } - } elseif (in_array($feature, $checktask)) { + + $checkonentitydone = 1; + } + if (in_array($feature, $checktask)) { if (!empty($conf->projet->enabled) && empty($user->rights->projet->all->lire)) { $task = new Task($db); $task->fetch($objectid); + $projectid = $task->fk_project; include_once DOL_DOCUMENT_ROOT.'/projet/class/project.class.php'; $projectstatic = new Project($db); $tmps = $projectstatic->getProjectsAuthorizedForUser($user, 0, 1, 0); + $tmparray = explode(',', $tmps); - if (!in_array($task->fk_project, $tmparray)) { + if (!in_array($projectid, $tmparray)) { return false; } } else { @@ -750,7 +817,10 @@ function checkUserAccessToObject($user, array $featuresarray, $objectid = 0, $ta $sql .= " WHERE dbt.".$dbt_select." IN (".$db->sanitize($objectid, 1).")"; $sql .= " AND dbt.entity IN (".getEntity($sharedelement, 1).")"; } - } elseif (!in_array($feature, $nocheck)) { // By default (case of $checkdefault), we check on object entity + link to third party on field $dbt_keyfield + + $checkonentitydone = 1; + } + if (!$checkonentitydone && !in_array($feature, $nocheck)) { // By default (case of $checkdefault), we check on object entity + link to third party on field $dbt_keyfield // If external user: Check permission for external users if ($user->socid > 0) { if (empty($dbt_keyfield)) { @@ -792,11 +862,47 @@ function checkUserAccessToObject($user, array $featuresarray, $objectid = 0, $ta } //print $sql; + // For events, check on users assigned to event + if ($feature === 'agenda') { + // Also check owner or attendee for users without allactions->read + if ($objectid > 0 && empty($user->rights->agenda->allactions->read)) { + require_once DOL_DOCUMENT_ROOT.'/comm/action/class/actioncomm.class.php'; + $action = new ActionComm($db); + $action->fetch($objectid); + if ($action->authorid != $user->id && $action->userownerid != $user->id && !(array_key_exists($user->id, $action->userassigned))) { + return false; + } + } + } + + // For some object, we also have to check it is in the user hierarchy + // Param $object must be the full object and not a simple id to have this test possible. + if (in_array($feature, $checkhierarchy) && is_object($object)) { + $childids = $user->getAllChildIds(1); + $useridtocheck = 0; + if ($feature == 'holiday') { + $useridtocheck = $object->fk_user; + if (!in_array($useridtocheck, $childids)) { + return false; + } + $useridtocheck = $object->fk_validator; + if (!in_array($useridtocheck, $childids)) { + return false; + } + } + if ($feature == 'expensereport') { + $useridtocheck = $object->fk_user_author; + if (!in_array($useridtocheck, $childids)) { + return false; + } + } + } + if ($sql) { $resql = $db->query($sql); if ($resql) { $obj = $db->fetch_object($resql); - if (!$obj || $obj->nb < count(explode(',', $objectid))) { + if (!$obj || $obj->nb < count(explode(',', $objectid))) { // error if we found 0 or less record than nb of id provided return false; } } else { @@ -857,9 +963,11 @@ function accessforbidden($message = '', $printheader = 1, $printfooter = 1, $sho $reshook = $hookmanager->executeHooks('getAccessForbiddenMessage', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks print $hookmanager->resPrint; if (empty($reshook)) { + $langs->loadLangs(array("errors")); if ($user->login) { print $langs->trans("CurrentLogin").': '.$user->login.'
    '; print $langs->trans("ErrorForbidden2", $langs->transnoentitiesnoconv("Home"), $langs->transnoentitiesnoconv("Users")); + print $langs->trans("ErrorForbidden4"); } else { print $langs->trans("ErrorForbidden3"); } diff --git a/htdocs/core/lib/sendings.lib.php b/htdocs/core/lib/sendings.lib.php index 0814c0f4825..e2ab74b8cfc 100644 --- a/htdocs/core/lib/sendings.lib.php +++ b/htdocs/core/lib/sendings.lib.php @@ -180,7 +180,7 @@ function delivery_prepare_head($object) require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php'; require_once DOL_DOCUMENT_ROOT.'/core/class/link.class.php'; - $upload_dir = $conf->commande->dir_output."/".dol_sanitizeFileName($tmpobject->ref); + $upload_dir = $conf->expedition->dir_output."/sending/".dol_sanitizeFileName($object->ref); $nbFiles = count(dol_dir_list($upload_dir, 'files', 0, '', '(\.meta|_preview.*\.png)$')); $nbLinks = Link::count($db, $tmpobject->element, $tmpobject->id); $head[$h][0] = DOL_URL_ROOT.'/expedition/document.php?id='.$tmpobject->id; diff --git a/htdocs/core/lib/signature.lib.php b/htdocs/core/lib/signature.lib.php index 13df1ad8d9d..1d529be6327 100644 --- a/htdocs/core/lib/signature.lib.php +++ b/htdocs/core/lib/signature.lib.php @@ -18,7 +18,7 @@ */ /** - * Return string with full Url + * Return string with full online Url to accept and sign a quote * * @param string $type Type of URL ('proposal', ...) * @param string $ref Ref of object @@ -41,7 +41,7 @@ function showOnlineSignatureUrl($type, $ref) } else { $out .= ''; } - $out .= ''.img_picto('', 'globe', 'class="paddingleft"').''; + $out .= ''.img_picto('', 'globe', 'class="paddingleft"').''; $out .= '
    '; $out .= ajax_autoselect("onlinesignatureurl", 0); return $out; @@ -51,20 +51,35 @@ function showOnlineSignatureUrl($type, $ref) /** * Return string with full Url * - * @param int $mode 0=True url, 1=Url formated with colors - * @param string $type Type of URL ('proposal', ...) - * @param string $ref Ref of object - * @return string Url string + * @param int $mode 0=True url, 1=Url formated with colors + * @param string $type Type of URL ('proposal', ...) + * @param string $ref Ref of object + * @param string $localorexternal 0=Url for browser, 1=Url for external access + * @return string Url string */ -function getOnlineSignatureUrl($mode, $type, $ref = '') +function getOnlineSignatureUrl($mode, $type, $ref = '', $localorexternal = 1) { - global $conf, $db, $langs; + global $conf, $db, $langs, $dolibarr_main_url_root; $ref = str_replace(' ', '', $ref); $out = ''; + // Define $urlwithroot + $urlwithouturlroot = preg_replace('/'.preg_quote(DOL_URL_ROOT, '/').'$/i', '', trim($dolibarr_main_url_root)); + $urlwithroot = $urlwithouturlroot.DOL_URL_ROOT; // This is to use external domain name found into config file + //$urlwithroot=DOL_MAIN_URL_ROOT; // This is to use same domain name than current + + $urltouse = DOL_MAIN_URL_ROOT; + if ($localorexternal) { + $urltouse = $urlwithroot; + } + + $securekeyseed = ''; + if ($type == 'proposal') { - $out = DOL_MAIN_URL_ROOT.'/public/onlinesign/newonlinesign.php?source=proposal&ref='.($mode ? '' : ''); + $securekeyseed = $conf->global->PROPOSAL_ONLINE_SIGNATURE_SECURITY_TOKEN; + + $out = $urltouse.'/public/onlinesign/newonlinesign.php?source=proposal&ref='.($mode ? '' : ''); if ($mode == 1) { $out .= 'proposal_ref'; } @@ -72,6 +87,12 @@ function getOnlineSignatureUrl($mode, $type, $ref = '') $out .= urlencode($ref); } $out .= ($mode ? '' : ''); + if ($mode == 1) { + $out .= "hash('".$securekeyseed."' + '".$type."' + proposal_ref)"; + } else { + $out .= '&securekey='.dol_hash($securekeyseed.$type.$ref, '0'); + } + /* if ($mode == 1) { $out .= '&hashp=hash_of_file'; } else { @@ -94,13 +115,15 @@ function getOnlineSignatureUrl($mode, $type, $ref = '') } else { $out .= '&hashp='.$hashp; } - } + }*/ } // For multicompany + /* if (!empty($out)) { $out .= "&entity=".$conf->entity; // Check the entity because He may be the same reference in several entities } + */ return $out; } diff --git a/htdocs/core/lib/ticket.lib.php b/htdocs/core/lib/ticket.lib.php index e2067240c45..3599c17887f 100644 --- a/htdocs/core/lib/ticket.lib.php +++ b/htdocs/core/lib/ticket.lib.php @@ -164,7 +164,7 @@ function showDirectPublicLink($object) if ($url) { $out .= ''; $out .= ajax_autoselect("directpubliclink", 0); } else { @@ -178,16 +178,16 @@ function showDirectPublicLink($object) /** * Generate a random id * - * @param int $car Length of string to generate key + * @param int $car Length of string to generate key * @return string */ function generate_random_id($car = 16) { $string = ""; $chaine = "abcdefghijklmnopqrstuvwxyz123456789"; - srand((double) microtime() * 1000000); + mt_srand((double) microtime() * 1000000); for ($i = 0; $i < $car; $i++) { - $string .= $chaine[rand() % strlen($chaine)]; + $string .= $chaine[mt_rand() % strlen($chaine)]; } return $string; } @@ -635,7 +635,7 @@ function show_ticket_messaging($conf, $langs, $db, $filterobj, $objcon = '', $no if ($donetodo) { $tmp = ''; if (get_class($filterobj) == 'Societe') { - $tmp .= ''; + $tmp .= ''; } $tmp .= ($donetodo != 'done' ? $langs->trans("ActionsToDoShort") : ''); $tmp .= ($donetodo != 'done' && $donetodo != 'todo' ? ' / ' : ''); @@ -850,7 +850,7 @@ function show_ticket_messaging($conf, $langs, $db, $filterobj, $objcon = '', $no $class .= ' documentpreview'; } - $footer .= ''; + $footer .= ''; $footer .= img_mime($filePath).' '.$doc->filename; $footer .= ''; diff --git a/htdocs/core/lib/treeview.lib.php b/htdocs/core/lib/treeview.lib.php index 05e6d51a78d..78703c669c5 100644 --- a/htdocs/core/lib/treeview.lib.php +++ b/htdocs/core/lib/treeview.lib.php @@ -121,7 +121,7 @@ function tree_recur($tab, $pere, $rang, $iddivjstree = 'iddivjstree', $donoreset if ($rang == 0) { // Test also done with jstree and dynatree (not able to have inside label) - print ''; echo $dialog; if ($parameters['currentcontext'] == 'thirdpartycard' && in_array($object->forme_juridique_code, array(11, 12, 13, 15, 17, 18, 19, 35, 60, 200, 311, 312, 316, 401, 600, 700, 1005)) || $object->typent_id == 8) { - echo ''; + echo ''; } elseif ($parameters['currentcontext'] == 'membercard') { - echo ''; + echo ''; } elseif ($parameters['currentcontext'] == 'contactcard') { - echo ''; + echo ''; } if (!empty($object->mail) && empty($object->array_options['options_datapolicy_send']) && $parameters['currentcontext'] == 'thirdpartycard' && in_array($object->forme_juridique_code, array(11, 12, 13, 15, 17, 18, 19, 35, 60, 200, 311, 312, 316, 401, 600, 700, 1005)) || $object->typent_id == 8) { echo ''; diff --git a/htdocs/datapolicy/class/datapolicy.class.php b/htdocs/datapolicy/class/datapolicy.class.php index a7ed08d7e6f..d0cf2e48434 100644 --- a/htdocs/datapolicy/class/datapolicy.class.php +++ b/htdocs/datapolicy/class/datapolicy.class.php @@ -182,8 +182,8 @@ class DataPolicy $deliveryreceipt = 0; $substitutionarray = array( - '__LINKACCEPT__' => ''.$linka.'', - '__LINKREFUSED__' => ''.$linkr.'', + '__LINKACCEPT__' => ''.$linka.'', + '__LINKREFUSED__' => ''.$linkr.'', '__FIRSTNAME__' => $contact->firstname, '__NAME__' => $contact->lastname, '__CIVILITY__' => $contact->civility, @@ -259,8 +259,8 @@ class DataPolicy $deliveryreceipt = 0; $substitutionarray = array( - '__LINKACCEPT__' => ''.$linka.'', - '__LINKREFUSED__' => ''.$linkr.'', + '__LINKACCEPT__' => ''.$linka.'', + '__LINKREFUSED__' => ''.$linkr.'', ); $subject = make_substitutions($subject, $substitutionarray); $message = make_substitutions($message, $substitutionarray); @@ -269,11 +269,11 @@ class DataPolicy $actionmsg = $langs->transnoentities('MailSentBy').' '.$from.' '.$langs->transnoentities('To').' '.$sendto; if ($message) { if ($sendtocc) { - $actionmsg .= dol_concatdesc($actionmsg, $langs->transnoentities('Bcc').": ".$sendtocc); + $actionmsg = dol_concatdesc($actionmsg, $langs->transnoentities('Bcc').": ".$sendtocc); } - $actionmsg .= dol_concatdesc($actionmsg, $langs->transnoentities('MailTopic').": ".$subject); - $actionmsg .= dol_concatdesc($actionmsg, $langs->transnoentities('TextUsedInTheMessageBody').":"); - $actionmsg .= dol_concatdesc($actionmsg, $message); + $actionmsg = dol_concatdesc($actionmsg, $langs->transnoentities('MailTopic').": ".$subject); + $actionmsg = dol_concatdesc($actionmsg, $langs->transnoentities('TextUsedInTheMessageBody').":"); + $actionmsg = dol_concatdesc($actionmsg, $message); } // Send mail @@ -311,7 +311,6 @@ class DataPolicy $sendto = $adherent->email; - $code = md5($adherent->email); if (!empty($adherent->default_lang)) { $l = $adherent->default_lang; @@ -333,8 +332,8 @@ class DataPolicy $deliveryreceipt = 0; $substitutionarray = array( - '__LINKACCEPT__' => ''.$linka.'', - '__LINKREFUSED__' => ''.$linkr.'', + '__LINKACCEPT__' => ''.$linka.'', + '__LINKREFUSED__' => ''.$linkr.'', ); $subject = make_substitutions($subject, $substitutionarray); $message = make_substitutions($message, $substitutionarray); @@ -343,11 +342,11 @@ class DataPolicy $actionmsg = $langs->transnoentities('MailSentBy').' '.$from.' '.$langs->transnoentities('To').' '.$sendto; if ($message) { if ($sendtocc) { - $actionmsg .= dol_concatdesc($actionmsg, $langs->transnoentities('Bcc').": ".$sendtocc); + $actionmsg = dol_concatdesc($actionmsg, $langs->transnoentities('Bcc').": ".$sendtocc); } - $actionmsg .= dol_concatdesc($actionmsg, $langs->transnoentities('MailTopic').": ".$subject); - $actionmsg .= dol_concatdesc($actionmsg, $langs->transnoentities('TextUsedInTheMessageBody').":"); - $actionmsg .= dol_concatdesc($actionmsg, $message); + $actionmsg = dol_concatdesc($actionmsg, $langs->transnoentities('MailTopic').": ".$subject); + $actionmsg = dol_concatdesc($actionmsg, $langs->transnoentities('TextUsedInTheMessageBody').":"); + $actionmsg = dol_concatdesc($actionmsg, $message); } diff --git a/htdocs/datapolicy/langs/en_US/datapolicy.lang b/htdocs/datapolicy/langs/en_US/datapolicy.lang index 368caffcbda..a870c3499a6 100644 --- a/htdocs/datapolicy/langs/en_US/datapolicy.lang +++ b/htdocs/datapolicy/langs/en_US/datapolicy.lang @@ -23,7 +23,7 @@ Module4100Desc = Module to manage Data Privacy (Conformity with the GDPR) # datapolicySetup = Module Data Privacy Policy Setup Deletion = Deletion of data -datapolicySetupPage = Depending of laws of your countries (Example Article 5 of the GDPR), personal data must be kept for a period not exceeding that necessary for the purposes for which they were collected, except for archival purposes.
    The deletion will be done automatically after a certain duration without event (the duration which you will have indicated below). +datapolicySetupPage = Depending of laws of your countries (Example Article 5 of the GDPR), personal data must be kept for a period not exceeding that necessary for the purposes for which they were collected, except for archival purposes.
    The deletion will be done automatically after a certain duration without event (the duration which you will have indicated below). NB_MONTHS = %s months ONE_YEAR = 1 year NB_YEARS = %s years diff --git a/htdocs/datapolicy/langs/fr_FR/datapolicy.lang b/htdocs/datapolicy/langs/fr_FR/datapolicy.lang index 48b6a88cce3..6bf0c6a904d 100644 --- a/htdocs/datapolicy/langs/fr_FR/datapolicy.lang +++ b/htdocs/datapolicy/langs/fr_FR/datapolicy.lang @@ -27,7 +27,7 @@ Module4100Desc = Module de gestion de la protection des données (RGPD) # datapolicySetup = Configuration du module Protection des données Settings_DATAPOLICY = Paramétrage du module Protection des données -datapolicySetupPage = Selon la loi de votre pays (Exemple l’article 5 du RGPD), les données à caractère personnel doivent être conservées pendant une durée n’excédant pas celle nécessaire au regard des finalités pour lesquelles elles ont été traitées, à l’exception de fins archivistiques. La suppression se fera automatiquement après une certaine durée sans évènement (la durée que vous aurez indiquée ci-dessous). +datapolicySetupPage = Selon la loi de votre pays (Exemple l’article 5 du RGPD), les données à caractère personnel doivent être conservées pendant une durée n’excédant pas celle nécessaire au regard des finalités pour lesquelles elles ont été traitées, à l’exception de fins archivistiques. La suppression se fera automatiquement après une certaine durée sans évènement (la durée que vous aurez indiquée ci-dessous). NB_MONTHS = %s mois ONE_YEAR = 1 an NB_YEARS = %s ans diff --git a/htdocs/datapolicy/langs/it_IT/datapolicy.lang b/htdocs/datapolicy/langs/it_IT/datapolicy.lang index d8858b56c5b..68284e17844 100644 --- a/htdocs/datapolicy/langs/it_IT/datapolicy.lang +++ b/htdocs/datapolicy/langs/it_IT/datapolicy.lang @@ -10,7 +10,7 @@ Module4100Desc = Conformità con GDPR # datapolicySetup = Module Setup Settings_DATAPOLICY = Configurazione modulo GDPR -datapolicySetupPage = In accordo con l'art 5 del GDPR i dati personali devono essere conservati per un periodo di tempo che .... ed eliminati se non sono più utili agli scopi per cui sono stati processati. +datapolicySetupPage = In accordo con l'art 5 del GDPR i dati personali devono essere conservati per un periodo di tempo che .... ed eliminati se non sono più utili agli scopi per cui sono stati processati. NB_MONTHS = %s mesi ONE_YEAR = 1 anno NB_YEARS = %s anni diff --git a/htdocs/delivery/card.php b/htdocs/delivery/card.php index 62955da7f06..00975946f6b 100644 --- a/htdocs/delivery/card.php +++ b/htdocs/delivery/card.php @@ -101,7 +101,7 @@ if ($action == 'add') { $object->fk_incoterms = GETPOST('incoterm_id', 'int'); if (!$conf->expedition_bon->enabled && !empty($conf->stock->enabled)) { - $expedition->entrepot_id = GETPOST('entrepot_id'); + $expedition->entrepot_id = GETPOST('entrepot_id', 'int'); } // We loop on each line of order to complete object delivery with qty to delivery @@ -282,7 +282,7 @@ if ($action == 'create') { // Create. Seems to no be used print ''; print ''; - print dol_get_fiche_head($head, 'delivery', $langs->trans("Shipment"), -1, 'sending'); + print dol_get_fiche_head($head, 'delivery', $langs->trans("Shipment"), -1, 'dolly'); /* * Confirmation de la suppression diff --git a/htdocs/document.php b/htdocs/document.php index 2a708ff3334..7498fbb3cf6 100644 --- a/htdocs/document.php +++ b/htdocs/document.php @@ -214,7 +214,7 @@ $check_access = dol_check_secure_access_document($modulepart, $original_file, $e $accessallowed = $check_access['accessallowed']; $sqlprotectagainstexternals = $check_access['sqlprotectagainstexternals']; $fullpath_original_file = $check_access['original_file']; // $fullpath_original_file is now a full path name -//var_dump($fullpath_original_file);exit; +//var_dump($fullpath_original_file.' '.$original_file.' '.$refname.' '.$accessallowed);exit; if (!empty($hashp)) { $accessallowed = 1; // When using hashp, link is public so we force $accessallowed diff --git a/htdocs/don/card.php b/htdocs/don/card.php index 7aa7ea802ce..960ff7e7b2d 100644 --- a/htdocs/don/card.php +++ b/htdocs/don/card.php @@ -140,6 +140,7 @@ if (empty($reshook)) { exit; } else { setEventMessages($object->error, $object->errors, 'errors'); + $action = 'create'; } } @@ -172,7 +173,7 @@ if (empty($reshook)) { $object->lastname = (string) GETPOST("lastname", 'alpha'); $object->societe = (string) GETPOST("societe", 'alpha'); $object->address = (string) GETPOST("address", 'alpha'); - $object->amount = price2num(GETPOST("amount", 'alpha')); + $object->amount = price2num(GETPOST("amount", 'alpha'), '', 2); $object->town = (string) GETPOST("town", 'alpha'); $object->zip = (string) GETPOST("zipcode", 'alpha'); $object->country_id = (int) GETPOST('country_id', 'int'); @@ -185,7 +186,7 @@ if (empty($reshook)) { $object->modepaymentid = (int) GETPOST('modepayment', 'int'); // Fill array 'array_options' with data from add form - $ret = $extrafields->setOptionalsFromPost(null, $object); + $ret = $extrafields->setOptionalsFromPost(null, $object, '@GETPOSTISSET'); if ($ret < 0) { $error++; } @@ -193,6 +194,9 @@ if (empty($reshook)) { if ($object->update($user) > 0) { header("Location: ".$_SERVER['PHP_SELF']."?id=".$object->id); exit; + } else { + setEventMessages($object->error, $object->errors, 'errors'); + $action = "create"; } } } @@ -230,7 +234,7 @@ if (empty($reshook)) { $object->lastname = (string) GETPOST("lastname", 'alpha'); $object->societe = (string) GETPOST("societe", 'alpha'); $object->address = (string) GETPOST("address", 'alpha'); - $object->amount = price2num(GETPOST("amount", 'alpha')); + $object->amount = price2num(GETPOST("amount", 'alpha'), '', 2); $object->zip = (string) GETPOST("zipcode", 'alpha'); $object->town = (string) GETPOST("town", 'alpha'); $object->country_id = (int) GETPOST('country_id', 'int'); @@ -254,6 +258,7 @@ if (empty($reshook)) { exit; } else { setEventMessages($object->error, $object->errors, 'errors'); + $action = "create"; } } } diff --git a/htdocs/don/class/api_donations.class.php b/htdocs/don/class/api_donations.class.php index cfd9e8ccd8b..597f15b7f10 100644 --- a/htdocs/don/class/api_donations.class.php +++ b/htdocs/don/class/api_donations.class.php @@ -128,8 +128,9 @@ class Donations extends DolibarrApi // Add sql filters if ($sqlfilters) { - if (!DolibarrApi::_checkFilters($sqlfilters)) { - throw new RestException(503, 'Error when validating parameter sqlfilters '.$sqlfilters); + $errormessage = ''; + if (!DolibarrApi::_checkFilters($sqlfilters, $errormessage)) { + throw new RestException(503, 'Error when validating parameter sqlfilters -> '.$errormessage); } $regexstring = '\(([^:\'\(\)]+:[^:\'\(\)]+:[^\(\)]+)\)'; $sql .= " AND (".preg_replace_callback('/'.$regexstring.'/', 'DolibarrApi::_forge_criteria_callback', $sqlfilters).")"; diff --git a/htdocs/don/class/don.class.php b/htdocs/don/class/don.class.php index 7ca70a31aa2..cbd5a386354 100644 --- a/htdocs/don/class/don.class.php +++ b/htdocs/don/class/don.class.php @@ -353,6 +353,13 @@ class Don extends CommonObject $this->town = ($this->town > 0 ? $this->town : $this->town); $this->country_id = ($this->country_id > 0 ? $this->country_id : $this->country_id); $this->country = ($this->country ? $this->country : $this->country); + $this->amount = price2num($this->amount); + + // Check parameters + if ($this->amount < 0) { + $this->error = $langs->trans('FieldCannotBeNegative', $langs->transnoentitiesnoconv("Amount")); + return -1; + } $this->db->begin(); @@ -382,7 +389,7 @@ class Don extends CommonObject $sql .= ") VALUES ("; $sql .= "'".$this->db->idate($this->date ? $this->date : $now)."'"; $sql .= ", ".((int) $conf->entity); - $sql .= ", ".price2num($this->amount); + $sql .= ", ".((float) $this->amount); $sql .= ", ".($this->modepaymentid ? $this->modepaymentid : "null"); $sql .= ", ".($this->socid > 0 ? $this->socid : "null"); $sql .= ", '".$this->db->escape($this->firstname)."'"; @@ -464,29 +471,36 @@ class Don extends CommonObject $this->town = ($this->town > 0 ? $this->town : $this->town); $this->country_id = ($this->country_id > 0 ? $this->country_id : $this->country_id); $this->country = ($this->country ? $this->country : $this->country); + $this->amount = price2num($this->amount); + + // Check parameters + if ($this->amount < 0) { + $this->error = $langs->trans('FieldCannotBeNegative', $langs->transnoentitiesnoconv("Amount")); + return -1; + } $this->db->begin(); - $sql = "UPDATE ".MAIN_DB_PREFIX."don SET "; - $sql .= "amount = ".price2num($this->amount); - $sql .= ",fk_payment = ".($this->modepaymentid ? $this->modepaymentid : "null"); - $sql .= ",firstname = '".$this->db->escape($this->firstname)."'"; - $sql .= ",lastname='".$this->db->escape($this->lastname)."'"; - $sql .= ",societe='".$this->db->escape($this->societe)."'"; - $sql .= ",address='".$this->db->escape($this->address)."'"; - $sql .= ",zip='".$this->db->escape($this->zip)."'"; - $sql .= ",town='".$this->db->escape($this->town)."'"; - $sql .= ",fk_country = ".($this->country_id > 0 ? ((int) $this->country_id) : '0'); - $sql .= ",public=".((int) $this->public); - $sql .= ",fk_projet=".($this->fk_project > 0 ? $this->fk_project : 'null'); - $sql .= ",note_private=".(!empty($this->note_private) ? ("'".$this->db->escape($this->note_private)."'") : "NULL"); - $sql .= ",note_public=".(!empty($this->note_public) ? ("'".$this->db->escape($this->note_public)."'") : "NULL"); - $sql .= ",datedon='".$this->db->idate($this->date)."'"; - $sql .= ",date_valid=".($this->date_valid ? "'".$this->db->idate($this->date)."'" : "null"); - $sql .= ",email='".$this->db->escape(trim($this->email))."'"; - $sql .= ",phone='".$this->db->escape(trim($this->phone))."'"; - $sql .= ",phone_mobile='".$this->db->escape(trim($this->phone_mobile))."'"; - $sql .= ",fk_statut=".((int) $this->statut); + $sql = "UPDATE ".MAIN_DB_PREFIX."don SET"; + $sql .= " amount = ".((float) $this->amount); + $sql .= ", fk_payment = ".($this->modepaymentid ? $this->modepaymentid : "null"); + $sql .= ", firstname = '".$this->db->escape($this->firstname)."'"; + $sql .= ", lastname='".$this->db->escape($this->lastname)."'"; + $sql .= ", societe='".$this->db->escape($this->societe)."'"; + $sql .= ", address='".$this->db->escape($this->address)."'"; + $sql .= ", zip='".$this->db->escape($this->zip)."'"; + $sql .= ", town='".$this->db->escape($this->town)."'"; + $sql .= ", fk_country = ".($this->country_id > 0 ? ((int) $this->country_id) : '0'); + $sql .= ", public=".((int) $this->public); + $sql .= ", fk_projet=".($this->fk_project > 0 ? $this->fk_project : 'null'); + $sql .= ", note_private=".(!empty($this->note_private) ? ("'".$this->db->escape($this->note_private)."'") : "NULL"); + $sql .= ", note_public=".(!empty($this->note_public) ? ("'".$this->db->escape($this->note_public)."'") : "NULL"); + $sql .= ", datedon='".$this->db->idate($this->date)."'"; + $sql .= ", date_valid=".($this->date_valid ? "'".$this->db->idate($this->date)."'" : "null"); + $sql .= ", email='".$this->db->escape(trim($this->email))."'"; + $sql .= ", phone='".$this->db->escape(trim($this->phone))."'"; + $sql .= ", phone_mobile='".$this->db->escape(trim($this->phone_mobile))."'"; + $sql .= ", fk_statut=".((int) $this->statut); $sql .= " WHERE rowid = ".((int) $this->id); dol_syslog(get_class($this)."::Update", LOG_DEBUG); diff --git a/htdocs/don/list.php b/htdocs/don/list.php index 0c8f51281a3..d7697dba43d 100644 --- a/htdocs/don/list.php +++ b/htdocs/don/list.php @@ -37,8 +37,8 @@ $langs->loadLangs(array("companies", "donations")); $contextpage = GETPOST('contextpage', 'aZ') ?GETPOST('contextpage', 'aZ') : 'sclist'; $limit = GETPOST('limit', 'int') ?GETPOST('limit', 'int') : $conf->liste_limit; -$sortfield = GETPOST("sortfield", 'alpha'); -$sortorder = GETPOST("sortorder", 'alpha'); +$sortfield = GETPOST('sortfield', 'aZ09comma'); +$sortorder = GETPOST('sortorder', 'aZ09comma'); $page = GETPOSTISSET('pageplusone') ? (GETPOST('pageplusone') - 1) : GETPOST("page", 'int'); if (empty($page) || $page == -1) { $page = 0; diff --git a/htdocs/don/payment/payment.php b/htdocs/don/payment/payment.php index 719b6e9951b..e81a4a30af5 100644 --- a/htdocs/don/payment/payment.php +++ b/htdocs/don/payment/payment.php @@ -162,7 +162,7 @@ if ($action == 'create') { print load_fiche_titre($langs->trans("DoPayment")); if (!empty($conf->use_javascript_ajax)) { - print "\n".''; @@ -2002,7 +2025,7 @@ if ($action == 'create') { $morehtmlref .= '
    '; $morehtmlref .= ''; $morehtmlref .= ''; - $morehtmlref .= $formproject->select_projects((empty($conf->global->PROJECT_CAN_ALWAYS_LINK_TO_ALL_SUPPLIERS) ? $object->socid : -1), $object->fk_project, 'projectid', 0, 0, 1, 0, 1, 0, 0, '', 1, 0, 'maxwidth500'); + $morehtmlref .= $formproject->select_projects((empty($conf->global->PROJECT_CAN_ALWAYS_LINK_TO_ALL_SUPPLIERS) ? $object->socid : -1), $object->fk_project, 'projectid', 0, 0, 1, 1, 1, 0, 0, '', 1, 0, 'maxwidth500'); $morehtmlref .= ''; $morehtmlref .= '
    '; } else { @@ -2505,7 +2528,7 @@ if ($action == 'create') { } if (in_array($object->statut, array(3, 4, 5))) { - if (((!empty($conf->fournisseur->enabled) && empty($conf->global->MAIN_USE_NEW_SUPPLIERMOD)) || !empty($conf->supplier_order->enabled)) && $usercanreceived) { + if (((!empty($conf->fournisseur->enabled) && empty($conf->global->MAIN_USE_NEW_SUPPLIERMOD)) || !empty($conf->supplier_order->enabled)) && $usercanreceive) { print ''; } else { print ''; @@ -2523,7 +2546,7 @@ if ($action == 'create') { // Classify received (this does not record reception) if ($object->statut == CommandeFournisseur::STATUS_ORDERSENT || $object->statut == CommandeFournisseur::STATUS_RECEIVED_PARTIALLY) { - if ($usercanreceived) { + if ($usercanreceive) { print ''; } } @@ -2564,14 +2587,14 @@ if ($action == 'create') { } // Cancel - if ($object->statut == 2) { + if ($object->statut == CommandeFournisseur::STATUS_ACCEPTED) { if ($usercanorder) { print ''.$langs->trans("CancelOrder").''; } } // Delete - if (!empty($usercandelete) || ($object->statut == CommandeFournisseur::STATUS_DRAFT && !empty($usercancreate))) { + if (!empty($usercandelete)) { if ($hasreception) { print ''.$langs->trans("Delete").''; } else { @@ -2642,7 +2665,7 @@ if ($action == 'create') { print '
    '; if ($action == 'classifyreception') { - if ($usercanreceived && ($object->statut == CommandeFournisseur::STATUS_ORDERSENT || $object->statut == CommandeFournisseur::STATUS_RECEIVED_PARTIALLY)) { + if ($usercanreceive && ($object->statut == CommandeFournisseur::STATUS_ORDERSENT || $object->statut == CommandeFournisseur::STATUS_RECEIVED_PARTIALLY)) { // Set status to received (action=livraison) print ''."\n"; print '
    '; diff --git a/htdocs/fourn/commande/dispatch.php b/htdocs/fourn/commande/dispatch.php index 170785c0118..abeee1a9e26 100644 --- a/htdocs/fourn/commande/dispatch.php +++ b/htdocs/fourn/commande/dispatch.php @@ -10,19 +10,18 @@ * Copyright (C) 2018 Frédéric France * Copyright (C) 2019-2020 Christophe Battarel * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * - * This program is distributed in the hope that it will be useful, + * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . - * or see https://www.gnu.org/ */ /** @@ -63,11 +62,6 @@ $confirm = GETPOST('confirm', 'alpha'); if ($user->socid) { $socid = $user->socid; } -$result = restrictedArea($user, 'fournisseur', $id, 'commande_fournisseur', 'commande'); - -if (empty($conf->stock->enabled)) { - accessforbidden(); -} $hookmanager->initHooks(array('ordersupplierdispatch')); @@ -90,6 +84,21 @@ if ($id > 0 || !empty($ref)) { } } +if (empty($conf->reception->enabled)) { + $permissiontoreceive = $user->rights->fournisseur->commande->receptionner; + $permissiontocontrol = ((empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->fournisseur->commande->receptionner)) || (!empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->fournisseur->commande_advance->check))); +} else { + $permissiontoreceive = $user->rights->reception->creer; + $permissiontocontrol = ((empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->reception->creer)) || (!empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->reception->reception_advance->validate))); +} + +// $id is id of a purchase order. +$result = restrictedArea($user, 'fournisseur', $id, 'commande_fournisseur', 'commande'); + +if (empty($conf->stock->enabled)) { + accessforbidden(); +} + /* * Actions @@ -101,7 +110,7 @@ if ($reshook < 0) { setEventMessages($hookmanager->error, $hookmanager->errors, 'errors'); } -if ($action == 'checkdispatchline' && !((empty($conf->global->MAIN_USE_ADVANCED_PERMS) && empty($user->rights->fournisseur->commande->receptionner)) || (!empty($conf->global->MAIN_USE_ADVANCED_PERMS) && empty($user->rights->fournisseur->commande_advance->check)))) { +if ($action == 'checkdispatchline' && $permissiontocontrol) { $error = 0; $supplierorderdispatch = new CommandeFournisseurDispatch($db); @@ -138,7 +147,7 @@ if ($action == 'checkdispatchline' && !((empty($conf->global->MAIN_USE_ADVANCED_ } } -if ($action == 'uncheckdispatchline' && !((empty($conf->global->MAIN_USE_ADVANCED_PERMS) && empty($user->rights->fournisseur->commande->receptionner)) || (!empty($conf->global->MAIN_USE_ADVANCED_PERMS) && empty($user->rights->fournisseur->commande_advance->check)))) { +if ($action == 'uncheckdispatchline' && $permissiontocontrol) { $error = 0; $supplierorderdispatch = new CommandeFournisseurDispatch($db); @@ -174,7 +183,7 @@ if ($action == 'uncheckdispatchline' && !((empty($conf->global->MAIN_USE_ADVANCE } } -if ($action == 'denydispatchline' && !((empty($conf->global->MAIN_USE_ADVANCED_PERMS) && empty($user->rights->fournisseur->commande->receptionner)) || (!empty($conf->global->MAIN_USE_ADVANCED_PERMS) && empty($user->rights->fournisseur->commande_advance->check)))) { +if ($action == 'denydispatchline' && $permissiontocontrol) { $error = 0; $supplierorderdispatch = new CommandeFournisseurDispatch($db); @@ -210,7 +219,7 @@ if ($action == 'denydispatchline' && !((empty($conf->global->MAIN_USE_ADVANCED_P } } -if ($action == 'dispatch' && $user->rights->fournisseur->commande->receptionner) { +if ($action == 'dispatch' && $permissiontoreceive) { $error = 0; $db->begin(); @@ -388,7 +397,7 @@ if ($action == 'dispatch' && $user->rights->fournisseur->commande->receptionner) } // Remove a dispatched line -if ($action == 'confirm_deleteline' && $confirm == 'yes' && $user->rights->fournisseur->commande->receptionner) { +if ($action == 'confirm_deleteline' && $confirm == 'yes' && $permissiontoreceive) { $db->begin(); $supplierorderdispatch = new CommandeFournisseurDispatch($db); @@ -431,7 +440,7 @@ if ($action == 'confirm_deleteline' && $confirm == 'yes' && $user->rights->fourn } // Update a dispatched line -if ($action == 'updateline' && $user->rights->fournisseur->commande->receptionner) { +if ($action == 'updateline' && $permissiontoreceive) { $db->begin(); $error = 0; @@ -752,9 +761,9 @@ if ($id > 0 || !empty($ref)) { // Select warehouse to force it everywhere if (count($listwarehouses) > 1) { - print '
    '.$langs->trans("ForceTo").' '.$form->selectarray('fk_default_warehouse', $listwarehouses, $fk_default_warehouse, 1, 0, 0, '', 0, 0, $disabled, '', 'minwidth100 maxwidth300', 1); + print '
    '.$langs->trans("ForceTo").' '.$form->selectarray('fk_default_warehouse', $listwarehouses, $fk_default_warehouse, 1, 0, 0, '', 0, 0, $disabled, '', 'minwidth100 maxwidth300', 1); } elseif (count($listwarehouses) == 1) { - print '
    '.$langs->trans("ForceTo").' '.$form->selectarray('fk_default_warehouse', $listwarehouses, $fk_default_warehouse, 0, 0, 0, '', 0, 0, $disabled, '', 'minwidth100 maxwidth300', 1); + print '
    '.$langs->trans("ForceTo").' '.$form->selectarray('fk_default_warehouse', $listwarehouses, $fk_default_warehouse, 0, 0, 0, '', 0, 0, $disabled, '', 'minwidth100 maxwidth300', 1); } print ''; @@ -779,6 +788,8 @@ if ($id > 0 || !empty($ref)) { $nbproduct = 0; // Nb of predefined product lines to dispatch (already done or not) if SUPPLIER_ORDER_DISABLE_STOCK_DISPATCH_WHEN_TOTAL_REACHED is off (default) // or nb of line that remain to dispatch if SUPPLIER_ORDER_DISABLE_STOCK_DISPATCH_WHEN_TOTAL_REACHED is on. + $conf->cache['product'] = array(); + while ($i < $num) { $objp = $db->fetch_object($resql); @@ -806,11 +817,20 @@ if ($id > 0 || !empty($ref)) { print ''; print ''; - $linktoprod = ''.img_object($langs->trans("ShowProduct"), 'product').' '.$objp->ref.''; + if (empty($conf->cache['product'][$objp->fk_product])) { + $tmpproduct = new Product($db); + $tmpproduct->fetch($objp->fk_product); + $conf->cache['product'][$objp->fk_product] = $tmpproduct; + } else { + $tmpproduct = $conf->cache['product'][$objp->fk_product]; + } + + $linktoprod = $tmpproduct->getNomUrl(1); $linktoprod .= ' - '.$objp->label."\n"; if (!empty($conf->productbatch->enabled)) { if ($objp->tobatch) { + // Product print ''; print $linktoprod; print ""; @@ -822,6 +842,7 @@ if ($id > 0 || !empty($ref)) { print ''; } } else { + // Product print ''; print $linktoprod; print ""; @@ -1056,10 +1077,19 @@ if ($id > 0 || !empty($ref)) { $dispatchBt = empty($conf->reception->enabled) ? $langs->trans("Receive") : $langs->trans("CreateReception"); - print '
    '; + print ''; } print '
    '; @@ -1132,10 +1162,11 @@ if ($id > 0 || !empty($ref)) { print ''; print ''; + // Reception ref if ($conf->reception->enabled) { print ''; } - + // Product print ''; print ''; print ''; @@ -1163,9 +1194,14 @@ if ($id > 0 || !empty($ref)) { print "\n"; + while ($i < $num) { $objp = $db->fetch_object($resql); + $tmpproduct->id = $objp->fk_product; + $tmpproduct->ref = $objp->ref; + $tmpproduct->label = $objp->label; + if ($action == 'editline' && $lineid == $objp->dispatchlineid) { print ' @@ -1176,6 +1212,7 @@ if ($id > 0 || !empty($ref)) { print ''; + // Reception ref if (!empty($conf->reception->enabled)) { print '"; } + // Product print '\n"; print ''; @@ -1258,7 +1303,7 @@ if ($id > 0 || !empty($ref)) { // Add button to check/uncheck disaptching print ''; } diff --git a/htdocs/product/stock/massstockmove.php b/htdocs/product/stock/massstockmove.php index d7bd8bee9b4..781f2037dba 100644 --- a/htdocs/product/stock/massstockmove.php +++ b/htdocs/product/stock/massstockmove.php @@ -486,28 +486,36 @@ if (!empty($conf->global->MAIN_UPLOAD_DOC)) { $max = $conf->global->MAIN_UPLOAD_DOC; // In Kb $maxphp = @ini_get('upload_max_filesize'); // In unknown if (preg_match('/k$/i', $maxphp)) { + $maxphp = preg_replace('/k$/i', '', $maxphp); $maxphp = $maxphp * 1; } if (preg_match('/m$/i', $maxphp)) { + $maxphp = preg_replace('/m$/i', '', $maxphp); $maxphp = $maxphp * 1024; } if (preg_match('/g$/i', $maxphp)) { + $maxphp = preg_replace('/g$/i', '', $maxphp); $maxphp = $maxphp * 1024 * 1024; } if (preg_match('/t$/i', $maxphp)) { + $maxphp = preg_replace('/t$/i', '', $maxphp); $maxphp = $maxphp * 1024 * 1024 * 1024; } $maxphp2 = @ini_get('post_max_size'); // In unknown if (preg_match('/k$/i', $maxphp2)) { + $maxphp2 = preg_replace('/k$/i', '', $maxphp2); $maxphp2 = $maxphp2 * 1; } if (preg_match('/m$/i', $maxphp2)) { + $maxphp2 = preg_replace('/m$/i', '', $maxphp2); $maxphp2 = $maxphp2 * 1024; } if (preg_match('/g$/i', $maxphp2)) { + $maxphp2 = preg_replace('/g$/i', '', $maxphp2); $maxphp2 = $maxphp2 * 1024 * 1024; } if (preg_match('/t$/i', $maxphp2)) { + $maxphp2 = preg_replace('/t$/i', '', $maxphp2); $maxphp2 = $maxphp2 * 1024 * 1024 * 1024; } // Now $max and $maxphp and $maxphp2 are in Kb diff --git a/htdocs/product/stock/movement_card.php b/htdocs/product/stock/movement_card.php index b5373fc9de6..ebe8f0552ce 100644 --- a/htdocs/product/stock/movement_card.php +++ b/htdocs/product/stock/movement_card.php @@ -75,8 +75,8 @@ $search_type_mouvement = GETPOST('search_type_mouvement', 'int'); $limit = GETPOST('limit', 'int') ?GETPOST('limit', 'int') : $conf->liste_limit; $page = GETPOSTISSET('pageplusone') ? (GETPOST('pageplusone') - 1) : GETPOST("page", 'int'); -$sortfield = GETPOST("sortfield", 'alpha'); -$sortorder = GETPOST("sortorder", 'alpha'); +$sortfield = GETPOST('sortfield', 'aZ09comma'); +$sortorder = GETPOST('sortorder', 'aZ09comma'); if (empty($page) || $page == -1) { $page = 0; } // If $page is not defined, or '' or -1 @@ -489,7 +489,7 @@ if ($search_warehouse != '' && $search_warehouse != '-1') { $sql .= natural_search('e.rowid', $search_warehouse, 2); } if (!empty($search_user)) { - $sql .= natural_search('u.login', $search_user); + $sql .= natural_search(array('u.lastname', 'u.firstname', 'u.login'), $search_user); } if (!empty($search_batch)) { $sql .= natural_search('m.batch', $search_batch); @@ -1207,14 +1207,12 @@ if ($action != 'create' && $action != 'edit' && $action != 'delete' && $id > 0) $MAXEVENT = 10; - $morehtmlright = ''; - $morehtmlright .= $langs->trans("SeeAll"); - $morehtmlright .= ''; + $morehtmlcenter = dolGetButtonTitle($langs->trans('SeeAll'), '', 'fa fa-list-alt imgforviewmode', DOL_URL_ROOT.'/product/agenda.php?id='.$object->id); // List of actions on element include_once DOL_DOCUMENT_ROOT.'/core/class/html.formactions.class.php'; $formactions = new FormActions($db); - $somethingshown = $formactions->showactions($object, 'mouvement', 0, 1, '', $MAXEVENT, '', $morehtmlright); // Show all action for product + $somethingshown = $formactions->showactions($object, 'mouvement', 0, 1, '', $MAXEVENT, '', $morehtmlcenter); // Show all action for product print ''; } diff --git a/htdocs/product/stock/movement_list.php b/htdocs/product/stock/movement_list.php index 7d261a73f7a..405528a3480 100644 --- a/htdocs/product/stock/movement_list.php +++ b/htdocs/product/stock/movement_list.php @@ -3,7 +3,7 @@ * Copyright (C) 2004-2017 Laurent Destailleur * Copyright (C) 2005-2014 Regis Houssin * Copyright (C) 2015 Juanjo Menent - * Copyright (C) 2018 Ferran Marcet + * Copyright (C) 2018-2022 Ferran Marcet * Copyright (C) 2019 Frédéric France * * This program is free software; you can redistribute it and/or modify @@ -49,9 +49,6 @@ if (!empty($conf->productbatch->enabled)) { $langs->load("productbatch"); } -// Security check -$result = restrictedArea($user, 'stock'); - $id = GETPOST('id', 'int'); $ref = GETPOST('ref', 'alpha'); $msid = GETPOST('msid', 'int'); @@ -63,11 +60,8 @@ $cancel = GETPOST('cancel', 'alpha'); $contextpage = GETPOST('contextpage', 'aZ') ?GETPOST('contextpage', 'aZ') : 'movementlist'; $toselect = GETPOST('toselect', 'array'); // Array of ids of elements selected into a list -// Security check -//$result=restrictedArea($user, 'stock', $id, 'entrepot&stock'); -$result = restrictedArea($user, 'stock'); - $idproduct = GETPOST('idproduct', 'int'); +$sall = trim((GETPOST('search_all', 'alphanohtml') != '') ?GETPOST('search_all', 'alphanohtml') : GETPOST('sall', 'alphanohtml')); $search_date_startday = GETPOST('search_date_startday', 'int'); $search_date_startmonth = GETPOST('search_date_startmonth', 'int'); $search_date_startyear = GETPOST('search_date_startyear', 'int'); @@ -87,15 +81,22 @@ $search_batch = trim(GETPOST("search_batch")); $search_qty = trim(GETPOST("search_qty")); $search_type_mouvement = GETPOST('search_type_mouvement', 'int'); $search_fk_projet=GETPOST("search_fk_projet", 'int'); +$optioncss = GETPOST('optioncss', 'alpha'); +$type = GETPOST("type", "int"); +// Load variable for pagination $limit = GETPOST('limit', 'int') ?GETPOST('limit', 'int') : $conf->liste_limit; +$sortfield = GETPOST('sortfield', 'aZ09comma'); +$sortorder = GETPOST('sortorder', 'aZ09comma'); $page = GETPOSTISSET('pageplusone') ? (GETPOST('pageplusone') - 1) : GETPOST("page", 'int'); -$sortfield = GETPOST("sortfield", 'alpha'); -$sortorder = GETPOST("sortorder", 'alpha'); -if (empty($page) || $page == -1) { +if (empty($page) || $page < 0 || GETPOST('button_search', 'alpha') || GETPOST('button_removefilter', 'alpha')) { + // If $page is not defined, or '' or -1 or if we click on clear filters $page = 0; -} // If $page is not defined, or '' or -1 +} $offset = $limit * $page; +$pageprev = $page - 1; +$pagenext = $page + 1; + if (!$sortfield) { $sortfield = "m.datem"; } @@ -107,8 +108,10 @@ $pdluoid = GETPOST('pdluoid', 'int'); // Initialize technical object to manage hooks of page. Note that conf->hooks_modules contains array of hook context $object = new MouvementStock($db); -$hookmanager->initHooks(array('movementlist')); $extrafields = new ExtraFields($db); +$diroutputmassaction = $conf->stock->dir_output.'/temp/massgeneration/'.$user->id; +$hookmanager->initHooks(array('movementlist')); + $formfile = new FormFile($db); // fetch optionals attributes and labels @@ -143,11 +146,25 @@ if (!empty($conf->global->PRODUCT_DISABLE_EATBY)) { unset($arrayfields['pl.eatby']); } + +$tmpwarehouse = new Entrepot($db); +if ($id > 0 || !empty($ref)) { + $tmpwarehouse->fetch($id, $ref); + $id = $tmpwarehouse->id; +} + + +// Security check +//$result=restrictedArea($user, 'stock', $id, 'entrepot&stock'); +$result = restrictedArea($user, 'stock'); + // Security check if (!$user->rights->stock->mouvement->lire) { accessforbidden(); } +$uploaddir = $conf->stock->dir_output.'/movements'; + $permissiontoread = $user->rights->stock->mouvement->lire; $permissiontoadd = $user->rights->stock->mouvement->creer; $permissiontodelete = $user->rights->stock->mouvement->creer; // There is no deletion permission for stock movement as we shoul dnever delete @@ -164,7 +181,8 @@ $error = 0; */ if (GETPOST('cancel', 'alpha')) { - $action = 'list'; $massaction = ''; + $action = 'list'; + $massaction = ''; } if (!GETPOST('confirmmassaction', 'alpha') && $massaction != 'presend' && $massaction != 'confirm_presend') { $massaction = ''; @@ -177,9 +195,10 @@ if ($reshook < 0) { } if (empty($reshook)) { + // Selection of new fields include DOL_DOCUMENT_ROOT.'/core/actions_changeselectedfields.inc.php'; - // Do we click on purge search criteria ? + // Purge search criteria if (GETPOST('button_removefilter_x', 'alpha') || GETPOST('button_removefilter.x', 'alpha') || GETPOST('button_removefilter', 'alpha')) { // Both test are required to be compatible with all browsers $search_date_startday = ''; $search_date_startmonth = ''; @@ -204,18 +223,96 @@ if (empty($reshook)) { $toselect = ''; $search_array_options = array(); } + if (GETPOST('button_removefilter_x', 'alpha') || GETPOST('button_removefilter.x', 'alpha') || GETPOST('button_removefilter', 'alpha') + || GETPOST('button_search_x', 'alpha') || GETPOST('button_search.x', 'alpha') || GETPOST('button_search', 'alpha')) { + $massaction = ''; // Protection to avoid mass action if we force a new search during a mass action confirmation + } // Mass actions $objectclass = 'MouvementStock'; $objectlabel = 'MouvementStock'; - $uploaddir = $conf->stock->dir_output; + + if (!$error && $massaction == "builddoc" && $permissiontoread && !GETPOST('button_search')) { + if (empty($diroutputmassaction)) { + dol_print_error(null, 'include of actions_massactions.inc.php is done but var $diroutputmassaction was not defined'); + exit; + } + + require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php'; + require_once DOL_DOCUMENT_ROOT.'/core/lib/pdf.lib.php'; + require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php'; + + $objecttmp = new $objectclass($db); + $listofobjectid = array(); + foreach ($toselect as $toselectid) { + $objecttmp = new $objectclass($db); // must create new instance because instance is saved into $listofobjectref array for future use + $result = $objecttmp->fetch($toselectid); + if ($result > 0) { + $listofobjectid[$toselectid] = $toselectid; + } + } + + $arrayofinclusion = array(); + foreach ($listofobjectref as $tmppdf) { + $arrayofinclusion[] = '^'.preg_quote(dol_sanitizeFileName($tmppdf), '/').'\.pdf$'; + } + foreach ($listofobjectref as $tmppdf) { + $arrayofinclusion[] = '^'.preg_quote(dol_sanitizeFileName($tmppdf), '/').'_[a-zA-Z0-9-_]+\.pdf$'; // To include PDF generated from ODX files + } + $listoffiles = dol_dir_list($uploaddir, 'all', 1, implode('|', $arrayofinclusion), '\.meta$|\.png', 'date', SORT_DESC, 0, true); + + // Define output language (Here it is not used because we do only merging existing PDF) + $outputlangs = $langs; + $newlang = ''; + if ($conf->global->MAIN_MULTILANGS && empty($newlang) && GETPOST('lang_id', 'aZ09')) { + $newlang = GETPOST('lang_id', 'aZ09'); + } + //elseif ($conf->global->MAIN_MULTILANGS && empty($newlang) && is_object($objecttmp->thirdparty)) { // On massaction, we can have several values for $objecttmp->thirdparty + // $newlang = $objecttmp->thirdparty->default_lang; + //} + if (!empty($newlang)) { + $outputlangs = new Translate("", $conf); + $outputlangs->setDefaultLang($newlang); + } + + // Create output dir if not exists + dol_mkdir($diroutputmassaction); + + // Defined name of merged file + $filename = strtolower(dol_sanitizeFileName($langs->transnoentities($objectlabel))); + $filename = preg_replace('/\s/', '_', $filename); + + // Save merged file + /* + if ($year) { + $filename .= '_'.$year; + } + if ($month) { + $filename .= '_'.$month; + } + */ + $now = dol_now(); + $file = $diroutputmassaction.'/'.$filename.'_'.dol_print_date($now, 'dayhourlog').'.pdf'; + + + // Create PDF + // TODO Create the pdf including list of movement ids found into $listofobjectid + // ... + + + if (!$error) { + $langs->load("exports"); + setEventMessages($langs->trans('FileSuccessfullyBuilt', $filename.'_'.dol_print_date($now, 'dayhourlog')), null, 'mesgs'); + } + + $massaction = ''; + $action = ''; + } + include DOL_DOCUMENT_ROOT.'/core/actions_massactions.inc.php'; } if ($action == 'update_extras') { - $tmpwarehouse = new Entrepot($db); - $tmpwarehouse->fetch($id); - $tmpwarehouse->oldcopy = dol_clone($tmpwarehouse); // Fill array 'array_options' with data from update form @@ -477,6 +574,8 @@ if (!empty($conf->projet->enabled)) { $formproject = new FormProjets($db); } +// Build and execute select +// -------------------------------------------------------------------- $sql = "SELECT p.rowid, p.ref as product_ref, p.label as produit, p.tosell, p.tobuy, p.tobatch, p.fk_product_type as type, p.entity,"; $sql .= " e.ref as warehouse_ref, e.rowid as entrepot_id, e.lieu, e.fk_parent, e.statut,"; $sql .= " m.rowid as mid, m.value as qty, m.datem, m.fk_user_author, m.label, m.inventorycode, m.fk_origin, m.origintype,"; @@ -494,11 +593,12 @@ if (!empty($extrafields->attributes[$object->table_element]['label'])) { // Add fields from hooks $parameters = array(); $reshook = $hookmanager->executeHooks('printFieldListSelect', $parameters); // Note that $action and $object may have been modified by hook -$sql .= $hookmanager->resPrint; +$sql .= preg_replace('/^,/', '', $hookmanager->resPrint); +$sql = preg_replace('/,\s*$/', '', $sql); $sql .= " FROM ".MAIN_DB_PREFIX."entrepot as e,"; $sql .= " ".MAIN_DB_PREFIX."product as p,"; $sql .= " ".MAIN_DB_PREFIX."stock_mouvement as m"; -if (is_array($extrafields->attributes[$object->table_element]['label']) && count($extrafields->attributes[$object->table_element]['label'])) { +if (!empty($extrafields->attributes[$object->table_element]['label']) && is_array($extrafields->attributes[$object->table_element]['label']) && count($extrafields->attributes[$object->table_element]['label'])) { $sql .= " LEFT JOIN ".MAIN_DB_PREFIX.$object->table_element."_extrafields as ef on (m.rowid = ef.fk_object)"; } $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."user as u ON m.fk_user_author = u.rowid"; @@ -543,7 +643,7 @@ if ($search_warehouse != '' && $search_warehouse != '-1') { $sql .= natural_search('e.rowid', $search_warehouse, 2); } if (!empty($search_user)) { - $sql .= natural_search('u.login', $search_user); + $sql .= natural_search(array('u.lastname', 'u.firstname', 'u.login'), $search_user); } if (!empty($search_batch)) { $sql .= natural_search('m.batch', $search_batch); @@ -567,6 +667,31 @@ $parameters = array(); $reshook = $hookmanager->executeHooks('printFieldListWhere', $parameters); // Note that $action and $object may have been modified by hook $sql .= $hookmanager->resPrint; +/* If a group by is required + $sql .= " GROUP BY "; + foreach($object->fields as $key => $val) { + $sql .= "t.".$key.", "; + } + // Add fields from extrafields + if (!empty($extrafields->attributes[$object->table_element]['label'])) { + foreach ($extrafields->attributes[$object->table_element]['label'] as $key => $val) { + $sql .= ($extrafields->attributes[$object->table_element]['type'][$key] != 'separate' ? "ef.".$key.', ' : ''); + } + } + // Add where from hooks + $parameters = array(); + $reshook = $hookmanager->executeHooks('printFieldListGroupBy', $parameters, $object); // Note that $action and $object may have been modified by hook + $sql .= $hookmanager->resPrint; + $sql = preg_replace('/,\s*$/', '', $sql); + */ + +// Add HAVING from hooks +/* + $parameters = array(); + $reshook = $hookmanager->executeHooks('printFieldListHaving', $parameters, $object); // Note that $action and $object may have been modified by hook + $sql .= empty($hookmanager->resPrint) ? "" : " HAVING 1=1 ".$hookmanager->resPrint; + */ + // Count total nb of records $nbtotalofrecords = ''; if (empty($conf->global->MAIN_DISABLE_FULL_SCANLIST)) { @@ -584,20 +709,19 @@ if (empty($conf->global->MAIN_DISABLE_FULL_SCANLIST)) { $resql = $db->query($sqlforcount); $objforcount = $db->fetch_object($resql); $nbtotalofrecords = $objforcount->nbtotalofrecords; - if (($page * $limit) > $nbtotalofrecords) { // if total resultset is smaller then paging size (filtering), goto and load page 0 + if (($page * $limit) > $nbtotalofrecords) { // if total of record found is smaller than page * limit, goto and load page 0 $page = 0; $offset = 0; } $db->free($resql); } +// Complete request and execute it with limit $sql .= $db->order($sortfield, $sortorder); if ($limit) { $sql .= $db->plimit($limit + 1, $offset); } -//print $sql; - $resql = $db->query($sql); if (!$resql) { dol_print_error($db); @@ -619,20 +743,24 @@ if ($id > 0 || $ref) { $num = $db->num_rows($resql); -$arrayofselected = is_array($toselect) ? $toselect : array(); +// Output page +// -------------------------------------------------------------------- $i = 0; $help_url = 'EN:Module_Stocks_En|FR:Module_Stock|ES:Módulo_Stocks'; if ($msid) { - $texte = $langs->trans('StockMovementForId', $msid); + $title = $langs->trans('StockMovementForId', $msid); } else { - $texte = $langs->trans("ListOfStockMovements"); + $title = $langs->trans("ListOfStockMovements"); if ($id) { - $texte .= ' ('.$langs->trans("ForThisWarehouse").')'; + $title .= ' ('.$langs->trans("ForThisWarehouse").')'; } } -llxHeader("", $texte, $help_url); + +llxHeader('', $title, $help_url); + +$arrayofselected = is_array($toselect) ? $toselect : array(); /* * Show tab only if we ask a particular warehouse @@ -769,26 +897,20 @@ if ($object->id > 0) { } -/* - * Correct stock - */ +// Correct stock if ($action == "correction") { include DOL_DOCUMENT_ROOT.'/product/stock/tpl/stockcorrection.tpl.php'; print '
    '; } -/* - * Transfer of units - */ +// Transfer of units if ($action == "transfert") { include DOL_DOCUMENT_ROOT.'/product/stock/tpl/stocktransfer.tpl.php'; print '
    '; } -/* - * Action bar - */ +// Action bar if ((empty($action) || $action == 'list') && $id > 0) { print "
    \n"; @@ -862,12 +984,11 @@ if ($idproduct > 0) { include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_search_param.tpl.php'; // List of mass actions available -$arrayofmassactions = array( - 'generate_doc'=>img_picto('', 'pdf', 'class="pictofixedwidth"').$langs->trans("ReGeneratePDF"), -// 'presend'=>img_picto('', 'email', 'class="pictofixedwidth"').$langs->trans("SendByMail"), -// 'builddoc'=>img_picto('', 'pdf', 'class="pictofixedwidth"').$langs->trans("PDFMerge"), -); -// By default, we should never accept deletion of stock movement. +$arrayofmassactions = array(); +if (getDolGlobalInt('MAIN_FEATURES_LEVEL') >= 2) { + $arrayofmassactions['builddoc'] = img_picto('', 'pdf', 'class="pictofixedwidth"').$langs->trans("GeneratePDF"); +} +// By default, we should never accept deletion of stock movement if (!empty($conf->global->STOCK_ALLOW_DELETE_OF_MOVEMENT) && $permissiontodelete) { $arrayofmassactions['predelete'] = img_picto('', 'delete', 'class="pictofixedwidth"').$langs->trans("Delete"); } @@ -876,7 +997,7 @@ if (GETPOST('nomassaction', 'int') || in_array($massaction, array('presend', 'pr } $massactionbutton = $form->selectMassAction('', $arrayofmassactions); -print ''; +print ''."\n"; if ($optioncss != '') { print ''; } @@ -886,15 +1007,16 @@ print ''; print ''; print ''; print ''; +print ''; print ''; if ($id > 0) { print ''; } if ($id > 0) { - print_barre_liste($texte, $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, $massactionbutton, $num, $nbtotalofrecords, 'movement', 0, '', '', $limit, 0, 0, 1); + print_barre_liste($title, $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, $massactionbutton, $num, $nbtotalofrecords, 'movement', 0, '', '', $limit, 0, 0, 1); } else { - print_barre_liste($texte, $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, $massactionbutton, $num, $nbtotalofrecords, 'movement', 0, '', '', $limit, 0, 0, 1); + print_barre_liste($title, $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, $massactionbutton, $num, $nbtotalofrecords, 'movement', 0, '', '', $limit, 0, 0, 1); } // Add code for pre mass action (confirmation or email presend form) @@ -914,7 +1036,7 @@ if ($sall) { $moreforfilter = ''; $parameters = array('arrayfields'=>&$arrayfields); -$reshook = $hookmanager->executeHooks('printFieldPreListTitle', $parameters); // Note that $action and $object may have been modified by hook +$reshook = $hookmanager->executeHooks('printFieldPreListTitle', $parameters, $object); // Note that $action and $object may have been modified by hook if (empty($reshook)) { $moreforfilter .= $hookmanager->resPrint; } else { @@ -929,12 +1051,14 @@ if (!empty($moreforfilter)) { $varpage = empty($contextpage) ? $_SERVER["PHP_SELF"] : $contextpage; $selectedfields = $form->multiSelectArrayWithCheckbox('selectedfields', $arrayfields, $varpage); // This also change content of $arrayfields +$selectedfields .= (count($arrayofmassactions) ? $form->showCheckAddButtons('checkforselect', 1) : ''); -print '
    '; -print '
    '.$langs->trans("Reception").''.$langs->trans("Product").''.$langs->trans("DateCreation").''.$langs->trans("DateDeliveryPlanned").'
    '; if (!empty($objp->fk_reception)) { @@ -1187,8 +1224,16 @@ if ($id > 0 || !empty($ref)) { print "'; - print ''.img_object($langs->trans("ShowProduct"), 'product').' '.$objp->ref.''; + if (empty($conf->cache['product'][$objp->fk_product])) { + $tmpproduct = new Product($db); + $tmpproduct->fetch($objp->fk_product); + $conf->cache['product'][$objp->fk_product] = $tmpproduct; + } else { + $tmpproduct = $conf->cache['product'][$objp->fk_product]; + } + print $tmpproduct->getNomUrl(1); print ' - '.$objp->label; print "'.dol_print_date($db->jdate($objp->datec), 'day').''; - if ((empty($conf->global->MAIN_USE_ADVANCED_PERMS) && empty($user->rights->fournisseur->commande->receptionner)) || (!empty($conf->global->MAIN_USE_ADVANCED_PERMS) && empty($user->rights->fournisseur->commande_advance->check))) { + if (!$permissiontocontrol) { if (empty($objp->status)) { print ''.$langs->trans("Approve").''; print ''.$langs->trans("Deny").''; diff --git a/htdocs/fourn/commande/document.php b/htdocs/fourn/commande/document.php index c7e3f609164..d54d5553efb 100644 --- a/htdocs/fourn/commande/document.php +++ b/htdocs/fourn/commande/document.php @@ -48,8 +48,8 @@ $confirm = GETPOST('confirm', 'alpha'); // Get parameters $limit = GETPOST('limit', 'int') ? GETPOST('limit', 'int') : $conf->liste_limit; -$sortfield = GETPOST("sortfield", 'alpha'); -$sortorder = GETPOST("sortorder", 'alpha'); +$sortfield = GETPOST('sortfield', 'aZ09comma'); +$sortorder = GETPOST('sortorder', 'aZ09comma'); $page = GETPOSTISSET('pageplusone') ? (GETPOST('pageplusone') - 1) : GETPOST("page", 'int'); if (empty($page) || $page == -1) { $page = 0; diff --git a/htdocs/fourn/commande/info.php b/htdocs/fourn/commande/info.php index 09bf1ab08e6..6c64672ee84 100644 --- a/htdocs/fourn/commande/info.php +++ b/htdocs/fourn/commande/info.php @@ -41,8 +41,8 @@ $ref = GETPOST('ref', 'alpha'); $action = GETPOST('action', 'aZ09'); $limit = GETPOST('limit', 'int') ?GETPOST('limit', 'int') : $conf->liste_limit; -$sortfield = GETPOST("sortfield", 'alpha'); -$sortorder = GETPOST("sortorder", 'alpha'); +$sortfield = GETPOST('sortfield', 'aZ09comma'); +$sortorder = GETPOST('sortorder', 'aZ09comma'); $page = GETPOST("page", 'int'); if (empty($page) || $page == -1) { $page = 0; diff --git a/htdocs/fourn/commande/list.php b/htdocs/fourn/commande/list.php index 419e75b55aa..59c3a57c4ac 100644 --- a/htdocs/fourn/commande/list.php +++ b/htdocs/fourn/commande/list.php @@ -135,8 +135,8 @@ $result = restrictedArea($user, 'fournisseur', $orderid, '', 'commande'); $diroutputmassaction = $conf->fournisseur->commande->dir_output.'/temp/massgeneration/'.$user->id; $limit = GETPOST('limit', 'int') ?GETPOST('limit', 'int') : $conf->liste_limit; -$sortfield = GETPOST("sortfield", 'alpha'); -$sortorder = GETPOST("sortorder", 'alpha'); +$sortfield = GETPOST('sortfield', 'aZ09comma'); +$sortorder = GETPOST('sortorder', 'aZ09comma'); $page = GETPOSTISSET('pageplusone') ? (GETPOST('pageplusone') - 1) : GETPOST("page", 'int'); if (empty($page) || $page == -1 || !empty($search_btn) || !empty($search_remove_btn) || (empty($toselect) && $massaction === '0')) { $page = 0; @@ -738,7 +738,7 @@ $help_url = ''; // llxHeader('',$title,$help_url); $sql = 'SELECT'; -if ($sall || $search_product_category > 0 || $search_user > 0) { +if ($sall || $search_product_category > 0) { $sql = 'SELECT DISTINCT'; } $sql .= ' s.rowid as socid, s.nom as name, s.town, s.zip, s.fk_pays, s.client, s.code_client, s.email,'; @@ -765,7 +765,7 @@ $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_country as country on (country.rowid = s $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_typent as typent on (typent.id = s.fk_typent)"; $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_departements as state on (state.rowid = s.fk_departement)"; $sql .= ", ".MAIN_DB_PREFIX."commande_fournisseur as cf"; -if (is_array($extrafields->attributes[$object->table_element]['label']) && count($extrafields->attributes[$object->table_element]['label'])) { +if (!empty($extrafields->attributes[$object->table_element]['label']) && is_array($extrafields->attributes[$object->table_element]['label']) && count($extrafields->attributes[$object->table_element]['label'])) { $sql .= " LEFT JOIN ".MAIN_DB_PREFIX.$object->table_element."_extrafields as ef on (cf.rowid = ef.fk_object)"; } if ($sall || $search_product_category > 0) { @@ -780,10 +780,6 @@ $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."projet as p ON p.rowid = cf.fk_projet"; if ($search_sale > 0 || (empty($user->rights->societe->client->voir) && !$socid)) { $sql .= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc"; } -if ($search_user > 0) { - $sql .= ", ".MAIN_DB_PREFIX."element_contact as ec"; - $sql .= ", ".MAIN_DB_PREFIX."c_type_contact as tc"; -} $parameters = array(); $reshook = $hookmanager->executeHooks('printFieldListFrom', $parameters, $object); // Note that $action and $object may have been modified by hook $sql .= $hookmanager->resPrint; @@ -869,7 +865,13 @@ if ($search_sale > 0) { $sql .= " AND s.rowid = sc.fk_soc AND sc.fk_user = ".((int) $search_sale); } if ($search_user > 0) { - $sql .= " AND ec.fk_c_type_contact = tc.rowid AND tc.element='supplier_order' AND tc.source='internal' AND ec.element_id = cf.rowid AND ec.fk_socpeople = ".((int) $search_user); + $sql .= " AND EXISTS ("; + $sql .= " SELECT ec.rowid "; + $sql .= " FROM " . MAIN_DB_PREFIX . "element_contact as ec"; + $sql .= " INNER JOIN " . MAIN_DB_PREFIX . "c_type_contact as tc ON tc.rowid = ec.fk_c_type_contact"; + $sql .= " WHERE ec.element_id = cf.rowid AND ec.fk_socpeople = " . ((int) $search_user); + $sql .= " AND tc.element = 'order_supplier' AND tc.source = 'internal'"; + $sql .= ")"; } if ($search_total_ht != '') { $sql .= natural_search('cf.total_ht', $search_total_ht, 1); @@ -1200,14 +1202,14 @@ if ($resql) { $langs->load("commercial"); $moreforfilter .= '
    '; $tmptitle = $langs->trans('ThirdPartiesOfSaleRepresentative'); - $moreforfilter .= img_picto($tmptitle, 'user', 'class="pictofixedwidth"').$formother->select_salesrepresentatives($search_sale, 'search_sale', $user, 0, $tmptitle, 'maxwidth250'); + $moreforfilter .= img_picto($tmptitle, 'user', 'class="pictofixedwidth"').$formother->select_salesrepresentatives($search_sale, 'search_sale', $user, 0, $tmptitle, 'maxwidth250 widthcentpercentminusx'); $moreforfilter .= '
    '; } // If the user can view other users if ($user->rights->user->user->lire) { $moreforfilter .= '
    '; $tmptitle = $langs->trans('LinkedToSpecificUsers'); - $moreforfilter .= img_picto($tmptitle, 'user', 'class="pictofixedwidth"').$form->select_dolusers($search_user, 'search_user', $tmptitle, '', 0, '', '', 0, 0, 0, '', 0, '', 'maxwidth250'); + $moreforfilter .= img_picto($tmptitle, 'user', 'class="pictofixedwidth"').$form->select_dolusers($search_user, 'search_user', $tmptitle, '', 0, '', '', 0, 0, 0, '', 0, '', 'maxwidth250 widthcentpercentminusx'); $moreforfilter .= '
    '; } // If the user can view prospects other than his' @@ -1216,7 +1218,7 @@ if ($resql) { $moreforfilter .= '
    '; $tmptitle = $langs->trans('IncludingProductWithTag'); $cate_arbo = $form->select_all_categories(Categorie::TYPE_PRODUCT, null, 'parent', null, null, 1); - $moreforfilter .= img_picto($tmptitle, 'category', 'class="pictofixedwidth"').$form->selectarray('search_product_category', $cate_arbo, $search_product_category, $tmptitle, 0, 0, '', 0, 0, 0, 0, 'maxwidth300', 1); + $moreforfilter .= img_picto($tmptitle, 'category', 'class="pictofixedwidth"').$form->selectarray('search_product_category', $cate_arbo, $search_product_category, $tmptitle, 0, 0, '', 0, 0, 0, 0, 'maxwidth300 widthcentpercentminusx', 1); $moreforfilter .= '
    '; } $parameters = array(); diff --git a/htdocs/fourn/contact.php b/htdocs/fourn/contact.php index cabf726beee..9d6e3abd4cf 100644 --- a/htdocs/fourn/contact.php +++ b/htdocs/fourn/contact.php @@ -41,8 +41,8 @@ if ($user->socid > 0) { } $limit = GETPOST('limit', 'int') ? GETPOST('limit', 'int') : $conf->liste_limit; -$sortfield = GETPOST("sortfield", 'alpha'); -$sortorder = GETPOST("sortorder", 'alpha'); +$sortfield = GETPOST('sortfield', 'aZ09comma'); +$sortorder = GETPOST('sortorder', 'aZ09comma'); $page = GETPOSTISSET('pageplusone') ? (GETPOST('pageplusone') - 1) : GETPOST("page", 'int'); if (empty($page) || $page == -1) { $page = 0; diff --git a/htdocs/fourn/facture/card.php b/htdocs/fourn/facture/card.php old mode 100755 new mode 100644 index 7ad7d434f46..76159d177c5 --- a/htdocs/fourn/facture/card.php +++ b/htdocs/fourn/facture/card.php @@ -265,6 +265,8 @@ if (empty($reshook)) { // Remove a product line $result = $object->deleteline($lineid); if ($result > 0) { + // reorder lines + $object->line_order(true); // Define output language /*$outputlangs = $langs; $newlang = ''; @@ -1297,7 +1299,7 @@ if (empty($reshook)) { $localtax2_tx = get_localtax($tva_tx, 2, $mysoc, $object->thirdparty); $remise_percent = price2num(GETPOST('remise_percent'), '', 2); - $pu_ht_devise = price2num(GETPOST('multicurrency_subprice'), 'MU', 2); + $pu_devise = price2num(GETPOST('multicurrency_subprice'), 'MU', 2); // Extrafields Lines $extralabelsline = $extrafields->fetch_name_optionals_label($object->table_element_line); @@ -1309,7 +1311,7 @@ if (empty($reshook)) { } } - $result = $object->updateline(GETPOST('lineid', 'int'), $label, $up, $tva_tx, $localtax1_tx, $localtax2_tx, price2num(GETPOST('qty'), 'MS'), GETPOST('productid', 'int'), $price_base_type, $info_bits, $type, $remise_percent, 0, $date_start, $date_end, $array_options, GETPOST('units'), $pu_ht_devise, GETPOST('fourn_ref', 'alpha')); + $result = $object->updateline(GETPOST('lineid', 'int'), $label, $up, $tva_tx, $localtax1_tx, $localtax2_tx, price2num(GETPOST('qty'), 'MS'), GETPOST('productid', 'int'), $price_base_type, $info_bits, $type, $remise_percent, 0, $date_start, $date_end, $array_options, GETPOST('units'), $pu_devise, GETPOST('fourn_ref', 'alpha')); if ($result >= 0) { unset($_POST['label']); unset($_POST['fourn_ref']); @@ -1325,6 +1327,8 @@ if (empty($reshook)) { unset($_POST['date_endday']); unset($_POST['date_endmonth']); unset($_POST['date_endyear']); + unset($_POST['price_ttc']); + unset($_POST['price_ht']); $db->commit(); } else { @@ -1353,17 +1357,18 @@ if (empty($reshook)) { $prod_entry_mode = GETPOST('prod_entry_mode'); if ($prod_entry_mode == 'free') { $idprod = 0; - $price_ht = price2num(GETPOST('price_ht'), 'MU', 2); - $tva_tx = (GETPOST('tva_tx') ? GETPOST('tva_tx') : 0); } else { $idprod = GETPOST('idprod', 'int'); - $price_ht = price2num(GETPOST('price_ht'), 'MU', 2); - $tva_tx = ''; } + $tva_tx = (GETPOST('tva_tx') ? GETPOST('tva_tx') : 0); // Can be '1.2' or '1.2 (CODE)' + + $price_ht = price2num(GETPOST('price_ht'), 'MU', 2); + $price_ht_devise = price2num(GETPOST('multicurrency_price_ht'), 'CU', 2); + $price_ttc = price2num(GETPOST('price_ttc'), 'MU', 2); + $price_ttc_devise = price2num(GETPOST('multicurrency_price_ttc'), 'CU', 2); $qty = price2num(GETPOST('qty'.$predef, 'alpha'), 'MS'); $remise_percent = price2num(GETPOST('remise_percent'.$predef), 2); - $price_ht_devise = price2num(GETPOST('multicurrency_price_ht'), 'MU', 2); // Extrafields $extralabelsline = $extrafields->fetch_name_optionals_label($object->table_element_line); @@ -1453,8 +1458,8 @@ if (empty($reshook)) { } //If text set in desc is the same as product descpription (as now it's preloaded) whe add it only one time - if ($product_desc==$desc && !empty($conf->global->PRODUIT_AUTOFILL_DESC)) { - $product_desc=''; + if (trim($product_desc) == trim($desc) && !empty($conf->global->PRODUIT_AUTOFILL_DESC)) { + $product_desc = ''; } if (!empty($product_desc) && !empty($conf->global->MAIN_NO_CONCAT_DESCRIPTION)) { $desc = $product_desc; @@ -1463,32 +1468,39 @@ if (empty($reshook)) { $desc = dol_concatdesc($desc, $product_desc, '', !empty($conf->global->MAIN_CHANGE_ORDER_CONCAT_DESCRIPTION)); } - $type = $productsupplier->type; - if (GETPOST('price_ht') != '' || GETPOST('price_ht_devise') != '') { - $price_base_type = 'HT'; - $pu = price2num($price_ht, 'MU'); - $pu_ht_devise = price2num($price_ht_devise, 'CU'); - } else { - $price_base_type = ($productsupplier->fourn_price_base_type ? $productsupplier->fourn_price_base_type : 'HT'); - if (empty($object->multicurrency_code) || ($productsupplier->fourn_multicurrency_code != $object->multicurrency_code)) { // If object is in a different currency and price not in this currency - $pu = $productsupplier->fourn_pu; - $pu_ht_devise = 0; - } else { - $pu = $productsupplier->fourn_pu; - $pu_ht_devise = $productsupplier->fourn_multicurrency_unitprice; - } - } - $ref_supplier = $productsupplier->ref_supplier; - $tva_tx = get_default_tva($object->thirdparty, $mysoc, $productsupplier->id, GETPOST('idprodfournprice', 'alpha')); - $tva_npr = get_default_npr($object->thirdparty, $mysoc, $productsupplier->id, GETPOST('idprodfournprice', 'alpha')); + // Get vat rate + if (!GETPOSTISSET('tva_tx')) { // If vat rate not provided from the form (the form has the priority) + $tva_tx = get_default_tva($object->thirdparty, $mysoc, $productsupplier->id, GETPOST('idprodfournprice', 'alpha')); + $tva_npr = get_default_npr($object->thirdparty, $mysoc, $productsupplier->id, GETPOST('idprodfournprice', 'alpha')); + } if (empty($tva_tx)) { $tva_npr = 0; } $localtax1_tx = get_localtax($tva_tx, 1, $mysoc, $object->thirdparty, $tva_npr); $localtax2_tx = get_localtax($tva_tx, 2, $mysoc, $object->thirdparty, $tva_npr); + $type = $productsupplier->type; + if (GETPOST('price_ht') != '' || GETPOST('price_ht_devise') != '') { + $price_base_type = 'HT'; + $pu = price2num($price_ht, 'MU'); + $pu_devise = price2num($price_ht_devise, 'CU'); + } elseif (GETPOST('price_ttc') != '' || GETPOST('price_ttc_devise') != '') { + $price_base_type = 'TTC'; + $pu = price2num($price_ttc, 'MU'); + $pu_devise = price2num($price_ttc_devise, 'CU'); + } else { + $price_base_type = ($productsupplier->fourn_price_base_type ? $productsupplier->fourn_price_base_type : 'HT'); + if (empty($object->multicurrency_code) || ($productsupplier->fourn_multicurrency_code != $object->multicurrency_code)) { // If object is in a different currency and price not in this currency + $pu = $productsupplier->fourn_pu; + $pu_devise = 0; + } else { + $pu = $productsupplier->fourn_pu; + $pu_devise = $productsupplier->fourn_multicurrency_unitprice; + } + } + if (empty($pu)) { $pu = 0; // If pu is '' or null, we force to have a numeric value } @@ -1513,7 +1525,7 @@ if (empty($reshook)) { $array_options, $productsupplier->fk_unit, 0, - $pu_ht_devise, + $pu_devise, $ref_supplier, '' ); @@ -1555,9 +1567,9 @@ if (empty($reshook)) { $pu_ht = price2num($pu_ttc / (1 + ($tva_tx / 100)), 'MU'); // $pu_ht must be rounded according to settings } $price_base_type = 'HT'; - $pu_ht_devise = price2num($price_ht_devise, 'CU'); + $pu_devise = price2num($price_devise, 'CU'); - $result = $object->addline($product_desc, $pu_ht, $tva_tx, $localtax1_tx, $localtax2_tx, $qty, 0, $remise_percent, $date_start, $date_end, 0, $tva_npr, $price_base_type, $type, -1, 0, $array_options, $fk_unit, 0, $pu_ht_devise, $ref_supplier); + $result = $object->addline($product_desc, $pu_ht, $tva_tx, $localtax1_tx, $localtax2_tx, $qty, 0, $remise_percent, $date_start, $date_end, 0, $tva_npr, $price_base_type, $type, -1, 0, $array_options, $fk_unit, 0, $pu_devise, $ref_supplier); } //print "xx".$tva_tx; exit; @@ -1954,7 +1966,7 @@ if ($action == 'create') { print $societe->getNomUrl(1, 'supplier'); print ''; } else { - print img_picto('', 'company').$form->select_company($societe->id, 'socid', 's.fournisseur=1', 'SelectThirdParty', 0, 0, null, 0, 'minwidth300 widthcentpercentminusxx'); + print img_picto('', 'company').$form->select_company($societe->id, 'socid', 's.fournisseur=1', 'SelectThirdParty', 0, 0, null, 0, 'minwidth300 widthcentpercentminusxx maxwidth500'); // reload page to retrieve supplier informations if (!empty($conf->global->RELOAD_PAGE_ON_SUPPLIER_CHANGE)) { print ''; + } } return $ret; diff --git a/htdocs/hrm/position.php b/htdocs/hrm/position.php index 208a397f135..d36d352e895 100644 --- a/htdocs/hrm/position.php +++ b/htdocs/hrm/position.php @@ -42,7 +42,6 @@ //if (! defined("MAIN_LANG_DEFAULT")) define('MAIN_LANG_DEFAULT', 'auto'); // Force lang to a particular value //if (! defined("MAIN_AUTHENTICATION_MODE")) define('MAIN_AUTHENTICATION_MODE', 'aloginmodule'); // Force authentication handler //if (! defined("NOREDIRECTBYMAINTOLOGIN")) define('NOREDIRECTBYMAINTOLOGIN', 1); // The main.inc.php does not make a redirect if not logged, instead show simple error message -//if (! defined("FORCECSP")) define('FORCECSP', 'none'); // Disable all Content Security Policies //if (! defined('CSRFCHECK_WITH_TOKEN')) define('CSRFCHECK_WITH_TOKEN', '1'); // Force use of CSRF protection with tokens even for GET //if (! defined('NOBROWSERNOTIF')) define('NOBROWSERNOTIF', '1'); // Disable browser notification diff --git a/htdocs/hrm/position_agenda.php b/htdocs/hrm/position_agenda.php index b3b68d9bcbc..fda60a29259 100644 --- a/htdocs/hrm/position_agenda.php +++ b/htdocs/hrm/position_agenda.php @@ -58,8 +58,8 @@ if (GETPOST('actioncode', 'array')) { $search_agenda_label = GETPOST('search_agenda_label'); $limit = GETPOST('limit', 'int') ? GETPOST('limit', 'int') : $conf->liste_limit; -$sortfield = GETPOST("sortfield", 'alpha'); -$sortorder = GETPOST("sortorder", 'alpha'); +$sortfield = GETPOST('sortfield', 'aZ09comma'); +$sortorder = GETPOST('sortorder', 'aZ09comma'); $page = GETPOSTISSET('pageplusone') ? (GETPOST('pageplusone') - 1) : GETPOST("page", 'int'); if (empty($page) || $page == -1) { $page = 0; diff --git a/htdocs/hrm/position_card.php b/htdocs/hrm/position_card.php index 4d6b36b52f3..3287091dcdb 100644 --- a/htdocs/hrm/position_card.php +++ b/htdocs/hrm/position_card.php @@ -377,13 +377,12 @@ if ($action !== 'edit' && $action !== 'create') { $MAXEVENT = 10; - $morehtmlright = ''; - $morehtmlright .= $langs->trans("SeeAll"); - $morehtmlright .= ''; + $morehtmlcenter = dolGetButtonTitle($langs->trans('SeeAll'), '', 'fa fa-list-alt imgforviewmode', DOL_URL_ROOT.'/hrm/position_agenda.php?id='.$object->id); + // List of actions on element include_once DOL_DOCUMENT_ROOT . '/core/class/html.formactions.class.php'; $formactions = new FormActions($db); - $somethingshown = $formactions->showactions($object, $object->element . '@' . $object->module, (is_object($object->thirdparty) ? $object->thirdparty->id : 0), 1, '', $MAXEVENT, '', $morehtmlright); + $somethingshown = $formactions->showactions($object, $object->element . '@' . $object->module, (is_object($object->thirdparty) ? $object->thirdparty->id : 0), 1, '', $MAXEVENT, '', $morehtmlcenter); print ''; } diff --git a/htdocs/hrm/position_document.php b/htdocs/hrm/position_document.php index 7a820310ee2..845f846a212 100644 --- a/htdocs/hrm/position_document.php +++ b/htdocs/hrm/position_document.php @@ -47,8 +47,8 @@ $ref = GETPOST('ref', 'alpha'); // Get parameters $limit = GETPOST('limit', 'int') ? GETPOST('limit', 'int') : $conf->liste_limit; -$sortfield = GETPOST("sortfield", 'alpha'); -$sortorder = GETPOST("sortorder", 'alpha'); +$sortfield = GETPOST('sortfield', 'aZ09comma'); +$sortorder = GETPOST('sortorder', 'aZ09comma'); $page = GETPOSTISSET('pageplusone') ? (GETPOST('pageplusone') - 1) : GETPOST("page", 'int'); if (empty($page) || $page == -1) { $page = 0; diff --git a/htdocs/hrm/skill_agenda.php b/htdocs/hrm/skill_agenda.php index 9c1821fb8d7..d73271a4f26 100644 --- a/htdocs/hrm/skill_agenda.php +++ b/htdocs/hrm/skill_agenda.php @@ -57,8 +57,8 @@ if (GETPOST('actioncode', 'array')) { $search_agenda_label = GETPOST('search_agenda_label'); $limit = GETPOST('limit', 'int') ? GETPOST('limit', 'int') : $conf->liste_limit; -$sortfield = GETPOST("sortfield", 'alpha'); -$sortorder = GETPOST("sortorder", 'alpha'); +$sortfield = GETPOST('sortfield', 'aZ09comma'); +$sortorder = GETPOST('sortorder', 'aZ09comma'); $page = GETPOSTISSET('pageplusone') ? (GETPOST('pageplusone') - 1) : GETPOST("page", 'int'); if (empty($page) || $page == -1) { $page = 0; @@ -147,7 +147,7 @@ if ($object->id > 0) { // Object card // ------------------------------------------------------------ - $linkback = ''.$langs->trans("BackToList").''; + $linkback = ''.$langs->trans("BackToList").''; $morehtmlref = '
    '; $morehtmlref.= $object->label; diff --git a/htdocs/hrm/skill_card.php b/htdocs/hrm/skill_card.php index ed66dc4ebb9..a06cf40952a 100644 --- a/htdocs/hrm/skill_card.php +++ b/htdocs/hrm/skill_card.php @@ -21,8 +21,8 @@ /** * \file skill_card.php - * \ingroup hrm - * \brief Page to create/edit/view skill + * \ingroup hrm + * \brief Page to create/edit/view skill */ @@ -84,6 +84,8 @@ $upload_dir = $conf->hrm->multidir_output[isset($object->entity) ? $object->enti if (empty($conf->hrm->enabled)) accessforbidden(); if (!$permissiontoread || ($action === 'create' && !$permissiontoadd)) accessforbidden(); +$MaxNumberSkill = isset($conf->global->HRM_MAXRANK) ? $conf->global->HRM_MAXRANK : Skill::DEFAULT_MAX_RANK_PER_SKILL; + /* * Actions @@ -98,14 +100,14 @@ if ($reshook < 0) { if (empty($reshook)) { $error = 0; - $backurlforlist = dol_buildpath('/hrm/skill_list.php', 1); + $backurlforlist = DOL_URL_ROOT.'/hrm/skill_list.php'; if (empty($backtopage) || ($cancel && empty($id))) { if (empty($backtopage) || ($cancel && strpos($backtopage, '__ID__'))) { if (empty($id) && (($action != 'add' && $action != 'create') || $cancel)) { $backtopage = $backurlforlist; } else { - $backtopage = dol_buildpath('/hrm/skill_card.php', 1) . '?id=' . ($id > 0 ? $id : '__ID__'); + $backtopage = DOL_URL_ROOT.'/hrm/skill_card.php?id=' . ($id > 0 ? $id : '__ID__'); } } } @@ -166,8 +168,6 @@ if (empty($reshook)) { /* * View - * - * Put here all code to build page */ $form = new Form($db); @@ -186,9 +186,9 @@ if ($action == 'create') { print ''; print ''; print ''; - $backtopage .= "&objecttype=job"; + $backtopage .= (strpos($backtopage, '?') > 0 ? '&' : '?' ) ."objecttype=job"; if ($backtopage) { - print ''; + print ''; } if ($backtopageforcancel) { print ''; @@ -254,12 +254,17 @@ if (($id || $ref) && $action == 'edit') { // SKILLDET print dol_get_fiche_head(array(), ''); + $SkilldetRecords = $object->fetchLines(); + + if (is_array($SkilldetRecords) && count($SkilldetRecords) == 0) { + $object->createSkills(1); + } + if (is_array($SkilldetRecords) && count($SkilldetRecords) > 0) { print ''; foreach ($SkilldetRecords as $sk) { - $MaxNumberSkill = isset($conf->global->HRM_MAXRANK) ? $conf->global->HRM_MAXRANK : Skill::DEFAULT_MAX_RANK_PER_SKILL; - if ($sk->rank > $MaxNumberSkill) { + if ($sk->rankorder > $MaxNumberSkill) { continue; } @@ -286,7 +291,7 @@ if (($id || $ref) && $action == 'edit') { // if (!empty($val['help'])) { // print $form->textwithpicto($langs->trans($val['label']), $langs->trans($val['help'])); // } else { - print $langs->trans($val['label']).' '.$langs->trans('rank').' '.$sk->rank; + print $langs->trans($val['label']).' '.$langs->trans('rank').' '.$sk->rankorder; // } print ''; print ''; if ($objecttype != 'user' && $permissiontoadd) { print ''; diff --git a/htdocs/imports/import.php b/htdocs/imports/import.php index cd416585101..41059a45e92 100644 --- a/htdocs/imports/import.php +++ b/htdocs/imports/import.php @@ -519,7 +519,7 @@ if ($step == 2 && $datatoimport) { $text = $objmodelimport->getDriverDescForKey($key); print ''; print ''; @@ -608,7 +608,7 @@ if ($step == 3 && $datatoimport) { $text = $objmodelimport->getDriverDescForKey($format); print $form->textwithpicto($objmodelimport->getDriverLabelForKey($format), $text); print ''; @@ -735,7 +735,7 @@ if ($step == 3 && $datatoimport) { print ''; print ''; @@ -937,7 +937,7 @@ if ($step == 4 && $datatoimport) { print ''; } -$sortfield = GETPOST("sortfield", 'alpha'); -$sortorder = GETPOST("sortorder", 'alpha'); +$sortfield = GETPOST('sortfield', 'aZ09comma'); +$sortorder = GETPOST('sortorder', 'aZ09comma'); if (!$sortorder) { $sortorder = "ASC"; } diff --git a/htdocs/margin/tabs/productMargins.php b/htdocs/margin/tabs/productMargins.php index 062462be267..93895b01e2f 100644 --- a/htdocs/margin/tabs/productMargins.php +++ b/htdocs/margin/tabs/productMargins.php @@ -43,8 +43,8 @@ if (!empty($user->socid)) { $object = new Product($db); $limit = GETPOST('limit', 'int') ? GETPOST('limit', 'int') : $conf->liste_limit; -$sortfield = GETPOST("sortfield", 'alpha'); -$sortorder = GETPOST("sortorder", 'alpha'); +$sortfield = GETPOST('sortfield', 'aZ09comma'); +$sortorder = GETPOST('sortorder', 'aZ09comma'); $page = GETPOSTISSET('pageplusone') ? (GETPOST('pageplusone') - 1) : GETPOST("page", 'int'); if (empty($page) || $page == -1) { $page = 0; diff --git a/htdocs/margin/tabs/thirdpartyMargins.php b/htdocs/margin/tabs/thirdpartyMargins.php index 43e9c87c6e6..c69c0443e28 100644 --- a/htdocs/margin/tabs/thirdpartyMargins.php +++ b/htdocs/margin/tabs/thirdpartyMargins.php @@ -35,8 +35,8 @@ if (!empty($user->socid)) { } $limit = GETPOST('limit', 'int') ? GETPOST('limit', 'int') : $conf->liste_limit; -$sortfield = GETPOST("sortfield", 'alpha'); -$sortorder = GETPOST("sortorder", 'alpha'); +$sortfield = GETPOST('sortfield', 'aZ09comma'); +$sortorder = GETPOST('sortorder', 'aZ09comma'); $page = GETPOSTISSET('pageplusone') ? (GETPOST('pageplusone') - 1) : GETPOST("page", 'int'); if (empty($page) || $page == -1) { $page = 0; diff --git a/htdocs/master.inc.php b/htdocs/master.inc.php index e04adb4ee86..aa836842e71 100644 --- a/htdocs/master.inc.php +++ b/htdocs/master.inc.php @@ -32,7 +32,8 @@ * This script reads the conf file, init $lang, $db and and empty $user */ -// Declaration of variables. May have been already require by main.inc.php. But may not by scripts. So, here the require_once must be kept. +// Include the conf.php and functions.lib.php and security.lib.php. This defined the constants like DOL_DOCUMENT_ROOT, DOL_DATA_ROOT, DOL_URL_ROOT... +// This file may have been already required by main.inc.php. But may not by scripts. So, here the require_once must be kept. require_once 'filefunc.inc.php'; @@ -161,9 +162,10 @@ if (!defined('NOREQUIREDB')) { } // Now database connexion is known, so we can forget password -//unset($dolibarr_main_db_pass); // We comment this because this constant is used in a lot of pages +//unset($dolibarr_main_db_pass); // We comment this because this constant is used in some other pages unset($conf->db->pass); // This is to avoid password to be shown in memory/swap dump + /* * Object $user */ @@ -171,9 +173,9 @@ if (!defined('NOREQUIREUSER')) { $user = new User($db); } + /* * Load object $conf - * After this, all parameters conf->global->CONSTANTS are loaded */ // By default conf->entity is 1, but we change this if we ask another value. @@ -190,15 +192,12 @@ if (session_id() && !empty($_SESSION["dol_entity"])) { // For public page with MultiCompany module $conf->entity = constant('DOLENTITY'); } - // Sanitize entity if (!is_numeric($conf->entity)) { $conf->entity = 1; } - -//print "We work with data into entity instance number '".$conf->entity."'"; - // Here we read database (llx_const table) and define $conf->global->XXX var. +//print "We work with data into entity instance number '".$conf->entity."'"; $conf->setValues($db); // Create object $mysoc (A thirdparty object that contains properties of companies managed by Dolibarr. diff --git a/htdocs/modulebuilder/index.php b/htdocs/modulebuilder/index.php index 1ab03743747..4e66e655f5e 100644 --- a/htdocs/modulebuilder/index.php +++ b/htdocs/modulebuilder/index.php @@ -742,9 +742,9 @@ if ($dirins && $action == 'addlanguage' && !empty($module)) { // Dir for module $diroflang = dol_buildpath($modulelowercase, 0); - if ($diroflang == $dirread.'/'.$modulelowercase) { + if ($diroflang == $dolibarr_main_document_root.'/'.$modulelowercase) { // This is not a custom module, we force diroflang to htdocs root - $diroflang = $dirread; + $diroflang = $dolibarr_main_document_root; $srcfile = $diroflang.'/langs/en_US/'.$modulelowercase.'.lang'; $destfile = $diroflang.'/langs/'.$newlangcode.'/'.$modulelowercase.'.lang'; @@ -767,7 +767,7 @@ if ($dirins && $action == 'addlanguage' && !empty($module)) { // remove/delete File if ($dirins && $action == 'confirm_removefile' && !empty($module)) { - $relativefilename = dol_sanitizePathName(GETPOST('file', 'none')); + $relativefilename = dol_sanitizePathName(GETPOST('file', 'restricthtml')); if ($relativefilename) { $dirnametodelete = dirname($relativefilename); $filetodelete = $dirins.'/'.$relativefilename; @@ -2067,8 +2067,8 @@ if ($module == 'initmodule') { print $langs->trans("Numero"); print ''; print ''; print ''; print ''; - if ($conf->productbatch->enabled) { + if (!empty($conf->productbatch->enabled)) { print ''; } print ''; - print ''; - if ($object->status == $object::STATUS_VALIDATED) { - // Actions + if ($object->status == $object::STATUS_DRAFT || $object->status == $object::STATUS_VALIDATED) { + // Actions or link to stock movement print ''; - print ''; + } else { + // Actions or link to stock movement + print ''; } + print ''; // Line to add a new line in inventory - if ($object->status == $object::STATUS_VALIDATED) { + if ($object->status == $object::STATUS_DRAFT || $object->status == $object::STATUS_VALIDATED) { print ''; print ''; - if ($conf->productbatch->enabled) { + if (!empty($conf->productbatch->enabled)) { print ''; } print ''; - print ''; // Actions @@ -704,7 +902,7 @@ if ($object->id > 0) { // Request to show lines of inventory (prefilled after start/validate step) $sql = 'SELECT id.rowid, id.datec as date_creation, id.tms as date_modification, id.fk_inventory, id.fk_warehouse,'; - $sql .= ' id.fk_product, id.batch, id.qty_stock, id.qty_view, id.qty_regulated'; + $sql .= ' id.fk_product, id.batch, id.qty_stock, id.qty_view, id.qty_regulated, id.fk_movement'; $sql .= ' FROM '.MAIN_DB_PREFIX.'inventorydet as id'; $sql .= ' WHERE id.fk_inventory = '.((int) $object->id); @@ -731,15 +929,17 @@ if ($object->id > 0) { $cacheOfWarehouses[$warehouse_static->id] = $warehouse_static; } + // Load real stock we have now + $option = ''; if (isset($cacheOfProducts[$obj->fk_product])) { $product_static = $cacheOfProducts[$obj->fk_product]; } else { $product_static = new Product($db); $result = $product_static->fetch($obj->fk_product, '', '', '', 1, 1, 1); - $option = 'nobatch'; + //$option = 'nobatch'; $option .= ',novirtual'; - $product_static->load_stock($option); // Load stock_reel + stock_warehouse. This can also call load_virtual_stock() + $product_static->load_stock($option); // Load stock_reel + stock_warehouse. $cacheOfProducts[$product_static->id] = $product_static; } @@ -752,35 +952,59 @@ if ($object->id > 0) { print $product_static->getNomUrl(1).' - '.$product_static->label; print ''; - if ($conf->productbatch->enabled) { + if (!empty($conf->productbatch->enabled)) { print ''; } - // Expected quantity - print ''; // Real quantity - print ''; + + // Picto delete line print ''; $qty_tmp = price2num(GETPOST("id_".$obj->rowid."_input_tmp", 'MS')) >= 0 ? GETPOST("id_".$obj->rowid."_input_tmp") : $qty_view; print ''; + print ''; } else { - print $obj->qty_view; + print ''; + print ''; } print ''; @@ -801,10 +1025,13 @@ if ($object->id > 0) { print ''; + // Call method to disable the button if no qty entered yet for inventory + if ($object->status != $object::STATUS_VALIDATED || !$hasinput) { - print ''; diff --git a/htdocs/product/list.php b/htdocs/product/list.php index 14d9b320e8e..778565dd2cb 100644 --- a/htdocs/product/list.php +++ b/htdocs/product/list.php @@ -65,7 +65,12 @@ $search_barcode = GETPOST("search_barcode", 'alpha'); $search_label = GETPOST("search_label", 'alpha'); $search_type = GETPOST("search_type", 'int'); $search_vatrate = GETPOST("search_vatrate", 'alpha'); -$searchCategoryProductOperator = (GETPOST('search_category_product_operator', 'int') ? GETPOST('search_category_product_operator', 'int') : 0); +$searchCategoryProductOperator = 0; +if (GETPOSTISSET('formfilteraction')) { + $searchCategoryProductOperator = GETPOST('search_category_product_operator', 'int'); +} elseif (!empty($conf->global->MAIN_SEARCH_CAT_OR_BY_DEFAULT)) { + $searchCategoryProductOperator = $conf->global->MAIN_SEARCH_CAT_OR_BY_DEFAULT; +} $searchCategoryProductList = GETPOST('search_category_product_list', 'array'); $search_tosell = GETPOST("search_tosell", 'int'); $search_tobuy = GETPOST("search_tobuy", 'int'); @@ -94,8 +99,8 @@ if (!empty($conf->variants->enabled) && !empty($conf->global->PRODUIT_ATTRIBUTES $diroutputmassaction = $conf->product->dir_output.'/temp/massgeneration/'.$user->id; $limit = GETPOST('limit', 'int') ?GETPOST('limit', 'int') : $conf->liste_limit; -$sortfield = GETPOST("sortfield", 'alpha'); -$sortorder = GETPOST("sortorder", 'alpha'); +$sortfield = GETPOST('sortfield', 'aZ09comma'); +$sortorder = GETPOST('sortorder', 'aZ09comma'); $page = GETPOSTISSET('pageplusone') ? (GETPOST('pageplusone') - 1) : GETPOST("page", 'int'); if (empty($page) || $page < 0 || GETPOST('button_search', 'alpha') || GETPOST('button_removefilter', 'alpha')) { $page = 0; @@ -355,6 +360,25 @@ if (empty($reshook)) { $permissiontoadd = $user->rights->{$rightskey}->creer; $uploaddir = $conf->product->dir_output; include DOL_DOCUMENT_ROOT.'/core/actions_massactions.inc.php'; + + if (!$error && $massaction == 'switchonsalestatus' && $permissiontoadd) { + $product = new Product($db); + foreach ($toselect as $toselectid) { + $result = $product->fetch($toselectid); + if ($result > 0 && $product->id > 0) { + $product->setStatut($product->status ? 0 : 1, null, 'product', 'PRODUCT_MODIFY', 'tosell'); + } + } + } + if (!$error && $massaction == 'switchonpurchasestatus' && $permissiontoadd) { + $product = new Product($db); + foreach ($toselect as $toselectid) { + $result = $product->fetch($toselectid); + if ($result > 0 && $product->id > 0) { + $product->setStatut($product->status_buy ? 0 : 1, null, 'product', 'PRODUCT_MODIFY', 'tobuy'); + } + } + } } @@ -710,6 +734,8 @@ if ($resql) { $arrayofmassactions['predelete'] = img_picto('', 'delete', 'class="pictofixedwidth"').$langs->trans("Delete"); } if ($user->rights->{$rightskey}->creer) { + $arrayofmassactions['switchonsalestatus'] = img_picto('', 'stop-circle', 'class="pictofixedwidth"').$langs->trans("SwitchOnSaleStatus"); + $arrayofmassactions['switchonpurchasestatus'] = img_picto('', 'stop-circle', 'class="pictofixedwidth"').$langs->trans("SwitchOnPurchaseStatus"); $arrayofmassactions['preaffecttag'] = img_picto('', 'category', 'class="pictofixedwidth"').$langs->trans("AffectTag"); } if (in_array($massaction, array('presend', 'predelete','preaffecttag'))) { diff --git a/htdocs/product/popuprop.php b/htdocs/product/popuprop.php index 32a2cd08cc9..707f95d2202 100644 --- a/htdocs/product/popuprop.php +++ b/htdocs/product/popuprop.php @@ -30,7 +30,7 @@ require '../main.inc.php'; require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php'; // Load translation files required by the page -$langs->loadLangs(array('commande', 'propal', 'bills', 'other')); +$langs->loadLangs(array('commande', 'propal', 'bills', 'other', 'products')); $backtopage = GETPOST('backtopage', 'alpha'); $backtopageforcancel = GETPOST('backtopageforcancel', 'alpha'); @@ -44,8 +44,8 @@ if (!empty($user->socid)) { } $limit = GETPOST('limit', 'int') ?GETPOST('limit', 'int') : $conf->liste_limit; -$sortfield = GETPOST("sortfield", 'alpha'); -$sortorder = GETPOST("sortorder", 'alpha'); +$sortfield = GETPOST('sortfield', 'aZ09comma'); +$sortorder = GETPOST('sortorder', 'aZ09comma'); $page = GETPOSTISSET('pageplusone') ? (GETPOST('pageplusone') - 1) : GETPOST("page", 'int'); if (empty($page) || $page == -1) { $page = 0; @@ -212,7 +212,7 @@ if ($mode && $mode != '-1') { $sql = "SELECT label"; $sql .= " FROM ".MAIN_DB_PREFIX."product_lang"; $sql .= " WHERE fk_product = ".((int) $prodid); - $sql .= " AND lang='".$db->escape($langs->getDefaultLang())."'"; + $sql .= " AND lang = '".$db->escape($langs->getDefaultLang())."'"; $sql .= " LIMIT 1"; $resultp = $db->query($sql); diff --git a/htdocs/product/price.php b/htdocs/product/price.php index 773a097fed8..d21574e696e 100644 --- a/htdocs/product/price.php +++ b/htdocs/product/price.php @@ -127,7 +127,7 @@ if (empty($reshook)) { $reg = array(); $vatratecode = ''; if (preg_match('/\((.*)\)/', $tva_tx_txt, $reg)) { - $vat_src_code = $reg[1]; + $vatratecode = $reg[1]; $tva_tx = preg_replace('/\s*\(.*\)/', '', $tva_tx_txt); // Remove code into vatrate. } @@ -143,15 +143,35 @@ if (empty($reshook)) { $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c"; $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($mysoc->country_code)."'"; $sql .= " AND t.taux = ".((float) $tva_tx)." AND t.active = 1"; - $sql .= " AND t.code ='".$db->escape($vatratecode)."'"; + $sql .= " AND t.code = '".$db->escape($vatratecode)."'"; $resql = $db->query($sql); if ($resql) { $obj = $db->fetch_object($resql); - $npr = $obj->recuperableonly; - $localtax1 = $obj->localtax1; - $localtax2 = $obj->localtax2; - $localtax1_type = $obj->localtax1_type; - $localtax2_type = $obj->localtax2_type; + if ($obj) { + $npr = $obj->recuperableonly; + $localtax1 = $obj->localtax1; + $localtax2 = $obj->localtax2; + $localtax1_type = $obj->localtax1_type; + $localtax2_type = $obj->localtax2_type; + } + } + } else { + // Get record with empty code + $sql = "SELECT t.rowid, t.code, t.recuperableonly, t.localtax1, t.localtax2, t.localtax1_type, t.localtax2_type"; + $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c"; + $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($mysoc->country_code)."'"; + $sql .= " AND t.taux = ".((float) $tva_tx)." AND t.active = 1"; + $sql .= " AND t.code = ''"; + $resql = $db->query($sql); + if ($resql) { + $obj = $db->fetch_object($resql); + if ($obj) { + $npr = $obj->recuperableonly; + $localtax1 = $obj->localtax1; + $localtax2 = $obj->localtax2; + $localtax1_type = $obj->localtax1_type; + $localtax2_type = $obj->localtax2_type; + } } } @@ -172,6 +192,8 @@ if (empty($reshook)) { } if ($error) { + // Force the update of the price of the product to 0 if error + //$localtaxarray=array('0'=>$localtax1_type,'1'=>$localtax1,'2'=>$localtax2_type,'3'=>$localtax2); $localtaxarray = array(); // We do not store localtaxes into product, we will use instead the "vat code" to retrieve them. $object->updatePrice(0, $object->price_base_type, $user, $tva_tx, '', 0, $npr, 0, 0, $localtaxarray, $vatratecode); @@ -256,11 +278,37 @@ if (empty($reshook)) { $resql = $db->query($sql); if ($resql) { $obj = $db->fetch_object($resql); - $npr = $obj->recuperableonly; - $localtax1 = $obj->localtax1; - $localtax2 = $obj->localtax2; - $localtax1_type = $obj->localtax1_type; - $localtax2_type = $obj->localtax2_type; + if ($obj) { + $npr = $obj->recuperableonly; + $localtax1 = $obj->localtax1; + $localtax2 = $obj->localtax2; + $localtax1_type = $obj->localtax1_type; + $localtax2_type = $obj->localtax2_type; + } + + // If spain, we don't use the localtax found into tax record in database with same code, but using the get_localtax rule. + if (in_array($mysoc->country_code, array('ES'))) { + $localtax1 = get_localtax($tva_tx, 1); + $localtax2 = get_localtax($tva_tx, 2); + } + } + } else { + // Get record with empty code + $sql = "SELECT t.rowid, t.code, t.recuperableonly, t.localtax1, t.localtax2, t.localtax1_type, t.localtax2_type"; + $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c"; + $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($mysoc->country_code)."'"; + $sql .= " AND t.taux = ".((float) $tva_tx)." AND t.active = 1"; + $sql .= " AND t.code = ''"; + $resql = $db->query($sql); + if ($resql) { + $obj = $db->fetch_object($resql); + if ($obj) { + $npr = $obj->recuperableonly; + $localtax1 = $obj->localtax1; + $localtax2 = $obj->localtax2; + $localtax1_type = $obj->localtax1_type; + $localtax2_type = $obj->localtax2_type; + } } } @@ -309,19 +357,40 @@ if (empty($reshook)) { $resql = $db->query($sql); if ($resql) { $obj = $db->fetch_object($resql); - $npr = $obj->recuperableonly; - $localtax1 = $obj->localtax1; - $localtax2 = $obj->localtax2; - $localtax1_type = $obj->localtax1_type; - $localtax2_type = $obj->localtax2_type; + if ($obj) { + $npr = $obj->recuperableonly; + $localtax1 = $obj->localtax1; + $localtax2 = $obj->localtax2; + $localtax1_type = $obj->localtax1_type; + $localtax2_type = $obj->localtax2_type; + } - // If spain, we don't use the localtax found into tax record in database with same code, but using the get_localtax rule + // If spain, we don't use the localtax found into tax record in database with same code, but using the get_localtax rule. if (in_array($mysoc->country_code, array('ES'))) { $localtax1 = get_localtax($tva_tx, 1); $localtax2 = get_localtax($tva_tx, 2); } } + } else { + // Get record with empty code + $sql = "SELECT t.rowid, t.code, t.recuperableonly, t.localtax1, t.localtax2, t.localtax1_type, t.localtax2_type"; + $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c"; + $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($mysoc->country_code)."'"; + $sql .= " AND t.taux = ".((float) $tva_tx)." AND t.active = 1"; + $sql .= " AND t.code = ''"; + $resql = $db->query($sql); + if ($resql) { + $obj = $db->fetch_object($resql); + if ($obj) { + $npr = $obj->recuperableonly; + $localtax1 = $obj->localtax1; + $localtax2 = $obj->localtax2; + $localtax1_type = $obj->localtax1_type; + $localtax2_type = $obj->localtax2_type; + } + } } + $pricestoupdate[0] = array( 'price' => $newprice, 'price_min' => $newprice_min, @@ -532,11 +601,37 @@ if (empty($reshook)) { $resql = $db->query($sql); if ($resql) { $obj = $db->fetch_object($resql); - $npr = $obj->recuperableonly; - $localtax1 = $obj->localtax1; - $localtax2 = $obj->localtax2; - $localtax1_type = $obj->localtax1_type; - $localtax2_type = $obj->localtax2_type; + if ($obj) { + $npr = $obj->recuperableonly; + $localtax1 = $obj->localtax1; + $localtax2 = $obj->localtax2; + $localtax1_type = $obj->localtax1_type; + $localtax2_type = $obj->localtax2_type; + } + + // If spain, we don't use the localtax found into tax record in database with same code, but using the get_localtax rule. + if (in_array($mysoc->country_code, array('ES'))) { + $localtax1 = get_localtax($tva_tx, 1); + $localtax2 = get_localtax($tva_tx, 2); + } + } + } else { + // Get record with empty code + $sql = "SELECT t.rowid, t.code, t.recuperableonly, t.localtax1, t.localtax2, t.localtax1_type, t.localtax2_type"; + $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c"; + $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($mysoc->country_code)."'"; + $sql .= " AND t.taux = ".((float) $tva_tx)." AND t.active = 1"; + $sql .= " AND t.code = ''"; + $resql = $db->query($sql); + if ($resql) { + $obj = $db->fetch_object($resql); + if ($obj) { + $npr = $obj->recuperableonly; + $localtax1 = $obj->localtax1; + $localtax2 = $obj->localtax2; + $localtax1_type = $obj->localtax1_type; + $localtax2_type = $obj->localtax2_type; + } } } @@ -625,11 +720,37 @@ if (empty($reshook)) { $resql = $db->query($sql); if ($resql) { $obj = $db->fetch_object($resql); - $npr = $obj->recuperableonly; - $localtax1 = $obj->localtax1; - $localtax2 = $obj->localtax2; - $localtax1_type = $obj->localtax1_type; - $localtax2_type = $obj->localtax2_type; + if ($obj) { + $npr = $obj->recuperableonly; + $localtax1 = $obj->localtax1; + $localtax2 = $obj->localtax2; + $localtax1_type = $obj->localtax1_type; + $localtax2_type = $obj->localtax2_type; + } + + // If spain, we don't use the localtax found into tax record in database with same code, but using the get_localtax rule. + if (in_array($mysoc->country_code, array('ES'))) { + $localtax1 = get_localtax($tva_tx, 1); + $localtax2 = get_localtax($tva_tx, 2); + } + } + } else { + // Get record with empty code + $sql = "SELECT t.rowid, t.code, t.recuperableonly, t.localtax1, t.localtax2, t.localtax1_type, t.localtax2_type"; + $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c"; + $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($mysoc->country_code)."'"; + $sql .= " AND t.taux = ".((float) $tva_tx)." AND t.active = 1"; + $sql .= " AND t.code = ''"; + $resql = $db->query($sql); + if ($resql) { + $obj = $db->fetch_object($resql); + if ($obj) { + $npr = $obj->recuperableonly; + $localtax1 = $obj->localtax1; + $localtax2 = $obj->localtax2; + $localtax1_type = $obj->localtax1_type; + $localtax2_type = $obj->localtax2_type; + } } } @@ -733,9 +854,9 @@ if (!empty($conf->global->PRODUIT_MULTIPRICES) || !empty($conf->global->PRODUIT_ print ''; print ''; if ($object->multiprices_base_type [$i] == 'TTC') { - print ''; + print ' '.$langs->trans($object->multiprices_base_type [$i]).''; } else { - print ' '.$langs->trans($object->price_base_type).''; + print ' '.$langs->trans($object->price_base_type).''; } // Prix min @@ -1669,8 +1791,8 @@ if (!empty($conf->global->PRODUIT_CUSTOMER_PRICES)) { $prodcustprice = new Productcustomerprice($db); $limit = GETPOST('limit', 'int') ? GETPOST('limit', 'int') : $conf->liste_limit; - $sortfield = GETPOST("sortfield", 'alpha'); - $sortorder = GETPOST("sortorder", 'alpha'); + $sortfield = GETPOST('sortfield', 'aZ09comma'); + $sortorder = GETPOST('sortorder', 'aZ09comma'); $page = (GETPOST("page", 'int') ?GETPOST("page", 'int') : 0); if (empty($page) || $page == -1) { $page = 0; diff --git a/htdocs/product/reassort.php b/htdocs/product/reassort.php index b0a1ce87ddd..62bfd626f82 100644 --- a/htdocs/product/reassort.php +++ b/htdocs/product/reassort.php @@ -48,8 +48,8 @@ $tobuy = GETPOST("tobuy"); $fourn_id = GETPOST("fourn_id", 'int'); $sbarcode = GETPOST("sbarcode", 'int'); -$sortfield = GETPOST("sortfield", 'alpha'); -$sortorder = GETPOST("sortorder", 'alpha'); +$sortfield = GETPOST('sortfield', 'aZ09comma'); +$sortorder = GETPOST('sortorder', 'aZ09comma'); $page = GETPOSTISSET('pageplusone') ? (GETPOST('pageplusone') - 1) : GETPOST("page", 'int'); if (empty($page) || $page < 0) { $page = 0; diff --git a/htdocs/product/reassortlot.php b/htdocs/product/reassortlot.php index e75d1504267..2bd62b2ce22 100644 --- a/htdocs/product/reassortlot.php +++ b/htdocs/product/reassortlot.php @@ -52,8 +52,8 @@ $tobuy = GETPOST("tobuy"); $fourn_id = GETPOST("fourn_id", 'int'); $sbarcode = GETPOST("sbarcode", 'int'); -$sortfield = GETPOST("sortfield", 'alpha'); -$sortorder = GETPOST("sortorder", 'alpha'); +$sortfield = GETPOST('sortfield', 'aZ09comma'); +$sortorder = GETPOST('sortorder', 'aZ09comma'); $page = GETPOSTISSET('pageplusone') ? (GETPOST('pageplusone') - 1) : GETPOST("page", 'int'); if (empty($page) || $page < 0) { $page = 0; diff --git a/htdocs/product/stats/bom.php b/htdocs/product/stats/bom.php index ef4923e89be..b7c4321dfd1 100644 --- a/htdocs/product/stats/bom.php +++ b/htdocs/product/stats/bom.php @@ -50,8 +50,8 @@ $option = ''; // Load variable for pagination $limit = GETPOST('limit', 'int') ?GETPOST('limit', 'int') : $conf->liste_limit; -$sortfield = GETPOST("sortfield", 'alpha'); -$sortorder = GETPOST("sortorder", 'alpha'); +$sortfield = GETPOST('sortfield', 'aZ09comma'); +$sortorder = GETPOST('sortorder', 'aZ09comma'); $page = GETPOSTISSET('pageplusone') ? (GETPOST('pageplusone') - 1) : GETPOST("page", 'int'); if (empty($page) || $page == -1) { $page = 0; @@ -129,11 +129,11 @@ if ($id > 0 || !empty($ref)) { //Calcul total qty and amount for global if full scan list $total_qty_toconsume = 0; $total_qty_toproduce = 0; + $product_cache=array(); $bom_data_result = array(); - //Qauntity to produce - $sql = "SELECT b.rowid as rowid, b.ref, b.status, b.date_valid,"; + $sql = "SELECT b.rowid as rowid, b.ref, b.status, b.date_valid, b.fk_product,"; $sql .= " b.qty as qty_toproduce"; $sql .= " FROM ".MAIN_DB_PREFIX."bom_bom as b"; $sql .= " WHERE "; @@ -166,7 +166,20 @@ if ($id > 0 || !empty($ref)) { $objp = $db->fetch_object($result); $bomtmp->id = $objp->rowid; $bomtmp->ref = $objp->ref; + $product = new Product($db); + if (!empty($objp->fk_product)) { + if (!array_key_exists($product->id, $product_cache)) { + $resultFetch = $product->fetch($objp->fk_product); + if ($resultFetch < 0) { + setEventMessages($product->error, $product->errors, 'errors'); + } else { + $product_cache[$product->id] = $product; + } + } + } + $bomtmp->fk_product = $objp->fk_product; $bom_data_result[$objp->rowid]['link'] = $bomtmp->getNomUrl(1, 'production'); + $bom_data_result[$objp->rowid]['product'] = (array_key_exists($objp->fk_product, $product_cache)? $product_cache[$objp->fk_product]->getNomUrl(1): ''); $bom_data_result[$objp->rowid]['qty_toproduce'] += ($objp->qty_toproduce > 0 ? $objp->qty_toproduce : 0); $bom_data_result[$objp->rowid]['qty_toconsume'] = 0; $bom_data_result[$objp->rowid]['date_valid'] = dol_print_date($db->jdate($objp->date_valid), 'dayhour'); @@ -180,7 +193,7 @@ if ($id > 0 || !empty($ref)) { $db->free($result); //Qauntity to consume - $sql = "SELECT b.rowid as rowid, b.ref, b.status, b.date_valid,"; + $sql = "SELECT b.rowid as rowid, b.ref, b.status, b.date_valid, b.fk_product,"; $sql .= " SUM(bl.qty) as qty_toconsume"; $sql .= " FROM ".MAIN_DB_PREFIX."bom_bom as b"; $sql .= " INNER JOIN ".MAIN_DB_PREFIX."bom_bomline as bl ON bl.fk_bom=b.rowid"; @@ -214,9 +227,22 @@ if ($id > 0 || !empty($ref)) { $objp = $db->fetch_object($result); $bomtmp->id = $objp->rowid; $bomtmp->ref = $objp->ref; + $product = new Product($db); + if (!empty($objp->fk_product)) { + if (!array_key_exists($product->id, $product_cache)) { + $resultFetch = $product->fetch($objp->fk_product); + if ($resultFetch < 0) { + setEventMessages($product->error, $product->errors, 'errors'); + } else { + $product_cache[$product->id] = $product; + } + } + } + $bomtmp->fk_product = $objp->fk_product; if (!array_key_exists($objp->rowid, $bom_data_result)) { $bom_data_result[$objp->rowid]['link'] = $bomtmp->getNomUrl(1, 'production'); + $bom_data_result[$objp->rowid]['product'] = (array_key_exists($objp->fk_product, $product_cache)? $product_cache[$objp->fk_product]->getNomUrl(1): ''); $bom_data_result[$objp->rowid]['qty_toproduce'] = 0; $bom_data_result[$objp->rowid]['qty_toconsume'] += ($objp->qty_toconsume > 0 ? $objp->qty_toconsume : 0); $bom_data_result[$objp->rowid]['date_valid'] = dol_print_date($db->jdate($objp->date_valid), 'dayhour'); @@ -232,7 +258,6 @@ if ($id > 0 || !empty($ref)) { } $db->free($result); - if ($limit > 0 && $limit != $conf->liste_limit) { $option .= '&limit='.urlencode($limit); } @@ -265,6 +290,7 @@ if ($id > 0 || !empty($ref)) { print ''; print_liste_field_titre("Ref", $_SERVER["PHP_SELF"], "b.rowid", "", "&id=".$product->id, '', $sortfield, $sortorder); + print_liste_field_titre("Product", $_SERVER["PHP_SELF"], "b.fk_product", "", "&id=".$product->id, '', $sortfield, $sortorder); print_liste_field_titre("Date", $_SERVER["PHP_SELF"], "b.date_valid", "", "&id=".$product->id, 'align="center"', $sortfield, $sortorder); print_liste_field_titre("RowMaterial", $_SERVER["PHP_SELF"], "", "", "&id=".$product->id, '', $sortfield, $sortorder, 'center '); print_liste_field_titre("Finished", $_SERVER["PHP_SELF"], "", "", "&id=".$product->id, '', $sortfield, $sortorder, 'center '); @@ -277,6 +303,9 @@ if ($id > 0 || !empty($ref)) { print '\n"; + print '\n"; print ""; print ''; diff --git a/htdocs/product/stats/card.php b/htdocs/product/stats/card.php index d2d9f289fd1..f45a851ecad 100644 --- a/htdocs/product/stats/card.php +++ b/htdocs/product/stats/card.php @@ -192,20 +192,20 @@ if ($result || !($id > 0)) { // Type print ''; // Product print ''; // Tag if ($conf->categorie->enabled) { print ''; } @@ -276,7 +276,7 @@ if ($result || !($id > 0)) { print '
    '; // Generation of graphs - $dir = (!empty($conf->product->multidir_temp[$object->entity]) ? $conf->product->multidir_temp[$object->entity] : $conf->service->multidir_temp[$object->entity]); + $dir = (!empty($conf->product->multidir_temp[$conf->entity]) ? $conf->product->multidir_temp[$conf->entity] : $conf->service->multidir_temp[$conf->entity]); if ($object->id > 0) { // We are on statistics for a dedicated product if (!file_exists($dir.'/'.$object->id)) { if (dol_mkdir($dir.'/'.$object->id) < 0) { @@ -465,7 +465,9 @@ if ($result || !($id > 0)) { $linktoregenerate .= img_picto($langs->trans("ReCalculate").' ('.$dategenerated.')', 'refresh'); $linktoregenerate .= ''; + // Show graph + print '
    '; print '
    '; @@ -397,7 +402,7 @@ if ($object->id > 0 && (empty($action) || ($action != 'edit' && $action != 'crea // Object card // ------------------------------------------------------------ - $linkback = '' . $langs->trans("BackToList") . ''; + $linkback = '' . $langs->trans("BackToList") . ''; $morehtmlref = '
    '; @@ -691,8 +696,7 @@ if ($action != "create" && $action != "edit") { break; // Should not happen } - $MaxNumberSkill = isset($conf->global->HRM_MAXRANK) ? $conf->global->HRM_MAXRANK : Skill::DEFAULT_MAX_RANK_PER_SKILL; - if ($obj->rank > $MaxNumberSkill) { + if ($obj->rankorder > $MaxNumberSkill) { continue; } @@ -840,13 +844,12 @@ if ($action != "create" && $action != "edit") { $MAXEVENT = 10; - $morehtmlright = ''; - $morehtmlright .= $langs->trans("SeeAll"); - $morehtmlright .= ''; + $morehtmlcenter = dolGetButtonTitle($langs->trans('SeeAll'), '', 'fa fa-list-alt imgforviewmode', DOL_URL_ROOT.'/hrm/skill_agenda.php?id='.$object->id); + // List of actions on element include_once DOL_DOCUMENT_ROOT . '/core/class/html.formactions.class.php'; $formactions = new FormActions($db); - $somethingshown = $formactions->showactions($object, $object->element . '@' . $object->module, (is_object($object->thirdparty) ? $object->thirdparty->id : 0), 1, '', $MAXEVENT, '', $morehtmlright); + $somethingshown = $formactions->showactions($object, $object->element . '@' . $object->module, (is_object($object->thirdparty) ? $object->thirdparty->id : 0), 1, '', $MAXEVENT, '', $morehtmlcenter); print '
    '; } diff --git a/htdocs/hrm/skill_contact.php b/htdocs/hrm/skill_contact.php index f8ea2a04a9f..d8fecd74610 100644 --- a/htdocs/hrm/skill_contact.php +++ b/htdocs/hrm/skill_contact.php @@ -129,7 +129,7 @@ if ($object->id) { print dol_get_fiche_head($head, 'contact', '', -1, $object->picto); - $linkback = ''.$langs->trans("BackToList").''; + $linkback = ''.$langs->trans("BackToList").''; $morehtmlref = '
    '; /* diff --git a/htdocs/hrm/skill_document.php b/htdocs/hrm/skill_document.php index 77f3325de15..f305ecd1e2e 100644 --- a/htdocs/hrm/skill_document.php +++ b/htdocs/hrm/skill_document.php @@ -47,8 +47,8 @@ $ref = GETPOST('ref', 'alpha'); // Get parameters $limit = GETPOST('limit', 'int') ? GETPOST('limit', 'int') : $conf->liste_limit; -$sortfield = GETPOST("sortfield", 'alpha'); -$sortorder = GETPOST("sortorder", 'alpha'); +$sortfield = GETPOST('sortfield', 'aZ09comma'); +$sortorder = GETPOST('sortorder', 'aZ09comma'); $page = GETPOSTISSET('pageplusone') ? (GETPOST('pageplusone') - 1) : GETPOST("page", 'int'); if (empty($page) || $page == -1) { $page = 0; @@ -127,7 +127,7 @@ if ($object->id) { // Object card // ------------------------------------------------------------ - $linkback = ''.$langs->trans("BackToList").''; + $linkback = ''.$langs->trans("BackToList").''; $morehtmlref = '
    '; $morehtmlref.= $object->label; diff --git a/htdocs/hrm/skill_note.php b/htdocs/hrm/skill_note.php index 1510f746e5e..45a0690f176 100644 --- a/htdocs/hrm/skill_note.php +++ b/htdocs/hrm/skill_note.php @@ -100,7 +100,7 @@ if ($id > 0 || !empty($ref)) { // Object card // ------------------------------------------------------------ - $linkback = ''.$langs->trans("BackToList").''; + $linkback = ''.$langs->trans("BackToList").''; $morehtmlref = '
    '; $morehtmlref.= $object->label; diff --git a/htdocs/hrm/skill_tab.php b/htdocs/hrm/skill_tab.php index 9a9ea39e31d..1ed9e370839 100644 --- a/htdocs/hrm/skill_tab.php +++ b/htdocs/hrm/skill_tab.php @@ -93,14 +93,14 @@ if ($reshook < 0) { if (empty($reshook)) { $error = 0; - $backurlforlist = dol_buildpath('/hrm/skill_list.php', 1); + $backurlforlist = DOL_URL_ROOT.'/hrm/skill_list.php'; if (empty($backtopage) || ($cancel && empty($id))) { if (empty($backtopage) || ($cancel && strpos($backtopage, '__ID__'))) { if (empty($id) && (($action != 'add' && $action != 'create') || $cancel)) { $backtopage = $backurlforlist; } else { - $backtopage = dol_buildpath('/hrm/skill_list.php', 1) . '?id=' . ($id > 0 ? $id : '__ID__'); + $backtopage = DOL_URL_ROOT.'/hrm/skill_list.php?id=' . ($id > 0 ? $id : '__ID__'); } } } @@ -130,7 +130,7 @@ if (empty($reshook)) { $TSkills = $skill->fetchAll('ASC', 't.rowid', 0, 0, array('customsql' => 'fk_object=' . ((int) $id) . " AND objecttype='" . $db->escape($objecttype) . "' AND fk_skill = " . ((int) $skillId))); if (is_array($TSkills) && !empty($TSkills)) { foreach ($TSkills as $tmpObj) { - $tmpObj->rank = $rank; + $tmpObj->rankorder = $rank; $tmpObj->update($user); } } @@ -305,7 +305,7 @@ if ($object->id > 0 && (empty($action) || ($action != 'edit' && $action != 'crea print '
    '; print $sk->description; print ''; - print displayRankInfos($skillElement->rank, $skillElement->fk_skill, 'TNote', $objecttype == 'job' && $permissiontoadd ? 'edit' : 'view'); + print displayRankInfos($skillElement->rankorder, $skillElement->fk_skill, 'TNote', $objecttype == 'job' && $permissiontoadd ? 'edit' : 'view'); print ''.$form->textwithpicto($objmodelimport->getDriverLabelForKey($key), $text).''; - print img_picto('', 'download', 'class="paddingright opacitymedium"').''.$langs->trans("DownloadEmptyExample"); + print img_picto('', 'download', 'class="paddingright opacitymedium"').''.$langs->trans("DownloadEmptyExample"); print ''; print ' ('.$langs->trans("StarAreMandatory").')'; print ''; - print img_picto('', 'download', 'class="paddingright opacitymedium"').''.$langs->trans("DownloadEmptyExample"); + print img_picto('', 'download', 'class="paddingright opacitymedium"').''.$langs->trans("DownloadEmptyExample"); print ''; print ' ('.$langs->trans("StarAreMandatory").')'; print '
    '; print img_mime($file, '', 'pictofixedwidth'); - print ''; + print ''; print $file; print ''; print ''; $modulepart = 'import'; $relativepath = GETPOST('filetoimport'); - print ''; + print ''; print img_mime($file, '', 'pictofixedwidth'); print $filetoimport; print ''; @@ -1178,7 +1178,7 @@ if ($step == 4 && $datatoimport) { if ($conf->use_javascript_ajax) { - print ''."\n"; + $urlforjs = dol_buildpath($jsfile, 1); + if ($urlforjs && $urlforjs != '/') { + print ''."\n".''."\n"; + } else { + dol_syslog("Warning: module ".$modjs." declared a js path file for a file we can't find.", LOG_WARNING); + } } } } @@ -1748,6 +1767,14 @@ function top_htmlhead($head, $title = '', $disablejs = 0, $disablehead = 0, $arr } } + //If you want to load custom javascript file from your selected theme directory + if (!empty($conf->global->ALLOW_THEME_JS)) { + $theme_js = dol_buildpath('/theme/'.$conf->theme.'/'.$conf->theme.'.js', 0); + if (file_exists($theme_js)) { + print ''."\n"; + } + } + if (!empty($head)) { print $head."\n"; } @@ -1847,7 +1874,7 @@ function top_menu($head, $title = '', $target = '', $disablejs = 0, $disablehead if ($_SESSION["dol_authmode"] != 'forceuser' && $_SESSION["dol_authmode"] != 'http') { $logouthtmltext .= $langs->trans("Logout").'
    '; - $logouttext .= ''; + $logouttext .= ''; $logouttext .= img_picto($langs->trans('Logout'), 'sign-out', '', false, 0, 0, '', 'atoplogin'); $logouttext .= ''; } else { @@ -1894,7 +1921,7 @@ function top_menu($head, $title = '', $target = '', $disablejs = 0, $disablehead } } $qs .= (($qs && $morequerystring) ? '&' : '').$morequerystring; - $text = ''; + $text = ''; //$text.= img_picto(":".$langs->trans("PrintContentArea"), 'printer_top.png', 'class="printer"'); $text .= ''; $text .= ''; @@ -1925,16 +1952,16 @@ function top_menu($head, $title = '', $target = '', $disablejs = 0, $disablehead // Link to help pages if ($helpbaseurl && $helppage) { $text = ''; - $title = $langs->trans($mode == 'wiki' ? 'GoToWikiHelpPage' : 'GoToHelpPage').'...'; + $title = $langs->trans($mode == 'wiki' ? 'GoToWikiHelpPage' : 'GoToHelpPage').', '; if ($mode == 'wiki') { - $title .= '
    '.$langs->trans("PageWiki").' '.dol_escape_htmltag('"'.strtr($helppage, '_', ' ').'"'); + $title .= '
    '.img_picto('', 'globe', 'class="pictofixedwidth"').$langs->trans("PageWiki").' '.dol_escape_htmltag('"'.strtr($helppage, '_', ' ').'"'); if ($helppresent) { $title .= ' ('.$langs->trans("DedicatedPageAvailable").')'; } else { $title .= ' ('.$langs->trans("HomePage").')'; } } - $text .= ''; $text .= ''; - $text .= ''; + $text .= ''; $text .= ''; $toprightmenu .= $form->textwithtooltip('', $title, 2, 1, $text, 'login_block_elem', 2); } @@ -2013,7 +2040,7 @@ function top_menu($head, $title = '', $target = '', $disablejs = 0, $disablehead * Build the tooltip on user login * * @param int $hideloginname Hide login name. Show only the image. - * @param string $urllogout URL for logout + * @param string $urllogout URL for logout (Will use DOL_URL_ROOT.'/user/logout.php?token=...' if empty) * @return string HTML content */ function top_menu_user($hideloginname = 0, $urllogout = '') @@ -2129,7 +2156,7 @@ function top_menu_user($hideloginname = 0, $urllogout = '') } if (empty($urllogout)) { - $urllogout = DOL_URL_ROOT.'/user/logout.php'; + $urllogout = DOL_URL_ROOT.'/user/logout.php?token='.newToken(); } $logoutLink = ' '.$langs->trans("Logout").''; $profilLink = ' '.$langs->trans("Card").''; @@ -2817,7 +2844,7 @@ function left_menu($menu_array_before, $helppagename = '', $notused = '', $menu_ } print '
    '; if ($doliurl) { - print ''; + print ''; } else { print ''; } @@ -2885,7 +2912,7 @@ function left_menu($menu_array_before, $helppagename = '', $notused = '', $menu_ } print ''; } @@ -2922,7 +2949,7 @@ function left_menu($menu_array_before, $helppagename = '', $notused = '', $menu_ */ function main_area($title = '') { - global $conf, $langs; + global $conf, $langs, $hookmanager; if (empty($conf->dol_hide_leftmenu)) { print '
    '; @@ -2932,14 +2959,17 @@ function main_area($title = '') print ''."\n".'
    '."\n"; + $hookmanager->initHooks(array('main')); + $parameters = array(); + $reshook = $hookmanager->executeHooks('printMainArea', $parameters); // Note that $action and $object may have been modified by some hooks + print $hookmanager->resPrint; + if (!empty($conf->global->MAIN_ONLY_LOGIN_ALLOWED)) { print info_admin($langs->trans("WarningYouAreInMaintenanceMode", $conf->global->MAIN_ONLY_LOGIN_ALLOWED), 0, 0, 1, 'warning maintenancemode'); } // Permit to add user company information on each printed document by setting SHOW_SOCINFO_ON_PRINT if (!empty($conf->global->SHOW_SOCINFO_ON_PRINT) && GETPOST('optioncss', 'aZ09') == 'print' && empty(GETPOST('disable_show_socinfo_on_print', 'az09'))) { - global $hookmanager; - $hookmanager->initHooks(array('main')); $parameters = array(); $reshook = $hookmanager->executeHooks('showSocinfoOnPrint', $parameters); if (empty($reshook)) { @@ -3208,7 +3238,7 @@ if (!function_exists("llxFooter")) { } // A div for the address popup - print "\n\n"; + print "\n\n"; print ''."\n"; // Add code for the asynchronous anonymous first ping (for telemetry) diff --git a/htdocs/margin/customerMargins.php b/htdocs/margin/customerMargins.php index 7fe151b93f7..290a94908c0 100644 --- a/htdocs/margin/customerMargins.php +++ b/htdocs/margin/customerMargins.php @@ -129,8 +129,8 @@ if ($socid > 0) { print '
    '; print $moduleobj->numero; - print '   ('.$langs->trans("SeeIDsInUse").''; - print ' - '.$langs->trans("SeeReservedIDsRangeHere").')'; + print '   ('.$langs->trans("SeeIDsInUse").''; + print ' - '.$langs->trans("SeeReservedIDsRangeHere").')'; print '
    '; @@ -2697,7 +2697,7 @@ if ($module == 'initmodule') { print ''; print ''; print ''; print ''; // Rank - print ''; + print ''; // Product ref print ''; // Product label @@ -401,8 +401,8 @@ if ($id > 0 || !empty($ref)) { $totalline = price2num($value['nb'] * ($fourn_unitprice * (1 - ($fourn_remise_percent / 100)) - $fourn_remise), 'MT'); $total += $totalline; - print ''; // Best selling price @@ -414,11 +414,11 @@ if ($id > 0 || !empty($ref)) { $totalsell += $totallinesell; } print ''; @@ -603,6 +603,7 @@ if ($id > 0 || !empty($ref)) { print ''; print ''; print ''; + print '
    '.$langs->trans("Property"); - print ' ('.$langs->trans("SeeExamples").')'; + print ' ('.$langs->trans("SeeExamples").')'; print ''; print $form->textwithpicto($langs->trans("Label"), $langs->trans("YouCanUseTranslationKey")); @@ -3840,7 +3840,7 @@ if ($module == 'initmodule') { print ''.$langs->trans("FileNotYetGenerated").''; } else { print ''; - print ''; + print ''; print $outputfiledoc; print ''; print ''; @@ -3854,7 +3854,7 @@ if ($module == 'initmodule') { print ''.$langs->trans("FileNotYetGenerated").''; } else { print ''; - print ''; + print ''; print $outputfiledocpdf; print ''; print ''; diff --git a/htdocs/modulebuilder/template/admin/setup.php b/htdocs/modulebuilder/template/admin/setup.php index 8a9937dfd2f..c9eb9043084 100644 --- a/htdocs/modulebuilder/template/admin/setup.php +++ b/htdocs/modulebuilder/template/admin/setup.php @@ -60,6 +60,9 @@ require_once '../lib/mymodule.lib.php'; // Translations $langs->loadLangs(array("admin", "mymodule@mymodule")); +// Initialize technical object to manage hooks of page. Note that conf->hooks_modules contains array of hook context +$hookmanager->initHooks(array('mymodulesetup', 'globalsetup')); + // Access control if (!$user->admin) { accessforbidden(); @@ -68,6 +71,7 @@ if (!$user->admin) { // Parameters $action = GETPOST('action', 'aZ09'); $backtopage = GETPOST('backtopage', 'alpha'); +$modulepart = GETPOST('modulepart', 'aZ09'); // Used by actions_setmoduleoptions.inc.php $value = GETPOST('value', 'alpha'); $label = GETPOST('label', 'alpha'); @@ -88,6 +92,52 @@ $arrayofparameters = array( $error = 0; $setupnotempty = 0; +// Set this to 1 to use the factory to manage constants. Warning, the generated module will be compatible with version v15+ only +$useFormSetup = 0; +// Convert arrayofparameter into a formSetup object +if (!empty($arrayofparameters) && $useFormSetup && (float) DOL_VERSION >= 15) { + require_once DOL_DOCUMENT_ROOT.'/core/class/html.formsetup.class.php'; + $formSetup = new FormSetup($db); + + // you can use the param convertor + $formSetup->addItemsFromParamsArray($arrayofparameters); + + // or use the new system see exemple as follow (or use both because you can ;-) ) + + /* + // Hôte + $item = $formSetup->newItem('NO_PARAM_JUST_TEXT'); + $item->fieldOverride = (empty($_SERVER['HTTPS']) ? 'http://' : 'https://') . $_SERVER['HTTP_HOST']; + $item->cssClass = 'minwidth500'; + + // Setup conf MYMODULE_MYPARAM1 as a simple string input + $item = $formSetup->newItem('MYMODULE_MYPARAM1'); + + // Setup conf MYMODULE_MYPARAM1 as a simple textarea input but we replace the text of field title + $item = $formSetup->newItem('MYMODULE_MYPARAM2'); + $item->nameText = $item->getNameText().' more html text '; + + // Setup conf MYMODULE_MYPARAM3 + $item = $formSetup->newItem('MYMODULE_MYPARAM3'); + $item->setAsThirdpartyType(); + + // Setup conf MYMODULE_MYPARAM4 : exemple of quick define write style + $formSetup->newItem('MYMODULE_MYPARAM4')->setAsYesNo(); + + // Setup conf MYMODULE_MYPARAM5 + $formSetup->newItem('MYMODULE_MYPARAM5')->setAsEmailTemplate('thirdparty'); + + // Setup conf MYMODULE_MYPARAM6 + $formSetup->newItem('MYMODULE_MYPARAM6')->setAsSecureKey()->enabled = 0; // disabled + + // Setup conf MYMODULE_MYPARAM7 + $formSetup->newItem('MYMODULE_MYPARAM7')->setAsProduct(); + */ + + $setupnotempty = count($formSetup->items); +} + + $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']); @@ -95,16 +145,14 @@ $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']); * Actions */ -if ((float) DOL_VERSION >= 6) { - include DOL_DOCUMENT_ROOT.'/core/actions_setmoduleoptions.inc.php'; -} +include DOL_DOCUMENT_ROOT.'/core/actions_setmoduleoptions.inc.php'; if ($action == 'updateMask') { - $maskconstorder = GETPOST('maskconstorder', 'alpha'); - $maskorder = GETPOST('maskorder', 'alpha'); + $maskconst = GETPOST('maskconst', 'alpha'); + $maskvalue = GETPOST('maskvalue', 'alpha'); - if ($maskconstorder) { - $res = dolibarr_set_const($db, $maskconstorder, $maskorder, 'chaine', 0, '', $conf->entity); + if ($maskconst) { + $res = dolibarr_set_const($db, $maskconst, $maskvalue, 'chaine', 0, '', $conf->entity); if (!($res > 0)) { $error++; } @@ -223,71 +271,74 @@ echo ''.$langs->trans("MyModuleSetupPage").'< if ($action == 'edit') { - print ''; - print ''; - print ''; + if ($useFormSetup && (float) DOL_VERSION >= 15) { + print $formSetup->generateOutput(true); + } else { + print ''; + print ''; + print ''; - print ''; - print ''; + print '
    '.$langs->trans("Parameter").''.$langs->trans("Value").'
    '; + print ''; - foreach ($arrayofparameters as $constname => $val) { - if ($val['enabled']==1) { - $setupnotempty++; - print ''; - } - } - print '
    '.$langs->trans("Parameter").''.$langs->trans("Value").'
    '; - $tooltiphelp = (($langs->trans($constname . 'Tooltip') != $constname . 'Tooltip') ? $langs->trans($constname . 'Tooltip') : ''); - print ''.$form->textwithpicto($langs->trans($constname), $tooltiphelp, 1, 'info', '', 0, 3, 'tootips'.$constname).''; - print ''; + foreach ($arrayofparameters as $constname => $val) { + if ($val['enabled']==1) { + $setupnotempty++; + print '
    '; + $tooltiphelp = (($langs->trans($constname . 'Tooltip') != $constname . 'Tooltip') ? $langs->trans($constname . 'Tooltip') : ''); + print ''.$form->textwithpicto($langs->trans($constname), $tooltiphelp, 1, 'info', '', 0, 3, 'tootips'.$constname).''; + print ''; - if ($val['type'] == 'textarea') { - print '\n"; - } elseif ($val['type']== 'html') { - require_once DOL_DOCUMENT_ROOT . '/core/class/doleditor.class.php'; - $doleditor = new DolEditor($constname, $conf->global->{$constname}, '', 160, 'dolibarr_notes', '', false, false, $conf->fckeditor->enabled, ROWS_5, '90%'); - $doleditor->Create(); - } elseif ($val['type'] == 'yesno') { - print $form->selectyesno($constname, $conf->global->{$constname}, 1); - } elseif (preg_match('/emailtemplate:/', $val['type'])) { - include_once DOL_DOCUMENT_ROOT . '/core/class/html.formmail.class.php'; - $formmail = new FormMail($db); + if ($val['type'] == 'textarea') { + print '\n"; + } elseif ($val['type']== 'html') { + require_once DOL_DOCUMENT_ROOT . '/core/class/doleditor.class.php'; + $doleditor = new DolEditor($constname, $conf->global->{$constname}, '', 160, 'dolibarr_notes', '', false, false, $conf->fckeditor->enabled, ROWS_5, '90%'); + $doleditor->Create(); + } elseif ($val['type'] == 'yesno') { + print $form->selectyesno($constname, $conf->global->{$constname}, 1); + } elseif (preg_match('/emailtemplate:/', $val['type'])) { + include_once DOL_DOCUMENT_ROOT . '/core/class/html.formmail.class.php'; + $formmail = new FormMail($db); - $tmp = explode(':', $val['type']); - $nboftemplates = $formmail->fetchAllEMailTemplate($tmp[1], $user, null, 1); // We set lang=null to get in priority record with no lang - //$arraydefaultmessage = $formmail->getEMailTemplate($db, $tmp[1], $user, null, 0, 1, ''); - $arrayofmessagename = array(); - if (is_array($formmail->lines_model)) { - foreach ($formmail->lines_model as $modelmail) { - //var_dump($modelmail); - $moreonlabel = ''; - if (!empty($arrayofmessagename[$modelmail->label])) { - $moreonlabel = ' (' . $langs->trans("SeveralLangugeVariatFound") . ')'; + $tmp = explode(':', $val['type']); + $nboftemplates = $formmail->fetchAllEMailTemplate($tmp[1], $user, null, 1); // We set lang=null to get in priority record with no lang + //$arraydefaultmessage = $formmail->getEMailTemplate($db, $tmp[1], $user, null, 0, 1, ''); + $arrayofmessagename = array(); + if (is_array($formmail->lines_model)) { + foreach ($formmail->lines_model as $modelmail) { + //var_dump($modelmail); + $moreonlabel = ''; + if (!empty($arrayofmessagename[$modelmail->label])) { + $moreonlabel = ' (' . $langs->trans("SeveralLangugeVariatFound") . ')'; + } + // The 'label' is the key that is unique if we exclude the language + $arrayofmessagename[$modelmail->id] = $langs->trans(preg_replace('/\(|\)/', '', $modelmail->label)) . $moreonlabel; } - // The 'label' is the key that is unique if we exclude the language - $arrayofmessagename[$modelmail->id] = $langs->trans(preg_replace('/\(|\)/', '', $modelmail->label)) . $moreonlabel; } - } - print $form->selectarray($constname, $arrayofmessagename, $conf->global->{$constname}, 'None', 0, 0, '', 0, 0, 0, '', '', 1); - } elseif (preg_match('/category:/', $val['type'])) { - require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php'; - require_once DOL_DOCUMENT_ROOT.'/core/class/html.formother.class.php'; - $formother = new FormOther($db); + print $form->selectarray($constname, $arrayofmessagename, $conf->global->{$constname}, 'None', 0, 0, '', 0, 0, 0, '', '', 1); + } elseif (preg_match('/category:/', $val['type'])) { + require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php'; + require_once DOL_DOCUMENT_ROOT.'/core/class/html.formother.class.php'; + $formother = new FormOther($db); - $tmp = explode(':', $val['type']); - print img_picto('', 'category', 'class="pictofixedwidth"'); - print $formother->select_categories($tmp[1], $conf->global->{$constname}, $constname, 0, $langs->trans('CustomersProspectsCategoriesShort')); - } elseif (preg_match('/thirdparty_type/', $val['type'])) { - require_once DOL_DOCUMENT_ROOT.'/core/class/html.formcompany.class.php'; - $formcompany = new FormCompany($db); - print $formcompany->selectProspectCustomerType($conf->global->{$constname}, $constname); - } elseif ($val['type'] == 'securekey') { - print ''; - if (!empty($conf->use_javascript_ajax)) { - print ' '.img_picto($langs->trans('Generate'), 'refresh', 'id="generate_token'.$constname.'" class="linkobject"'); - } - if (!empty($conf->use_javascript_ajax)) { - print "\n".''; - } - } elseif ($val['type'] == 'product') { - if (!empty($conf->product->enabled) || !empty($conf->service->enabled)) { - $selected = (empty($conf->global->$constname) ? '' : $conf->global->$constname); - $form->select_produits($selected, $constname, '', 0); - } - } else { - print ''; - } - print '
    '; - - print '
    '; - print ''; - print '
    '; - - print ''; - print '
    '; -} else { - if (!empty($arrayofparameters)) { - print ''; - print ''; - - foreach ($arrayofparameters as $constname => $val) { - if ($val['enabled']==1) { - $setupnotempty++; - print ''; } } - print '
    '.$langs->trans("Parameter").''.$langs->trans("Value").'
    '; - $tooltiphelp = (($langs->trans($constname . 'Tooltip') != $constname . 'Tooltip') ? $langs->trans($constname . 'Tooltip') : ''); - print $form->textwithpicto($langs->trans($constname), $tooltiphelp); - print ''; - - if ($val['type'] == 'textarea') { - print dol_nl2br($conf->global->{$constname}); - } elseif ($val['type']== 'html') { - print $conf->global->{$constname}; - } elseif ($val['type'] == 'yesno') { - print ajax_constantonoff($constname); - } elseif (preg_match('/emailtemplate:/', $val['type'])) { - include_once DOL_DOCUMENT_ROOT . '/core/class/html.formmail.class.php'; - $formmail = new FormMail($db); - - $tmp = explode(':', $val['type']); - - $template = $formmail->getEMailTemplate($db, $tmp[1], $user, $langs, $conf->global->{$constname}); - if ($template<0) { - setEventMessages(null, $formmail->errors, 'errors'); - } - print $langs->trans($template->label); - } elseif (preg_match('/category:/', $val['type'])) { - $c = new Categorie($db); - $result = $c->fetch($conf->global->{$constname}); - if ($result < 0) { - setEventMessages(null, $c->errors, 'errors'); - } elseif ($result > 0 ) { - $ways = $c->print_all_ways(' >> ', 'none', 0, 1); // $ways[0] = "ccc2 >> ccc2a >> ccc2a1" with html formated text - $toprint = array(); - foreach ($ways as $way) { - $toprint[] = '
  • color ? ' style="background: #' . $c->color . ';"' : ' style="background: #bbb"') . '>' . $way . '
  • '; - } - print '
      ' . implode(' ', $toprint) . '
    '; - } - } elseif (preg_match('/thirdparty_type/', $val['type'])) { - if ($conf->global->{$constname}==2) { - print $langs->trans("Prospect"); - } elseif ($conf->global->{$constname}==3) { - print $langs->trans("ProspectCustomer"); - } elseif ($conf->global->{$constname}==1) { - print $langs->trans("Customer"); - } elseif ($conf->global->{$constname}==0) { - print $langs->trans("NorProspectNorCustomer"); + print ''; } } elseif ($val['type'] == 'product') { - $product = new Product($db); - $resprod = $product->fetch($conf->global->{$constname}); - if ($resprod > 0) { - print $product->ref; - } elseif ($resprod < 0) { - setEventMessages(null, $object->errors, "errors"); + if (!empty($conf->product->enabled) || !empty($conf->service->enabled)) { + $selected = (empty($conf->global->$constname) ? '' : $conf->global->$constname); + $form->select_produits($selected, $constname, '', 0); } } else { - print $conf->global->{$constname}; + print ''; } print '
    '; + print '
    '; + print ''; + print '
    '; + + print ''; + } + + print '
    '; +} else { + if ($useFormSetup && (float) DOL_VERSION >= 15) { + if (!empty($formSetup->items)) { + print $formSetup->generateOutput(); + } + } else { + if (!empty($arrayofparameters)) { + print ''; + print ''; + + foreach ($arrayofparameters as $constname => $val) { + if ($val['enabled']==1) { + $setupnotempty++; + print ''; + } + } + + print '
    '.$langs->trans("Parameter").''.$langs->trans("Value").'
    '; + $tooltiphelp = (($langs->trans($constname . 'Tooltip') != $constname . 'Tooltip') ? $langs->trans($constname . 'Tooltip') : ''); + print $form->textwithpicto($langs->trans($constname), $tooltiphelp); + print ''; + + if ($val['type'] == 'textarea') { + print dol_nl2br($conf->global->{$constname}); + } elseif ($val['type']== 'html') { + print $conf->global->{$constname}; + } elseif ($val['type'] == 'yesno') { + print ajax_constantonoff($constname); + } elseif (preg_match('/emailtemplate:/', $val['type'])) { + include_once DOL_DOCUMENT_ROOT . '/core/class/html.formmail.class.php'; + $formmail = new FormMail($db); + + $tmp = explode(':', $val['type']); + + $template = $formmail->getEMailTemplate($db, $tmp[1], $user, $langs, $conf->global->{$constname}); + if ($template<0) { + setEventMessages(null, $formmail->errors, 'errors'); + } + print $langs->trans($template->label); + } elseif (preg_match('/category:/', $val['type'])) { + $c = new Categorie($db); + $result = $c->fetch($conf->global->{$constname}); + if ($result < 0) { + setEventMessages(null, $c->errors, 'errors'); + } elseif ($result > 0 ) { + $ways = $c->print_all_ways(' >> ', 'none', 0, 1); // $ways[0] = "ccc2 >> ccc2a >> ccc2a1" with html formated text + $toprint = array(); + foreach ($ways as $way) { + $toprint[] = '
  • color ? ' style="background: #' . $c->color . ';"' : ' style="background: #bbb"') . '>' . $way . '
  • '; + } + print '
      ' . implode(' ', $toprint) . '
    '; + } + } elseif (preg_match('/thirdparty_type/', $val['type'])) { + if ($conf->global->{$constname}==2) { + print $langs->trans("Prospect"); + } elseif ($conf->global->{$constname}==3) { + print $langs->trans("ProspectCustomer"); + } elseif ($conf->global->{$constname}==1) { + print $langs->trans("Customer"); + } elseif ($conf->global->{$constname}==0) { + print $langs->trans("NorProspectNorCustomer"); + } + } elseif ($val['type'] == 'product') { + $product = new Product($db); + $resprod = $product->fetch($conf->global->{$constname}); + if ($resprod > 0) { + print $product->ref; + } elseif ($resprod < 0) { + setEventMessages(null, $object->errors, "errors"); + } + } else { + print $conf->global->{$constname}; + } + print '
    '; + } + } + + if ($setupnotempty) { print '
    '; print ''.$langs->trans("Modify").''; print '
    '; diff --git a/htdocs/modulebuilder/template/class/api_mymodule.class.php b/htdocs/modulebuilder/template/class/api_mymodule.class.php index cb2fbda68a6..4cb50c8de2f 100644 --- a/htdocs/modulebuilder/template/class/api_mymodule.class.php +++ b/htdocs/modulebuilder/template/class/api_mymodule.class.php @@ -155,8 +155,9 @@ class MyModuleApi extends DolibarrApi $sql .= " AND sc.fk_user = ".((int) $search_sale); } if ($sqlfilters) { - if (!DolibarrApi::_checkFilters($sqlfilters)) { - throw new RestException(503, 'Error when validating parameter sqlfilters '.$sqlfilters); + $errormessage = ''; + if (!DolibarrApi::_checkFilters($sqlfilters, $errormessage)) { + throw new RestException(503, 'Error when validating parameter sqlfilters -> '.$errormessage); } $regexstring = '\(([^:\'\(\)]+:[^:\'\(\)]+:[^\(\)]+)\)'; $sql .= " AND (".preg_replace_callback('/'.$regexstring.'/', 'DolibarrApi::_forge_criteria_callback', $sqlfilters).")"; diff --git a/htdocs/modulebuilder/template/class/myobject.class.php b/htdocs/modulebuilder/template/class/myobject.class.php index 6e5d6efcff4..ca9de1cc5ab 100644 --- a/htdocs/modulebuilder/template/class/myobject.class.php +++ b/htdocs/modulebuilder/template/class/myobject.class.php @@ -362,7 +362,7 @@ class MyObject extends CommonObject if (!$error) { // copy external contacts if same company - if (property_exists($this, 'fk_soc') && $this->fk_soc == $object->socid) { + if (!empty($object->socid) && property_exists($this, 'fk_soc') && $this->fk_soc == $object->socid) { if ($this->copy_linked_contact($object, 'external') < 0) { $error++; } @@ -918,19 +918,19 @@ class MyObject extends CommonObject if ($this->db->num_rows($result)) { $obj = $this->db->fetch_object($result); $this->id = $obj->rowid; - if ($obj->fk_user_author) { + if (!empty($obj->fk_user_author)) { $cuser = new User($this->db); $cuser->fetch($obj->fk_user_author); $this->user_creation = $cuser; } - if ($obj->fk_user_valid) { + if (!empty($obj->fk_user_valid)) { $vuser = new User($this->db); $vuser->fetch($obj->fk_user_valid); $this->user_validation = $vuser; } - if ($obj->fk_user_cloture) { + if (!empty($obj->fk_user_cloture)) { $cluser = new User($this->db); $cluser->fetch($obj->fk_user_cloture); $this->user_cloture = $cluser; diff --git a/htdocs/modulebuilder/template/core/boxes/README.md b/htdocs/modulebuilder/template/core/boxes/README.md index b641e7136bc..3989bca5847 100644 --- a/htdocs/modulebuilder/template/core/boxes/README.md +++ b/htdocs/modulebuilder/template/core/boxes/README.md @@ -1 +1 @@ -Directory where widgets files are stored. \ No newline at end of file +# Directory where widgets files are stored diff --git a/htdocs/modulebuilder/template/core/boxes/mymodulewidget1.php b/htdocs/modulebuilder/template/core/boxes/mymodulewidget1.php index 82632773c87..7ea4b553e71 100644 --- a/htdocs/modulebuilder/template/core/boxes/mymodulewidget1.php +++ b/htdocs/modulebuilder/template/core/boxes/mymodulewidget1.php @@ -92,8 +92,8 @@ class mymodulewidget1 extends ModeleBoxes public function __construct(DoliDB $db, $param = '') { global $user, $conf, $langs; - $langs->load("boxes"); - $langs->load('mymodule@mymodule'); + // Translations + $langs->loadLangs(array("boxes", "mymodule@mymodule")); parent::__construct($db, $param); diff --git a/htdocs/modulebuilder/template/core/modules/mailings/mailinglist_mymodule_myobject.modules.php b/htdocs/modulebuilder/template/core/modules/mailings/mailinglist_mymodule_myobject.modules.php index dc797b99a94..b50f4acf741 100644 --- a/htdocs/modulebuilder/template/core/modules/mailings/mailinglist_mymodule_myobject.modules.php +++ b/htdocs/modulebuilder/template/core/modules/mailings/mailinglist_mymodule_myobject.modules.php @@ -55,9 +55,9 @@ class mailing_mailinglist_mymodule_myobject extends MailingTargets /** - * Affiche formulaire de filtre qui apparait dans page de selection des destinataires de mailings + * Displays the filter form that appears in the mailing recipient selection page * - * @return string Retourne zone select + * @return string Return select zone */ public function formFilter() { @@ -83,7 +83,7 @@ class mailing_mailinglist_mymodule_myobject extends MailingTargets /** - * Renvoie url lien vers fiche de la source du destinataire du mailing + * Returns url link to file of the source of the recipient of the mailing * * @param int $id ID * @return string Url lien @@ -115,7 +115,7 @@ class mailing_mailinglist_mymodule_myobject extends MailingTargets } $sql .= " ORDER BY email"; - // Stocke destinataires dans target + // Store recipients in target $result = $this->db->query($sql); if ($result) { $num = $this->db->num_rows($result); diff --git a/htdocs/modulebuilder/template/core/modules/modMyModule.class.php b/htdocs/modulebuilder/template/core/modules/modMyModule.class.php index de9188e714d..80cee0a35b3 100644 --- a/htdocs/modulebuilder/template/core/modules/modMyModule.class.php +++ b/htdocs/modulebuilder/template/core/modules/modMyModule.class.php @@ -148,8 +148,8 @@ class modMyModule extends DolibarrModules $this->need_dolibarr_version = array(11, -3); // Minimum version of Dolibarr required by module // Messages at activation - $this->warnings_activation = array(); // Warning to show when we activate module. array('always'='text') or array('FR'='textfr','ES'='textes'...) - $this->warnings_activation_ext = array(); // Warning to show when we activate an external module. array('always'='text') or array('FR'='textfr','ES'='textes'...) + $this->warnings_activation = array(); // Warning to show when we activate module. array('always'='text') or array('FR'='textfr','MX'='textmx'...) + $this->warnings_activation_ext = array(); // Warning to show when we activate an external module. array('always'='text') or array('FR'='textfr','MX'='textmx'...) //$this->automatic_activation = array('FR'=>'MyModuleWasAutomaticallyActivatedBecauseOfYourCountryChoice'); //$this->always_enabled = true; // If true, can't be disabled diff --git a/htdocs/modulebuilder/template/core/modules/mymodule/doc/doc_generic_myobject_odt.modules.php b/htdocs/modulebuilder/template/core/modules/mymodule/doc/doc_generic_myobject_odt.modules.php index 9126da0070d..8040c13d606 100644 --- a/htdocs/modulebuilder/template/core/modules/mymodule/doc/doc_generic_myobject_odt.modules.php +++ b/htdocs/modulebuilder/template/core/modules/mymodule/doc/doc_generic_myobject_odt.modules.php @@ -122,9 +122,10 @@ class doc_generic_myobject_odt extends ModelePDFMyObject $texte = $this->description.".
    \n"; $texte .= '
    '; $texte .= ''; + $texte .= ''; $texte .= ''; $texte .= ''; - $texte .= ''; + $texte .= '
    '; // List of directories area $texte .= ''; + // Add input to upload a new template file. + $texte .= '
    '.$langs->trans("UploadNewTemplate").' '; + $texte .= ''; + $texte .= ''; + $texte .= '
    '; + $texte .= ''; + $texte .= ''; $texte .= ''; diff --git a/htdocs/modulebuilder/template/core/modules/mymodule/mod_myobject_advanced.php b/htdocs/modulebuilder/template/core/modules/mymodule/mod_myobject_advanced.php index b7fd7085783..0573edc6b2a 100644 --- a/htdocs/modulebuilder/template/core/modules/mymodule/mod_myobject_advanced.php +++ b/htdocs/modulebuilder/template/core/modules/mymodule/mod_myobject_advanced.php @@ -68,8 +68,8 @@ class mod_myobject_advanced extends ModeleNumRefMyObject $texte .= ''; $texte .= ''; $texte .= ''; - $texte .= ''; - $texte .= '
    '; @@ -158,7 +159,7 @@ class doc_generic_myobject_odt extends ModelePDFMyObject $texte .= $conf->global->MYMODULE_MYOBJECT_ADDON_PDF_ODT_PATH; $texte .= ''; $texte .= '
    '; - $texte .= ''; + $texte .= ''; $texte .= '
    '; // Scan directories @@ -174,15 +175,27 @@ class doc_generic_myobject_odt extends ModelePDFMyObject if ($nbofiles) { $texte .= ''; } $texte .= '
    '; + $texte .= ''; $texte .= $langs->trans("ExampleOfDirectoriesForModelGen"); + $texte .= ''; $texte .= '
    '; + $texte .= ''; + $texte .= '
    '; $tooltip = $langs->trans("GenericMaskCodes", $langs->transnoentities("MyObject"), $langs->transnoentities("MyObject")); $tooltip .= $langs->trans("GenericMaskCodes2"); @@ -79,10 +79,8 @@ class mod_myobject_advanced extends ModeleNumRefMyObject // Parametrage du prefix $texte .= ''; - $texte .= ''; - - $texte .= ''; - + $texte .= ''; + $texte .= ''; $texte .= ''; $texte .= '
    '.$langs->trans("Mask").':'.$form->textwithpicto('', $tooltip, 1, 1).'  '.$form->textwithpicto('', $tooltip, 1, 1).' 
    '; diff --git a/htdocs/modulebuilder/template/lib/mymodule.lib.php b/htdocs/modulebuilder/template/lib/mymodule.lib.php index 32ae980e946..ab8a647efe4 100644 --- a/htdocs/modulebuilder/template/lib/mymodule.lib.php +++ b/htdocs/modulebuilder/template/lib/mymodule.lib.php @@ -62,7 +62,7 @@ function mymoduleAdminPrepareHead() //); // to remove a tab complete_head_from_modules($conf, $langs, null, $head, $h, 'mymodule@mymodule'); - complete_head_from_modules($conf, $langs, $object, $head, $h, 'mymodule@mymodule', 'remove'); + complete_head_from_modules($conf, $langs, null, $head, $h, 'mymodule@mymodule', 'remove'); return $head; } diff --git a/htdocs/modulebuilder/template/myobject_agenda.php b/htdocs/modulebuilder/template/myobject_agenda.php index add786498b2..5eb4f6e3028 100644 --- a/htdocs/modulebuilder/template/myobject_agenda.php +++ b/htdocs/modulebuilder/template/myobject_agenda.php @@ -102,8 +102,8 @@ if (GETPOST('actioncode', 'array')) { $search_agenda_label = GETPOST('search_agenda_label'); $limit = GETPOST('limit', 'int') ? GETPOST('limit', 'int') : $conf->liste_limit; -$sortfield = GETPOST("sortfield", 'alpha'); -$sortorder = GETPOST("sortorder", 'alpha'); +$sortfield = GETPOST('sortfield', 'aZ09comma'); +$sortorder = GETPOST('sortorder', 'aZ09comma'); $page = GETPOSTISSET('pageplusone') ? (GETPOST('pageplusone') - 1) : GETPOST("page", 'int'); if (empty($page) || $page == -1) { $page = 0; @@ -129,7 +129,7 @@ $extrafields->fetch_name_optionals_label($object->table_element); // Load object include DOL_DOCUMENT_ROOT.'/core/actions_fetchobject.inc.php'; // Must be include, not include_once // Must be include, not include_once. Include fetch and fetch_thirdparty but not fetch_optionals if ($id > 0 || !empty($ref)) { - $upload_dir = $conf->mymodule->multidir_output[$object->entity]."/".$object->id; + $upload_dir = $conf->mymodule->multidir_output[!empty($object->entity) ? $object->entity : $conf->entity]."/".$object->id; } $permissiontoadd = $user->rights->mymodule->myobject->write; // Used by the include of actions_addupdatedelete.inc.php diff --git a/htdocs/modulebuilder/template/myobject_card.php b/htdocs/modulebuilder/template/myobject_card.php index 43c57ca1f94..66279849abb 100644 --- a/htdocs/modulebuilder/template/myobject_card.php +++ b/htdocs/modulebuilder/template/myobject_card.php @@ -28,7 +28,7 @@ //if (! defined('NOREQUIRETRAN')) define('NOREQUIRETRAN', '1'); // Do not load object $langs //if (! defined('NOSCANGETFORINJECTION')) define('NOSCANGETFORINJECTION', '1'); // Do not check injection attack on GET parameters //if (! defined('NOSCANPOSTFORINJECTION')) define('NOSCANPOSTFORINJECTION', '1'); // Do not check injection attack on POST parameters -//if (! defined('NOCSRFCHECK')) define('NOCSRFCHECK', '1'); // Do not check CSRF attack (test on referer + on token if option MAIN_SECURITY_CSRF_WITH_TOKEN is on). +//if (! defined('NOCSRFCHECK')) define('NOCSRFCHECK', '1'); // Do not check CSRF attack (test on referer + on token). //if (! defined('NOTOKENRENEWAL')) define('NOTOKENRENEWAL', '1'); // Do not roll the Anti CSRF token (used if MAIN_SECURITY_CSRF_WITH_TOKEN is on) //if (! defined('NOSTYLECHECK')) define('NOSTYLECHECK', '1'); // Do not check style html tag into posted data //if (! defined('NOREQUIREMENU')) define('NOREQUIREMENU', '1'); // If there is no need to load and show top and left menu @@ -92,7 +92,7 @@ $cancel = GETPOST('cancel', 'aZ09'); $contextpage = GETPOST('contextpage', 'aZ') ? GETPOST('contextpage', 'aZ') : 'myobjectcard'; // To manage different context of search $backtopage = GETPOST('backtopage', 'alpha'); $backtopageforcancel = GETPOST('backtopageforcancel', 'alpha'); -//$lineid = GETPOST('lineid', 'int'); +$lineid = GETPOST('lineid', 'int'); // Initialize technical objects $object = new MyObject($db); @@ -132,7 +132,7 @@ $upload_dir = $conf->mymodule->multidir_output[isset($object->entity) ? $object- // Security check (enable the most restrictive one) //if ($user->socid > 0) accessforbidden(); //if ($user->socid > 0) $socid = $user->socid; -//$isdraft = (($object->status == $object::STATUS_DRAFT) ? 1 : 0); +//$isdraft = (isset($object->status) && ($object->status == $object::STATUS_DRAFT) ? 1 : 0); //restrictedArea($user, $object->element, $object->id, $object->table_element, '', 'fk_soc', 'rowid', $isdraft); //if (empty($conf->mymodule->enabled)) accessforbidden(); //if (!$permissiontoread) accessforbidden(); @@ -212,7 +212,7 @@ $help_url = ''; llxHeader('', $title, $help_url); // Example : Adding jquery code -// print ''."\n"; print "\n"; diff --git a/htdocs/paypal/admin/paypal.php b/htdocs/paypal/admin/paypal.php index f4edec7630c..df5bb4161a5 100644 --- a/htdocs/paypal/admin/paypal.php +++ b/htdocs/paypal/admin/paypal.php @@ -350,7 +350,7 @@ $sandboxpaypalurl = 'developer.paypal.com'; print '
    '; print 'Your API authentication information can be found with following steps. We recommend that you open a separate Web browser session when carrying out this procedure.
    -1. Log in to your PayPal account (on real paypal '.$realpaypalurl.' (or sandbox '.$sandboxpaypalurl.').
    +1. Log in to your PayPal account (on real paypal '.$realpaypalurl.' (or sandbox '.$sandboxpaypalurl.').
    2. Click the "Profile" or "Preferencies" subtab located under the My Account heading.
    3. Click the link "API Access".
    4. Click the View API Certificate link in the right column.
    diff --git a/htdocs/product/admin/product.php b/htdocs/product/admin/product.php index 4f6094e1787..2d7d99a9ab3 100644 --- a/htdocs/product/admin/product.php +++ b/htdocs/product/admin/product.php @@ -46,6 +46,8 @@ if (!$user->admin || (empty($conf->product->enabled) && empty($conf->service->en $action = GETPOST('action', 'aZ09'); $value = GETPOST('value', 'alpha'); +$modulepart = GETPOST('modulepart', 'aZ09'); // Used by actions_setmoduleoptions.inc.php + $label = GETPOST('label', 'alpha'); $scandir = GETPOST('scan_dir', 'alpha'); $type = 'product'; diff --git a/htdocs/product/agenda.php b/htdocs/product/agenda.php index 815014e259f..40d451653e7 100644 --- a/htdocs/product/agenda.php +++ b/htdocs/product/agenda.php @@ -54,8 +54,8 @@ if ($user->socid) { } $limit = GETPOST('limit', 'int') ?GETPOST('limit', 'int') : $conf->liste_limit; -$sortfield = GETPOST("sortfield", 'alpha'); -$sortorder = GETPOST("sortorder", 'alpha'); +$sortfield = GETPOST('sortfield', 'aZ09comma'); +$sortorder = GETPOST('sortorder', 'aZ09comma'); $page = GETPOSTISSET('pageplusone') ? (GETPOST('pageplusone') - 1) : GETPOST("page", 'int'); if (empty($page) || $page == -1) { $page = 0; diff --git a/htdocs/product/ajax/products.php b/htdocs/product/ajax/products.php index 84562bf95a4..32c4c83378c 100644 --- a/htdocs/product/ajax/products.php +++ b/htdocs/product/ajax/products.php @@ -47,7 +47,7 @@ if (empty($_GET['keysearch']) && !defined('NOREQUIREHTML')) { require '../../main.inc.php'; -$htmlname = GETPOST('htmlname', 'alpha'); +$htmlname = GETPOST('htmlname', 'aZ09'); $socid = GETPOST('socid', 'int'); $type = GETPOST('type', 'int'); $mode = GETPOST('mode', 'int'); @@ -73,7 +73,7 @@ restrictedArea($user, 'produit|service', 0, 'product&product'); // print ''."\n"; // print_r($_GET); -if (!empty($action) && $action == 'fetch' && !empty($id)) { +if ($action == 'fetch' && !empty($id)) { // action='fetch' is used to get product information on a product. So when action='fetch', id must be the product id. require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php'; require_once DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php'; @@ -85,10 +85,15 @@ if (!empty($action) && $action == 'fetch' && !empty($id)) { if ($ret > 0) { $outref = $object->ref; $outlabel = $object->label; - $outlabel_trans =''; + $outlabel_trans = ''; $outdesc = $object->description; - $outdesc_trans =''; + $outdesc_trans = ''; $outtype = $object->type; + $outprice_ht = null; + $outprice_ttc = null; + $outpricebasetype = null; + $outtva_tx = 0; + $outdefault_vat_code = ''; $outqty = 1; $outdiscount = 0; $mandatory_period = $object->mandatory_period; @@ -132,17 +137,21 @@ if (!empty($action) && $action == 'fetch' && !empty($id)) { $found = true; $outprice_ht = price($objp->unitprice); $outprice_ttc = price($objp->unitprice * (1 + ($object->tva_tx / 100))); + $outpricebasetype = $object->price_base_type; $outtva_tx = $object->tva_tx; + $outdefault_vat_code = $object->default_vat_code; + $outqty = $objp->quantity; $outdiscount = $objp->remise_percent; } } } - // Multiprice + // Multiprice (1 price per level) if (!$found && isset($price_level) && $price_level >= 1 && (!empty($conf->global->PRODUIT_MULTIPRICES) || !empty($conf->global->PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES))) { // If we need a particular price level (from 1 to 6) - $sql = "SELECT price, price_ttc, price_base_type, tva_tx"; + $sql = "SELECT price, price_ttc, price_base_type,"; + $sql .= " tva_tx, default_vat_code"; // Vat rate and code will be used if PRODUIT_MULTIPRICES_USE_VAT_PER_LEVEL is on. $sql .= " FROM ".MAIN_DB_PREFIX."product_price "; $sql .= " WHERE fk_product = ".((int) $id); $sql .= " AND entity IN (".getEntity('productprice').")"; @@ -158,7 +167,14 @@ if (!empty($action) && $action == 'fetch' && !empty($id)) { $outprice_ht = price($objp->price); $outprice_ttc = price($objp->price_ttc); $outpricebasetype = $objp->price_base_type; - $outtva_tx = $objp->tva_tx; + if (!empty($conf->global->PRODUIT_MULTIPRICES_USE_VAT_PER_LEVEL)) { + $outtva_tx = $objp->tva_tx; + $outdefault_vat_code = $objp->default_vat_code; + } else { + // The common and default behaviour. + $outtva_tx = $object->tva_tx; + $outdefault_vat_code = $object->default_vat_code; + } } } } @@ -175,10 +191,11 @@ if (!empty($action) && $action == 'fetch' && !empty($id)) { if ($result) { if (count($prodcustprice->lines) > 0) { $found = true; - $outprice_ht = price($prodcustprice->lines [0]->price); - $outprice_ttc = price($prodcustprice->lines [0]->price_ttc); - $outpricebasetype = $prodcustprice->lines [0]->price_base_type; - $outtva_tx = $prodcustprice->lines [0]->tva_tx; + $outprice_ht = price($prodcustprice->lines[0]->price); + $outprice_ttc = price($prodcustprice->lines[0]->price_ttc); + $outpricebasetype = $prodcustprice->lines[0]->price_base_type; + $outtva_tx = $prodcustprice->lines[0]->tva_tx; + $outdefault_vat_code = $prodcustprice->lines[0]->default_vat_code; } } } @@ -188,6 +205,7 @@ if (!empty($action) && $action == 'fetch' && !empty($id)) { $outprice_ttc = price($object->price_ttc); $outpricebasetype = $object->price_base_type; $outtva_tx = $object->tva_tx; + $outdefault_vat_code = $object->default_vat_code; } $outjson = array( @@ -201,6 +219,7 @@ if (!empty($action) && $action == 'fetch' && !empty($id)) { 'price_ttc' => $outprice_ttc, 'pricebasetype' => $outpricebasetype, 'tva_tx' => $outtva_tx, + 'default_vat_code' => $outdefault_vat_code, 'qty' => $outqty, 'discount' => $outdiscount, 'mandatory_period' => $mandatory_period, @@ -223,7 +242,7 @@ if (!empty($action) && $action == 'fetch' && !empty($id)) { // Filter on the product to search can be: // Into an array with key $htmlname123 (we take first one found). Which page use this ? // Into a var with name $htmlname can be 'prodid', 'productid', ... - $match = preg_grep('/('.$htmlname.'[0-9]+)/', array_keys($_GET)); + $match = preg_grep('/('.preg_quote($htmlname, '/').'[0-9]+)/', array_keys($_GET)); sort($match); $idprod = (empty($match[0]) ? '' : $match[0]); // Take first key found into GET array with matching $htmlname123 diff --git a/htdocs/product/canvas/service/actions_card_service.class.php b/htdocs/product/canvas/service/actions_card_service.class.php index c4296dc63a8..b07c3d96a52 100644 --- a/htdocs/product/canvas/service/actions_card_service.class.php +++ b/htdocs/product/canvas/service/actions_card_service.class.php @@ -39,6 +39,13 @@ class ActionsCardService public $field_list = array(); public $list_datas = array(); + public $id; + public $ref; + public $description; + public $note; + public $price; + public $price_min; + /** * Constructor @@ -196,6 +203,7 @@ class ActionsCardService } // Duration + $dur = array(); if ($this->object->duration_value > 1) { $dur = array("h"=>$langs->trans("Hours"), "d"=>$langs->trans("Days"), "w"=>$langs->trans("Weeks"), "m"=>$langs->trans("Months"), "y"=>$langs->trans("Years")); } elseif ($this->object->duration_value > 0) { @@ -286,6 +294,7 @@ class ActionsCardService if ($search_categ) { $sql .= ", ".MAIN_DB_PREFIX."categorie_product as cp"; } + $fourn_id = 0; if (GETPOST("fourn_id", 'int') > 0) { $fourn_id = GETPOST("fourn_id", 'int'); $sql .= ", ".MAIN_DB_PREFIX."product_fournisseur_price as pfp"; diff --git a/htdocs/product/card.php b/htdocs/product/card.php index b14d4a4efca..420203eedae 100644 --- a/htdocs/product/card.php +++ b/htdocs/product/card.php @@ -2705,14 +2705,12 @@ if ($action != 'create' && $action != 'edit' && $action != 'delete') { $MAXEVENT = 10; - $morehtmlright = ''; - $morehtmlright .= $langs->trans("SeeAll"); - $morehtmlright .= ''; + $morehtmlcenter = dolGetButtonTitle($langs->trans('SeeAll'), '', 'fa fa-list-alt imgforviewmode', DOL_URL_ROOT.'/product/agenda.php?id='.$object->id); // List of actions on element include_once DOL_DOCUMENT_ROOT.'/core/class/html.formactions.class.php'; $formactions = new FormActions($db); - $somethingshown = $formactions->showactions($object, 'product', 0, 1, '', $MAXEVENT, '', $morehtmlright); // Show all action for product + $somethingshown = $formactions->showactions($object, 'product', 0, 1, '', $MAXEVENT, '', $morehtmlcenter); // Show all action for product print '
    '; } diff --git a/htdocs/product/class/api_products.class.php b/htdocs/product/class/api_products.class.php index 6283b692a28..91edbe5eaff 100644 --- a/htdocs/product/class/api_products.class.php +++ b/htdocs/product/class/api_products.class.php @@ -172,9 +172,10 @@ class Products extends DolibarrApi * @param bool $ids_only Return only IDs of product instead of all properties (faster, above all if list is long) * @param int $variant_filter Use this param to filter list (0 = all, 1=products without variants, 2=parent of variants, 3=variants only) * @param bool $pagination_data If this parameter is set to true the response will include pagination data. Default value is false. Page starts from 0 + * @param int $includestockdata Load also information about stock (slower) * @return array Array of product objects */ - public function index($sortfield = "t.ref", $sortorder = 'ASC', $limit = 100, $page = 0, $mode = 0, $category = 0, $sqlfilters = '', $ids_only = false, $variant_filter = 0, $pagination_data = false) + public function index($sortfield = "t.ref", $sortorder = 'ASC', $limit = 100, $page = 0, $mode = 0, $category = 0, $sqlfilters = '', $ids_only = false, $variant_filter = 0, $pagination_data = false, $includestockdata = 0) { global $db, $conf; @@ -218,8 +219,9 @@ class Products extends DolibarrApi } // Add sql filters if ($sqlfilters) { - if (!DolibarrApi::_checkFilters($sqlfilters)) { - throw new RestException(503, 'Error when validating parameter sqlfilters '.$sqlfilters); + $errormessage = ''; + if (!DolibarrApi::_checkFilters($sqlfilters, $errormessage)) { + throw new RestException(503, 'Error when validating parameter sqlfilters -> '.$errormessage); } //var_dump($sqlfilters);exit; $regexstring = '\(([^:\'\(\)]+:[^:\'\(\)]+:[^\(\)]+)\)'; // We must accept datc:<:2020-01-01 10:10:10 @@ -249,6 +251,21 @@ class Products extends DolibarrApi if (!$ids_only) { $product_static = new Product($this->db); if ($product_static->fetch($obj->rowid)) { + if ($includestockdata && DolibarrApiAccess::$user->rights->stock->lire) { + $product_static->load_stock(); + + if (is_array($product_static->stock_warehouse)) { + foreach ($product_static->stock_warehouse as $keytmp => $valtmp) { + if (is_array($product_static->stock_warehouse[$keytmp]->detail_batch)) { + foreach ($product_static->stock_warehouse[$keytmp]->detail_batch as $keytmp2 => $valtmp2) { + unset($product_static->stock_warehouse[$keytmp]->detail_batch[$keytmp2]->db); + } + } + } + } + } + + $obj_ret[] = $this->_cleanObjectDatas($product_static); } } else { @@ -873,8 +890,9 @@ class Products extends DolibarrApi } // Add sql filters if ($sqlfilters) { - if (!DolibarrApi::_checkFilters($sqlfilters)) { - throw new RestException(503, 'Error when validating parameter sqlfilters '.$sqlfilters); + $errormessage = ''; + if (!DolibarrApi::_checkFilters($sqlfilters, $errormessage)) { + throw new RestException(503, 'Error when validating parameter sqlfilters -> '.$errormessage); } $regexstring = '\(([^:\'\(\)]+:[^:\'\(\)]+:[^\(\)]+)\)'; $sql .= " AND (".preg_replace_callback('/'.$regexstring.'/', 'DolibarrApi::_forge_criteria_callback', $sqlfilters).")"; @@ -1000,8 +1018,9 @@ class Products extends DolibarrApi // Add sql filters if ($sqlfilters) { - if (!DolibarrApi::_checkFilters($sqlfilters)) { - throw new RestException(503, 'Error when validating parameter sqlfilters '.$sqlfilters); + $errormessage = ''; + if (!DolibarrApi::_checkFilters($sqlfilters, $errormessage)) { + throw new RestException(503, 'Error when validating parameter sqlfilters -> '.$errormessage); } $regexstring = '\(([^:\'\(\)]+:[^:\'\(\)]+:[^\(\)]+)\)'; $sql .= " AND (".preg_replace_callback('/'.$regexstring.'/', 'DolibarrApi::_forge_criteria_callback', $sqlfilters).")"; @@ -1948,6 +1967,7 @@ class Products extends DolibarrApi if (empty(DolibarrApiAccess::$user->rights->stock->lire)) { unset($object->stock_reel); unset($object->stock_theorique); + unset($object->stock_warehouse); } return $object; diff --git a/htdocs/product/class/product.class.php b/htdocs/product/class/product.class.php index 1d4a5926b6e..27b2bdaff2c 100644 --- a/htdocs/product/class/product.class.php +++ b/htdocs/product/class/product.class.php @@ -2037,7 +2037,7 @@ class Product extends CommonObject /** - * Modify customer price of a product/Service + * Modify customer price of a product/Service * * @param double $newprice New price * @param string $newpricebase HT or TTC @@ -4800,9 +4800,10 @@ class Product extends CommonObject if (!empty($this->entity)) { $tmpphoto = $this->show_photos('product', $conf->product->multidir_output[$this->entity], 1, 1, 0, 0, 0, 80); if ($this->nbphoto > 0) { - $label .= '
    '; + $label .= '
    '; $label .= $tmpphoto; - $label .= '
    '; + $label .= '
    '; + //$label .= '
    '; } } diff --git a/htdocs/product/class/productcustomerprice.class.php b/htdocs/product/class/productcustomerprice.class.php index 407eac7c30a..a9444d1c569 100644 --- a/htdocs/product/class/productcustomerprice.class.php +++ b/htdocs/product/class/productcustomerprice.class.php @@ -392,9 +392,9 @@ class Productcustomerprice extends CommonObject $sql .= " t.import_key,"; $sql .= " soc.nom as socname,"; $sql .= " prod.ref as prodref"; - $sql .= " FROM ".MAIN_DB_PREFIX."product_customer_price as t "; - $sql .= " ,".MAIN_DB_PREFIX."product as prod "; - $sql .= " ,".MAIN_DB_PREFIX."societe as soc "; + $sql .= " FROM ".MAIN_DB_PREFIX."product_customer_price as t,"; + $sql .= " ".MAIN_DB_PREFIX."product as prod,"; + $sql .= " ".MAIN_DB_PREFIX."societe as soc"; $sql .= " WHERE soc.rowid=t.fk_soc "; $sql .= " AND prod.rowid=t.fk_product "; $sql .= " AND prod.entity IN (".getEntity('product').")"; diff --git a/htdocs/product/composition/card.php b/htdocs/product/composition/card.php index 2cecfc84bc4..d5325029771 100644 --- a/htdocs/product/composition/card.php +++ b/htdocs/product/composition/card.php @@ -339,7 +339,7 @@ if ($id > 0 || !empty($ref)) { print '
    '.$langs->trans('Rank').''.$langs->trans('Position').''.$langs->trans('ComposedProduct').''; - print ($notdefined ? '' : ($value['nb'] > 1 ? $value['nb'].'x' : '').price($unitline, '', '', 0, 0, -1, $conf->currency)); + print ''; + print ($notdefined ? '' : ($value['nb'] > 1 ? $value['nb'].'x ' : '').''.price($unitline, '', '', 0, 0, -1, $conf->currency)).''; print ''; - print ($notdefined ? '' : ($value['nb'] > 1 ? $value['nb'].'x' : '')); + print ($notdefined ? '' : ($value['nb'] > 1 ? $value['nb'].'x ' : '')); if (is_numeric($pricesell)) { - print price($pricesell, '', '', 0, 0, -1, $conf->currency); + print ''.price($pricesell, '', '', 0, 0, -1, $conf->currency).''; } else { - print $langs->trans($pricesell); + print ''.$langs->trans($pricesell).''; } print '
    '; print ''; print '"; + print '"; } } } diff --git a/htdocs/product/index.php b/htdocs/product/index.php index 082e8a4d8d2..e4a0bfc7421 100644 --- a/htdocs/product/index.php +++ b/htdocs/product/index.php @@ -225,6 +225,7 @@ if (!empty($conf->categorie->enabled) && !empty($conf->global->CATEGORY_GRAPHSTA $sql .= " WHERE c.type = 0"; $sql .= " AND c.entity IN (".getEntity('category').")"; $sql .= " GROUP BY c.label"; + $sql .= " ORDER BY nb desc"; $total = 0; $result = $db->query($sql); if ($result) { @@ -341,8 +342,8 @@ if ((!empty($conf->product->enabled) || !empty($conf->service->enabled)) && ($us if (!empty($conf->global->MAIN_MULTILANGS)) { $sql = "SELECT label"; $sql .= " FROM ".MAIN_DB_PREFIX."product_lang"; - $sql .= " WHERE fk_product=".((int) $objp->rowid); - $sql .= " AND lang='".$db->escape($langs->getDefaultLang())."'"; + $sql .= " WHERE fk_product = ".((int) $objp->rowid); + $sql .= " AND lang = '".$db->escape($langs->getDefaultLang())."'"; $resultd = $db->query($sql); if ($resultd) { diff --git a/htdocs/product/inventory/ajax/ajax.inventory.php b/htdocs/product/inventory/ajax/ajax.inventory.php deleted file mode 100644 index 920607f4c1e..00000000000 --- a/htdocs/product/inventory/ajax/ajax.inventory.php +++ /dev/null @@ -1,47 +0,0 @@ -rights->stock->creer)) { - echo -1; exit; - } - - $fk_det_inventory = GETPOST('fk_det_inventory'); - - $det = new InventoryLine($db); - if ($det->fetch($fk_det_inventory)) { - $det->qty_view += GETPOST('qty'); - $res = $det->update($user); - - echo $det->qty_view; - } else { - echo -2; - } - - break; - - case 'pmp': - if (empty($user->rights->stock->creer) || empty($user->rights->stock->changePMP)) { - echo -1; exit; - } - - $fk_det_inventory = GETPOST('fk_det_inventory'); - - $det = new InventoryLine($db); - if ($det->fetch($fk_det_inventory)) { - $det->new_pmp = price2num(GETPOST('pmp')); - $det->update($user); - - echo $det->new_pmp; - } else { - echo -2; - } - - break; -} diff --git a/htdocs/product/inventory/ajax/searchfrombarcode.php b/htdocs/product/inventory/ajax/searchfrombarcode.php new file mode 100644 index 00000000000..68ffee43c23 --- /dev/null +++ b/htdocs/product/inventory/ajax/searchfrombarcode.php @@ -0,0 +1,135 @@ +. + */ + +/** + * \file /htdocs/product/inventory/ajax/searchfrombarcode.php + * \brief File to make Ajax action on product and stock + */ + +if (!defined('NOTOKENRENEWAL')) { + define('NOTOKENRENEWAL', 1); // Disables token renewal +} +if (!defined('NOREQUIREMENU')) { + define('NOREQUIREMENU', '1'); +} +if (!defined('NOREQUIREHTML')) { + define('NOREQUIREHTML', '1'); +} +if (!defined('NOREQUIREAJAX')) { + define('NOREQUIREAJAX', '1'); +} +if (!defined('NOREQUIRESOC')) { + define('NOREQUIRESOC', '1'); +} +if (!defined('NOCSRFCHECK')) { + define('NOCSRFCHECK', '1'); +} +require '../../../main.inc.php'; +require_once DOL_DOCUMENT_ROOT."/product/stock/class/entrepot.class.php"; +$warehouse = new Entrepot($db); + +$action = GETPOST("action", "alpha"); +$barcode = GETPOST("barcode", "aZ09"); +$product = GETPOST("product"); +$response = ""; + +$fk_entrepot = GETPOST("fk_entrepot", "int"); +$fk_inventory = GETPOST("fk_inventory", "int"); +$fk_product = GETPOST("fk_product", "int"); +$reelqty = GETPOST("reelqty", "int"); +$batch = GETPOST("batch", "int"); +$mode = GETPOST("mode", "aZ"); + +$warehousefound = 0; +$warehouseid = 0; +$objectreturn = array(); + +if ($action == "existbarcode" && !empty($barcode)) { + if (!empty($mode) && $mode == "lotserial") { + $sql = "SELECT ps.fk_entrepot, ps.fk_product, p.barcode, ps.reel, pb.batch"; + $sql .= " FROM ".MAIN_DB_PREFIX."product_batch as pb"; + $sql .= " JOIN ".MAIN_DB_PREFIX."product_stock as ps ON pb.fk_product_stock = ps.rowid JOIN ".MAIN_DB_PREFIX."product as p ON ps.fk_product = p.rowid"; + $sql .= " WHERE pb.batch = '".$db->escape($barcode)."'"; + } else { + $sql = "SELECT ps.fk_entrepot, ps.fk_product, p.barcode,ps.reel"; + $sql .= " FROM ".MAIN_DB_PREFIX."product_stock as ps JOIN ".MAIN_DB_PREFIX."product as p ON ps.fk_product = p.rowid"; + $sql .= " WHERE p.barcode = '".$db->escape($barcode)."'"; + } + if (!empty($fk_entrepot)) { + $sql .= " AND ps.fk_entrepot = '".$db->escape($fk_entrepot)."'"; + } + if (!empty($fk_product)) { + $sql .= " AND ps.fk_product = '".$db->escape($fk_product)."'"; + } + $result = $db->query($sql); + if ($result) { + $nbline = $db->num_rows($result); + for ($i=0; $i < $nbline; $i++) { + $object = $db->fetch_object($result); + if (($mode == "barcode" && $barcode == $object->barcode) || ($mode == "lotserial" && $barcode == $object->batch)) { + $warehouse->fetch(0, $product["Warehouse"]); + if (!empty($object->fk_entrepot) && $warehouse->id == $object->fk_entrepot) { + $warehousefound++; + $warehouseid = $object->fk_entrepot; + $fk_product = $object->fk_product; + $reelqty = $object->reel; + + $objectreturn = array('fk_warehouse'=>$warehouseid,'fk_product'=>$fk_product,'reelqty'=>$reelqty); + } + } + } + if ($warehousefound < 1) { + $response = array('status'=>'error','errorcode'=>'NotFound','message'=>'No warehouse found for barcode'.$barcode); + } elseif ($warehousefound > 1) { + $response = array('status'=>'error','errorcode'=>'TooManyWarehouse','message'=>'Too many warehouse found'); + } else { + $response = array('status'=>'success','message'=>'Warehouse found','object'=>$objectreturn); + } + } else { + $response = array('status'=>'error','errorcode'=>'NotFound','message'=>"No results found for barcode"); + } +} else { + $response = array('status'=>'error','errorcode'=>'ActionError','message'=>"Error on action"); +} + +if ($action == "addnewlineproduct") { + require_once DOL_DOCUMENT_ROOT."/product/inventory/class/inventory.class.php"; + $inventoryline = new InventoryLine($db); + if (!empty($fk_inventory)) { + $inventoryline->fk_inventory = $fk_inventory; + + $inventoryline->fk_warehouse = $fk_entrepot; + $inventoryline->fk_product = $fk_product; + $inventoryline->qty_stock = $reelqty; + if (!empty($batch)) { + $inventoryline->batch = $batch; + } + $inventoryline->datec = dol_now(); + + $result = $inventoryline->create($user); + if ($result > 0) { + $response = array('status'=>'success','message'=>'Success on creating line','id_line'=>$result); + } else { + $response = array('status'=>'error','errorcode'=>'ErrorCreation','message'=>"Error on line creation"); + } + } else { + $response = array('status'=>'error','errorcode'=>'NoIdForInventory','message'=>"No id for inventory"); + } +} + +$response = json_encode($response); +echo $response; diff --git a/htdocs/product/inventory/card.php b/htdocs/product/inventory/card.php index b3a3fdc0b00..23473545b11 100644 --- a/htdocs/product/inventory/card.php +++ b/htdocs/product/inventory/card.php @@ -109,6 +109,7 @@ if ($reshook < 0) { } if (empty($reshook)) { + $savaction = $action; $error = 0; $backurlforlist = DOL_URL_ROOT.'/product/inventory/list.php'; @@ -152,6 +153,12 @@ if (empty($reshook)) { $autocopy = 'MAIN_MAIL_AUTOCOPY_INVENTORY_TO'; $trackid = 'stockinv'.$object->id; include DOL_DOCUMENT_ROOT.'/core/actions_sendmails.inc.php'; + + if (!$error && $savaction == 'confirm_validate' && $action == '' && $object->id > 0) { + // Switch to the tab inventory + header("Location: ".DOL_URL_ROOT.'/product/inventory/inventory.php?id='.$object->id); + exit; + } } @@ -200,6 +207,8 @@ if ($action == 'create') { // Other attributes include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_add.tpl.php'; + //print ''; + print '
    '.$langs->trans("ComposedProduct").''; diff --git a/htdocs/product/document.php b/htdocs/product/document.php index 230f8375538..79ff858ba6e 100644 --- a/htdocs/product/document.php +++ b/htdocs/product/document.php @@ -58,8 +58,8 @@ $hookmanager->initHooks(array('productdocuments')); // Get parameters $limit = GETPOST('limit', 'int') ? GETPOST('limit', 'int') : $conf->liste_limit; -$sortfield = GETPOST("sortfield", 'alpha'); -$sortorder = GETPOST("sortorder", 'alpha'); +$sortfield = GETPOST('sortfield', 'aZ09comma'); +$sortorder = GETPOST('sortorder', 'aZ09comma'); $page = GETPOSTISSET('pageplusone') ? (GETPOST('pageplusone') - 1) : GETPOST("page", 'int'); if (empty($page) || $page == -1) { $page = 0; diff --git a/htdocs/product/dynamic_price/class/price_parser.class.php b/htdocs/product/dynamic_price/class/price_parser.class.php index 3d28f4b6011..9bd986a3c73 100644 --- a/htdocs/product/dynamic_price/class/price_parser.class.php +++ b/htdocs/product/dynamic_price/class/price_parser.class.php @@ -263,13 +263,16 @@ class PriceParser return -1; } elseif ($res == 0) { $supplier_min_price = 0; + $supplier_min_price_with_discount = 0; } else { $supplier_min_price = $productFournisseur->fourn_unitprice; + $supplier_min_price_with_discount = $productFournisseur->fourn_unitprice_with_discount; } //Accessible values by expressions $extra_values = array_merge($extra_values, array( "supplier_min_price" => $supplier_min_price, + "supplier_min_price_with_discount" => $supplier_min_price_with_discount, )); //Parse the expression and return the price, if not error occurred check if price is higher than min @@ -329,12 +332,13 @@ class PriceParser //Values for product expressions $extra_values = array_merge($extra_values, array( "supplier_min_price" => 1, + "supplier_min_price_with_discount" => 2, )); //Values for supplier product expressions $extra_values = array_merge($extra_values, array( - "supplier_quantity" => 2, - "supplier_tva_tx" => 3, + "supplier_quantity" => 3, + "supplier_tva_tx" => 4, )); return $this->parseExpression($product, $expression, $extra_values); } diff --git a/htdocs/product/fournisseurs.php b/htdocs/product/fournisseurs.php index 02e0211c01d..98ab414a8c3 100644 --- a/htdocs/product/fournisseurs.php +++ b/htdocs/product/fournisseurs.php @@ -63,10 +63,9 @@ $error = 0; $extrafields = new ExtraFields($db); // If socid provided by ajax company selector -if (!empty($_REQUEST['search_fourn_id'])) { +if (GETPOST('search_fourn_id', 'int')) { $_GET['id_fourn'] = GETPOST('search_fourn_id', 'int'); $_POST['id_fourn'] = GETPOST('search_fourn_id', 'int'); - $_REQUEST['id_fourn'] = GETPOST('search_fourn_id', 'int'); } // Security check @@ -81,8 +80,8 @@ if (empty($user->rights->fournisseur->lire)) { } $limit = GETPOST('limit', 'int') ?GETPOST('limit', 'int') : $conf->liste_limit; -$sortfield = GETPOST("sortfield", 'alpha'); -$sortorder = GETPOST("sortorder", 'alpha'); +$sortfield = GETPOST('sortfield', 'aZ09comma'); +$sortorder = GETPOST('sortorder', 'aZ09comma'); $page = (GETPOST("page", 'int') ?GETPOST("page", 'int') : 0); if (empty($page) || $page == -1) { $page = 0; @@ -518,7 +517,7 @@ if ($id > 0 || $ref) { } else { $events = array(); $events[] = array('method' => 'getVatRates', 'url' => dol_buildpath('/core/ajax/vatrates.php', 1), 'htmlname' => 'tva_tx', 'params' => array()); - print $form->select_company(GETPOST("id_fourn", 'alpha'), 'id_fourn', 'fournisseur=1', 'SelectThirdParty', 0, 0, $events); + print img_picto('', 'company', 'class="pictofixedwidth"').$form->select_company(GETPOST("id_fourn", 'alpha'), 'id_fourn', 'fournisseur=1', 'SelectThirdParty', 0, 0, $events); $parameters = array('filtre'=>"fournisseur=1", 'html_name'=>'id_fourn', 'selected'=>GETPOST("id_fourn"), 'showempty'=>1, 'prod_id'=>$object->id); $reshook = $hookmanager->executeHooks('formCreateThirdpartyOptions', $parameters, $object, $action); @@ -1211,7 +1210,7 @@ END; $obj = $db->fetch_object($resql); foreach ($extralabels as $key => $value) { if (!empty($arrayfields['ef.'.$key]['checked']) && !empty($extrafields->attributes["product_fournisseur_price"]['list'][$key]) && $extrafields->attributes["product_fournisseur_price"]['list'][$key] != 3) { - print ''.$extrafields->showOutputField($key, $obj->{$key})."'.$extrafields->showOutputField($key, $obj->{$key}, '', 'product_fournisseur_price')."
    '.$langs->trans("InventoryCode").'INV'.$object->id.'
    '."\n"; print dol_get_fiche_end(); @@ -455,14 +464,13 @@ if ($object->id > 0 && (empty($action) || ($action != 'edit' && $action != 'crea $MAXEVENT = 10; - $morehtmlright = ''; - $morehtmlright .= $langs->trans("SeeAll"); - $morehtmlright .= ''; + //$morehtmlcenter = dolGetButtonTitle($langs->trans('SeeAll'), '', 'fa fa-list-alt imgforviewmode', DOL_URL_ROOT.'/product/inventory/inventory_info.php?id='.$object->id); + $morehtmlcenter = ''; // List of actions on element include_once DOL_DOCUMENT_ROOT.'/core/class/html.formactions.class.php'; $formactions = new FormActions($db); - $somethingshown = $formactions->showactions($object, $object->element, 0, 1, '', $MAXEVENT, '', $morehtmlright); + $somethingshown = $formactions->showactions($object, $object->element, 0, 1, '', $MAXEVENT, '', $morehtmlcenter); print ''; } diff --git a/htdocs/product/inventory/class/inventory.class.php b/htdocs/product/inventory/class/inventory.class.php index 57abc333a58..bb55bc1b143 100644 --- a/htdocs/product/inventory/class/inventory.class.php +++ b/htdocs/product/inventory/class/inventory.class.php @@ -26,6 +26,7 @@ // Put here all includes required by your class file require_once DOL_DOCUMENT_ROOT.'/core/class/commonobject.class.php'; +require_once DOL_DOCUMENT_ROOT.'/core/class/commonobjectline.class.php'; //require_once DOL_DOCUMENT_ROOT . '/societe/class/societe.class.php'; //require_once DOL_DOCUMENT_ROOT . '/product/class/product.class.php'; @@ -59,10 +60,10 @@ class Inventory extends CommonObject */ public $picto = 'inventory'; - const STATUS_DRAFT = 0; - const STATUS_VALIDATED = 1; - const STATUS_RECORDED = 2; - const STATUS_CANCELED = 9; + const STATUS_DRAFT = 0; // Draft + const STATUS_VALIDATED = 1; // Inventory is in process + const STATUS_RECORDED = 2; // Inventory is finisged. Stock movement has been recorded. + const STATUS_CANCELED = 9; // Canceled /** * 'type' field format ('integer', 'integer:ObjectClass:PathToClass[:AddCreateButtonOrNot[:Filter]]', 'sellist:TableName:LabelFieldName[:KeyFieldName[:KeyFieldParent[:Filter]]]', 'varchar(x)', 'double(24,8)', 'real', 'price', 'text', 'text:none', 'html', 'date', 'datetime', 'timestamp', 'duration', 'mail', 'phone', 'url', 'password') @@ -101,8 +102,7 @@ class Inventory extends CommonObject 'title' => array('type'=>'varchar(255)', 'label'=>'Label', 'visible'=>1, 'enabled'=>1, 'position'=>25, 'css'=>'minwidth300', 'csslist'=>'tdoverflowmax200'), 'fk_warehouse' => array('type'=>'integer:Entrepot:product/stock/class/entrepot.class.php', 'label'=>'Warehouse', 'visible'=>1, 'enabled'=>1, 'position'=>30, 'index'=>1, 'help'=>'InventoryForASpecificWarehouse', 'picto'=>'stock', 'css'=>'minwidth300 maxwidth500 widthcentpercentminusx', 'csslist'=>'tdoverflowmax200'), 'fk_product' => array('type'=>'integer:Product:product/class/product.class.php', 'label'=>'Product', 'visible'=>1, 'enabled'=>1, 'position'=>32, 'index'=>1, 'help'=>'InventoryForASpecificProduct', 'picto'=>'product', 'css'=>'minwidth300 maxwidth500 widthcentpercentminusx', 'csslist'=>'tdoverflowmax200'), - 'date_inventory' => array('type'=>'date', 'label'=>'DateValue', 'visible'=>1, 'enabled'=>1, 'position'=>35), - + 'date_inventory' => array('type'=>'date', 'label'=>'DateValue', 'visible'=>1, 'enabled'=>'$conf->global->STOCK_INVENTORY_ADD_A_VALUE_DATE', 'position'=>35), // This date is not used so disabled by default. 'date_creation' => array('type'=>'datetime', 'label'=>'DateCreation', 'enabled'=>1, 'visible'=>-2, 'notnull'=>1, 'position'=>500), 'tms' => array('type'=>'timestamp', 'label'=>'DateModification', 'enabled'=>1, 'visible'=>-2, 'notnull'=>1, 'position'=>501), 'date_validation' => array('type'=>'datetime', 'label'=>'DateValidation', 'visible'=>-2, 'enabled'=>1, 'position'=>502), @@ -111,7 +111,7 @@ class Inventory extends CommonObject 'fk_user_valid' => array('type'=>'integer:User:user/class/user.class.php', 'label'=>'UserValidation', 'visible'=>-2, 'enabled'=>1, 'position'=>512, 'csslist'=>'tdoverflowmax200'), 'import_key' => array('type'=>'varchar(14)', 'label'=>'ImportId', 'enabled'=>1, 'visible'=>-2, 'notnull'=>-1, 'index'=>0, 'position'=>1000), - 'status' => array('type'=>'integer', 'label'=>'Status', 'visible'=>4, 'enabled'=>1, 'position'=>1000, 'notnull'=>1, 'default'=>0, 'index'=>1, 'arrayofkeyval'=>array(0=>'Draft', 1=>'Validated', 2=>'Recorded', 9=>'Canceled')) + 'status' => array('type'=>'integer', 'label'=>'Status', 'visible'=>4, 'enabled'=>1, 'position'=>1000, 'notnull'=>1, 'default'=>0, 'index'=>1, 'arrayofkeyval'=>array(0=>'Draft', 1=>'Validated', 2=>'Closed', 9=>'Canceled')) ); /** @@ -368,7 +368,7 @@ class Inventory extends CommonObject } /** - * Set to Recorded + * Set to inventory to status "Closed". It means all stock movements were recorded. * * @param User $user User that creates * @param bool $notrigger false=launch triggers after, true=disable triggers @@ -616,17 +616,17 @@ class Inventory extends CommonObject $labelStatus = array(); $labelStatus[self::STATUS_DRAFT] = $langs->transnoentitiesnoconv('Draft'); - $labelStatus[self::STATUS_VALIDATED] = $langs->transnoentitiesnoconv('Validated').' ('.$langs->transnoentitiesnoconv('Started').')'; + $labelStatus[self::STATUS_VALIDATED] = $langs->transnoentitiesnoconv('Validated').' ('.$langs->transnoentitiesnoconv('InventoryStartedShort').')'; $labelStatus[self::STATUS_CANCELED] = $langs->transnoentitiesnoconv('Canceled'); $labelStatus[self::STATUS_RECORDED] = $langs->transnoentitiesnoconv('Closed'); $labelStatusShort[self::STATUS_DRAFT] = $langs->transnoentitiesnoconv('Draft'); - $labelStatusShort[self::STATUS_VALIDATED] = $langs->transnoentitiesnoconv('Started'); + $labelStatusShort[self::STATUS_VALIDATED] = $langs->transnoentitiesnoconv('InventoryStartedShort'); $labelStatusShort[self::STATUS_CANCELED] = $langs->transnoentitiesnoconv('Canceled'); $labelStatusShort[self::STATUS_RECORDED] = $langs->transnoentitiesnoconv('Closed'); $statusType = 'status'.$status; if ($status == self::STATUS_RECORDED) { - $statusType = 'status5'; + $statusType = 'status6'; } return dolGetStatus($labelStatus[$status], $labelStatusShort[$status], '', $statusType, $mode); diff --git a/htdocs/product/inventory/inventory.php b/htdocs/product/inventory/inventory.php index 9a4d61209ba..95c9e2972cb 100644 --- a/htdocs/product/inventory/inventory.php +++ b/htdocs/product/inventory/inventory.php @@ -103,122 +103,6 @@ if ($cancel) { $action = ''; } -if ($action == 'cancel_record' && $permissiontoadd) { - $object->setCanceled($user); -} - -if ($action == 'update' && !empty($user->rights->stock->mouvement->creer)) { - $stockmovment = new MouvementStock($db); - $stockmovment->setOrigin($object->element, $object->id); - - $db->begin(); - - $sql = 'SELECT id.rowid, id.datec as date_creation, id.tms as date_modification, id.fk_inventory, id.fk_warehouse,'; - $sql .= ' id.fk_product, id.batch, id.qty_stock, id.qty_view, id.qty_regulated'; - $sql .= ' FROM '.MAIN_DB_PREFIX.'inventorydet as id'; - $sql .= ' WHERE id.fk_inventory = '.((int) $object->id); - - $resql = $db->query($sql); - if ($resql) { - $num = $db->num_rows($resql); - $i = 0; - $totalarray = array(); - while ($i < $num) { - $line = $db->fetch_object($resql); - $qty_stock = $line->qty_stock; - $qty_view = $line->qty_view; // The quantity viewed by inventorier, the qty we target - - if (!is_null($qty_view)) { - $stock_movement_qty = price2num($qty_view - $qty_stock, 'MS'); - if ($stock_movement_qty != 0) { - if ($stock_movement_qty < 0) { - $movement_type = 1; - } else { - $movement_type = 0; - } - - $datemovement = ''; - - $idstockmove = $stockmovment->_create($user, $line->fk_product, $line->fk_warehouse, $stock_movement_qty, $movement_type, 0, $langs->trans('LabelOfInventoryMovemement', $object->id), 'INV'.$object->id, $datemovement, '', '', $line->batch); - if ($idstockmove < 0) { - $error++; - setEventMessages($stockmovment->error, $stockmovment->errors, 'errors'); - break; - } - } - } - $i++; - } - - if (!$error) { - $object->setRecorded($user); - } - } else { - setEventMessages($db->lasterror, null, 'errors'); - $error++; - } - - if (! $error) { - $db->commit(); - } else { - $db->rollback(); - } -} - -if ($action =='updateinventorylines' && $permissiontoadd) { - $sql = 'SELECT id.rowid, id.datec as date_creation, id.tms as date_modification, id.fk_inventory, id.fk_warehouse,'; - $sql .= ' id.fk_product, id.batch, id.qty_stock, id.qty_view, id.qty_regulated'; - $sql .= ' FROM '.MAIN_DB_PREFIX.'inventorydet as id'; - $sql .= ' WHERE id.fk_inventory = '.((int) $object->id); - - $db->begin(); - - $resql = $db->query($sql); - if ($resql) { - $num = $db->num_rows($resql); - $i = 0; - $totalarray = array(); - $inventoryline = new InventoryLine($db); - - while ($i < $num) { - $line = $db->fetch_object($resql); - $lineid = $line->rowid; - - if (GETPOST("id_".$lineid, 'alpha') != '') { // If a value was set ('0' or something else) - $qtytoupdate = price2num(GETPOST("id_".$lineid, 'alpha'), 'MS'); - $result = $inventoryline->fetch($lineid); - if ($qtytoupdate < 0) { - $result = -1; - setEventMessages($langs->trans("FieldCannotBeNegative", $langs->transnoentitiesnoconv("RealQty")), null, 'errors'); - } - if ($result > 0) { - $inventoryline->qty_view = $qtytoupdate; - $resultupdate = $inventoryline->update($user); - } - } else { - // Delete record - $result = $inventoryline->fetch($lineid); - if ($result > 0) { - $inventoryline->qty_view = null; - $resultupdate = $inventoryline->update($user); - } - } - - if ($result < 0 || $resultupdate < 0) { - $error++; - } - - $i++; - } - } - - if (!$error) { - $db->commit(); - } else { - $db->rollback(); - } -} - $parameters = array(); $reshook = $hookmanager->executeHooks('doActions', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks if ($reshook < 0) { @@ -228,6 +112,179 @@ if ($reshook < 0) { if (empty($reshook)) { $error = 0; + if ($action == 'cancel_record' && $permissiontoadd) { + $object->setCanceled($user); + } + + // Close inventory by recording the stock movements + if ($action == 'update' && !empty($user->rights->stock->mouvement->creer)) { + $stockmovment = new MouvementStock($db); + $stockmovment->setOrigin($object->element, $object->id); + + $cacheOfProducts = array(); + + $db->begin(); + + $sql = 'SELECT id.rowid, id.datec as date_creation, id.tms as date_modification, id.fk_inventory, id.fk_warehouse,'; + $sql .= ' id.fk_product, id.batch, id.qty_stock, id.qty_view, id.qty_regulated'; + $sql .= ' FROM '.MAIN_DB_PREFIX.'inventorydet as id'; + $sql .= ' WHERE id.fk_inventory = '.((int) $object->id); + + $resql = $db->query($sql); + if ($resql) { + $num = $db->num_rows($resql); + $i = 0; + $totalarray = array(); + while ($i < $num) { + $line = $db->fetch_object($resql); + + $qty_stock = $line->qty_stock; + $qty_view = $line->qty_view; // The quantity viewed by inventorier, the qty we target + + + // Load real stock we have now. + if (isset($cacheOfProducts[$line->fk_product])) { + $product_static = $cacheOfProducts[$line->fk_product]; + } else { + $product_static = new Product($db); + $result = $product_static->fetch($line->fk_product, '', '', '', 1, 1, 1); + + //$option = 'nobatch'; + $option .= ',novirtual'; + $product_static->load_stock($option); // Load stock_reel + stock_warehouse. + + $cacheOfProducts[$product_static->id] = $product_static; + } + + // Get the real quantity in stock now, but before the stock move for inventory. + $realqtynow = $product_static->stock_warehouse[$line->fk_warehouse]->real; + if ($conf->productbatch->enabled && $product_static->hasbatch()) { + $realqtynow = $product_static->stock_warehouse[$line->fk_warehouse]->detail_batch[$line->batch]->qty; + } + + + if (!is_null($qty_view)) { + $stock_movement_qty = price2num($qty_view - $realqtynow, 'MS'); + if ($stock_movement_qty != 0) { + if ($stock_movement_qty < 0) { + $movement_type = 1; + } else { + $movement_type = 0; + } + + $datemovement = ''; + //$inventorycode = 'INV'.$object->id; + $inventorycode = 'INV-'.$object->ref; + + $idstockmove = $stockmovment->_create($user, $line->fk_product, $line->fk_warehouse, $stock_movement_qty, $movement_type, 0, $langs->trans('LabelOfInventoryMovemement', $object->ref), $inventorycode, $datemovement, '', '', $line->batch); + if ($idstockmove < 0) { + $error++; + setEventMessages($stockmovment->error, $stockmovment->errors, 'errors'); + break; + } + + // Update line with id of stock movement (and the start quantity if it has changed this last recording) + $sqlupdate = "UPDATE ".MAIN_DB_PREFIX."inventorydet"; + $sqlupdate .= " SET fk_movement = ".((int) $idstockmove); + if ($qty_stock != $realqtynow) { + $sqlupdate .= ", qty_stock = ".((float) $realqtynow); + } + $sqlupdate .= " WHERE rowid = ".((int) $line->rowid); + $resqlupdate = $db->query($sqlupdate); + if (! $resqlupdate) { + $error++; + setEventMessages($db->lasterror(), null, 'errors'); + break; + } + } + } + $i++; + } + + if (!$error) { + $object->setRecorded($user); + } + } else { + setEventMessages($db->lasterror, null, 'errors'); + $error++; + } + + if (! $error) { + $db->commit(); + } else { + $db->rollback(); + } + } + + // Save quantity found during inventory + if ($action =='updateinventorylines' && $permissiontoadd) { + $sql = 'SELECT id.rowid, id.datec as date_creation, id.tms as date_modification, id.fk_inventory, id.fk_warehouse,'; + $sql .= ' id.fk_product, id.batch, id.qty_stock, id.qty_view, id.qty_regulated'; + $sql .= ' FROM '.MAIN_DB_PREFIX.'inventorydet as id'; + $sql .= ' WHERE id.fk_inventory = '.((int) $object->id); + + $db->begin(); + + $resql = $db->query($sql); + if ($resql) { + $num = $db->num_rows($resql); + $i = 0; + $totalarray = array(); + $inventoryline = new InventoryLine($db); + + while ($i < $num) { + $line = $db->fetch_object($resql); + $lineid = $line->rowid; + + if (GETPOST("id_".$lineid, 'alpha') != '') { // If a value was set ('0' or something else) + $qtytoupdate = price2num(GETPOST("id_".$lineid, 'alpha'), 'MS'); + $result = $inventoryline->fetch($lineid); + if ($qtytoupdate < 0) { + $result = -1; + setEventMessages($langs->trans("FieldCannotBeNegative", $langs->transnoentitiesnoconv("RealQty")), null, 'errors'); + } + if ($result > 0) { + $inventoryline->qty_stock = price2num(GETPOST('stock_qty_'.$lineid, 'alpha'), 'MS'); // The new value that was set in as hidden field + $inventoryline->qty_view = $qtytoupdate; // The new value we want + $resultupdate = $inventoryline->update($user); + } + } else { + // Delete record + $result = $inventoryline->fetch($lineid); + if ($result > 0) { + $inventoryline->qty_view = null; // The new value we want + $resultupdate = $inventoryline->update($user); + } + } + + if ($result < 0 || $resultupdate < 0) { + $error++; + } + + $i++; + } + } + + // Update line with id of stock movement (and the start quantity if it has changed this last recording) + if (! $error) { + $sqlupdate = "UPDATE ".MAIN_DB_PREFIX."inventory"; + $sqlupdate .= " SET fk_user_modif = ".((int) $user->id); + $sqlupdate .= " WHERE rowid = ".((int) $object->id); + $resqlupdate = $db->query($sqlupdate); + if (! $resqlupdate) { + $error++; + setEventMessages($db->lasterror(), null, 'errors'); + } + } + + if (!$error) { + $db->commit(); + } else { + $db->rollback(); + } + } + + $backurlforlist = DOL_URL_ROOT.'/product/inventory/list.php'; $backtopage = DOL_URL_ROOT.'/product/inventory/inventory.php?id='.$object->id; @@ -286,10 +343,15 @@ if (empty($reshook)) { $result = $tmp->create($user); if ($result < 0) { if ($db->lasterrno() == 'DB_ERROR_RECORD_ALREADY_EXISTS') { - setEventMessages($langs->trans("DuplicateRecord"), null, 'errors'); + $langs->load("errors"); + setEventMessages($langs->trans("ErrorRecordAlreadyExists"), null, 'errors'); } else { dol_print_error($db, $tmp->error, $tmp->errors); } + } else { + // Clear var + $_POST['batch'] = ''; + $_POST['qtytoadd'] = ''; } } } @@ -310,20 +372,22 @@ $help_url = ''; llxHeader('', $langs->trans('Inventory'), $help_url); -// Disable button Generate movement if data were not saved -print ''; + // Link to autofill + print ''.img_picto('', 'autofill', 'class="paddingrightonly"').$langs->trans('AutofillWithExpected').''; + print ''; + + // Link to reset qty + print ''.img_picto('', 'eraser', 'class="paddingrightonly"').$langs->trans("ClearQtys").''; + } else { + print ''.$langs->trans("Save").''."\n"; + } } print '
    '; print '
    '; @@ -539,17 +606,37 @@ if ($object->id > 0) { // Popup for mass barcode scanning if ($action == 'updatebyscaning') { if ($permissiontoadd) { + // Output the javascript to manage the scanner tool. print ''; } include DOL_DOCUMENT_ROOT.'/core/class/html.formother.class.php'; $formother = new FormOther($db); - print $formother->getHTMLScannerForm(); + print $formother->getHTMLScannerForm("barcodescannerjs", 'all'); } //Call method to undo changes in real qty print ''; @@ -661,24 +854,29 @@ if ($object->id > 0) { print '
    '.$langs->trans("Warehouse").''.$langs->trans("Product").''; print $langs->trans("Batch"); print ''.$langs->trans("ExpectedQty").''; + print ''; print $form->textwithpicto($langs->trans("RealQty"), $langs->trans("InventoryRealQtyHelp")); print ''; print '
    '; + //print $langs->trans("StockMovement"); + print '
    '; print $formproduct->selectWarehouses((GETPOSTISSET('fk_warehouse') ? GETPOST('fk_warehouse', 'int') : $object->fk_warehouse), 'fk_warehouse', 'warehouseopen', 1, 0, 0, '', 0, 0, array(), 'maxwidth300'); @@ -686,13 +884,13 @@ if ($object->id > 0) { print ''; print $form->select_produits((GETPOSTISSET('fk_product') ? GETPOST('fk_product', 'int') : $object->fk_product), 'fk_product', '', 0, 0, -1, 2, '', 0, null, 0, '1', 0, 'maxwidth300'); print ''; print ''; print ''; + print ''; print ''; print ''; print $obj->batch; print ''; - print $obj->qty_stock; + // Expected quantity = Quantity in stock when we start inventory + print ''; + $valuetoshow = $obj->qty_stock; + // For inventory not yet close, we overwrite with the real value in stock now + if ($object->status == $object::STATUS_DRAFT || $object->status == $object::STATUS_VALIDATED) { + if (!empty($conf->productbatch->enabled) && $product_static->hasbatch()) { + $valuetoshow = $product_static->stock_warehouse[$obj->fk_warehouse]->detail_batch[$obj->batch]->qty; + } else { + $valuetoshow = $product_static->stock_warehouse[$obj->fk_warehouse]->real; + } + } + print price2num($valuetoshow, 'MS'); + print ''; print ''; - if ($object->status == $object::STATUS_VALIDATED) { + if ($object->status == $object::STATUS_DRAFT || $object->status == $object::STATUS_VALIDATED) { + print ''; $qty_view = GETPOST("id_".$obj->rowid) && price2num(GETPOST("id_".$obj->rowid), 'MS') >= 0 ? GETPOST("id_".$obj->rowid) : $obj->qty_view; - if (!$hasinput && $qty_view !== null && $obj->qty_stock != $qty_view) { + + //if (!$hasinput && $qty_view !== null && $obj->qty_stock != $qty_view) { + if ($qty_view != '') { $hasinput = true; } + print ''; + print img_picto('', 'eraser', 'class="opacitymedium"'); + print ''; print ''; print ''; - print '  '; print ''.img_delete().''; - print ''; + print $obj->qty_view; // qty found + print ''; + if ($obj->fk_movement > 0) { + $stockmovment = new MouvementStock($db); + $stockmovment->fetch($obj->fk_movement); + print $stockmovment->getNomUrl(1, 'movements'); + } print '
    '; if ($object->multiprices_base_type[$soc->price_level] == 'TTC') { - print price($object->multiprices_ttc[$soc->price_level]); + print ''.price($object->multiprices_ttc[$soc->price_level]).''; } else { - print price($object->multiprices[$soc->price_level]); + print ''.price($object->multiprices[$soc->price_level]).''; } if ($object->multiprices_base_type[$soc->price_level]) { print ' '.$langs->trans($object->multiprices_base_type[$soc->price_level]); @@ -827,22 +948,23 @@ if (!empty($conf->global->PRODUIT_MULTIPRICES) || !empty($conf->global->PRODUIT_ } // TVA + print ''; print '
    '.$langs->trans("DefaultTaxRate").''; $positiverates = ''; if (price2num($object->tva_tx)) { - $positiverates .= ($positiverates ? '/' : '').price2num($object->tva_tx); + $positiverates .= ($positiverates ? '/' : '').price2num($object->tva_tx); } if (price2num($object->localtax1_type)) { - $positiverates .= ($positiverates ? '/' : '').price2num($object->localtax1_tx); + $positiverates .= ($positiverates ? '/' : '').price2num($object->localtax1_tx); } if (price2num($object->localtax2_type)) { - $positiverates .= ($positiverates ? '/' : '').price2num($object->localtax2_tx); + $positiverates .= ($positiverates ? '/' : '').price2num($object->localtax2_tx); } if (empty($positiverates)) { $positiverates = '0'; } - echo vatrate($positiverates.($object->default_vat_code ? ' ('.$object->default_vat_code.')' : ''), '%', $object->tva_npr); + print vatrate($positiverates.($object->default_vat_code ? ' ('.$object->default_vat_code.')' : ''), true, $object->tva_npr, 1); /* if ($object->default_vat_code) { @@ -890,15 +1012,15 @@ if (!empty($conf->global->PRODUIT_MULTIPRICES) || !empty($conf->global->PRODUIT_ print ''.price($object->multiprices_ttc[$i]); + print ''.price($object->multiprices_ttc[$i]); } else { - print ''.price($object->multiprices[$i]); + print ''.price($object->multiprices[$i]); } if ($object->multiprices_base_type[$i]) { - print ' '.$langs->trans($object->multiprices_base_type [$i]).'
    '; print $data['link']; print "'; + print $data['product']; + print ""; print $data['date_valid']."'.$data['qty_toconsume'].'
    '.$langs->trans("Type").''; $array = array('-1'=>' ', '0'=>$langs->trans('Product'), '1'=>$langs->trans('Service')); - print $form->selectarray('type', $array, $type); + print $form->selectarray('type', $array, $type, 0, 0, 0, '', 0, 0, 0, '', 'minwidth100'); print '
    '.$langs->trans("ProductOrService").''; print img_picto('', 'product', 'class="pictofixedwidth"'); - print $form->select_produits($id, 'id', '', 0, 0, 1, 2, '', 0, array(), 0, '1', 0, 'maxwidth500'); + print $form->select_produits($id, 'id', '', 0, 0, 1, 2, '', ($conf->dol_optimize_smallscreen ? 1 : 0), array(), 0, '1', 0, 'widthcentpercentminusx maxwidth400'); print '
    '.$langs->trans("Categories").''; $moreforfilter .= img_picto($langs->trans("Categories"), 'category', 'class="pictofixedwidth"'); - $moreforfilter .= $htmlother->select_categories(Categorie::TYPE_PRODUCT, $search_categ, 'search_categ', 1, 1, 'widthcentpercentminusx maxwidth300'); + $moreforfilter .= $htmlother->select_categories(Categorie::TYPE_PRODUCT, $search_categ, 'search_categ', 1, 1, 'widthcentpercentminusx maxwidth400'); print $moreforfilter; print '
    '; // Label print ''; print '
    '; @@ -478,6 +480,7 @@ if ($result || !($id > 0)) { print $graphfiles[$key]['output']; print '
    '; + print '
    '; if ($i % 2 == 0) { print "\n".''."\n"; diff --git a/htdocs/product/stats/commande.php b/htdocs/product/stats/commande.php index abfd329dfa4..9f38f68ee33 100644 --- a/htdocs/product/stats/commande.php +++ b/htdocs/product/stats/commande.php @@ -49,8 +49,8 @@ $hookmanager->initHooks(array('productstatsorder')); // Load variable for pagination $limit = GETPOST('limit', 'int') ?GETPOST('limit', 'int') : $conf->liste_limit; -$sortfield = GETPOST("sortfield", 'alpha'); -$sortorder = GETPOST("sortorder", 'alpha'); +$sortfield = GETPOST('sortfield', 'aZ09comma'); +$sortorder = GETPOST('sortorder', 'aZ09comma'); $page = GETPOSTISSET('pageplusone') ? (GETPOST('pageplusone') - 1) : GETPOST("page", 'int'); if (empty($page) || $page == -1) { $page = 0; diff --git a/htdocs/product/stats/contrat.php b/htdocs/product/stats/contrat.php index e44619d1b19..0c569d3530c 100644 --- a/htdocs/product/stats/contrat.php +++ b/htdocs/product/stats/contrat.php @@ -46,8 +46,8 @@ $hookmanager->initHooks(array('productstatscontract')); // Load variable for pagination $limit = GETPOST('limit', 'int') ?GETPOST('limit', 'int') : $conf->liste_limit; -$sortfield = GETPOST("sortfield", 'alpha'); -$sortorder = GETPOST("sortorder", 'alpha'); +$sortfield = GETPOST('sortfield', 'aZ09comma'); +$sortorder = GETPOST('sortorder', 'aZ09comma'); $page = GETPOSTISSET('pageplusone') ? (GETPOST('pageplusone') - 1) : GETPOST("page", 'int'); if (empty($page) || $page == -1) { $page = 0; diff --git a/htdocs/product/stats/facture.php b/htdocs/product/stats/facture.php index 8da6e97f55c..13c6329b34f 100644 --- a/htdocs/product/stats/facture.php +++ b/htdocs/product/stats/facture.php @@ -52,8 +52,8 @@ $showmessage = GETPOST('showmessage'); // Load variable for pagination $limit = GETPOST('limit', 'int') ?GETPOST('limit', 'int') : $conf->liste_limit; -$sortfield = GETPOST("sortfield", 'alpha'); -$sortorder = GETPOST("sortorder", 'alpha'); +$sortfield = GETPOST('sortfield', 'aZ09comma'); +$sortorder = GETPOST('sortorder', 'aZ09comma'); $page = GETPOSTISSET('pageplusone') ? (GETPOST('pageplusone') - 1) : GETPOST("page", 'int'); if (empty($page) || $page == -1) { $page = 0; diff --git a/htdocs/product/stats/facture_fournisseur.php b/htdocs/product/stats/facture_fournisseur.php index d61b6de9491..9db71e92148 100644 --- a/htdocs/product/stats/facture_fournisseur.php +++ b/htdocs/product/stats/facture_fournisseur.php @@ -50,8 +50,8 @@ $hookmanager->initHooks(array('productstatssupplierinvoice')); // Load variable for pagination $limit = GETPOST('limit', 'int') ?GETPOST('limit', 'int') : $conf->liste_limit; -$sortfield = GETPOST("sortfield", 'alpha'); -$sortorder = GETPOST("sortorder", 'alpha'); +$sortfield = GETPOST('sortfield', 'aZ09comma'); +$sortorder = GETPOST('sortorder', 'aZ09comma'); $page = GETPOSTISSET('pageplusone') ? (GETPOST('pageplusone') - 1) : GETPOST("page", 'int'); if (empty($page) || $page == -1) { $page = 0; diff --git a/htdocs/product/stats/mo.php b/htdocs/product/stats/mo.php index c63034e95a6..693f1fbae03 100644 --- a/htdocs/product/stats/mo.php +++ b/htdocs/product/stats/mo.php @@ -46,8 +46,8 @@ $hookmanager->initHooks(array('productstatsmo')); // Load variable for pagination $limit = GETPOST('limit', 'int') ?GETPOST('limit', 'int') : $conf->liste_limit; -$sortfield = GETPOST("sortfield", 'alpha'); -$sortorder = GETPOST("sortorder", 'alpha'); +$sortfield = GETPOST('sortfield', 'aZ09comma'); +$sortorder = GETPOST('sortorder', 'aZ09comma'); $page = GETPOSTISSET('pageplusone') ? (GETPOST('pageplusone') - 1) : GETPOST("page", 'int'); if (empty($page) || $page == -1) { $page = 0; diff --git a/htdocs/product/stock/card.php b/htdocs/product/stock/card.php index ce9c26c5480..18a394559a3 100644 --- a/htdocs/product/stock/card.php +++ b/htdocs/product/stock/card.php @@ -54,8 +54,8 @@ $id = GETPOST('id', 'int'); $socid = GETPOST('socid', 'int'); $ref = GETPOST('ref', 'alpha'); -$sortfield = GETPOST("sortfield", 'alpha'); -$sortorder = GETPOST("sortorder", 'alpha'); +$sortfield = GETPOST('sortfield', 'aZ09comma'); +$sortorder = GETPOST('sortorder', 'aZ09comma'); if (!$sortfield) { $sortfield = "p.ref"; } @@ -207,7 +207,7 @@ if (empty($reshook)) { $object->fax = GETPOST("fax"); // Fill array 'array_options' with data from add form - $ret = $extrafields->setOptionalsFromPost(null, $object); + $ret = $extrafields->setOptionalsFromPost(null, $object, '@GETPOSTISSET'); if ($ret < 0) { $error++; } @@ -966,14 +966,13 @@ if ($action != 'create' && $action != 'edit' && $action != 'delete') { $MAXEVENT = 10; - $morehtmlright = ''; - $morehtmlright .= $langs->trans("SeeAll"); - $morehtmlright .= ''; + $morehtmlcenter = ''; + //$morehtmlcenter = dolGetButtonTitle($langs->trans('SeeAll'), '', 'fa fa-list-alt imgforviewmode', DOL_URL_ROOT.'/product/stock/agenda.php?id='.$object->id); // List of actions on element include_once DOL_DOCUMENT_ROOT.'/core/class/html.formactions.class.php'; $formactions = new FormActions($db); - $somethingshown = $formactions->showactions($object, 'stock', 0, 1, '', $MAXEVENT, '', $morehtmlright); // Show all action for product + $somethingshown = $formactions->showactions($object, 'stock', 0, 1, '', $MAXEVENT, '', $morehtmlcenter); // Show all action for product print ''; } diff --git a/htdocs/product/stock/class/api_stockmovements.class.php b/htdocs/product/stock/class/api_stockmovements.class.php index 19f23edd4f4..8291e910917 100644 --- a/htdocs/product/stock/class/api_stockmovements.class.php +++ b/htdocs/product/stock/class/api_stockmovements.class.php @@ -109,8 +109,9 @@ class StockMovements extends DolibarrApi $sql .= ' WHERE 1 = 1'; // Add sql filters if ($sqlfilters) { - if (!DolibarrApi::_checkFilters($sqlfilters)) { - throw new RestException(503, 'Error when validating parameter sqlfilters '.$sqlfilters); + $errormessage = ''; + if (!DolibarrApi::_checkFilters($sqlfilters, $errormessage)) { + throw new RestException(503, 'Error when validating parameter sqlfilters -> '.$errormessage); } $regexstring = '\(([^:\'\(\)]+:[^:\'\(\)]+:[^\(\)]+)\)'; $sql .= " AND (".preg_replace_callback('/'.$regexstring.'/', 'DolibarrApi::_forge_criteria_callback', $sqlfilters).")"; diff --git a/htdocs/product/stock/class/api_warehouses.class.php b/htdocs/product/stock/class/api_warehouses.class.php index 3bddbaff8f9..3a128a12680 100644 --- a/htdocs/product/stock/class/api_warehouses.class.php +++ b/htdocs/product/stock/class/api_warehouses.class.php @@ -116,8 +116,9 @@ class Warehouses extends DolibarrApi } // Add sql filters if ($sqlfilters) { - if (!DolibarrApi::_checkFilters($sqlfilters)) { - throw new RestException(503, 'Error when validating parameter sqlfilters '.$sqlfilters); + $errormessage = ''; + if (!DolibarrApi::_checkFilters($sqlfilters, $errormessage)) { + throw new RestException(503, 'Error when validating parameter sqlfilters -> '.$errormessage); } $regexstring = '\(([^:\'\(\)]+:[^:\'\(\)]+:[^\(\)]+)\)'; $sql .= " AND (".preg_replace_callback('/'.$regexstring.'/', 'DolibarrApi::_forge_criteria_callback', $sqlfilters).")"; diff --git a/htdocs/product/stock/class/entrepot.class.php b/htdocs/product/stock/class/entrepot.class.php index fe36b6364fc..617550c09dd 100644 --- a/htdocs/product/stock/class/entrepot.class.php +++ b/htdocs/product/stock/class/entrepot.class.php @@ -445,7 +445,7 @@ class Entrepot extends CommonObject if ($id) { $sql .= " WHERE rowid = ".((int) $id); } else { - $sql .= " WHERE entity = ".$conf->entity; + $sql .= " WHERE entity IN (".getEntity('stock').")"; if ($ref) { $sql .= " AND ref = '".$this->db->escape($ref)."'"; } diff --git a/htdocs/product/stock/class/mouvementstock.class.php b/htdocs/product/stock/class/mouvementstock.class.php index 3a43d9ee6bb..a6bc6c2ec26 100644 --- a/htdocs/product/stock/class/mouvementstock.class.php +++ b/htdocs/product/stock/class/mouvementstock.class.php @@ -156,7 +156,7 @@ class MouvementStock extends CommonObject * @param int $price Unit price HT of product, used to calculate average weighted price (AWP or PMP in french). If 0, average weighted price is not changed. * @param string $label Label of stock movement * @param string $inventorycode Inventory code - * @param string $datem Force date of movement + * @param integer|string $datem Force date of movement * @param integer|string $eatby eat-by date. Will be used if lot does not exists yet and will be created. * @param integer|string $sellby sell-by date. Will be used if lot does not exists yet and will be created. * @param string $batch batch number @@ -439,7 +439,7 @@ class MouvementStock extends CommonObject $sql .= " datem, fk_product, batch, eatby, sellby,"; $sql .= " fk_entrepot, value, type_mouvement, fk_user_author, label, inventorycode, price, fk_origin, origintype, fk_projet"; $sql .= ")"; - $sql .= " VALUES ('".$this->db->idate($now)."', ".((int) $this->product_id).", "; + $sql .= " VALUES ('".$this->db->idate($this->datem)."', ".((int) $this->product_id).", "; $sql .= " ".($batch ? "'".$this->db->escape($batch)."'" : "null").", "; $sql .= " ".($eatby ? "'".$this->db->idate($eatby)."'" : "null").", "; $sql .= " ".($sellby ? "'".$this->db->idate($sellby)."'" : "null").", "; @@ -764,19 +764,19 @@ class MouvementStock extends CommonObject /** * Decrease stock for product and subproducts * - * @param User $user Object user - * @param int $fk_product Id product - * @param int $entrepot_id Warehouse id - * @param int $qty Quantity - * @param int $price Price - * @param string $label Label of stock movement - * @param string $datem Force date of movement - * @param integer $eatby eat-by date - * @param integer $sellby sell-by date - * @param string $batch batch number - * @param int $id_product_batch Id product_batch - * @param string $inventorycode Inventory code - * @return int <0 if KO, >0 if OK + * @param User $user Object user + * @param int $fk_product Id product + * @param int $entrepot_id Warehouse id + * @param int $qty Quantity + * @param int $price Price + * @param string $label Label of stock movement + * @param integer|string $datem Force date of movement + * @param integer $eatby eat-by date + * @param integer $sellby sell-by date + * @param string $batch batch number + * @param int $id_product_batch Id product_batch + * @param string $inventorycode Inventory code + * @return int <0 if KO, >0 if OK */ public function livraison($user, $fk_product, $entrepot_id, $qty, $price = 0, $label = '', $datem = '', $eatby = '', $sellby = '', $batch = '', $id_product_batch = 0, $inventorycode = '') { @@ -799,7 +799,7 @@ class MouvementStock extends CommonObject * @param integer|string $eatby eat-by date * @param integer|string $sellby sell-by date * @param string $batch batch number - * @param string $datem Force date of movement + * @param integer|string $datem Force date of movement * @param int $id_product_batch Id product_batch * @param string $inventorycode Inventory code * @return int <0 if KO, >0 if OK @@ -813,28 +813,6 @@ class MouvementStock extends CommonObject return $this->_create($user, $fk_product, $entrepot_id, $qty, 3, $price, $label, $inventorycode, $datem, $eatby, $sellby, $batch, $skip_batch, $id_product_batch); } - - // /** - // * Return nb of subproducts lines for a product - // * - // * @param int $id Id of product - // * @return int <0 if KO, nb of subproducts if OK - // * @deprecated A count($product->getChildsArbo($id,1)) is same. No reason to have this in this class. - // */ - // public function nbOfSubProducts($id) - // { - // $nbSP=0; - - // $resql = "SELECT count(*) as nb FROM ".MAIN_DB_PREFIX."product_association"; - // $resql.= " WHERE fk_product_pere = ".((int) $id); - // if ($this->db->query($resql)) - // { - // $obj=$this->db->fetch_object($resql); - // $nbSP=$obj->nb; - // } - // return $nbSP; - // } - /** * Count number of product in stock before a specific date * @@ -1093,7 +1071,7 @@ class MouvementStock extends CommonObject * Use this->id,this->lastname, this->firstname * * @param int $withpicto Include picto in link (0=No picto, 1=Include picto into link, 2=Only picto) - * @param string $option On what the link point to + * @param string $option On what the link point to ('' = Tab of stock movement of warehouse, 'movements' = list of movements) * @param integer $notooltip 1=Disable tooltip * @param int $maxlen Max length of visible user name * @param string $morecss Add more css on link @@ -1104,16 +1082,21 @@ class MouvementStock extends CommonObject global $langs, $conf, $db; $result = ''; - $companylink = ''; - $label = ''.$langs->trans("Movement").' '.$this->id.''; + $label = img_picto('', 'stock', 'class="pictofixedwidth"').''.$langs->trans("Movement").' '.$this->id.''; $label .= '
    '; $label .= ''.$langs->trans('Label').': '.$this->label; - $label .= '
    '.$langs->trans('Qty').': '.$this->qty; + $label .= '
    '.$langs->trans('Qty').': '.($this->qty > 0 ? '+' : '').$this->qty; $label .= '
    '; - $link = 'id; + } else { + $url = DOL_URL_ROOT.'/product/stock/movement_list.php?id='.$this->warehouse_id.'&msid='.$this->id; + } + + $link = 'hooks_modules contains array of hook context $object = new Entrepot($db); $extrafields = new ExtraFields($db); -$diroutputmassaction = $conf->inventory->dir_output.'/temp/massgeneration/'.$user->id; +$diroutputmassaction = $conf->stock->dir_output.'/temp/massgeneration/'.$user->id; $hookmanager->initHooks(array('stocklist')); // Fetch optionals attributes and labels @@ -196,6 +196,7 @@ $help_url = 'EN:Module_Stocks_En|FR:Module_Stock|ES:Módulo_Stocks'; $title = $langs->trans("ListOfWarehouses"); $totalarray = array(); +$totalarray['nbfield'] = 0; // Build and execute select // -------------------------------------------------------------------- @@ -228,7 +229,7 @@ $sql .= " FROM ".MAIN_DB_PREFIX.$object->table_element." as t"; if (!empty($conf->categorie->enabled)) { $sql .= Categorie::getFilterJoinQuery(Categorie::TYPE_WAREHOUSE, "t.rowid"); } -if (is_array($extrafields->attributes[$object->table_element]['label']) && count($extrafields->attributes[$object->table_element]['label'])) { +if (!empty($extrafields->attributes[$object->table_element]['label']) && is_array($extrafields->attributes[$object->table_element]['label']) && count($extrafields->attributes[$object->table_element]['label'])) { $sql .= " LEFT JOIN ".MAIN_DB_PREFIX.$object->table_element."_extrafields as ef on (t.rowid = ef.fk_object)"; } $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."product_stock as ps ON t.rowid = ps.fk_entrepot"; @@ -473,7 +474,7 @@ foreach ($object->fields as $key => $val) { } elseif ((strpos($val['type'], 'integer:') === 0) || (strpos($val['type'], 'sellist:') === 0)) { print $object->showInputField($val, $key, $search[$key], '', '', 'search_', 'maxwidth125', 1); } elseif (!preg_match('/^(date|timestamp)/', $val['type'])) { - print ''; + print ''; } print '
    '."\n"; +print '
    '; // You can use div-table-responsive-no-min if you dont need reserved height for your table +print '
    '."\n"; // Fields title search -print ''; +// -------------------------------------------------------------------- +print ''; if (!empty($arrayfields['m.rowid']['checked'])) { // Ref print ''; } -// Actions +// Action column print ''; -print "\n"; +print ''."\n"; + +// Fields title label +// -------------------------------------------------------------------- print ''; if (!empty($arrayfields['m.rowid']['checked'])) { print_liste_field_titre($arrayfields['m.rowid']['label'], $_SERVER["PHP_SELF"], 'm.rowid', '', $param, '', $sortfield, $sortorder); @@ -1122,7 +1249,7 @@ include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_search_title.tpl.php'; // Hook fields $parameters = array('arrayfields'=>$arrayfields, 'param'=>$param, 'sortfield'=>$sortfield, 'sortorder'=>$sortorder); -$reshook = $hookmanager->executeHooks('printFieldListTitle', $parameters); // Note that $action and $object may have been modified by hook +$reshook = $hookmanager->executeHooks('printFieldListTitle', $parameters, $object); // Note that $action and $object may have been modified by hook print $hookmanager->resPrint; if (!empty($arrayfields['m.datec']['checked'])) { print_liste_field_titre($arrayfields['p.datec']['label'], $_SERVER["PHP_SELF"], "p.datec", "", $param, '', $sortfield, $sortorder, 'center nowrap '); @@ -1130,51 +1257,77 @@ if (!empty($arrayfields['m.datec']['checked'])) { if (!empty($arrayfields['m.tms']['checked'])) { print_liste_field_titre($arrayfields['p.tms']['label'], $_SERVER["PHP_SELF"], "p.tms", "", $param, '', $sortfield, $sortorder, 'center nowrap '); } +// Action column print_liste_field_titre($selectedfields, $_SERVER["PHP_SELF"], "", '', '', '', $sortfield, $sortorder, 'center maxwidthsearch '); -print "\n"; +print ''."\n"; $arrayofuniqueproduct = array(); + +// Loop on record +// -------------------------------------------------------------------- $i = 0; $totalarray = array(); -while ($i < min($num, $limit)) { - $objp = $db->fetch_object($resql); +$totalarray['nbfield'] = 0; +while ($i < ($limit ? min($num, $limit) : $num)) { + $obj = $db->fetch_object($resql); + if (empty($obj)) { + break; // Should not happen + } - $userstatic->id = $objp->fk_user_author; - $userstatic->login = $objp->login; - $userstatic->lastname = $objp->lastname; - $userstatic->firstname = $objp->firstname; - $userstatic->photo = $objp->photo; - $userstatic->email = $objp->user_email; - $userstatic->statut = $objp->user_status; + $userstatic->id = $obj->fk_user_author; + $userstatic->login = $obj->login; + $userstatic->lastname = $obj->lastname; + $userstatic->firstname = $obj->firstname; + $userstatic->photo = $obj->photo; + $userstatic->email = $obj->user_email; + $userstatic->statut = $obj->user_status; - $productstatic->id = $objp->rowid; - $productstatic->ref = $objp->product_ref; - $productstatic->label = $objp->produit; - $productstatic->type = $objp->type; - $productstatic->entity = $objp->entity; - $productstatic->status = $objp->tosell; - $productstatic->status_buy = $objp->tobuy; - $productstatic->status_batch = $objp->tobatch; + // Multilangs + if (!empty($conf->global->MAIN_MULTILANGS)) { // If multilang is enabled + // TODO Use a cache + $sql = "SELECT label"; + $sql .= " FROM ".MAIN_DB_PREFIX."product_lang"; + $sql .= " WHERE fk_product = ".((int) $obj->rowid); + $sql .= " AND lang = '".$db->escape($langs->getDefaultLang())."'"; + $sql .= " LIMIT 1"; - $productlot->id = $objp->lotid; - $productlot->batch = $objp->batch; - $productlot->eatby = $objp->eatby; - $productlot->sellby = $objp->sellby; + $result = $db->query($sql); + if ($result) { + $objtp = $db->fetch_object($result); + if (!empty($objtp->label)) { + $obj->produit = $objtp->label; + } + } + } - $warehousestatic->id = $objp->entrepot_id; - $warehousestatic->ref = $objp->warehouse_ref; - $warehousestatic->label = $objp->warehouse_ref; - $warehousestatic->lieu = $objp->lieu; - $warehousestatic->fk_parent = $objp->fk_parent; - $warehousestatic->statut = $objp->statut; + $productstatic->id = $obj->rowid; + $productstatic->ref = $obj->product_ref; + $productstatic->label = $obj->produit; + $productstatic->type = $obj->type; + $productstatic->entity = $obj->entity; + $productstatic->status = $obj->tosell; + $productstatic->status_buy = $obj->tobuy; + $productstatic->status_batch = $obj->tobatch; - $movement->type = $objp->type_mouvement; + $productlot->id = $obj->lotid; + $productlot->batch = $obj->batch; + $productlot->eatby = $obj->eatby; + $productlot->sellby = $obj->sellby; - $arrayofuniqueproduct[$objp->rowid] = $objp->produit; - if (!empty($objp->fk_origin)) { - $origin = $movement->get_origin($objp->fk_origin, $objp->origintype); + $warehousestatic->id = $obj->entrepot_id; + $warehousestatic->ref = $obj->warehouse_ref; + $warehousestatic->label = $obj->warehouse_ref; + $warehousestatic->lieu = $obj->lieu; + $warehousestatic->fk_parent = $obj->fk_parent; + $warehousestatic->statut = $obj->statut; + + $movement->type = $obj->type_mouvement; + + $arrayofuniqueproduct[$obj->rowid] = $obj->produit; + if (!empty($obj->fk_origin)) { + $origin = $movement->get_origin($obj->fk_origin, $obj->origintype); } else { $origin = ''; } @@ -1184,12 +1337,12 @@ while ($i < min($num, $limit)) { if (!empty($arrayfields['m.rowid']['checked'])) { print ''; // This is primary not movement id } if (!empty($arrayfields['m.datem']['checked'])) { // Date - print ''; + print ''; } if (!empty($arrayfields['p.ref']['checked'])) { // Product ref @@ -1213,10 +1366,10 @@ while ($i < min($num, $limit)) { print ''; } if (!empty($arrayfields['pl.eatby']['checked'])) { - print ''; + print ''; } if (!empty($arrayfields['pl.sellby']['checked'])) { - print ''; + print ''; } // Warehouse if (!empty($arrayfields['e.ref']['checked'])) { @@ -1232,11 +1385,11 @@ while ($i < min($num, $limit)) { } if (!empty($arrayfields['m.inventorycode']['checked'])) { // Inventory code - print ''; + print ''; } if (!empty($arrayfields['m.label']['checked'])) { // Label of movement - print ''; + print ''; } if (!empty($arrayfields['origin']['checked'])) { // Origin of movement @@ -1245,8 +1398,8 @@ while ($i < min($num, $limit)) { if (!empty($arrayfields['m.fk_projet']['checked'])) { // fk_project print ''; } @@ -1259,14 +1412,14 @@ while ($i < min($num, $limit)) { if (!empty($arrayfields['m.value']['checked'])) { // Qty print ''; @@ -1274,8 +1427,8 @@ while ($i < min($num, $limit)) { if (!empty($arrayfields['m.price']['checked'])) { // Price print ''; } @@ -1283,25 +1436,26 @@ while ($i < min($num, $limit)) { // Extra fields include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_print_fields.tpl.php'; // Fields from hook - $parameters = array('arrayfields'=>$arrayfields, 'objp'=>$objp, 'i'=>$i, 'totalarray'=>&$totalarray); - $reshook = $hookmanager->executeHooks('printFieldListValue', $parameters); // Note that $action and $object may have been modified by hook + $parameters = array('arrayfields'=>$arrayfields, 'object'=>$object, 'obj'=>$obj, 'i'=>$i, 'totalarray'=>&$totalarray); + $reshook = $hookmanager->executeHooks('printFieldListValue', $parameters, $object); // Note that $action and $object may have been modified by hook print $hookmanager->resPrint; // Action column print ''; if (!$i) { $totalarray['nbfield']++; } - print "\n"; + print ''."\n"; + $i++; } @@ -1341,6 +1495,26 @@ if (count($arrayofuniqueproduct) == 1 && is_numeric($year)) { //print ''; } +if (in_array('builddoc', $arrayofmassactions) && ($nbtotalofrecords === '' || $nbtotalofrecords)) { + $hidegeneratedfilelistifempty = 1; + if ($massaction == 'builddoc' || $action == 'remove_file' || $show_files) { + $hidegeneratedfilelistifempty = 0; + } + + require_once DOL_DOCUMENT_ROOT.'/core/class/html.formfile.class.php'; + $formfile = new FormFile($db); + + // Show list of available documents + $urlsource = $_SERVER['PHP_SELF'].'?sortfield='.$sortfield.'&sortorder='.$sortorder; + $urlsource .= str_replace('&', '&', $param); + + $filedir = $diroutputmassaction; + $genallowed = $permissiontoread; + $delallowed = $permissiontoadd; + + print $formfile->showdocuments('massfilesarea_mymodule', '', $filedir, $urlsource, 0, $delallowed, '', 1, 1, 0, 48, 1, $param, $title, '', '', '', null, $hidegeneratedfilelistifempty); +} + // End of page llxFooter(); $db->close(); diff --git a/htdocs/product/stock/product.php b/htdocs/product/stock/product.php index 23900c85c0b..f522e61bec2 100644 --- a/htdocs/product/stock/product.php +++ b/htdocs/product/stock/product.php @@ -552,7 +552,7 @@ if ($id > 0 || $ref) { if (!empty($conf->use_javascript_ajax)) { ?> - '.$addform; + } print load_fiche_titre($langs->trans($title), $addform, ''); @@ -1113,7 +1141,6 @@ foreach ($listofreferent as $key => $value) { } print ''; - $elementarray = $object->get_element_list($key, $tablename, $datefieldname, $dates, $datee, !empty($project_field) ? $project_field : 'fk_projet'); if (is_array($elementarray) && count($elementarray) > 0) { $total_ht = 0; $total_ttc = 0; @@ -1168,9 +1195,16 @@ foreach ($listofreferent as $key => $value) { if (!empty($element->close_code) && $element->close_code == 'replaced') { $qualifiedfortotal = false; // Replacement invoice, do not include into total } + } elseif ($key == 'order_supplier' && $element->status == 7) { + $qualifiedfortotal = false; // It makes no sense to include canceled orders in the total + } + + if ($key == "order_supplier" && $element->status == 7) { + print ''; + } else { + print ''; } - print ''; // Remove link print ''; if (in_array($tablename, array('projet_task'))) { print ''; } else { if (!is_array($elementarray)) { // error - print $elementarray; + print ''; + } else { + $colspan = 7; + if (in_array($tablename, array('projet_task'))) { + $colspan = 5; + } + if ($tablename == 'fichinter') { + $colspan++; + } + print ''; } } print "
    '; @@ -1047,7 +1171,7 @@ include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_search_input.tpl.php'; // Fields from hook $parameters = array('arrayfields'=>$arrayfields); -$reshook = $hookmanager->executeHooks('printFieldListOption', $parameters); // Note that $action and $object may have been modified by hook +$reshook = $hookmanager->executeHooks('printFieldListOption', $parameters, $object); // Note that $action and $object may have been modified by hook print $hookmanager->resPrint; // Date creation if (!empty($arrayfields['m.datec']['checked'])) { @@ -1059,13 +1183,16 @@ if (!empty($arrayfields['m.tms']['checked'])) { print ''; print ''; -$searchpicto = $form->showFilterAndCheckAddButtons(0); +$searchpicto = $form->showFilterButtons(); print $searchpicto; print '
    '; print img_picto($langs->trans("StockMovement"), 'movement', 'class="pictofixedwidth"'); - print $objp->mid; + print $obj->mid; print ''.dol_print_date($db->jdate($objp->datem), 'dayhour', 'tzuserrel').''.dol_print_date($db->jdate($obj->datem), 'dayhour', 'tzuserrel').''.dol_print_date($objp->eatby, 'day').''.dol_print_date($obj->eatby, 'day').''.dol_print_date($objp->sellby, 'day').''.dol_print_date($obj->sellby, 'day').'inventorycode.'$').'&search_type_mouvement='.urlencode($objp->type_mouvement).'">'.$objp->inventorycode.'inventorycode.'$').'&search_type_mouvement='.urlencode($obj->type_mouvement).'">'.$obj->inventorycode.''.$objp->label.''.$obj->label.''; - if ($objp->fk_project != 0) { - print $movement->get_origin($objp->fk_project, 'project'); + if ($obj->fk_project != 0) { + print $movement->get_origin($obj->fk_project, 'project'); } print ''; - if ($objp->qty > 0) { + if ($obj->qty > 0) { print ''; print '+'; - print $objp->qty; + print $obj->qty; print ''; } else { print ''; - print $objp->qty; + print $obj->qty; print ''; } print ''; - if ($objp->price != 0) { - print price($objp->price); + if ($obj->price != 0) { + print price($obj->price); } print ''; if ($massactionbutton || $massaction) { // If we are in select mode (massactionbutton defined) or if we have already selected and sent an action ($massaction) defined $selected = 0; - if (in_array($objp->mid, $arrayofselected)) { + if (in_array($obj->mid, $arrayofselected)) { $selected = 1; } - print ''; + print ''; } print '
    '; @@ -1356,7 +1390,7 @@ foreach ($listofreferent as $key => $value) { if (!$qualifiedfortotal) { print ''; } - print price($total_ht_by_line); + print ''.price($total_ht_by_line).''; if (!$qualifiedfortotal) { print ''; } @@ -1407,7 +1441,7 @@ foreach ($listofreferent as $key => $value) { if (!$qualifiedfortotal) { print ''; } - print price($total_ttc_by_line); + print ''.price($total_ttc_by_line).''; if (!$qualifiedfortotal) { print ''; } @@ -1480,6 +1514,7 @@ foreach ($listofreferent as $key => $value) { if (in_array($tablename, array('projet_task'))) { $colspan = 2; } + print '
    '.$langs->trans("Number").': '.$i.''; @@ -1518,7 +1553,16 @@ foreach ($listofreferent as $key => $value) { print '
    '.$elementarray.'
    '.$langs->trans("None").'
    "; diff --git a/htdocs/projet/index.php b/htdocs/projet/index.php index e1f86bcffc9..51e78f23d57 100644 --- a/htdocs/projet/index.php +++ b/htdocs/projet/index.php @@ -56,8 +56,8 @@ if (!$user->rights->projet->lire) { accessforbidden(); } -$sortfield = GETPOST("sortfield", 'alpha'); -$sortorder = GETPOST("sortorder", 'alpha'); +$sortfield = GETPOST('sortfield', 'aZ09comma'); +$sortorder = GETPOST('sortorder', 'aZ09comma'); $max = $conf->global->MAIN_SIZE_SHORTLIST_LIMIT; diff --git a/htdocs/projet/list.php b/htdocs/projet/list.php index 444dc6d525f..b19bc407537 100644 --- a/htdocs/projet/list.php +++ b/htdocs/projet/list.php @@ -98,6 +98,7 @@ $search_opp_amount = GETPOST("search_opp_amount", 'alpha'); $search_budget_amount = GETPOST("search_budget_amount", 'alpha'); $search_public = GETPOST("search_public", 'int'); $search_project_user = GETPOST('search_project_user', 'int'); +$search_project_contact = GETPOST('search_project_contact', 'int'); $search_sale = GETPOST('search_sale', 'int'); $search_usage_opportunity = GETPOST('search_usage_opportunity', 'int'); $search_usage_task = GETPOST('search_usage_task', 'int'); @@ -198,6 +199,7 @@ include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_array_fields.tpl.php'; // Add non object fields to fields for list $arrayfields['s.nom'] = array('label'=>$langs->trans("ThirdParty"), 'checked'=>1, 'position'=>21, 'enabled'=>(empty($conf->societe->enabled) ? 0 : 1)); $arrayfields['commercial'] = array('label'=>$langs->trans("SaleRepresentativesOfThirdParty"), 'checked'=>0, 'position'=>23); +$arrayfields['c.assigned'] = array('label'=>$langs->trans("AssignedTo"), 'checked'=>-1, 'position'=>120); $arrayfields['opp_weighted_amount'] = array('label'=>$langs->trans('OpportunityWeightedAmountShort'), 'checked'=>0, 'position'=> 116, 'enabled'=>(empty($conf->global->PROJECT_USE_OPPORTUNITIES) ? 0 : 1), 'position'=>106); // Force some fields according to search_usage filter... if (GETPOST('search_usage_opportunity')) { @@ -252,6 +254,7 @@ if (empty($reshook)) { $search_public = ""; $search_sale = ""; $search_project_user = ''; + $search_project_contact = ''; $search_sday = ""; $search_smonth = ""; $search_syear = ""; @@ -363,13 +366,14 @@ if (empty($user->rights->projet->all->lire)) { // Get id of types of contacts for projects (This list never contains a lot of elements) $listofprojectcontacttype = array(); -$sql = "SELECT ctc.rowid, ctc.code FROM ".MAIN_DB_PREFIX."c_type_contact as ctc"; +$listofprojectcontacttypeexternal = array(); +$sql = "SELECT ctc.rowid, ctc.code, ctc.source FROM ".MAIN_DB_PREFIX."c_type_contact as ctc"; $sql .= " WHERE ctc.element = '".$db->escape($object->element)."'"; -$sql .= " AND ctc.source = 'internal'"; $resql = $db->query($sql); if ($resql) { while ($obj = $db->fetch_object($resql)) { - $listofprojectcontacttype[$obj->rowid] = $obj->code; + if ($obj->source == 'internal') $listofprojectcontacttype[$obj->rowid] = $obj->code; + else $listofprojectcontacttypeexternal[$obj->rowid] = $obj->code; } } else { dol_print_error($db); @@ -377,6 +381,9 @@ if ($resql) { if (count($listofprojectcontacttype) == 0) { $listofprojectcontacttype[0] = '0'; // To avoid sql syntax error if not found } +if (count($listofprojectcontacttypeexternal) == 0) { + $listofprojectcontacttypeexternal[0] = '0'; // To avoid sql syntax error if not found +} $distinct = 'DISTINCT'; // We add distinct until we are added a protection to be sure a contact of a project and task is only once. $sql = "SELECT ".$distinct." p.rowid as id, p.ref, p.title, p.fk_statut as status, p.fk_opp_status, p.public, p.fk_user_creat,"; @@ -417,6 +424,9 @@ if ($search_sale > 0) { if ($search_project_user > 0) { $sql .= ", ".MAIN_DB_PREFIX."element_contact as ecp"; } +if ($search_project_contact > 0) { + $sql .= ", ".MAIN_DB_PREFIX."element_contact as ecp_contact"; +} $sql .= " WHERE p.entity IN (".getEntity('project').')'; if (!empty($conf->categorie->enabled)) { $sql .= Categorie::getFilterSelectQuery(Categorie::TYPE_PROJECT, "p.rowid", $search_category_array); @@ -501,6 +511,9 @@ if ($search_sale > 0) { if ($search_project_user > 0) { $sql .= " AND ecp.fk_c_type_contact IN (".$db->sanitize(join(',', array_keys($listofprojectcontacttype))).") AND ecp.element_id = p.rowid AND ecp.fk_socpeople = ".((int) $search_project_user); } +if ($search_project_contact > 0) { + $sql .= " AND ecp_contact.fk_c_type_contact IN (".$db->sanitize(join(',', array_keys($listofprojectcontacttypeexternal))).") AND ecp_contact.element_id = p.rowid AND ecp_contact.fk_socpeople = ".((int) $search_project_contact); +} if ($search_opp_amount != '') { $sql .= natural_search('p.opp_amount', $search_opp_amount, 1); } @@ -689,6 +702,9 @@ if ($search_public != '') { if ($search_project_user != '') { $param .= '&search_project_user='.urlencode($search_project_user); } +if ($search_project_contact != '') { + $param .= '&search_project_user='.urlencode($search_project_contact); +} if ($search_sale > 0) { $param .= '&search_sale='.urlencode($search_sale); } @@ -751,6 +767,9 @@ $url = DOL_URL_ROOT.'/projet/card.php?action=create'; if (!empty($socid)) { $url .= '&socid='.$socid; } +if ($search_usage_event_organization == 1) { + $url .= '&usage_organize_event=1'; +} $newcardbutton = dolGetButtonTitle($langs->trans('NewProject'), '', 'fa fa-plus-circle', $url, '', $user->rights->projet->creer); print ''; @@ -805,6 +824,11 @@ if (empty($user->rights->user->user->lire)) { $moreforfilter .= img_picto($tmptitle, 'user', 'class="pictofixedwidth"').$form->select_dolusers($search_project_user ? $search_project_user : '', 'search_project_user', $tmptitle, '', 0, $includeonly, '', 0, 0, 0, '', 0, '', 'maxwidth250'); $moreforfilter .= '
'; +$moreforfilter .= '
'; +$tmptitle = $langs->trans('ProjectsWithThisContact'); +$moreforfilter .= img_picto($tmptitle, 'user', 'class="pictofixedwidth"').$form->selectcontacts(0, $search_project_contact ? $search_project_contact : '', 'search_project_contact', $tmptitle); +$moreforfilter .= '
'; + // If the user can view thirdparties other than his' if ($user->rights->societe->client->voir || $socid) { $langs->load("commercial"); @@ -908,7 +932,7 @@ if (!empty($arrayfields['p.public']['checked'])) { // Opp status if (!empty($arrayfields['p.fk_opp_status']['checked'])) { print ''; - print $formproject->selectOpportunityStatus('search_opp_status', $search_opp_status, 1, 0, 1, 0, 'maxwidth100'); + print $formproject->selectOpportunityStatus('search_opp_status', $search_opp_status, 1, 0, 1, 0, 'maxwidth100', 1); print ''; } if (!empty($arrayfields['p.opp_amount']['checked'])) { @@ -930,6 +954,10 @@ if (!empty($arrayfields['p.budget_amount']['checked'])) { print ''; print ''; } +if (!empty($arrayfields['c.assigned']['checked'])) { + print ''; + print ''; +} if (!empty($arrayfields['p.usage_opportunity']['checked'])) { print ''; print $form->selectyesno('search_usage_opportunity', $search_usage_opportunity, 1, false, 1); @@ -1000,7 +1028,7 @@ if (!empty($arrayfields['p.fk_statut']['checked'])) { $arrayofstatus[$key] = $langs->trans($val); } $arrayofstatus['99'] = $langs->trans("NotClosed").' ('.$langs->trans('Draft').' + '.$langs->trans('Opened').')'; - print $form->selectarray('search_status', $arrayofstatus, $search_status, 1, 0, 0, '', 0, 0, 0, '', 'minwidth75imp maxwidth150 selectarrowonleft'); + print $form->selectarray('search_status', $arrayofstatus, $search_status, 1, 0, 0, '', 0, 0, 0, '', 'minwidth75imp maxwidth125 selectarrowonleft'); print ajax_combobox('search_status'); print ''; } @@ -1049,6 +1077,9 @@ if (!empty($arrayfields['opp_weighted_amount']['checked'])) { if (!empty($arrayfields['p.budget_amount']['checked'])) { print_liste_field_titre($arrayfields['p.budget_amount']['label'], $_SERVER["PHP_SELF"], 'p.budget_amount', "", $param, '', $sortfield, $sortorder, 'right '); } +if (!empty($arrayfields['c.assigned']['checked'])) { + print_liste_field_titre($arrayfields['c.assigned']['label'], $_SERVER["PHP_SELF"], "", '', $param, '', $sortfield, $sortorder, 'center ', ''); +} if (!empty($arrayfields['p.usage_opportunity']['checked'])) { print_liste_field_titre($arrayfields['p.usage_opportunity']['label'], $_SERVER["PHP_SELF"], 'p.usage_opportunity', "", $param, '', $sortfield, $sortorder, 'right '); } @@ -1309,6 +1340,41 @@ while ($i < min($num, $limit)) { $totalarray['pos'][$totalarray['nbfield']] = 'p.budget_amount'; } } + // Contacts of project + if (!empty($arrayfields['c.assigned']['checked'])) { + print ''; + $ifisrt = 1; + foreach (array('internal', 'external') as $source) { + $tab = $object->liste_contact(-1, $source); + $numcontact = count($tab); + if (!empty($numcontact)) { + foreach ($tab as $contactproject) { + //var_dump($contacttask); + if ($source == 'internal') { + $c = new User($db); + } else { + $c = new Contact($db); + } + $c->fetch($contactproject['id']); + if (!empty($c->photo)) { + if (get_class($c) == 'User') { + print $c->getNomUrl(-2, '', 0, 0, 24, 1, '', ($ifisrt ? '' : 'notfirst')); + } else { + print $c->getNomUrl(-2, '', 0, '', -1, 0, ($ifisrt ? '' : 'notfirst')); + } + } else { + if (get_class($c) == 'User') { + print $c->getNomUrl(2, '', 0, 0, 24, 1, '', ($ifisrt ? '' : 'notfirst')); + } else { + print $c->getNomUrl(2, '', 0, '', -1, 0, ($ifisrt ? '' : 'notfirst')); + } + } + $ifisrt = 0; + } + } + } + print ''; + } // Usage opportunity if (!empty($arrayfields['p.usage_opportunity']['checked'])) { print ''; diff --git a/htdocs/projet/stats/index.php b/htdocs/projet/stats/index.php index c5acc20bfe6..f41651e15d8 100644 --- a/htdocs/projet/stats/index.php +++ b/htdocs/projet/stats/index.php @@ -298,11 +298,13 @@ if (!in_array($nowyear, $arrayyears)) { $arrayyears[$nowyear] = $nowyear; } arsort($arrayyears); -print $form->selectarray('year', $arrayyears, $year, 0); +print $form->selectarray('year', $arrayyears, $year, 0, 0, 0, '', 0, 0, 0, '', 'width75'); print ''; print ''; print ''; + print ''; + print '

'; print '
'; @@ -369,10 +371,12 @@ $stringtoshow .= ''; print $stringtoshow; +print '
'; -print ''; print '
'; +print dol_get_fiche_end(); + // End of page llxFooter(); $db->close(); diff --git a/htdocs/projet/tasks.php b/htdocs/projet/tasks.php index 80d39cf08d4..64d134a7254 100644 --- a/htdocs/projet/tasks.php +++ b/htdocs/projet/tasks.php @@ -171,6 +171,7 @@ if ($object->usage_bill_time) { } // Extra fields +$extrafieldsobjectkey = $taskstatic->table_element; include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_array_fields.tpl.php'; $arrayfields = dol_sort_array($arrayfields, 'position'); @@ -625,7 +626,7 @@ if ($id > 0 || !empty($ref)) { // Budget print ''.$langs->trans("Budget").''; if (strcmp($object->budget_amount, '')) { - print price($object->budget_amount, '', $langs, 1, 0, 0, $conf->currency); + print ''.price($object->budget_amount, '', $langs, 1, 0, 0, $conf->currency).''; } print ''; @@ -810,9 +811,7 @@ if ($action == 'create' && $user->rights->projet->creer && (empty($object->third $linktocreatetask = dolGetButtonTitle($langs->trans('AddTask'), '', 'fa fa-plus-circle', DOL_URL_ROOT.'/projet/tasks.php?action=create'.$param.'&backtopage='.urlencode($_SERVER['PHP_SELF'].'?id='.$object->id), '', $linktocreatetaskUserRight, $linktocreatetaskParam); print '
'; - if ($optioncss != '') { - print ''; - } + print ''; print ''; print ''; print ''; @@ -851,8 +850,8 @@ if ($action == 'create' && $user->rights->projet->creer && (empty($object->third $moreforfilter = ''; if (count($tasksarray) > 0) { $moreforfilter .= '
'; - $moreforfilter .= $langs->trans("TasksAssignedTo").': '; - $moreforfilter .= $form->select_dolusers($tmpuser->id > 0 ? $tmpuser->id : '', 'search_user_id', 1); + $moreforfilter .= img_picto('', 'user', 'class="pictofixedwidth"'); + $moreforfilter .= $form->select_dolusers($tmpuser->id > 0 ? $tmpuser->id : '', 'search_user_id', $langs->trans("TasksAssignedTo"), null, 0, '', ''); $moreforfilter .= '
'; } if ($moreforfilter) { @@ -969,7 +968,6 @@ if ($action == 'create' && $user->rights->projet->creer && (empty($object->third print ''; } - $extrafieldsobjectkey = $taskstatic->table_element; include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_search_input.tpl.php'; // Action column diff --git a/htdocs/projet/tasks/document.php b/htdocs/projet/tasks/document.php index c60e3324741..155cb261040 100644 --- a/htdocs/projet/tasks/document.php +++ b/htdocs/projet/tasks/document.php @@ -46,8 +46,8 @@ $project_ref = GETPOST('project_ref', 'alpha'); // Get parameters $limit = GETPOST('limit', 'int') ? GETPOST('limit', 'int') : $conf->liste_limit; -$sortfield = GETPOST("sortfield", 'alpha'); -$sortorder = GETPOST("sortorder", 'alpha'); +$sortfield = GETPOST('sortfield', 'aZ09comma'); +$sortorder = GETPOST('sortorder', 'aZ09comma'); $page = GETPOSTISSET('pageplusone') ? (GETPOST('pageplusone') - 1) : GETPOST("page", 'int'); if (empty($page) || $page == -1) { $page = 0; diff --git a/htdocs/projet/tasks/list.php b/htdocs/projet/tasks/list.php index f56d531ffdf..697969c6060 100644 --- a/htdocs/projet/tasks/list.php +++ b/htdocs/projet/tasks/list.php @@ -74,6 +74,7 @@ if ($mine) { $search_task_user = $user->id; $mine = 0; } +$type = GETPOST('type'); $search_date_startday = GETPOST('search_date_startday', 'int'); $search_date_startmonth = GETPOST('search_date_startmonth', 'int'); @@ -114,8 +115,8 @@ if (!$user->rights->projet->lire) { $diroutputmassaction = $conf->projet->dir_output.'/tasks/temp/massgeneration/'.$user->id; $limit = GETPOST('limit', 'int') ?GETPOST('limit', 'int') : $conf->liste_limit; -$sortfield = GETPOST("sortfield", 'alpha'); -$sortorder = GETPOST("sortorder", 'alpha'); +$sortfield = GETPOST('sortfield', 'aZ09comma'); +$sortorder = GETPOST('sortorder', 'aZ09comma'); $page = GETPOSTISSET('pageplusone') ? (GETPOST('pageplusone') - 1) : GETPOST("page", 'int'); if (empty($page) || $page == -1) { $page = 0; diff --git a/htdocs/projet/tasks/note.php b/htdocs/projet/tasks/note.php index 3d1ec610b48..5f3d74a3c68 100644 --- a/htdocs/projet/tasks/note.php +++ b/htdocs/projet/tasks/note.php @@ -47,7 +47,6 @@ if (!$user->rights->projet->lire) { $hookmanager->initHooks(array('projettasknote')); -//$result = restrictedArea($user, 'projet', $id, '', 'task'); // TODO ameliorer la verification $object = new Task($db); $projectstatic = new Project($db); @@ -89,6 +88,7 @@ if ($id > 0 || $ref) { $object->fetch($id, $ref); } +//$result = restrictedArea($user, 'projet', $id, '', 'task'); // TODO ameliorer la verification restrictedArea($user, 'projet', $object->fk_project, 'projet&project'); $permissionnote = ($user->rights->projet->creer || $user->rights->projet->all->creer); diff --git a/htdocs/projet/tasks/stats/index.php b/htdocs/projet/tasks/stats/index.php index 60cbdf37072..70c093f46e3 100644 --- a/htdocs/projet/tasks/stats/index.php +++ b/htdocs/projet/tasks/stats/index.php @@ -168,11 +168,13 @@ if (!in_array($nowyear, $arrayyears)) { $arrayyears[$nowyear] = $nowyear; } arsort($arrayyears); -print $form->selectarray('year', $arrayyears, $year, 0); +print $form->selectarray('year', $arrayyears, $year, 0, 0, 0, '', 0, 0, 0, '', 'width75'); print ''; print ''; print ''; + print '
'; + print '

'; @@ -190,13 +192,13 @@ foreach ($data_all_year as $val) { $oldyear--; print ''; - print ' 0 ? '&userid='.$userid : '').'">'.$oldyear.''; + print ' 0 ? '&userid='.$userid : '').'">'.$oldyear.''; print '0'; print ''; } print ''; - print ' 0 ? '&userid='.$userid : '').'">'.$year.''; + print ' 0 ? '&userid='.$userid : '').'">'.$year.''; print ''.$val['nb'].''; print ''; $oldyear = $year; @@ -220,8 +222,11 @@ print $stringtoshow; print ''; + print '
'; +print dol_get_fiche_end(); + // End of page llxFooter(); $db->close(); diff --git a/htdocs/projet/tasks/task.php b/htdocs/projet/tasks/task.php index 62714c72216..8e773f92788 100644 --- a/htdocs/projet/tasks/task.php +++ b/htdocs/projet/tasks/task.php @@ -108,7 +108,7 @@ if ($action == 'update' && !GETPOST("cancel") && $user->rights->projet->creer) { $object->budget_amount = price2num(GETPOST('budget_amount', 'alphanohtml')); // Fill array 'array_options' with data from add form - $ret = $extrafields->setOptionalsFromPost(null, $object); + $ret = $extrafields->setOptionalsFromPost(null, $object, '@GETPOSTISSET'); if ($ret < 0) { $error++; } @@ -117,6 +117,7 @@ if ($action == 'update' && !GETPOST("cancel") && $user->rights->projet->creer) { $result = $object->update($user); if ($result < 0) { setEventMessages($object->error, $object->errors, 'errors'); + $action = 'edit'; } } } else { diff --git a/htdocs/projet/tasks/time.php b/htdocs/projet/tasks/time.php index e8dc79951f7..bc6ec7d15cf 100644 --- a/htdocs/projet/tasks/time.php +++ b/htdocs/projet/tasks/time.php @@ -1,6 +1,6 @@ - * Copyright (C) 2006-2020 Laurent Destailleur + * Copyright (C) 2006-2021 Laurent Destailleur * Copyright (C) 2010-2012 Regis Houssin * Copyright (C) 2011 Juanjo Menent * Copyright (C) 2018 Ferran Marcet @@ -74,16 +74,9 @@ $search_task_label = GETPOST('search_task_label', 'alpha'); $search_user = GETPOST('search_user', 'int'); $search_valuebilled = GETPOST('search_valuebilled', 'int'); -// Security check -$socid = 0; -//if ($user->socid > 0) $socid = $user->socid; // For external user, no check is done on company because readability is managed by public status of project and assignement. -if (!$user->rights->projet->lire) { - accessforbidden(); -} - $limit = GETPOST('limit', 'int') ?GETPOST('limit', 'int') : $conf->liste_limit; -$sortfield = GETPOST("sortfield", 'alpha'); -$sortorder = GETPOST("sortorder", 'alpha'); +$sortfield = GETPOST('sortfield', 'aZ09comma'); +$sortorder = GETPOST('sortorder', 'aZ09comma'); $page = GETPOSTISSET('pageplusone') ? (GETPOST('pageplusone') - 1) : GETPOST("page", 'int'); if (empty($page) || $page == -1) { $page = 0; @@ -98,6 +91,8 @@ if (!$sortorder) { $sortorder = 'DESC,DESC,DESC'; } +$childids = $user->getAllChildIds(1); + // Initialize technical object to manage hooks of page. Note that conf->hooks_modules contains array of hook context //$object = new TaskTime($db); $hookmanager->initHooks(array('projecttasktime', 'globalcard')); @@ -108,11 +103,28 @@ $extrafields = new ExtraFields($db); $extrafields->fetch_name_optionals_label($projectstatic->table_element); $extrafields->fetch_name_optionals_label($object->table_element); +// Load task if ($id > 0 || $ref) { $object->fetch($id, $ref); } -restrictedArea($user, 'projet', $object->fk_project, 'projet&project'); + +// Security check +$socid = 0; +//if ($user->socid > 0) $socid = $user->socid; // For external user, no check is done on company because readability is managed by public status of project and assignement. +if (!$user->rights->projet->lire) { + accessforbidden(); +} + +if ($object->fk_project > 0) { + restrictedArea($user, 'projet', $object->fk_project, 'projet&project'); +} else { + restrictedArea($user, 'projet', null, 'projet&project'); + // We check user has permission to see all tasks of all users + if (empty($projectid) && !$user->hasRight('projet', 'all', 'lire')) { + $search_user = $user->id; + } +} @@ -237,10 +249,14 @@ if (($action == 'updateline' || $action == 'updatesplitline') && !$cancel && $us $id = GETPOST('taskid', 'int'); $object->fetchTimeSpent(GETPOST('lineid', 'int')); - // TODO Check that ($task_time->fk_user == $user->id || in_array($task_time->fk_user, $childids)) - $result = $object->delTimeSpent($user); + + $result = 0; + if (in_array($object->timespent_fk_user, $childids) || $user->rights->projet->all->creer) { + $result = $object->delTimeSpent($user); + } $object->fetch($id, $ref); + $object->timespent_note = GETPOST("timespent_note_line", 'alpha'); $object->timespent_old_duration = GETPOST("old_duration"); $object->timespent_duration = GETPOSTINT("new_durationhour") * 60 * 60; // We store duration in seconds @@ -252,7 +268,12 @@ if (($action == 'updateline' || $action == 'updatesplitline') && !$cancel && $us $object->timespent_date = dol_mktime(12, 0, 0, GETPOST("timelinemonth"), GETPOST("timelineday"), GETPOST("timelineyear")); } $object->timespent_fk_user = GETPOST("userid_line", 'int'); - $result = $object->addTimeSpent($user); + + $result = 0; + if (in_array($object->timespent_fk_user, $childids) || $user->rights->projet->all->creer) { + $result = $object->addTimeSpent($user); + } + if ($result >= 0) { setEventMessages($langs->trans("RecordSaved"), null, 'mesgs'); } else { @@ -261,7 +282,6 @@ if (($action == 'updateline' || $action == 'updatesplitline') && !$cancel && $us } } else { $object->fetch($id, $ref); - // TODO Check that ($task_time->fk_user == $user->id || in_array($task_time->fk_user, $childids)) $object->timespent_id = GETPOST("lineid", 'int'); $object->timespent_note = GETPOST("timespent_note_line"); @@ -276,12 +296,16 @@ if (($action == 'updateline' || $action == 'updatesplitline') && !$cancel && $us } $object->timespent_fk_user = GETPOST("userid_line", 'int'); - $result = $object->updateTimeSpent($user); - if ($result >= 0) { - setEventMessages($langs->trans("RecordSaved"), null, 'mesgs'); - } else { - setEventMessages($langs->trans($object->error), null, 'errors'); - $error++; + $result = 0; + if (in_array($object->timespent_fk_user, $childids) || $user->rights->projet->all->creer) { + $result = $object->updateTimeSpent($user); + + if ($result >= 0) { + setEventMessages($langs->trans("RecordSaved"), null, 'mesgs'); + } else { + setEventMessages($langs->trans($object->error), null, 'errors'); + $error++; + } } } } else { @@ -289,18 +313,20 @@ if (($action == 'updateline' || $action == 'updatesplitline') && !$cancel && $us } } -if ($action == 'confirm_delete' && $confirm == "yes" && $user->rights->projet->lire) { - $object->fetchTimeSpent(GETPOST('lineid', 'int')); - // TODO Check that ($task_time->fk_user == $user->id || in_array($task_time->fk_user, $childids)) - $result = $object->delTimeSpent($user); +if ($action == 'confirm_deleteline' && $confirm == "yes" && $user->rights->projet->lire) { + $object->fetchTimeSpent(GETPOST('lineid', 'int')); // load properties like $object->timespent_id - if ($result < 0) { - $langs->load("errors"); - setEventMessages($langs->trans($object->error), null, 'errors'); - $error++; - $action = ''; - } else { - setEventMessages($langs->trans("RecordDeleted"), null, 'mesgs'); + if (in_array($object->timespent_fk_user, $childids) || $user->rights->projet->all->creer) { + $result = $object->delTimeSpent($user); // delete line with $object->timespent_id + + if ($result < 0) { + $langs->load("errors"); + setEventMessages($langs->trans($object->error), null, 'errors'); + $error++; + $action = ''; + } else { + setEventMessages($langs->trans("RecordDeleted"), null, 'mesgs'); + } } } @@ -335,6 +361,10 @@ if (GETPOST('projectid', 'int') > 0) { $object->fetch($id); $result = $projectstatic->fetch($object->fk_project); } +// If not task selected and no project selected +if ($id <= 0 && $projectidforalltimes == 0) { + $allprojectforuser = $user->id; +} if ($action == 'confirm_generateinvoice') { if (!empty($projectstatic->socid)) { @@ -678,7 +708,7 @@ $formother = new FormOther($db); $formproject = new FormProjets($db); $userstatic = new User($db); -if (($id > 0 || !empty($ref)) || $projectidforalltimes > 0) { +if (($id > 0 || !empty($ref)) || $projectidforalltimes > 0 || $allprojectforuser > 0) { /* * Fiche projet en mode visu */ @@ -720,6 +750,15 @@ if (($id > 0 || !empty($ref)) || $projectidforalltimes > 0) { print dol_get_fiche_head($head, $tab, $langs->trans("Project"), -1, ($projectstatic->public ? 'projectpub' : 'project')); $param = ((!empty($mode) && $mode == 'mine') ? '&mode=mine' : ''); + if ($search_user) { + $param .= '&search_user='.((int) $search_user); + } + if ($search_month) { + $param .= '&search_month='.((int) $search_month); + } + if ($search_year) { + $param .= '&search_year='.((int) $search_year); + } // Project card @@ -740,7 +779,7 @@ if (($id > 0 || !empty($ref)) || $projectidforalltimes > 0) { $projectstatic->next_prev_filter = " rowid IN (".$db->sanitize(count($objectsListId) ?join(',', array_keys($objectsListId)) : '0').")"; } - dol_banner_tab($projectstatic, 'project_ref', $linkback, 1, 'ref', 'ref', $morehtmlref); + dol_banner_tab($projectstatic, 'project_ref', $linkback, 1, 'ref', 'ref', $morehtmlref, $param); print '
'; print '
'; @@ -804,7 +843,7 @@ if (($id > 0 || !empty($ref)) || $projectidforalltimes > 0) { // Budget print ''.$langs->trans("Budget").''; if (strcmp($projectstatic->budget_amount, '')) { - print price($projectstatic->budget_amount, '', $langs, 1, 0, 0, $conf->currency); + print ''.price($projectstatic->budget_amount, '', $langs, 1, 0, 0, $conf->currency).''; } print ''; @@ -818,7 +857,7 @@ if (($id > 0 || !empty($ref)) || $projectidforalltimes > 0) { print '
'; print '
'; - print ''; + print '
'; // Description print ''; print ''; + if (!empty($allprojectforuser)) { + print ''; + } if (empty($id)) { print ''; } @@ -1343,6 +1395,12 @@ if (($id > 0 || !empty($ref)) || $projectidforalltimes > 0) { print $form->selectDate($newdate, 'time', ($conf->browser->layout == 'phone' ? 2 : 1), 1, 2, "timespent_date", 1, 0); print ''; + if (!empty($allprojectforuser)) { + print ''; + } + // Task $nboftasks = 0; if (empty($id)) { @@ -1445,6 +1503,10 @@ if (($id > 0 || !empty($ref)) || $projectidforalltimes > 0) { $formother->select_year($search_year, 'search_year', 1, 20, 5); print ''; } + if (!empty($allprojectforuser)) { + print ''; + } + // Task if ((empty($id) && empty($ref)) || !empty($projectidforalltimes)) { // Not a dedicated task if (!empty($arrayfields['t.task_ref']['checked'])) { print ''; @@ -1493,6 +1555,9 @@ if (($id > 0 || !empty($ref)) || $projectidforalltimes > 0) { if (!empty($arrayfields['t.task_date']['checked'])) { print_liste_field_titre($arrayfields['t.task_date']['label'], $_SERVER['PHP_SELF'], 't.task_date,t.task_datehour,t.rowid', '', $param, '', $sortfield, $sortorder); } + if (!empty($allprojectforuser)) { + print_liste_field_titre("Project", $_SERVER['PHP_SELF'], '', '', $param, '', $sortfield, $sortorder); + } if ((empty($id) && empty($ref)) || !empty($projectidforalltimes)) { // Not a dedicated task if (!empty($arrayfields['t.task_ref']['checked'])) { print_liste_field_titre($arrayfields['t.task_ref']['label'], $_SERVER['PHP_SELF'], 'pt.ref', '', $param, '', $sortfield, $sortorder); @@ -1532,8 +1597,6 @@ if (($id > 0 || !empty($ref)) || $projectidforalltimes > 0) { $i = 0; - $childids = $user->getAllChildIds(); - $total = 0; $totalvalue = 0; $totalarray = array(); @@ -1565,6 +1628,23 @@ if (($id > 0 || !empty($ref)) || $projectidforalltimes > 0) { } } + // Project ref + if (!empty($allprojectforuser)) { + print ''; + if (!$i) { + $totalarray['nbfield']++; + } + } + // Task ref if (!empty($arrayfields['t.task_ref']['checked'])) { if ((empty($id) && empty($ref)) || !empty($projectidforalltimes)) { // Not a dedicated task @@ -1732,7 +1812,7 @@ if (($id > 0 || !empty($ref)) || $projectidforalltimes > 0) { print '
'; print ''; } elseif ($user->rights->projet->lire || $user->rights->projet->all->creer) { // Read project and enter time consumed on assigned tasks - if ($task_time->fk_user == $user->id || in_array($task_time->fk_user, $childids) || $user->rights->projet->all->creer) { + if (in_array($task_time->fk_user, $childids) || $user->rights->projet->all->creer) { if ($conf->MAIN_FEATURES_LEVEL >= 2) { print ' '; print 'rowid.$param.((empty($id) || $tab == 'timespent') ? '&tab=timespent' : '').'">'; @@ -1788,6 +1868,14 @@ if (($id > 0 || !empty($ref)) || $projectidforalltimes > 0) { print ''; } + // Project ref + if (!empty($allprojectforuser)) { + if ((empty($id) && empty($ref)) || !empty($projectidforalltimes)) { // Not a dedicated task + print ''; + } + } + // Task ref if (!empty($arrayfields['t.task_ref']['checked'])) { if ((empty($id) && empty($ref)) || !empty($projectidforalltimes)) { // Not a dedicated task @@ -1916,6 +2004,14 @@ if (($id > 0 || !empty($ref)) || $projectidforalltimes > 0) { print ''; } + // Project ref + if (!empty($allprojectforuser)) { + if ((empty($id) && empty($ref)) || !empty($projectidforalltimes)) { // Not a dedicated task + print ''; + } + } + // Task ref if (!empty($arrayfields['t.task_ref']['checked'])) { if ((empty($id) && empty($ref)) || !empty($projectidforalltimes)) { // Not a dedicated task diff --git a/htdocs/public/demo/demo.css b/htdocs/public/demo/demo.css index b185f8c933f..8edd0faecce 100644 --- a/htdocs/public/demo/demo.css +++ b/htdocs/public/demo/demo.css @@ -3,14 +3,28 @@ /* Demo */ /* ============================================================================== */ +div.titre { + text-transform: none; +} + a:hover { text-decoration: none; } + +.CTable { + /* padding: 6px; */ + font-weight: normal; + color: #444444 !important; + + margin: 8px 2px 8px 2px; + max-width: 346px; +} .demobox { border: 1px solid #bbb; border-radius: 8px; -moz-border-radius: 8px; min-height: 210px; + background: -webkit-linear-gradient(bottom, rgb(255,255,255) 85%, rgb(255,255,255) 100%); } .demobox:hover { border: 1px solid #bbb; @@ -18,33 +32,29 @@ a:hover { -moz-border-radius: 8px; box-shadow: 2px 2px 8px #BBB; } -.CTable { - padding: 6px; - font-weight: normal; - color: #444444 !important; - - margin: 8px 2px 8px 2px; - - /*border: 1px solid #bbb; - border-radius: 8px; - -moz-border-radius: 8px;*/ - background: -webkit-linear-gradient(bottom, rgb(255,255,255) 85%, rgb(255,255,255) 100%); - - max-width: 346px; -} -.csscolumns { - margin-top: 6px; - margin-bottom: 5px; - -webkit-column-count: 2; /* Chrome, Safari, Opera */ - -moz-column-count: 2; /* Firefox */ - column-count: 2; - text-align: left; -} .demomaxoveflow { max-width: 120px; overflow: hidden; text-overflow: ellipsis; } +@media only screen and (min-width: 641px) +{ + .csscolumns { + margin-top: 6px; + margin-bottom: 5px; + -webkit-column-count: 3; /* Chrome, Safari, Opera */ + -moz-column-count: 3; /* Firefox */ + column-count: 3; + text-align: left; + } + #divprofdemoall { + max-width: unset; + } + + .demomaxoveflow { + max-width: 200px; + } +} @media only screen and (max-width: 840px) { .csscolumns { @@ -76,6 +86,9 @@ a:hover { column-count: 1; text-align: left; } + .demomaxoveflow { + max-width: 220px !important; + } } diff --git a/htdocs/public/demo/index.php b/htdocs/public/demo/index.php index 0932b17baf5..94ee0bb00b2 100644 --- a/htdocs/public/demo/index.php +++ b/htdocs/public/demo/index.php @@ -250,17 +250,18 @@ jQuery(document).ready(function () { jQuery(".modulelineshow").attr("href","#a1profdemoall"); jQuery(".cursorpointer").css("cursor","pointer"); jQuery(".modulelineshow").click(function() { - var idstring=$(this).attr("id"); - if (typeof idstring != "undefined") - { - var currentId = idstring.substring(2); - jQuery("tr.moduleline").hide(); - if (currentId != openedId) - { - openedId=currentId; - jQuery("#tr1"+currentId).show(); - jQuery("#tr2"+currentId).show(); - } + console.log("We select the custom demo"); + var idstring=$(this).attr("id"); + if (typeof idstring != "undefined") + { + var currentId = idstring.substring(2); + jQuery("tr.moduleline").hide(); + if (currentId != openedId) + { + openedId=currentId; + jQuery("#tr1"+currentId).show(); + jQuery("#tr2"+currentId).show(); + } else openedId = ""; } }); @@ -350,7 +351,7 @@ foreach ($demoprofiles as $profilearray) { // Modules (a profile you must choose modules) if (empty($profilearray['url'])) { - print ''; print ''; print '
'; - print '
'; + print '>'; + /* + $s = img_picto('', $modulekeyname, 'class="pictofixedwidth paddingleft"'); + if ($s) { + print $s; + } else { + print img_picto('', 'generic', 'class="pictofixedwidth paddingleft"'); + }*/ + print '
'; print '
'; //if ($modulo == ($nbcolsmod - 1)) print '
'; $j++; @@ -405,6 +416,7 @@ foreach ($demoprofiles as $profilearray) { print '
'; print ''; + print '

'; print '
'; print ''; @@ -419,6 +431,8 @@ foreach ($demoprofiles as $profilearray) { print ''; +print '
'; + // TODO Replace this with a hook // Google Adsense (need Google module) diff --git a/htdocs/public/members/new.php b/htdocs/public/members/new.php index 4795338a938..6589f2a17d0 100644 --- a/htdocs/public/members/new.php +++ b/htdocs/public/members/new.php @@ -174,6 +174,7 @@ function llxFooterVierge() /* * Actions */ + $parameters = array(); // Note that $action and $object may have been modified by some hooks $reshook = $hookmanager->executeHooks('doActions', $parameters, $object, $action); @@ -385,75 +386,25 @@ if (empty($reshook) && $action == 'add') { } if (!empty($conf->global->MEMBER_NEWFORM_PAYONLINE) && $conf->global->MEMBER_NEWFORM_PAYONLINE != '-1') { - if ($conf->global->MEMBER_NEWFORM_PAYONLINE == 'all') { - $urlback = DOL_MAIN_URL_ROOT.'/public/payment/newpayment.php?from=membernewform&source=membersubscription&ref='.urlencode($adh->ref); - if (price2num(GETPOST('amount', 'alpha'))) { - $urlback .= '&amount='.price2num(GETPOST('amount', 'alpha')); - } - if (GETPOST('email')) { - $urlback .= '&email='.urlencode(GETPOST('email')); - } - if (!empty($conf->global->PAYMENT_SECURITY_TOKEN)) { - if (!empty($conf->global->PAYMENT_SECURITY_TOKEN_UNIQUE)) { - $urlback .= '&securekey='.urlencode(dol_hash($conf->global->PAYMENT_SECURITY_TOKEN.'membersubscription'.$adh->ref, 2)); - } else { - $urlback .= '&securekey='.urlencode($conf->global->PAYMENT_SECURITY_TOKEN); - } - } - } elseif ($conf->global->MEMBER_NEWFORM_PAYONLINE == 'paybox') { - $urlback = DOL_MAIN_URL_ROOT.'/public/paybox/newpayment.php?from=membernewform&source=membersubscription&ref='.urlencode($adh->ref); - if (price2num(GETPOST('amount', 'alpha'))) { - $urlback .= '&amount='.price2num(GETPOST('amount', 'alpha')); - } - if (GETPOST('email')) { - $urlback .= '&email='.urlencode(GETPOST('email')); - } - if (!empty($conf->global->PAYMENT_SECURITY_TOKEN)) { - if (!empty($conf->global->PAYMENT_SECURITY_TOKEN_UNIQUE)) { - $urlback .= '&securekey='.urlencode(dol_hash($conf->global->PAYMENT_SECURITY_TOKEN.'membersubscription'.$adh->ref, 2)); - } else { - $urlback .= '&securekey='.urlencode($conf->global->PAYMENT_SECURITY_TOKEN); - } - } - } elseif ($conf->global->MEMBER_NEWFORM_PAYONLINE == 'paypal') { - $urlback = DOL_MAIN_URL_ROOT.'/public/paypal/newpayment.php?from=membernewform&source=membersubscription&ref='.urlencode($adh->ref); - if (price2num(GETPOST('amount', 'alpha'))) { - $urlback .= '&amount='.price2num(GETPOST('amount', 'alpha')); - } - if (GETPOST('email')) { - $urlback .= '&email='.urlencode(GETPOST('email')); - } - if (!empty($conf->global->PAYMENT_SECURITY_TOKEN)) { - if (!empty($conf->global->PAYMENT_SECURITY_TOKEN_UNIQUE)) { - $urlback .= '&securekey='.urlencode(dol_hash($conf->global->PAYMENT_SECURITY_TOKEN.'membersubscription'.$adh->ref, 2)); - } else { - $urlback .= '&securekey='.urlencode($conf->global->PAYMENT_SECURITY_TOKEN); - } - } - } elseif ($conf->global->MEMBER_NEWFORM_PAYONLINE == 'stripe') { - $urlback = DOL_MAIN_URL_ROOT.'/public/stripe/newpayment.php?from=membernewform&source=membersubscription&ref='.$adh->ref; - if (price2num(GETPOST('amount', 'alpha'))) { - $urlback .= '&amount='.price2num(GETPOST('amount', 'alpha')); - } - if (GETPOST('email')) { - $urlback .= '&email='.urlencode(GETPOST('email')); - } - if (!empty($conf->global->PAYMENT_SECURITY_TOKEN)) { - if (!empty($conf->global->PAYMENT_SECURITY_TOKEN_UNIQUE)) { - $urlback .= '&securekey='.urlencode(dol_hash($conf->global->PAYMENT_SECURITY_TOKEN.'membersubscription'.$adh->ref, 2)); - } else { - $urlback .= '&securekey='.urlencode($conf->global->PAYMENT_SECURITY_TOKEN); - } - } - } else { - dol_print_error('', "Autosubscribe form is setup to ask an online payment for a not managed online payment"); - exit; + if (empty($conf->global->MEMBER_NEWFORM_EDITAMOUNT)) { // If edition of amount not allowed + // TODO Check amount is same than the amount required for the type of member or if not defined as the defeault amount into $conf->global->MEMBER_NEWFORM_AMOUNT + // It is not so important because a test is done on return of payment validation. + } + + $urlback = getOnlinePaymentUrl(0, 'member', $adh->ref, price2num(GETPOST('amount', 'alpha'), 'MT'), '', 0); + + if (GETPOST('email')) { + $urlback .= '&email='.urlencode(GETPOST('email')); + } + if ($conf->global->MEMBER_NEWFORM_PAYONLINE != '-1' && $conf->global->MEMBER_NEWFORM_PAYONLINE != 'all') { + $urlback .= '&paymentmethod='.urlencode($conf->global->MEMBER_NEWFORM_PAYONLINE); + } + } else { + if (!empty($entity)) { + $urlback .= '&entity='.((int) $entity); } } - if (!empty($entity)) { - $urlback .= '&entity='.$entity; - } dol_syslog("member ".$adh->ref." was created, we redirect to ".$urlback); } else { $error++; @@ -721,21 +672,24 @@ if (!empty($conf->global->MEMBER_NEWFORM_DOLIBARRTURNOVER)) { print ''."\n"; } -if (!empty($conf->global->MEMBER_NEWFORM_AMOUNT) || !empty($conf->global->MEMBER_NEWFORM_PAYONLINE)) { - // $conf->global->MEMBER_NEWFORM_SHOWAMOUNT is an amount +if (!empty($conf->global->MEMBER_NEWFORM_PAYONLINE)) { + $amount = 0; + $typeid = $conf->global->MEMBER_NEWFORM_FORCETYPE ? $conf->global->MEMBER_NEWFORM_FORCETYPE : GETPOST('typeid', 'int'); - // Set amount for the subscription - $amountbytype = $adht->amountByType(1); - $amount = !empty($amountbytype[GETPOST('typeid', 'int')]) ? $amountbytype[GETPOST('typeid', 'int')] : (isset($amount) ? $amount : 0); - - if (!empty($conf->global->MEMBER_NEWFORM_AMOUNT)) { + // Set amount for the subscription: + // - First check the amount of the member type. + $amountbytype = $adht->amountByType(1); // Load the array of amount per type + $amount = empty($amountbytype[$typeid]) ? (isset($amount) ? $amount : 0) : $amountbytype[$typeid]; + // - If not found, take the default amount + if (empty($amount) && !empty($conf->global->MEMBER_NEWFORM_AMOUNT)) { $amount = $conf->global->MEMBER_NEWFORM_AMOUNT; } - - if (!empty($conf->global->MEMBER_NEWFORM_PAYONLINE)) { - $amount = $amount ? $amount : (GETPOST('amount') ? price2num(GETPOST('amount'), 'MT', 2) : $conf->global->MEMBER_NEWFORM_AMOUNT); + // - If not set, we accept ot have amount defined as parameter (for backward compatibility). + if (empty($amount)) { + $amount = (GETPOST('amount') ? price2num(GETPOST('amount', 'alpha'), 'MT', 2) : ''); } + // Clean the amount $amount = price2num($amount); // $conf->global->MEMBER_NEWFORM_PAYONLINE is 'paypal', 'paybox' or 'stripe' @@ -749,6 +703,7 @@ if (!empty($conf->global->MEMBER_NEWFORM_AMOUNT) || !empty($conf->global->MEMBER print ' '.$langs->trans("Currency".$conf->currency); print ''; } + print "
'.$langs->trans("Description").''; @@ -866,7 +905,8 @@ if (($id > 0 || !empty($ref)) || $projectidforalltimes > 0) { } } - $linktocreatetime = dolGetButtonTitle($langs->trans('AddTimeSpent'), $linktocreatetimeHelpText, 'fa fa-plus-circle', $linktocreatetimeUrl, '', $linktocreatetimeBtnStatus); + $paramsbutton = array('morecss'=>'reposition'); + $linktocreatetime = dolGetButtonTitle($langs->trans('AddTimeSpent'), $linktocreatetimeHelpText, 'fa fa-plus-circle', $linktocreatetimeUrl, '', $linktocreatetimeBtnStatus, $paramsbutton); } $massactionbutton = ''; @@ -888,12 +928,12 @@ if (($id > 0 || !empty($ref)) || $projectidforalltimes > 0) { $massactionbutton = $form->selectMassAction('', $arrayofmassactions); // Show section with information of task. If id of task is not defined and project id defined, then $projectidforalltimes is not empty. - if (empty($projectidforalltimes)) { + if (empty($projectidforalltimes) && empty($allprojectforuser)) { $head = task_prepare_head($object); print dol_get_fiche_head($head, 'task_time', $langs->trans("Task"), -1, 'projecttask', 0, '', 'reposition'); if ($action == 'deleteline') { - print $form->formconfirm($_SERVER["PHP_SELF"]."?".($object->id > 0 ? "id=".$object->id : 'projectid='.$projectstatic->id).'&lineid='.GETPOST("lineid", 'int').($withproject ? '&withproject=1' : ''), $langs->trans("DeleteATimeSpent"), $langs->trans("ConfirmDeleteATimeSpent"), "confirm_delete", '', '', 1); + print $form->formconfirm($_SERVER["PHP_SELF"]."?".($object->id > 0 ? "id=".$object->id : 'projectid='.$projectstatic->id).'&lineid='.GETPOST("lineid", 'int').($withproject ? '&withproject=1' : ''), $langs->trans("DeleteATimeSpent"), $langs->trans("ConfirmDeleteATimeSpent"), "confirm_deleteline", '', '', 1); } $param = ($withproject ? '&withproject=1' : ''); @@ -990,9 +1030,9 @@ if (($id > 0 || !empty($ref)) || $projectidforalltimes > 0) { } - if ($projectstatic->id > 0) { + if ($projectstatic->id > 0 || $allprojectforuser > 0) { if ($action == 'deleteline' && !empty($projectidforalltimes)) { - print $form->formconfirm($_SERVER["PHP_SELF"]."?".($object->id > 0 ? "id=".$object->id : 'projectid='.$projectstatic->id).'&lineid='.GETPOST('lineid', 'int').($withproject ? '&withproject=1' : ''), $langs->trans("DeleteATimeSpent"), $langs->trans("ConfirmDeleteATimeSpent"), "confirm_delete", '', '', 1); + print $form->formconfirm($_SERVER["PHP_SELF"]."?".($object->id > 0 ? "id=".$object->id : 'projectid='.$projectstatic->id).'&lineid='.GETPOST('lineid', 'int').($withproject ? '&withproject=1' : ''), $langs->trans("DeleteATimeSpent"), $langs->trans("ConfirmDeleteATimeSpent"), "confirm_deleteline", '', '', 1); } // Initialize technical object to manage hooks. Note that conf->hooks_modules contains array @@ -1216,7 +1256,7 @@ if (($id > 0 || !empty($ref)) || $projectidforalltimes > 0) { $tasks = array(); $sql = "SELECT t.rowid, t.fk_task, t.task_date, t.task_datehour, t.task_date_withhour, t.task_duration, t.fk_user, t.note, t.thm,"; - $sql .= " pt.ref, pt.label,"; + $sql .= " pt.ref, pt.label, pt.fk_projet,"; $sql .= " u.lastname, u.firstname, u.login, u.photo, u.statut as user_status,"; $sql .= " il.fk_facture as invoice_id, inv.fk_statut"; $sql .= " FROM ".MAIN_DB_PREFIX."projet_task_time as t"; @@ -1225,11 +1265,20 @@ if (($id > 0 || !empty($ref)) || $projectidforalltimes > 0) { $sql .= " ".MAIN_DB_PREFIX."projet_task as pt, ".MAIN_DB_PREFIX."user as u"; $sql .= " WHERE t.fk_user = u.rowid AND t.fk_task = pt.rowid"; - if (empty($projectidforalltimes)) { + if (empty($projectidforalltimes) && empty($allprojectforuser)) { + // Limit on one task $sql .= " AND t.fk_task =".((int) $object->id); - } else { + } elseif (!empty($projectidforalltimes)) { + // Limit on one project $sql .= " AND pt.fk_projet IN (".$db->sanitize($projectidforalltimes).")"; + } elseif (!empty($allprojectforuser)) { + // Limit on on user + if (empty($search_user)) { + $search_user = $user->id; + } + $sql .= " AND t.fk_user = ".((int) $search_user); } + if ($search_note) { $sql .= natural_search('t.note', $search_note); } @@ -1290,7 +1339,7 @@ if (($id > 0 || !empty($ref)) || $projectidforalltimes > 0) { print_barre_liste($title, $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, $massactionbutton, $num, $nbtotalofrecords, 'clock', 0, $linktocreatetime, '', $limit, 0, 0, 1); } else { - print ''."\n"; + print ''."\n"; $title = $langs->trans("ListTaskTimeForTask"); @@ -1322,6 +1371,9 @@ if (($id > 0 || !empty($ref)) || $projectidforalltimes > 0) { print '
'.$langs->trans("Date").''.$langs->trans("Project").''.$langs->trans("Task").''; + // Add project selector + print ''; + if (empty($conf->cache['project'][$task_time->fk_projet])) { + $tmpproject = new Project($db); + $tmpproject->fetch($task_time->fk_projet); + $conf->cache['project'][$task_time->fk_projet] = $tmpproject; + } else { + $tmpproject = $conf->cache['project'][$task_time->fk_projet]; + } + print $tmpproject->getNomUrl(1); + print ''; + print ''; + print '
\n"; print dol_get_fiche_end(); diff --git a/htdocs/public/members/public_list.php b/htdocs/public/members/public_list.php index 80e092f2951..4bf2a6b94bf 100644 --- a/htdocs/public/members/public_list.php +++ b/htdocs/public/members/public_list.php @@ -92,8 +92,8 @@ function llxFooterVierge() } -$sortfield = GETPOST("sortfield", 'alpha'); -$sortorder = GETPOST("sortorder", 'alpha'); +$sortfield = GETPOST('sortfield', 'aZ09comma'); +$sortorder = GETPOST('sortorder', 'aZ09comma'); $limit = GETPOST('limit', 'int') ?GETPOST('limit', 'int') : $conf->liste_limit; $page = GETPOSTISSET('pageplusone') ? (GETPOST('pageplusone') - 1) : GETPOST("page", 'int'); if (empty($page) || $page == -1) { diff --git a/htdocs/public/onlinesign/newonlinesign.php b/htdocs/public/onlinesign/newonlinesign.php index 66b4af425e7..8bd95d23c8d 100644 --- a/htdocs/public/onlinesign/newonlinesign.php +++ b/htdocs/public/onlinesign/newonlinesign.php @@ -21,6 +21,7 @@ * \file htdocs/public/onlinesign/newonlinesign.php * \ingroup core * \brief File to offer a way to make an online signature for a particular Dolibarr entity + * Example of URL: https://localhost/public/onlinesign/newonlinesign.php?ref=PR... */ if (!defined('NOLOGIN')) { @@ -47,12 +48,13 @@ if (is_numeric($entity)) { require '../../main.inc.php'; require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php'; require_once DOL_DOCUMENT_ROOT.'/core/lib/payments.lib.php'; +require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php'; require_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php'; require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php'; require_once DOL_DOCUMENT_ROOT.'/comm/propal/class/propal.class.php'; // Load translation files -$langs->loadLangs(array("main", "other", "dict", "bills", "companies", "errors", "paybox","propal")); +$langs->loadLangs(array("main", "other", "dict", "bills", "companies", "errors", "members", "paybox", "propal")); // Security check // No check on module enabled. Done later according to $validpaymentmethod @@ -60,6 +62,9 @@ $langs->loadLangs(array("main", "other", "dict", "bills", "companies", "errors", // Get parameters $action = GETPOST('action', 'aZ09'); $cancel = GETPOST('cancel', 'alpha'); +$confirm = GETPOST('confirm', 'alpha'); + + $refusepropal = GETPOST('refusepropal', 'alpha'); $message = GETPOST('message', 'aZ09'); @@ -118,15 +123,34 @@ $urlko = preg_replace('/&$/', '', $urlko); // Remove last & $creditor = $mysoc->name; -$object = new Propal($db); -$object->fetch(0, $ref); +$type = $source; +if ($source == 'proposal') { + $object = new Propal($db); + $object->fetch(0, $ref); +} else { + accessforbidden('Bad value for source'); + exit; +} + + +// Check securitykey +$securekeyseed = ''; +if ($source == 'proposal') { + $securekeyseed = $conf->global->PROPOSAL_ONLINE_SIGNATURE_SECURITY_TOKEN; +} + +if (!dol_verifyHash($securekeyseed.$type.$ref, $SECUREKEY, '0')) { + http_response_code(403); + print 'Bad value for securitykey. Value provided '.dol_escape_htmltag($SECUREKEY).' does not match expected value for ref='.dol_escape_htmltag($ref); + exit(-1); +} /* * Actions */ -if ($action == 'confirm_refusepropal') { +if ($action == 'confirm_refusepropal' && $confirm == 'yes') { $db->begin(); $sql = "UPDATE ".MAIN_DB_PREFIX."propal"; @@ -143,7 +167,7 @@ if ($action == 'confirm_refusepropal') { $db->commit(); $message = 'refused'; - setEventMessages("PropalRefused", null, 'warning'); + setEventMessages("PropalRefused", null, 'warnings'); } else { $db->rollback(); } @@ -169,11 +193,11 @@ $replacemainarea = (empty($conf->dol_hide_leftmenu) ? '
' : '').'
'; llxHeader($head, $langs->trans("OnlineSignature"), '', '', 0, 0, '', '', '', 'onlinepaymentbody', $replacemainarea, 1); if ($action == 'refusepropal') { - print $form->formconfirm($_SERVER["PHP_SELF"].'?ref='.$ref, $langs->trans('RefusePropal'), $langs->trans('ConfirmRefusePropal', $object->ref), 'confirm_refusepropal', '', '', 1); + print $form->formconfirm($_SERVER["PHP_SELF"].'?ref='.urlencode($ref).'&securekey='.urlencode($SECUREKEY), $langs->trans('RefusePropal'), $langs->trans('ConfirmRefusePropal', $object->ref), 'confirm_refusepropal', '', '', 1); } -// Check link validity for param 'source' -if (!empty($source) && in_array($ref, array('member_ref', 'contractline_ref', 'invoice_ref', 'order_ref', ''))) { +// Check link validity for param 'source' to avoid use of the examples as value +if (!empty($source) && in_array($ref, array('member_ref', 'contractline_ref', 'invoice_ref', 'order_ref', 'proposal_ref', ''))) { $langs->load("errors"); dol_print_error_email('BADREFINONLINESIGNFORM', $langs->trans("ErrorBadLinkSourceSetButBadValueForRef", $source, $ref)); // End of page @@ -191,10 +215,11 @@ print ''."\n print ''."\n"; print ''."\n"; print ''; +print ''; print "\n"; print ''."\n"; -print ''."\n"; +print '
'."\n"; // Show logo (search order: logo defined by ONLINE_SIGN_LOGO_suffix, then ONLINE_SIGN_LOGO_, then small company logo, large company logo, theme logo, common logo) // Define logo and logosmall @@ -229,16 +254,15 @@ if ($urllogo) { } print ''; } -if (!empty($conf->global->PROPOSAL_IMAGE_PUBLIC_SIGN)) { +if ($source == 'proposal' && !empty($conf->global->PROPOSAL_IMAGE_PUBLIC_SIGN)) { print '
'; - print ''; + print ''; print '
'; } // Output introduction text $text = ''; if (!empty($conf->global->ONLINE_SIGN_NEWFORM_TEXT)) { - $langs->load("members"); $reg = array(); if (preg_match('/^\((.*)\)$/', $conf->global->ONLINE_SIGN_NEWFORM_TEXT, $reg)) { $text .= $langs->trans($reg[1])."
\n"; @@ -294,6 +318,13 @@ if ($source == 'proposal') { print ''.$proposal->thirdparty->name.''; print ''."\n"; + // Amount + + print ''."\n"; + // Object $text = ''.$langs->trans("SignatureProposalRef", $proposal->ref).''; @@ -307,9 +338,32 @@ if ($source == 'proposal') { print $langs->trans("DownloadDocument").''; } } else { - /* TODO If proposal signed newer than proposal ref, get link of proposal signed + $last_main_doc_file = $proposal->last_main_doc; - */ + if ($proposal->status == $proposal::STATUS_NOTSIGNED) { + $directdownloadlink = $proposal->getLastMainDocLink('proposal'); + if ($directdownloadlink) { + print '
'; + print img_mime($proposal->last_main_doc, ''); + print $langs->trans("DownloadDocument").''; + } + } elseif ($proposal->status == $proposal::STATUS_SIGNED || $proposal->status == $proposal::STATUS_BILLED) { + if (preg_match('/_signed-(\d+)/', $last_main_doc_file)) { // If the last main doc has been signed + $last_main_doc_file_not_signed = preg_replace('/_signed-(\d+)/', '', $last_main_doc_file); + + $datefilesigned = dol_filemtime($last_main_doc_file); + $datefilenotsigned = dol_filemtime($last_main_doc_file_not_signed); + + if (empty($datefilenotsigned) || $datefilesigned > $datefilenotsigned) { + $directdownloadlink = $proposal->getLastMainDocLink('proposal'); + if ($directdownloadlink) { + print '
'; + print img_mime($proposal->last_main_doc, ''); + print $langs->trans("DownloadDocument").''; + } + } + } + } } print ''; @@ -345,23 +399,27 @@ if ($action != 'dosign') { print ''."\n"; print ''."\n"; diff --git a/htdocs/public/payment/newpayment.php b/htdocs/public/payment/newpayment.php index a5e24312ded..5158e294fb2 100644 --- a/htdocs/public/payment/newpayment.php +++ b/htdocs/public/payment/newpayment.php @@ -184,11 +184,12 @@ if ($source == 'organizedeventregistration') { } -$paymentmethod = GETPOST('paymentmethod', 'alphanohtml') ?GETPOST('paymentmethod', 'alphanohtml') : ''; // Empty in most cases. Defined when a payment mode is forced +$paymentmethod = GETPOST('paymentmethod', 'alphanohtml') ? GETPOST('paymentmethod', 'alphanohtml') : ''; // Empty in most cases. Defined when a payment mode is forced $validpaymentmethod = array(); // Detect $paymentmethod foreach ($_POST as $key => $val) { + $reg = array(); if (preg_match('/^dopayment_(.*)$/', $key, $reg)) { $paymentmethod = $reg[1]; break; @@ -246,7 +247,6 @@ $urlok = preg_replace('/&$/', '', $urlok); // Remove last & $urlko = preg_replace('/&$/', '', $urlko); // Remove last & - // Make special controls if ((empty($paymentmethod) || $paymentmethod == 'paypal') && !empty($conf->paypal->enabled)) { @@ -299,21 +299,23 @@ if ($tmpsource == 'membersubscription') { } $valid = true; if (!empty($conf->global->PAYMENT_SECURITY_TOKEN)) { - $token = ''; - $tokenoldcompat = ''; + $tokenisok = false; if (!empty($conf->global->PAYMENT_SECURITY_TOKEN_UNIQUE)) { if ($tmpsource && $REF) { - $token = dol_hash($conf->global->PAYMENT_SECURITY_TOKEN.$tmpsource.$REF, 2); // Use the source in the hash to avoid duplicates if the references are identical + // Use the source in the hash to avoid duplicates if the references are identical + $tokenisok = dol_verifyHash($conf->global->PAYMENT_SECURITY_TOKEN.$tmpsource.$REF, $SECUREKEY, '2'); + // Do a second test for retro-compatibility (token may have been hashed with membersubscription in external module) if ($tmpsource != $source) { - $tokenoldcompat = dol_hash($conf->global->PAYMENT_SECURITY_TOKEN.$source.$REF, 2); // for retro-compatibility (token may have been hashed with membersubscription in external module) + $tokenisok = dol_verifyHash($conf->global->PAYMENT_SECURITY_TOKEN.$source.$REF, $SECUREKEY, '2'); } } else { - $token = dol_hash($conf->global->PAYMENT_SECURITY_TOKEN, 2); + $tokenisok = dol_verifyHash($conf->global->PAYMENT_SECURITY_TOKEN, $SECUREKEY, '2'); } } else { - $token = $conf->global->PAYMENT_SECURITY_TOKEN; + $tokenisok = ($conf->global->PAYMENT_SECURITY_TOKEN == $SECUREKEY); } - if ($SECUREKEY != $token && (empty($tokenoldcompat) || $SECUREKEY != $tokenoldcompat)) { + + if (! $tokenisok) { if (empty($conf->global->PAYMENT_SECURITY_ACCEPT_ANY_TOKEN)) { $valid = false; // PAYMENT_SECURITY_ACCEPT_ANY_TOKEN is for backward compatibility } else { @@ -323,7 +325,7 @@ if (!empty($conf->global->PAYMENT_SECURITY_TOKEN)) { if (!$valid) { print '
Bad value for key.
'; - //print 'SECUREKEY='.$SECUREKEY.' token='.$token.' valid='.$valid; + //print 'SECUREKEY='.$SECUREKEY.' valid='.$valid; exit; } } @@ -439,21 +441,21 @@ if ($action == 'dopayment') { $origfulltag = GETPOST("fulltag", 'alpha'); // Securekey into back url useless for back url and we need an url lower than 150. - $urlok = preg_replace('/securekey=[^&]+/', '', $urlok); - $urlko = preg_replace('/securekey=[^&]+/', '', $urlko); + $urlok = preg_replace('/securekey=[^&]+&?/', '', $urlok); + $urlko = preg_replace('/securekey=[^&]+&?/', '', $urlko); if (empty($PRICE) || !is_numeric($PRICE)) { $mesg = $langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Amount")); } elseif (empty($email)) { - $mesg = $langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("YourEMail")); + $mesg = $langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("ONLINE_PAYMENT_SENDEMAIL")); } elseif (!isValidEMail($email)) { $mesg = $langs->trans("ErrorBadEMail", $email); } elseif (!$origfulltag) { $mesg = $langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("PaymentCode")); } elseif (dol_strlen($urlok) > 150) { - $mesg = 'Error urlok too long '.$urlok.'( Paybox requires 150, found '.strlen($urlok).')'; + $mesg = 'Error urlok too long '.$urlok.' (Paybox requires 150, found '.strlen($urlok).')'; } elseif (dol_strlen($urlko) > 150) { - $mesg = 'Error urlko too long '.$urlko.'( Paybox requires 150, found '.strlen($urlok).')'; + $mesg = 'Error urlko too long '.$urlko.' (Paybox requires 150, found '.strlen($urlok).')'; } if (empty($mesg)) { @@ -480,7 +482,7 @@ if ($action == 'dopayment') { // Called when choosing Stripe mode. // When using the Charge API architecture, this code is called after clicking the 'dopayment' with the Charge API architecture. -// When using the PaymentIntent API architecture, the Stripe customer is already created when creating PaymentIntent when showing payment page and the payment is already ok. +// When using the PaymentIntent API architecture, the Stripe customer was already created when creating PaymentIntent when showing payment page, and the payment is already ok when action=charge. if ($action == 'charge' && !empty($conf->stripe->enabled)) { $amountstripe = $amount; @@ -767,9 +769,23 @@ if ($action == 'charge' && !empty($conf->stripe->enabled)) { setEventMessages($paymentintent->status, null, 'errors'); $action = ''; } else { - // TODO We can alse record the payment mode into llx_societe_rib with stripe $paymentintent->payment_method + // TODO We can also record the payment mode into llx_societe_rib with stripe $paymentintent->payment_method // Note that with other old Stripe architecture (using Charge API), the payment mode was not recorded, so it is not mandatory to do it here. //dol_syslog("Create payment_method for ".$paymentintent->payment_method, LOG_DEBUG, 0, '_stripe'); + + // Get here amount and currency used for payment and force value into $amount and $currency so the real amount is saved into session instead + // of the amount and currency retreived from the POST. + if (!empty($paymentintent->currency) && !empty($paymentintent->amount)) { + $currency = strtoupper($paymentintent->currency); + $amount = $paymentintent->amount; + + // Correct the amount according to unit of currency + // See https://support.stripe.com/questions/which-zero-decimal-currencies-does-stripe-support + $arrayzerounitcurrency = array('BIF', 'CLP', 'DJF', 'GNF', 'JPY', 'KMF', 'KRW', 'MGA', 'PYG', 'RWF', 'VND', 'VUV', 'XAF', 'XOF', 'XPF'); + if (!in_array($currency, $arrayzerounitcurrency)) { + $amount = $amount / 100; + } + } } } @@ -911,7 +927,8 @@ print ''."\n"; print ''."\n"; print "\n"; -print '
'.$langs->trans("Amount"); + print ''; + print ''.price($proposal->total_ttc, 0, $langs, 1, -1, -1, $conf->currency).''; + print '
'; + if ($action == "dosign" && empty($cancel)) { print '
'; print ''; print '
'; print '
'; - print ''; + // Do not use class="reposition" here: It breaks the submit and there is a message on top to say it's ok, so going back top is better. + print ''; print ''; + + // Add js code managed into the div #signature print ' '; } else { - if ($object->status == $object::STATUS_SIGNED) { - print '
'; - if ($message == 'signed') { - print ''.$langs->trans("PropalSigned").''; + if ($source == 'proposal') { + if ($object->status == $object::STATUS_SIGNED) { + print '
'; + if ($message == 'signed') { + print ''.$langs->trans("PropalSigned").''; + } else { + print ''.$langs->trans("PropalAlreadySigned").''; + } + } elseif ($object->status == $object::STATUS_NOTSIGNED) { + print '
'; + if ($message == 'refused') { + print ''.$langs->trans("PropalRefused").''; + } else { + print ''.$langs->trans("PropalAlreadyRefused").''; + } } else { - print ''.$langs->trans("PropalAlreadySigned").''; + print ''; + print ''; } - } elseif ($object->status == $object::STATUS_NOTSIGNED) { - print '
'; - if ($message == 'refused') { - print ''.$langs->trans("PropalRefused").''; - } else { - print ''.$langs->trans("PropalAlreadyRefused").''; - } - } else { - print ''; - print ''; } } print '
'."\n"; +// Section with payment informationsummary +print '
'."\n"; // Output introduction text $text = ''; @@ -968,13 +985,13 @@ if (!$source) { if (empty($amount) || !is_numeric($amount)) { print ''; print ''; + // Currency + print ' '.$langs->trans("Currency".$currency).''; } else { - print ''.price($amount).''; + print ''.price($amount, 1, $langs, 1, -1, -1, $currency).''; // Price with currency print ''; print ''; } - // Currency - print ' '.$langs->trans("Currency".$currency).''; print ''; print ''."\n"; @@ -1067,13 +1084,13 @@ if ($source == 'order') { if (empty($amount) || !is_numeric($amount)) { print ''; print ''; + // Currency + print ' '.$langs->trans("Currency".$currency).''; } else { - print ''.price($amount).''; + print ''.price($amount, 1, $langs, 1, -1, -1, $currency).''; // Price with currency print ''; print ''; } - // Currency - print ' '.$langs->trans("Currency".$currency).''; print ''; print ''."\n"; @@ -1198,18 +1215,16 @@ if ($source == 'invoice') { if (empty($amount) || !is_numeric($amount)) { print ''; print ''; + print ' '.$langs->trans("Currency".$currency).''; } else { - print ''.price($amount).''; + print ''.price($amount, 1, $langs, 1, -1, -1, $currency).''; // Price with currency print ''; print ''; } - print ' '.$langs->trans("Currency".$currency).''; - print ''; } else { - print ''.price($object->total_ttc, 1, $langs).''; - print ' '.$langs->trans("Currency".$currency).''; - print ''; + print ''.price($object->total_ttc, 1, $langs, 1, -1, -1, $currency).''; // Price with currency } + print ''; print ''."\n"; // Tag @@ -1325,7 +1340,7 @@ if ($source == 'contractline') { $qty = 1; if (GETPOST('qty')) { - $qty = GETPOST('qty'); + $qty = price2num(GETPOST('qty', 'alpha'), 'MS'); } // Creditor @@ -1402,13 +1417,13 @@ if ($source == 'contractline') { if (empty($amount) || !is_numeric($amount)) { print ''; print ''; + // Currency + print ' '.$langs->trans("Currency".$currency).''; } else { - print ''.price($amount).''; + print ''.price($amount, 1, $langs, 1, -1, -1, $currency).''; // Price with currency print ''; print ''; } - // Currency - print ' '.$langs->trans("Currency".$currency).''; print ''; print ''."\n"; @@ -1460,9 +1475,12 @@ if ($source == 'member' || $source == 'membersubscription') { $langs->load("members"); require_once DOL_DOCUMENT_ROOT.'/adherents/class/adherent.class.php'; + require_once DOL_DOCUMENT_ROOT.'/adherents/class/adherent_type.class.php'; require_once DOL_DOCUMENT_ROOT.'/adherents/class/subscription.class.php'; $member = new Adherent($db); + $adht = new AdherentType($db); + $result = $member->fetch('', $ref); if ($result <= 0) { $mesg = $member->error; @@ -1470,6 +1488,8 @@ if ($source == 'member' || $source == 'membersubscription') { } else { $member->fetch_thirdparty(); $subscription = new Subscription($db); + + $adht->fetch($member->typeid); } $object = $member; @@ -1478,6 +1498,11 @@ if ($source == 'member' || $source == 'membersubscription') { if (GETPOST("amount", 'alpha')) { $amount = GETPOST("amount", 'alpha'); } + // If amount still not defined, we take amount of the type of member + if (empty($amount)) { + $amount = $adht->amount; + } + $amount = price2num($amount, 'MT'); } @@ -1499,10 +1524,13 @@ if ($source == 'member' || $source == 'membersubscription') { // Debitor print ''."\n"; @@ -1752,7 +1779,7 @@ if ($source == 'donation') { print ' ('.$langs->trans("ToComplete"); } if (!empty($conf->global->MEMBER_EXT_URL_SUBSCRIPTION_INFO)) { - print ' - '.$langs->trans("SeeHere").''; + print ' - '.$langs->trans("SeeHere").''; } if (empty($conf->global->MEMBER_NEWFORM_AMOUNT)) { print ')'; @@ -1782,18 +1809,18 @@ if ($source == 'donation') { } print ''; print ''; + // Currency + print ' '.$langs->trans("Currency".$currency).''; } else { $valtoshow = $amount; if (!empty($conf->global->MEMBER_MIN_AMOUNT) && $valtoshow) { $valtoshow = max($conf->global->MEMBER_MIN_AMOUNT, $valtoshow); $amount = $valtoshow; } - print ''.price($valtoshow).''; + print ''.price($valtoshow, 1, $langs, 1, -1, -1, $currency).''; // Price with currency print ''; print ''; } - // Currency - print ' '.$langs->trans("Currency".$currency).''; print ''; print ''."\n"; @@ -1882,12 +1909,9 @@ if ($source == 'organizedeventregistration') { print ''."\n"; @@ -1968,12 +1992,9 @@ if ($source == 'boothlocation') { print ''."\n"; @@ -2286,7 +2307,7 @@ if (preg_match('/^dopayment/', $action)) { // If we choosed/click on the payme print ''."\n"; // Code to ask the credit card. This use the default "API version". No way to force API version when using JS code. - print ' + + + + + + diff --git a/htdocs/public/test/test_exec.php b/htdocs/public/test/test_exec.php new file mode 100644 index 00000000000..732bfefa824 --- /dev/null +++ b/htdocs/public/test/test_exec.php @@ -0,0 +1,91 @@ +\n"; +print 'PHP_SESSION_DISABLED='.PHP_SESSION_DISABLED."
\n"; +print 'PHP_SESSION_NONE='.PHP_SESSION_NONE."
\n"; +print 'PHP_SESSION_ACTIVE='.PHP_SESSION_ACTIVE."
\n"; +print '
'; + +print 'session_status='.session_status().' (before main.inc.php)'; +print '
'; + +require '../../main.inc.php'; + +// Security +if ($dolibarr_main_prod) { + accessforbidden('Access forbidden when $dolibarr_main_prod is set to 1'); +} + + +/* + * View + */ + +echo "Test
\n"; +$out=''; +$ret=0; + +$file = '/tmp/test.txt'; +$f=fopen($file, 'r'); +if ($f) { + $s=fread($f, 4096); + print $s; + fclose($f); +} else { + print "Failed to open file ".$file."
\n"; +} + +print '

'."\n"; + +exec('cat /test.txt; ls /dev/std*; sleep 1;', $out, $ret); +print $ret."
\n"; +print_r($out); + +print '

'."\n"; + +$ret = 0; +$out = null; +exec('/usr/bin/clamdscan --fdpass filethatdoesnotexists.php', $out, $ret); +print $ret."
\n"; +print_r($out); diff --git a/htdocs/public/test/test_forms.php b/htdocs/public/test/test_forms.php index d89e04e12a6..47d827cbaf5 100644 --- a/htdocs/public/test/test_forms.php +++ b/htdocs/public/test/test_forms.php @@ -8,10 +8,16 @@ if (!defined('NOSESSION')) { require '../../main.inc.php'; include_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php'; +// Security if ($dolibarr_main_prod) { - accessforbidden(); + accessforbidden('Access forbidden when $dolibarr_main_prod is set to 1'); } + +/* + * View + */ + llxHeader(); ?> diff --git a/htdocs/public/test/test_sessionlock.php b/htdocs/public/test/test_sessionlock.php index 18a1ef73d08..8464ba2eb4f 100644 --- a/htdocs/public/test/test_sessionlock.php +++ b/htdocs/public/test/test_sessionlock.php @@ -52,11 +52,15 @@ print '
'; require '../../main.inc.php'; -/* No need for this. +// Security if ($dolibarr_main_prod) { accessforbidden(); } -*/ + + +/* + * View + */ print 'session_status='.session_status().' (after main.inc.php)'; print '
'; diff --git a/htdocs/public/ticket/create_ticket.php b/htdocs/public/ticket/create_ticket.php index e621feb6653..6a3fd1aa688 100644 --- a/htdocs/public/ticket/create_ticket.php +++ b/htdocs/public/ticket/create_ticket.php @@ -4,7 +4,7 @@ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or + * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, @@ -47,6 +47,12 @@ if (!defined('NOBROWSERNOTIF')) { define('NOBROWSERNOTIF', '1'); } +// For MultiCompany module. +// Do not use GETPOST here, function is not defined and define must be done before including main.inc.php +$entity = (!empty($_GET['entity']) ? (int) $_GET['entity'] : (!empty($_POST['entity']) ? (int) $_POST['entity'] : 1)); +if (is_numeric($entity)) { + define("DOLENTITY", $entity); +} require '../../main.inc.php'; require_once DOL_DOCUMENT_ROOT.'/ticket/class/actions_ticket.class.php'; @@ -167,7 +173,7 @@ if (empty($reshook) && $action == 'create_ticket' && GETPOST('save', 'alpha')) { // Check Captcha code if is enabled if (!empty($conf->global->MAIN_SECURITY_ENABLECAPTCHA)) { $sessionkey = 'dol_antispam_value'; - $ok = (array_key_exists($sessionkey, $_SESSION) === true && (strtolower($_SESSION[$sessionkey]) === strtolower(GETPOST('code', 'none')))); + $ok = (array_key_exists($sessionkey, $_SESSION) === true && (strtolower($_SESSION[$sessionkey]) === strtolower(GETPOST('code', 'restricthtml')))); if (!$ok) { $error++; array_push($object->errors, $langs->trans("ErrorBadValueForCode")); @@ -297,7 +303,7 @@ if (empty($reshook) && $action == 'create_ticket' && GETPOST('save', 'alpha')) { if (is_array($object->array_options) && count($object->array_options) > 0) { foreach ($object->array_options as $key => $value) { $key = substr($key, 8); // remove "options_" - $message_admin .= '
  • '.$langs->trans($extrafields->attributes[$object->element]['label'][$key]).' : '.$extrafields->showOutputField($key, $value).'
  • '; + $message_admin .= '
  • '.$langs->trans($extrafields->attributes[$object->table_element]['label'][$key]).' : '.$extrafields->showOutputField($key, $value, '', $object->table_element).'
  • '; } } $message_admin .= ''; @@ -342,7 +348,7 @@ if (empty($reshook) && $action == 'create_ticket' && GETPOST('save', 'alpha')) { $messagetoshow = str_replace(array('{s1}', '{s2}'), array(''.$object->track_id.'', ''.$object->ref.''), $messagetoshow); setEventMessages($messagetoshow, null, 'warnings'); setEventMessages($langs->trans('PleaseRememberThisId'), null, 'warnings'); - header("Location: index.php"); + header("Location: index.php".(!empty($entity) && !empty($conf->multicompany->enabled)?'?entity='.$entity:'')); exit; } } else { diff --git a/htdocs/public/ticket/index.php b/htdocs/public/ticket/index.php index 6abeb5f8b12..227dcf3867b 100644 --- a/htdocs/public/ticket/index.php +++ b/htdocs/public/ticket/index.php @@ -40,7 +40,6 @@ if (!defined('NOBROWSERNOTIF')) { // For MultiCompany module. // Do not use GETPOST here, function is not defined and define must be done before including main.inc.php -// TODO This should be useless. Because entity must be retrieve from object ref and not from url. $entity = (!empty($_GET['entity']) ? (int) $_GET['entity'] : (!empty($_POST['entity']) ? (int) $_POST['entity'] : 1)); if (is_numeric($entity)) { define("DOLENTITY", $entity); @@ -77,7 +76,6 @@ if (empty($conf->global->TICKET_ENABLE_PUBLIC_INTERFACE)) { print $langs->trans('TicketPublicInterfaceForbidden'); exit; } - $arrayofjs = array(); $arrayofcss = array('/ticket/css/styles.css.php'); @@ -86,9 +84,9 @@ llxHeaderTicket($langs->trans("Tickets"), "", 0, 0, $arrayofjs, $arrayofcss); print ''; diff --git a/htdocs/public/ticket/list.php b/htdocs/public/ticket/list.php index db5f5d8d754..1a3c0c3babd 100644 --- a/htdocs/public/ticket/list.php +++ b/htdocs/public/ticket/list.php @@ -40,6 +40,13 @@ if (!defined('NOBROWSERNOTIF')) { } // If this page is public (can be called outside logged session) +// For MultiCompany module. +// Do not use GETPOST here, function is not defined and define must be done before including main.inc.php +$entity = (!empty($_GET['entity']) ? (int) $_GET['entity'] : (!empty($_POST['entity']) ? (int) $_POST['entity'] : 1)); +if (is_numeric($entity)) { + define("DOLENTITY", $entity); +} + require '../../main.inc.php'; require_once DOL_DOCUMENT_ROOT.'/ticket/class/actions_ticket.class.php'; require_once DOL_DOCUMENT_ROOT.'/core/class/html.formticket.class.php'; @@ -70,6 +77,9 @@ if (isset($_SESSION['email_customer'])) { $object = new Ticket($db); +// Initialize technical object to manage hooks of page. Note that conf->hooks_modules contains array of hook context +$hookmanager->initHooks(array('ticketpubliclist', 'globalcard')); + if (empty($conf->ticket->enabled)) { accessforbidden('', 0, 0, 1); } @@ -154,10 +164,6 @@ if ($action == "view_ticketlist") { } } -//$object->doActions($action); - - - /* * View */ @@ -215,6 +221,9 @@ if ($action == "view_ticketlist") { $filter = array(); $param = 'action=view_ticketlist'; + if (!empty($entity) && !empty($conf->multicompany->enabled)) { + $param .= '&entity='.$entity; + } // Definition of fields for list $arrayfields = array( @@ -288,8 +297,8 @@ if ($action == "view_ticketlist") { require DOL_DOCUMENT_ROOT.'/core/actions_changeselectedfields.inc.php'; - $sortfield = GETPOST("sortfield", 'alpha'); - $sortorder = GETPOST("sortorder", 'alpha'); + $sortfield = GETPOST('sortfield', 'aZ09comma'); + $sortorder = GETPOST('sortorder', 'aZ09comma'); if (!$sortfield) { $sortfield = 't.datec'; @@ -392,7 +401,7 @@ if ($action == "view_ticketlist") { print_barre_liste($langs->trans('TicketList'), $page, 'public/list.php', $param, $sortfield, $sortorder, '', $num, $num_total, 'ticket'); // Search bar - print ''."\n"; + print ''."\n"; print ''; print ''; print ''; @@ -401,6 +410,11 @@ if ($action == "view_ticketlist") { $varpage = empty($contextpage) ? $url_page_current : $contextpage; $selectedfields = $form->multiSelectArrayWithCheckbox('selectedfields', $arrayfields, $varpage); // This also change content of $arrayfields + // allow to display information before list + $parameters=array('arrayfields'=>$arrayfields); + $reshook=$hookmanager->executeHooks('printFieldListHeader', $parameters, $object, $action); // Note that $action and $object may have been modified by hook + print $hookmanager->resPrint; + print '
    '.$langs->trans("Member"); - print ''; - if ($member->morphy == 'mor' && !empty($member->societe)) { - print $member->societe; + print ''; + print ''; + if ($member->morphy == 'mor' && !empty($member->company)) { + print img_picto('', 'company', 'class="pictofixedwidth"'); + print $member->company; } else { + print img_picto('', 'member', 'class="pictofixedwidth"'); print $member->getFullName($langs); } print ''; @@ -1591,7 +1619,7 @@ if ($source == 'member' || $source == 'membersubscription') { print ' ('.$langs->trans("ToComplete"); } if (!empty($conf->global->MEMBER_EXT_URL_SUBSCRIPTION_INFO)) { - print ' - '.$langs->trans("SeeHere").''; + print ' - '.$langs->trans("SeeHere").''; } if (empty($conf->global->MEMBER_NEWFORM_AMOUNT)) { print ')'; @@ -1621,23 +1649,22 @@ if ($source == 'member' || $source == 'membersubscription') { } print ''; if (empty($conf->global->MEMBER_NEWFORM_EDITAMOUNT)) { - print ''; + print ''; print ''; } else { print ''; } + print ' '.$langs->trans("Currency".$currency).''; } else { $valtoshow = $amount; if (!empty($conf->global->MEMBER_MIN_AMOUNT) && $valtoshow) { $valtoshow = max($conf->global->MEMBER_MIN_AMOUNT, $valtoshow); $amount = $valtoshow; } - print ''.price($valtoshow).''; + print ''.price($valtoshow, 1, $langs, 1, -1, -1, $currency).''; // Price with currency print ''; print ''; } - // Currency - print ' '.$langs->trans("Currency".$currency).''; print ''; print '
    '.$langs->trans("Amount"); print ''; $valtoshow = $amount; - print ''.price($valtoshow).''; + print ''.price($valtoshow, 1, $langs, 1, -1, -1, $currency).''; // Price with currency print ''; print ''; - - // Currency - print ' '.$langs->trans("Currency".$currency).''; print ''; print '
    '.$langs->trans("Amount"); print ''; $valtoshow = $amount; - print ''.price($valtoshow).''; + print ''.price($valtoshow, 1, $langs, 1, -1, -1, $currency).''; // Price with currency print ''; print ''; - - // Currency - print ' '.$langs->trans("Currency".$currency).''; print ''; print '
    '; // Filter bar @@ -651,7 +665,7 @@ if ($action == "view_ticketlist") { } print '>'; $tmpkey = 'options_'.$key; - print $extrafields->showOutputField($key, $obj->$tmpkey, '', 1); + print $extrafields->showOutputField($key, $obj->$tmpkey, '', $object->table_element); print ''; } } @@ -674,7 +688,7 @@ if ($action == "view_ticketlist") { print '
    '; print ''; - print '