From f166f15f7d8dfd15ae2e53fec92935e4b815c6ab Mon Sep 17 00:00:00 2001 From: MDW Date: Mon, 4 Mar 2024 18:21:32 +0100 Subject: [PATCH 01/25] Qual: Backport/pre commit / log annotation simplification (#28614) * Qual: ci: Run pre-commit/php-cs with cache (#28079) This adds a hook to .pre-commit-config.yaml and updates the workflow to run php-cs with cache when it is run for all files. When running on changed files only, the cache is not useful. The php-codesniffer ruleset.xml was cleaned up (duplicates removal/formatted) * Fix: Make all 'relative paths' absolute (#28196) # Fix: Make all 'relative paths' absolute The phpcs ruleset xml file's relative exclude patterns are relative to the filename(s) provided on the command line. Hence with partial verifications, the path exclusion does not function as we would like. Removing the relative-path attribute from the patterns the exclusion works. At the same time, the patterns were optimized and a comment was added. * Qual: Optimize workflow (#28386) # Qual: Optimize workflow The log annotation based on the pre-commit logs is now simplified. --- .github/logToCs.py | 588 ------------------------------- .github/workflows/pre-commit.yml | 18 +- 2 files changed, 5 insertions(+), 601 deletions(-) delete mode 100755 .github/logToCs.py diff --git a/.github/logToCs.py b/.github/logToCs.py deleted file mode 100755 index 7befd310a0a..00000000000 --- a/.github/logToCs.py +++ /dev/null @@ -1,588 +0,0 @@ -#!/usr/bin/env python3 -# pylint: disable=invalid-name -""" -Convert a log to CheckStyle format. - -Url: https://github.com/mdeweerd/LogToCheckStyle - -The log can then be used for generating annotations in a github action. - -Note: this script is very young and "quick and dirty". - Patterns can be added to "PATTERNS" to match more messages. - -# Examples - -Assumes that logToCs.py is available as .github/logToCs.py. - -## Example 1: - - -```yaml - - run: | - pre-commit run -all-files | tee pre-commit.log - .github/logToCs.py pre-commit.log pre-commit.xml - - uses: staabm/annotate-pull-request-from-checkstyle-action@v1 - with: - files: pre-commit.xml - notices-as-warnings: true # optional -``` - -## Example 2: - - -```yaml - - run: | - pre-commit run --all-files | tee pre-commit.log - - name: Add results to PR - if: ${{ always() }} - run: | - .github/logToCs.py pre-commit.log | cs2pr -``` - -Author(s): - - https://github.com/mdeweerd - -License: MIT License - -""" - -import argparse -import datetime as dt -import json -import os -import re -import sys -import xml.etree.ElementTree as ET # nosec - - -def remove_prefix(string, prefix): - """ - Remove prefix from string - - Provided for backward compatibility. - """ - if prefix and string.startswith(prefix): - return string[len(prefix) :] - return string - - -def convert_notices_to_checkstyle(notices, root_path=None): - """ - Convert annotation list to CheckStyle xml string - """ - root = ET.Element("checkstyle") - for fields in notices: - add_error_entry(root, **fields, root_path=root_path) - return ET.tostring(root, encoding="utf_8").decode("utf_8") - - -def convert_lines_to_notices(lines): - """ - Convert provided message to CheckStyle format. - """ - notices = [] - for line in lines: - fields = parse_message(line) - if fields: - notices.append(fields) - return notices - - -def convert_text_to_notices(text): - """ - Convert provided message to CheckStyle format. - """ - return parse_file(text) - - -# Initial version for Checkrun from: -# https://github.com/tayfun/flake8-your-pr/blob/50a175cde4dd26a656734c5b64ba1e5bb27151cb/src/main.py#L7C1-L123C36 -# MIT Licence -class CheckRun: - """ - Represents the check run - """ - - GITHUB_TOKEN = os.environ.get("GITHUB_TOKEN", None) - GITHUB_EVENT_PATH = os.environ.get("GITHUB_EVENT_PATH", None) - - URI = "https://api.github.com" - API_VERSION = "2022-11-28" - ACCEPT_HEADER_VALUE = "application/vnd.github+json" - AUTH_HEADER_VALUE = f"Bearer {GITHUB_TOKEN}" - # This is the max annotations Github API accepts in one go. - MAX_ANNOTATIONS = 50 - - def __init__(self): - """ - Initialise Check Run object with information from checkrun - """ - self.read_event_file() - self.read_meta_data() - - def read_event_file(self): - """ - Read the event file to get the event information later. - """ - if self.GITHUB_EVENT_PATH is None: - raise ValueError("Not running in github workflow") - with open(self.GITHUB_EVENT_PATH, encoding="utf_8") as event_file: - self.event = json.loads(event_file.read()) - - def read_meta_data(self): - """ - Get meta data from event information - """ - self.repo_full_name = self.event["repository"]["full_name"] - pull_request = self.event.get("pull_request") - print("%r", self.event) - if pull_request: - self.head_sha = pull_request["head"]["sha"] - else: - print("%r", self.event) - check_suite = self.event.get("check_suite", None) - if check_suite is not None: - self.head_sha = check_suite["pull_requests"][0]["base"]["sha"] - else: - self.head_sha = None # Can't annotate? - - def submit( # pylint: disable=too-many-arguments - self, - notices, - title=None, - summary=None, - text=None, - conclusion=None, - ): - """ - Submit annotations to github - - See: - https://docs.github.com/en/rest/checks/runs?apiVersion=2022-11-28 - #update-a-check-run - - :param conclusion: success, failure - """ - # pylint: disable=import-outside-toplevel - import requests # Import here to not impose presence of module - - if self.head_sha is None: - return - - output = { - "annotations": notices[: CheckRun.MAX_ANNOTATIONS], - } - if title is not None: - output["title"] = title - if summary is not None: - output["summary"] = summary - if text is not None: - output["text"] = text - if conclusion is None: - # action_required, cancelled, failure, neutral, success - # skipped, stale, timed_out - if bool(notices): - conclusion = "failure" - else: - conclusion = "success" - - payload = { - "name": "log-to-pr-annotation", - "head_sha": self.head_sha, - "status": "completed", # queued, in_progress, completed - "conclusion": conclusion, - # "started_at": dt.datetime.now(dt.timezone.utc).isoformat(), - "completed_at": dt.datetime.now(dt.timezone.utc).isoformat(), - "output": output, - } - - # Create the check-run - response = requests.post( - f"{self.URI}/repos/{self.repo_full_name}/check-runs", - headers={ - "Accept": self.ACCEPT_HEADER_VALUE, - "Authorization": self.AUTH_HEADER_VALUE, - "X-GitHub-Api-Version": self.API_VERSION, - }, - json=payload, - timeout=30, - ) - print(response.content) - response.raise_for_status() - - -ANY_REGEX = r".*?" -FILE_REGEX = r"\s*(?P\S.*?)\s*?" -FILEGROUP_REGEX = r"\s*(?P\S.*?)\s*?" -EOL_REGEX = r"[\r\n]" -LINE_REGEX = r"\s*(?P\d+?)\s*?" -COLUMN_REGEX = r"\s*(?P\d+?)\s*?" -SEVERITY_REGEX = r"\s*(?Perror|warning|notice|style|info)\s*?" -MSG_REGEX = r"\s*(?P.+?)\s*?" -MULTILINE_MSG_REGEX = r"\s*(?P(?:.|.[\r\n])+)" -# cpplint confidence index -CONFIDENCE_REGEX = r"\s*\[(?P\d+)\]\s*?" - - -# List of message patterns, add more specific patterns earlier in the list -# Creating patterns by using constants makes them easier to define and read. -PATTERNS = [ - # beautysh - # File ftp.sh: error: "esac" before "case" in line 90. - re.compile( - f"^File {FILE_REGEX}:{SEVERITY_REGEX}:" - f" {MSG_REGEX} in line {LINE_REGEX}.$" - ), - # beautysh - # File socks4echo.sh: error: indent/outdent mismatch: -2. - re.compile(f"^File {FILE_REGEX}:{SEVERITY_REGEX}: {MSG_REGEX}$"), - # yamllint - # ##[group].pre-commit-config.yaml - # ##[error]97:14 [trailing-spaces] trailing spaces - # ##[endgroup] - re.compile(rf"^##\[group\]{FILEGROUP_REGEX}$"), # Start file group - re.compile( - rf"^##\[{SEVERITY_REGEX}\]{LINE_REGEX}:{COLUMN_REGEX}{MSG_REGEX}$" - ), # Msg - re.compile(r"^##(?P\[endgroup\])$"), # End file group - # File socks4echo.sh: error: indent/outdent mismatch: -2. - re.compile(f"^File {FILE_REGEX}:{SEVERITY_REGEX}: {MSG_REGEX}$"), - # Emacs style - # path/to/file:845:5: error - Expected 1 space after closing brace - re.compile( - rf"^{FILE_REGEX}:{LINE_REGEX}:{COLUMN_REGEX}:{SEVERITY_REGEX}" - rf"-?\s{MSG_REGEX}$" - ), - # ESLint (JavaScript Linter), RoboCop, shellcheck - # path/to/file.js:10:2: Some linting issue - # path/to/file.rb:10:5: Style/Indentation: Incorrect indentation detected - # path/to/script.sh:10:1: SC2034: Some shell script issue - re.compile(f"^{FILE_REGEX}:{LINE_REGEX}:{COLUMN_REGEX}: {MSG_REGEX}$"), - # Cpplint default output: - # '%s:%s: %s [%s] [%d]\n' - # % (filename, linenum, message, category, confidence) - re.compile(f"^{FILE_REGEX}:{LINE_REGEX}:{MSG_REGEX}{CONFIDENCE_REGEX}$"), - # MSVC - # file.cpp(10): error C1234: Some error message - re.compile( - f"^{FILE_REGEX}\\({LINE_REGEX}\\):{SEVERITY_REGEX}{MSG_REGEX}$" - ), - # Java compiler - # File.java:10: error: Some error message - re.compile(f"^{FILE_REGEX}:{LINE_REGEX}:{SEVERITY_REGEX}:{MSG_REGEX}$"), - # Python - # File ".../logToCs.py", line 90 (note: code line follows) - re.compile(f'^File "{FILE_REGEX}", line {LINE_REGEX}$'), - # Pylint, others - # path/to/file.py:10: [C0111] Missing docstring - # others - re.compile(f"^{FILE_REGEX}:{LINE_REGEX}: {MSG_REGEX}$"), - # Shellcheck: - # In script.sh line 76: - re.compile( - f"^In {FILE_REGEX} line {LINE_REGEX}:{EOL_REGEX}?" - f"({MULTILINE_MSG_REGEX})?{EOL_REGEX}{EOL_REGEX}" - ), - # eslint: - # /path/to/filename - # 14:5 error Unexpected trailing comma comma-dangle - re.compile( - f"^{FILE_REGEX}{EOL_REGEX}" - rf"\s+{LINE_REGEX}:{COLUMN_REGEX}\s+{SEVERITY_REGEX}\s+{MSG_REGEX}$" - ), -] - -# Exceptionnaly some regexes match messages that are not error. -# This pattern matches those exceptions -EXCLUDE_MSG_PATTERN = re.compile( - r"^(" - r"Placeholder pattern" # To remove on first message pattern - r")" -) - -# Exceptionnaly some regexes match messages that are not error. -# This pattern matches those exceptions -EXCLUDE_FILE_PATTERN = re.compile( - r"^(" - # Codespell: (appears as a file name): - r"Used config files\b" - r")" -) - -# Severities available in CodeSniffer report format -SEVERITY_NOTICE = "notice" -SEVERITY_WARNING = "warning" -SEVERITY_ERROR = "error" - - -def strip_ansi(text: str): - """ - Strip ANSI escape sequences from string (colors, etc) - """ - return re.sub(r"\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])", "", text) - - -def parse_file(text): - """ - Parse all messages in a file - - Returns the fields in a dict. - """ - # pylint: disable=too-many-branches,too-many-statements - # regex required to allow same group names - try: - import regex # pylint: disable=import-outside-toplevel - except ImportError as exc: - raise ImportError( - "The 'parsefile' method requires 'python -m pip install regex'" - ) from exc - - patterns = [pattern.pattern for pattern in PATTERNS] - # patterns = [PATTERNS[0].pattern] - - file_group = None # The file name for the group (if any) - full_regex = "(?:(?:" + (")|(?:".join(patterns)) + "))" - results = [] - - for fields in regex.finditer( - full_regex, strip_ansi(text), regex.MULTILINE | regex.IGNORECASE - ): - if not fields: - continue - result = fields.groupdict() - - if len(result) == 0: - continue - - severity = result.get("severity", None) - file_name = result.get("file_name", None) - confidence = result.pop("confidence", None) - new_file_group = result.pop("file_group", None) - file_endgroup = result.pop("file_endgroup", None) - message = result.get("message", None) - - if new_file_group is not None: - # Start of file_group, just store file - file_group = new_file_group - continue - - if file_endgroup is not None: - file_group = None - continue - - if file_name is None: - if file_group is not None: - file_name = file_group - result["file_name"] = file_name - else: - # No filename, skip - continue - else: - if EXCLUDE_FILE_PATTERN.search(file_name): - # This file_name is excluded - continue - - if message is not None: - if EXCLUDE_MSG_PATTERN.search(message): - # This message is excluded - continue - - if confidence is not None: - # Convert confidence level of cpplint - # to warning, etc. - confidence = int(confidence) - - if confidence <= 1: - severity = SEVERITY_NOTICE - elif confidence >= 5: - severity = SEVERITY_ERROR - else: - severity = SEVERITY_WARNING - - if severity is None: - severity = SEVERITY_ERROR - else: - severity = severity.lower() - - if severity in ["info", "style"]: - severity = SEVERITY_NOTICE - - result["severity"] = severity - - results.append(result) - - return results - - -def parse_message(message): - """ - Parse message until it matches a pattern. - - Returns the fields in a dict. - """ - for pattern in PATTERNS: - fields = pattern.match(message, re.IGNORECASE) - if not fields: - continue - result = fields.groupdict() - if len(result) == 0: - continue - - if "confidence" in result: - # Convert confidence level of cpplint - # to warning, etc. - confidence = int(result["confidence"]) - del result["confidence"] - - if confidence <= 1: - severity = SEVERITY_NOTICE - elif confidence >= 5: - severity = SEVERITY_ERROR - else: - severity = SEVERITY_WARNING - result["severity"] = severity - - if "severity" not in result: - result["severity"] = SEVERITY_ERROR - else: - result["severity"] = result["severity"].lower() - - if result["severity"] in ["info", "style"]: - result["severity"] = SEVERITY_NOTICE - - return result - - # Nothing matched - return None - - -def add_error_entry( # pylint: disable=too-many-arguments - root, - severity, - file_name, - line=None, - column=None, - message=None, - source=None, - root_path=None, -): - """ - Add error information to the CheckStyle output being created. - """ - file_element = find_or_create_file_element( - root, file_name, root_path=root_path - ) - error_element = ET.SubElement(file_element, "error") - error_element.set("severity", severity) - if line: - error_element.set("line", line) - if column: - error_element.set("column", column) - if message: - error_element.set("message", message) - if source: - # To verify if this is a valid attribute - error_element.set("source", source) - - -def find_or_create_file_element(root, file_name: str, root_path=None): - """ - Find/create file element in XML document tree. - """ - - if root_path is not None: - file_name = remove_prefix(file_name, root_path) - for file_element in root.findall("file"): - if file_element.get("name") == file_name: - return file_element - file_element = ET.SubElement(root, "file") - file_element.set("name", file_name) - return file_element - - -def main(): - """ - Parse the script arguments and get the conversion done. - """ - parser = argparse.ArgumentParser( - description="Convert messages to Checkstyle XML format." - ) - parser.add_argument( - "input", help="Input file. Use '-' for stdin.", nargs="?", default="-" - ) - parser.add_argument( - "output", - help="Output file. Use '-' for stdout.", - nargs="?", - default="-", - ) - parser.add_argument( - "-i", - "--in", - dest="input_named", - help="Input filename. Overrides positional input.", - ) - parser.add_argument( - "-o", - "--out", - dest="output_named", - help="Output filename. Overrides positional output.", - ) - parser.add_argument( - "--root", - metavar="ROOT_PATH", - help="Root directory to remove from file paths." - " Defaults to working directory.", - default=os.getcwd(), - ) - parser.add_argument( - "--github-annotate", - action=argparse.BooleanOptionalAction, - help="Annotate when in Github workflow.", - # Currently disabled, - # Future: (os.environ.get("GITHUB_EVENT_PATH", None) is not None), - default=False, - ) - - args = parser.parse_args() - - if args.input == "-" and args.input_named: - with open( - args.input_named, encoding="utf_8", errors="surrogateescape" - ) as input_file: - text = input_file.read() - elif args.input != "-": - with open( - args.input, encoding="utf_8", errors="surrogateescape" - ) as input_file: - text = input_file.read() - else: - text = sys.stdin.read() - - root_path = os.path.join(args.root, "") - - try: - notices = convert_text_to_notices(text) - except ImportError: - notices = convert_lines_to_notices(re.split(r"[\r\n]+", text)) - - checkstyle_xml = convert_notices_to_checkstyle( - notices, root_path=root_path - ) - - if args.output == "-" and args.output_named: - with open(args.output_named, "w", encoding="utf_8") as output_file: - output_file.write(checkstyle_xml) - elif args.output != "-": - with open(args.output, "w", encoding="utf_8") as output_file: - output_file.write(checkstyle_xml) - else: - print(checkstyle_xml) - - if args.github_annotate: - checkrun = CheckRun() - checkrun.submit(notices) - - -if __name__ == "__main__": - main() diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml index d0cdcb90ef3..1ae4d3387f8 100644 --- a/.github/workflows/pre-commit.yml +++ b/.github/workflows/pre-commit.yml @@ -7,7 +7,6 @@ jobs: pre-commit: runs-on: ubuntu-latest env: - LOG_TO_CS: .github/logToCs.py RAW_LOG: pre-commit.log CS_XML: pre-commit.xml steps: @@ -37,7 +36,7 @@ jobs: with: cache: pip python-version: "3.11" - - run: python -m pip install pre-commit regex + - run: python -m pip install pre-commit # Restore previous cache of precommit - uses: actions/cache/restore@v4 with: @@ -102,19 +101,12 @@ jobs: pre-commit run --hook-stage manual -a php-cs-with-cache | tee -a ${RAW_LOG} ls -l ~/.cache/pre-commit/ - # If error, we convert log in the checkstyle format - - name: Convert Raw Log to CheckStyle format - if: ${{ failure() }} - run: | - python ${LOG_TO_CS} ${RAW_LOG} ${CS_XML} - # Annotate the git sources with the log messages - - name: Annotate Source Code with Messages - uses: staabm/annotate-pull-request-from-checkstyle-action@v1 + - name: Convert Raw Log to Annotations + uses: mdeweerd/logToCheckStyle@v2024.2.9 if: ${{ failure() }} with: - files: ${{ env.CS_XML }} - notices-as-warnings: true # optional - prepend-filename: true # optional + in: ${{ env.RAW_LOG }} + # Save the precommit cache - uses: actions/cache/save@v4 if: ${{ ! cancelled() }} From 55c0efae3fb5f8d387eec9674fa5c784a3caa9d2 Mon Sep 17 00:00:00 2001 From: noec764 <58433943+noec764@users.noreply.github.com> Date: Mon, 4 Mar 2024 18:26:38 +0100 Subject: [PATCH 02/25] FIX: Special code is now transmitted by args only in order supplier (#28619) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Noé --- htdocs/fourn/commande/card.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/htdocs/fourn/commande/card.php b/htdocs/fourn/commande/card.php index 17b99779094..4fa93836b76 100644 --- a/htdocs/fourn/commande/card.php +++ b/htdocs/fourn/commande/card.php @@ -1344,8 +1344,6 @@ if (empty($reshook)) { $tva_tx = get_default_tva($soc, $mysoc, $lines[$i]->fk_product, $product_fourn_price_id); } - $object->special_code = $lines[$i]->special_code; - $result = $object->addline( $desc, $lines[$i]->subprice, @@ -1368,7 +1366,9 @@ if (empty($reshook)) { $lines[$i]->fk_unit, 0, $element, - !empty($lines[$i]->id) ? $lines[$i]->id : $lines[$i]->rowid + !empty($lines[$i]->id) ? $lines[$i]->id : $lines[$i]->rowid, + -1, + $lines[$i]->special_code ); if ($result < 0) { From 75f1de8f8f5140235b45f80633f65192ab14b847 Mon Sep 17 00:00:00 2001 From: Ferran Marcet Date: Tue, 5 Mar 2024 18:07:05 +0100 Subject: [PATCH 03/25] Fix: Deposit lines are not created correctly (#28646) * FIX: The minimum price in proposals is not checked correctly * Fix: Deposit lines are not created correctly * Fix: Volver a dejar como estaba * Fix: Deposit lines are not created correctly --- htdocs/compta/facture/card.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/htdocs/compta/facture/card.php b/htdocs/compta/facture/card.php index 0dd1b661b33..bb96c2c2f81 100644 --- a/htdocs/compta/facture/card.php +++ b/htdocs/compta/facture/card.php @@ -12,7 +12,7 @@ * Copyright (C) 2013 Jean-Francois FERRY * Copyright (C) 2013-2014 Florian Henry * Copyright (C) 2013 Cédric Salvador - * Copyright (C) 2014-2019 Ferran Marcet + * Copyright (C) 2014-2024 Ferran Marcet * Copyright (C) 2015-2016 Marcos García * Copyright (C) 2018-2021 Frédéric France * Copyright (C) 2022 Gauthier VERDOL @@ -1596,7 +1596,7 @@ if (empty($reshook)) { null, 0, '', - 1 + (!empty($conf->global->MAIN_DEPOSIT_MULTI_TVA)?0:1) ); } From 2d9801ffe33c22ea9d6c65a0b5bdd49e487712be Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Wed, 6 Mar 2024 15:01:25 +0100 Subject: [PATCH 04/25] Fix sec more complete list of forbidden function --- htdocs/core/lib/functions.lib.php | 1 + 1 file changed, 1 insertion(+) diff --git a/htdocs/core/lib/functions.lib.php b/htdocs/core/lib/functions.lib.php index cbf8fee96e0..181825a689e 100644 --- a/htdocs/core/lib/functions.lib.php +++ b/htdocs/core/lib/functions.lib.php @@ -9297,6 +9297,7 @@ function dol_eval($s, $returnvalue = 0, $hideerrors = 1, $onlysimplestring = '1' $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("fopen", "file_put_contents", "fputs", "fputscsv", "fwrite", "fpassthru", "require", "include", "mkdir", "rmdir", "symlink", "touch", "unlink", "umask")); $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("get_defined_functions", "get_defined_vars", "get_defined_constants", "get_declared_classes")); $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("function", "call_user_func")); + $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("require", "include", "require_once", "include_once")); $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("eval", "create_function", "assert", "mb_ereg_replace")); // function with eval capabilities $forbiddenphpmethods = array('invoke', 'invokeArgs'); // Method of ReflectionFunction to execute a function From 91b994b89ce6a5263bdedd36dff1c43141f6767a Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Wed, 6 Mar 2024 17:20:44 +0100 Subject: [PATCH 05/25] Prepare 18.0.6 --- htdocs/filefunc.inc.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/htdocs/filefunc.inc.php b/htdocs/filefunc.inc.php index cba47566c2b..9e1046d439f 100644 --- a/htdocs/filefunc.inc.php +++ b/htdocs/filefunc.inc.php @@ -34,7 +34,7 @@ if (!defined('DOL_APPLICATION_TITLE')) { define('DOL_APPLICATION_TITLE', 'Dolibarr'); } if (!defined('DOL_VERSION')) { - define('DOL_VERSION', '18.0.5'); // a.b.c-alpha, a.b.c-beta, a.b.c-rcX or a.b.c + define('DOL_VERSION', '18.0.6'); // a.b.c-alpha, a.b.c-beta, a.b.c-rcX or a.b.c } if (!defined('EURO')) { From 4dcf98b9f7d5e26dd4c4c62bc4a980e389cc61aa Mon Sep 17 00:00:00 2001 From: ATM-NicolasV <92087862+ATM-NicolasV@users.noreply.github.com> Date: Thu, 7 Mar 2024 15:55:07 +0100 Subject: [PATCH 06/25] fix entity in sql from RemoveFromGroup (#28682) --- htdocs/user/class/user.class.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/htdocs/user/class/user.class.php b/htdocs/user/class/user.class.php index 1543d579839..0ec4d6fdf5a 100644 --- a/htdocs/user/class/user.class.php +++ b/htdocs/user/class/user.class.php @@ -2377,7 +2377,11 @@ class User extends CommonObject $sql = "DELETE FROM ".MAIN_DB_PREFIX."usergroup_user"; $sql .= " WHERE fk_user = ".((int) $this->id); $sql .= " AND fk_usergroup = ".((int) $group); - $sql .= " AND entity = ".((int) $entity); + if (empty($entity)) { + $sql .= " AND entity IN (0, 1)"; // group may be in entity 0 (so $entity=0) and link with user into entity 1. + } else { + $sql .= " AND entity = ".((int) $entity); + } $result = $this->db->query($sql); if ($result) { From 7bb55aa22528cf2ac35303a957113170ebc58af0 Mon Sep 17 00:00:00 2001 From: atm-adrien <67913809+atm-adrien@users.noreply.github.com> Date: Thu, 7 Mar 2024 15:56:17 +0100 Subject: [PATCH 07/25] FIX : Translation for select (#28677) * FIX : Adding thez dependencies list feature for select fields * FIX : Adding some spaces * FIX : View * FIX : Translating --- htdocs/core/class/commonobject.class.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/htdocs/core/class/commonobject.class.php b/htdocs/core/class/commonobject.class.php index 47060b949f4..9ef1f4c94f4 100644 --- a/htdocs/core/class/commonobject.class.php +++ b/htdocs/core/class/commonobject.class.php @@ -7330,7 +7330,7 @@ abstract class CommonObject $isDependList = 1; } $out .= (!empty($parent) ? ' parent="'.$parent.'"' : ''); - $out .= '>'.$valb.''; + $out .= '>'.$langs->trans($valb).''; } $out .= ''; } elseif ($type == 'sellist') { @@ -7954,7 +7954,7 @@ abstract class CommonObject } elseif ($type == 'select') { $value = isset($param['options'][$value]) ? $param['options'][$value] : ''; if (strpos($value, "|") !== false) { - $value = explode('|', $value)[0]; + $value = $langs->trans(explode('|', $value)[0]); } } elseif ($type == 'sellist') { $param_list = array_keys($param['options']); From 2d8223eefb5a3e493a71a358626c64e051068034 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Thu, 7 Mar 2024 16:16:49 +0100 Subject: [PATCH 08/25] Doc --- htdocs/admin/system/security.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/htdocs/admin/system/security.php b/htdocs/admin/system/security.php index 6ad04701a52..e19463934af 100644 --- a/htdocs/admin/system/security.php +++ b/htdocs/admin/system/security.php @@ -341,12 +341,13 @@ if (!getDolGlobalString('SECURITY_DISABLE_TEST_ON_OBFUSCATED_CONF')) { } print '$dolibarr_main_stream_to_disable: '; +// $arrayofstreamtodisable is defined into filefunc.inc.php if (empty($dolibarr_main_stream_to_disable)) { print ''.$langs->trans("Undefined").' = '.join(', ', $arrayofstreamtodisable).''; } else { print join(', ', $dolibarr_main_stream_to_disable); } -print ' -> PHP streams allowed = '; +print ' -> Current PHP streams allowed = '; $arrayofstreams = stream_get_wrappers(); if (!empty($arrayofstreams)) { sort($arrayofstreams); From 8870be3c3d211385f7a7061fb5eef4ee5cb3790d Mon Sep 17 00:00:00 2001 From: Rikard Bosnjakovic Date: Thu, 7 Mar 2024 16:43:04 +0100 Subject: [PATCH 09/25] Update card.php (#28679) Fix #25130 Co-authored-by: Laurent Destailleur --- htdocs/expedition/card.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/htdocs/expedition/card.php b/htdocs/expedition/card.php index fd83a09ab0e..45cbba28344 100644 --- a/htdocs/expedition/card.php +++ b/htdocs/expedition/card.php @@ -2762,7 +2762,7 @@ if ($action == 'create') { // Presend form $modelmail = 'shipping_send'; - $defaulttopic = $langs->trans('SendShippingRef'); + $defaulttopic = 'SendShippingRef'; $diroutput = $conf->expedition->dir_output.'/sending'; $trackid = 'shi'.$object->id; From cbd331256d937c92a384bf6183b277e4f4bb0678 Mon Sep 17 00:00:00 2001 From: Jon Bendtsen Date: Thu, 7 Mar 2024 19:01:38 +0100 Subject: [PATCH 10/25] using note_public since note_private is not working (#28675) Co-authored-by: Jon Bendtsen --- htdocs/adherents/subscription/card.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/htdocs/adherents/subscription/card.php b/htdocs/adherents/subscription/card.php index b2dc0a57a53..3b59dab63a2 100644 --- a/htdocs/adherents/subscription/card.php +++ b/htdocs/adherents/subscription/card.php @@ -238,7 +238,7 @@ if ($user->hasRight('adherent', 'cotisation', 'creer') && $action == 'edit') { // Label print ''.$langs->trans("Label").''; print ''; - print ''; + print ''; // Bank line if (isModEnabled("banque") && (getDolGlobalString('ADHERENT_BANK_USE') || $object->fk_bank)) { @@ -334,7 +334,7 @@ if ($rowid && $action != 'edit') { print ''.$langs->trans("Amount").''.price($object->amount).''; // Label - print ''.$langs->trans("Label").''.dol_string_onlythesehtmltags(dol_htmlentitiesbr($object->note_private)).''; + print ''.$langs->trans("Label").''.dol_string_onlythesehtmltags(dol_htmlentitiesbr($object->note_public)).''; // Bank line if (isModEnabled("banque") && (getDolGlobalString('ADHERENT_BANK_USE') || $object->fk_bank)) { From 7f10566d5e4f2ab900457405956d0112f11a251f Mon Sep 17 00:00:00 2001 From: Florian Mortgat <50440633+atm-florianm@users.noreply.github.com> Date: Thu, 7 Mar 2024 20:21:41 +0100 Subject: [PATCH 11/25] FIX 17.0: $num doesn't take trigger-modified newref into account, leading to inconsistencies if BILL_SUPPLIER_VALIDATE changes the invoice's ref (#28684) this is a follow-up of PR #27008 --- htdocs/fourn/class/fournisseur.facture.class.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/htdocs/fourn/class/fournisseur.facture.class.php b/htdocs/fourn/class/fournisseur.facture.class.php index 75874fbb86a..eebb2b95b51 100644 --- a/htdocs/fourn/class/fournisseur.facture.class.php +++ b/htdocs/fourn/class/fournisseur.facture.class.php @@ -1895,7 +1895,7 @@ class FactureFournisseur extends CommonInvoice // Set new ref and define current statut if (!$error) { - $this->ref = $num; + $this->ref = $this->newref; $this->statut = self::STATUS_VALIDATED; //$this->date_validation=$now; this is stored into log table } From 2636a644612bd181698392e8fd5416451afe23fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20NASSIET?= <109105553+comaiteseb@users.noreply.github.com> Date: Thu, 7 Mar 2024 20:43:45 +0100 Subject: [PATCH 12/25] Set import_key in import when update record (#28657) * Set import_key in import when update record * Update import_xlsx.modules.php --- htdocs/core/modules/import/import_csv.modules.php | 2 +- htdocs/core/modules/import/import_xlsx.modules.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/htdocs/core/modules/import/import_csv.modules.php b/htdocs/core/modules/import/import_csv.modules.php index 124fe9f7bbc..0a0a1aeb07f 100644 --- a/htdocs/core/modules/import/import_csv.modules.php +++ b/htdocs/core/modules/import/import_csv.modules.php @@ -963,7 +963,7 @@ class ImportCsv extends ModeleImports foreach ($data as $key => $val) { $set[] = $key." = ".$val; } - $sqlstart .= " SET ".implode(', ', $set); + $sqlstart .= " SET ".implode(', ', $set).", import_key = '".$this->db->escape($importid)."'"; if (empty($keyfield)) { $keyfield = 'rowid'; diff --git a/htdocs/core/modules/import/import_xlsx.modules.php b/htdocs/core/modules/import/import_xlsx.modules.php index 14e4b561eae..8d5157e63a4 100644 --- a/htdocs/core/modules/import/import_xlsx.modules.php +++ b/htdocs/core/modules/import/import_xlsx.modules.php @@ -1006,7 +1006,7 @@ class ImportXlsx extends ModeleImports foreach ($data as $key => $val) { $set[] = $key." = ".$val; } - $sqlstart .= " SET " . implode(', ', $set); + $sqlstart .= " SET " . implode(', ', $set) . ", import_key = '" . $this->db->escape($importid) . "'"; if (empty($keyfield)) { $keyfield = 'rowid'; From 10aa278acae9ba7c49e916dde8fcdd01fed8b9a2 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Thu, 7 Mar 2024 21:38:40 +0100 Subject: [PATCH 13/25] Fix add a type to allow sanitization of import rule='compute'. --- .../modules/import/import_csv.modules.php | 19 ++++++++++++++----- .../modules/import/import_xlsx.modules.php | 19 ++++++++++++++----- htdocs/core/modules/modAccounting.class.php | 12 ++++++------ 3 files changed, 34 insertions(+), 16 deletions(-) diff --git a/htdocs/core/modules/import/import_csv.modules.php b/htdocs/core/modules/import/import_csv.modules.php index 0a0a1aeb07f..aa979b5260a 100644 --- a/htdocs/core/modules/import/import_csv.modules.php +++ b/htdocs/core/modules/import/import_csv.modules.php @@ -738,7 +738,7 @@ class ImportCsv extends ModeleImports // ... } - // Define $listfields and $listvalues to build SQL request + // Define $listfields and $listvalues to build the SQL request if (isModEnabled("socialnetworks") && strpos($fieldname, "socialnetworks") !== false) { if (!in_array("socialnetworks", $listfields)) { $listfields[] = "socialnetworks"; @@ -765,7 +765,7 @@ class ImportCsv extends ModeleImports $listfields[] = $fieldname; // Note: arrayrecord (and 'type') is filled with ->import_read_record called by import.php page before calling import_insert if (empty($newval) && $arrayrecord[($key - 1)]['type'] < 0) { - $listvalues[] = ($newval == '0' ? $newval : "null"); + $listvalues[] = ($newval == '0' ? (int) $newval : "null"); } elseif (empty($newval) && $arrayrecord[($key - 1)]['type'] == 0) { $listvalues[] = "''"; } else { @@ -793,7 +793,7 @@ class ImportCsv extends ModeleImports $lastinsertid = (isset($last_insert_id_array[$tmp[1]])) ? $last_insert_id_array[$tmp[1]] : 0; $keyfield = preg_replace('/^'.preg_quote($alias, '/').'\./', '', $tmpkey); $listfields[] = $keyfield; - $listvalues[] = $lastinsertid; + $listvalues[] = (int) $lastinsertid; //print $tmpkey."-".$tmpval."-".$listfields."-".$listvalues."
";exit; } elseif (preg_match('/^const-/', $tmpval)) { $tmp = explode('-', $tmpval, 2); @@ -806,6 +806,7 @@ class ImportCsv extends ModeleImports $file = (empty($objimport->array_import_convertvalue[0][$fieldname]['classfile']) ? $objimport->array_import_convertvalue[0][$fieldname]['file'] : $objimport->array_import_convertvalue[0][$fieldname]['classfile']); $class = $objimport->array_import_convertvalue[0][$fieldname]['class']; $method = $objimport->array_import_convertvalue[0][$fieldname]['method']; + $type = $objimport->array_import_convertvalue[0][$fieldname]['type']; $resultload = dol_include_once($file); if (empty($resultload)) { dol_print_error('', 'Error trying to call file=' . $file . ', class=' . $class . ', method=' . $method); @@ -818,8 +819,16 @@ class ImportCsv extends ModeleImports if (count($fieldArr) > 0) { $fieldname = $fieldArr[1]; } + + // Set $listfields and $listvalues $listfields[] = $fieldname; - $listvalues[] = $res; + if ($type == 'int') { + $listvalues[] = (int) $res; + } elseif ($type == 'double') { + $listvalues[] = (float) $res; + } else { + $listvalues[] = "'".$this->db->escape($res)."'"; + } } else { $this->errors[$error]['type'] = 'CLASSERROR'; $this->errors[$error]['lib'] = implode( @@ -961,7 +970,7 @@ class ImportCsv extends ModeleImports $data = array_combine($listfields, $listvalues); $set = array(); foreach ($data as $key => $val) { - $set[] = $key." = ".$val; + $set[] = $key." = ".$val; // $val was escaped/sanitized previously } $sqlstart .= " SET ".implode(', ', $set).", import_key = '".$this->db->escape($importid)."'"; diff --git a/htdocs/core/modules/import/import_xlsx.modules.php b/htdocs/core/modules/import/import_xlsx.modules.php index 8d5157e63a4..e798fc659f0 100644 --- a/htdocs/core/modules/import/import_xlsx.modules.php +++ b/htdocs/core/modules/import/import_xlsx.modules.php @@ -781,7 +781,7 @@ class ImportXlsx extends ModeleImports // ... } - // Define $listfields and $listvalues to build SQL request + // Define $listfields and $listvalues to build the SQL request if (isModEnabled("socialnetworks") && strpos($fieldname, "socialnetworks") !== false) { if (!in_array("socialnetworks", $listfields)) { $listfields[] = "socialnetworks"; @@ -807,7 +807,7 @@ class ImportXlsx extends ModeleImports // Note: arrayrecord (and 'type') is filled with ->import_read_record called by import.php page before calling import_insert if (empty($newval) && $arrayrecord[($key)]['type'] < 0) { - $listvalues[] = ($newval == '0' ? $newval : "null"); + $listvalues[] = ($newval == '0' ? (int) $newval : "null"); } elseif (empty($newval) && $arrayrecord[($key)]['type'] == 0) { $listvalues[] = "''"; } else { @@ -835,7 +835,7 @@ class ImportXlsx extends ModeleImports $lastinsertid = (isset($last_insert_id_array[$tmp[1]])) ? $last_insert_id_array[$tmp[1]] : 0; $keyfield = preg_replace('/^' . preg_quote($alias, '/') . '\./', '', $key); $listfields[] = $keyfield; - $listvalues[] = $lastinsertid; + $listvalues[] = (int) $lastinsertid; //print $key."-".$val."-".$listfields."-".$listvalues."
";exit; } elseif (preg_match('/^const-/', $val)) { $tmp = explode('-', $val, 2); @@ -848,6 +848,7 @@ class ImportXlsx extends ModeleImports $file = (empty($objimport->array_import_convertvalue[0][$fieldname]['classfile']) ? $objimport->array_import_convertvalue[0][$fieldname]['file'] : $objimport->array_import_convertvalue[0][$fieldname]['classfile']); $class = $objimport->array_import_convertvalue[0][$fieldname]['class']; $method = $objimport->array_import_convertvalue[0][$fieldname]['method']; + $type = $objimport->array_import_convertvalue[0][$fieldname]['type']; $resultload = dol_include_once($file); if (empty($resultload)) { dol_print_error('', 'Error trying to call file=' . $file . ', class=' . $class . ', method=' . $method); @@ -859,8 +860,16 @@ class ImportXlsx extends ModeleImports if (count($fieldArr) > 0) { $fieldname = $fieldArr[1]; } + + // Set $listfields and $listvalues $listfields[] = $fieldname; - $listvalues[] = $res; + if ($type == 'int') { + $listvalues[] = (int) $res; + } elseif ($type == 'double') { + $listvalues[] = (float) $res; + } else { + $listvalues[] = "'".$this->db->escape($res)."'"; + } } else { $this->errors[$error]['type'] = 'CLASSERROR'; $this->errors[$error]['lib'] = implode( @@ -1004,7 +1013,7 @@ class ImportXlsx extends ModeleImports $data = array_combine($listfields, $listvalues); $set = array(); foreach ($data as $key => $val) { - $set[] = $key." = ".$val; + $set[] = $key." = ".$val; // $val was escaped/sanitized previously } $sqlstart .= " SET " . implode(', ', $set) . ", import_key = '" . $this->db->escape($importid) . "'"; diff --git a/htdocs/core/modules/modAccounting.class.php b/htdocs/core/modules/modAccounting.class.php index 07591de93a7..f8788d09ba4 100644 --- a/htdocs/core/modules/modAccounting.class.php +++ b/htdocs/core/modules/modAccounting.class.php @@ -362,14 +362,14 @@ class modAccounting extends DolibarrModules 'b.sens'=>'rule-computeDirection' ); // aliastable.field => ('user->id' or 'lastrowid-'.tableparent) $this->import_convertvalue_array[$r]=array( - 'b.piece_num' => array('rule' => 'compute', 'classfile' => '/accountancy/class/accountancyimport.class.php', 'class' => 'AccountancyImport', 'method' => 'cleanValue', 'element' => 'Accountancy'), + 'b.piece_num' => array('rule' => 'compute', 'type' => 'int', 'classfile' => '/accountancy/class/accountancyimport.class.php', 'class' => 'AccountancyImport', 'method' => 'cleanValue', 'element' => 'Accountancy'), 'b.numero_compte'=>array('rule'=>'accountingaccount'), 'b.subledger_account'=>array('rule'=>'accountingaccount'), - 'b.debit' => array('rule' => 'compute', 'classfile' => '/accountancy/class/accountancyimport.class.php', 'class' => 'AccountancyImport', 'method' => 'cleanAmount', 'element' => 'Accountancy'), - 'b.credit' => array('rule' => 'compute', 'classfile' => '/accountancy/class/accountancyimport.class.php', 'class' => 'AccountancyImport', 'method' => 'cleanAmount', 'element' => 'Accountancy'), - 'b.multicurrency_amount' => array('rule' => 'compute', 'classfile' => '/accountancy/class/accountancyimport.class.php', 'class' => 'AccountancyImport', 'method' => 'cleanAmount', 'element' => 'Accountancy'), - 'b.montant' => array('rule' => 'compute', 'classfile' => '/accountancy/class/accountancyimport.class.php', 'class' => 'AccountancyImport', 'method' => 'computeAmount', 'element' => 'Accountancy'), - 'b.sens' => array('rule' => 'compute', 'classfile' => '/accountancy/class/accountancyimport.class.php', 'class' => 'AccountancyImport', 'method' => 'computeDirection', 'element' => 'Accountancy'), + 'b.debit' => array('rule' => 'compute', 'type' => 'double', 'classfile' => '/accountancy/class/accountancyimport.class.php', 'class' => 'AccountancyImport', 'method' => 'cleanAmount', 'element' => 'Accountancy'), + 'b.credit' => array('rule' => 'compute', 'type' => 'double', 'classfile' => '/accountancy/class/accountancyimport.class.php', 'class' => 'AccountancyImport', 'method' => 'cleanAmount', 'element' => 'Accountancy'), + 'b.multicurrency_amount' => array('rule' => 'compute', 'type' => 'double', 'classfile' => '/accountancy/class/accountancyimport.class.php', 'class' => 'AccountancyImport', 'method' => 'cleanAmount', 'element' => 'Accountancy'), + 'b.montant' => array('rule' => 'compute', 'type' => 'double', 'classfile' => '/accountancy/class/accountancyimport.class.php', 'class' => 'AccountancyImport', 'method' => 'computeAmount', 'element' => 'Accountancy'), + 'b.sens' => array('rule' => 'compute', 'type' => 'varchar', 'classfile' => '/accountancy/class/accountancyimport.class.php', 'class' => 'AccountancyImport', 'method' => 'computeDirection', 'element' => 'Accountancy'), ); $this->import_regex_array[$r] = array( //'b.doc_date'=>'^\d{4}\d{2}\d{2}$', From 491e85037a9fb2974b021feab63c5776cbefe42b Mon Sep 17 00:00:00 2001 From: Can Arslan <138895927+mc2rcanarslan@users.noreply.github.com> Date: Thu, 7 Mar 2024 15:09:54 -0700 Subject: [PATCH 14/25] FIX: PHP Warning: Undefined variable $mode (#28697) --- htdocs/fourn/paiement/list.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/htdocs/fourn/paiement/list.php b/htdocs/fourn/paiement/list.php index 2c7009c8d42..3021fbdc898 100644 --- a/htdocs/fourn/paiement/list.php +++ b/htdocs/fourn/paiement/list.php @@ -48,7 +48,7 @@ $action = GETPOST('action', 'alpha'); $massaction = GETPOST('massaction', 'alpha'); $optioncss = GETPOST('optioncss', 'alpha'); $contextpage = GETPOST('contextpage', 'aZ') ? GETPOST('contextpage', 'aZ') : 'vendorpaymentlist'; - +$mode = GETPOST('mode', 'aZ'); $socid = GETPOST('socid', 'int'); $search_ref = GETPOST('search_ref', 'alpha'); From e74e638b26990d3e517579f6616616afa6f82860 Mon Sep 17 00:00:00 2001 From: Can Arslan <138895927+mc2rcanarslan@users.noreply.github.com> Date: Fri, 8 Mar 2024 04:28:31 -0700 Subject: [PATCH 15/25] FIX: skip adding tab if user doesn't have permission (#28698) verifCond function checks whether user has right and we should skip adding it if user doesn't have right --- htdocs/core/lib/functions.lib.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/htdocs/core/lib/functions.lib.php b/htdocs/core/lib/functions.lib.php index 0298ce0d439..846819f2433 100644 --- a/htdocs/core/lib/functions.lib.php +++ b/htdocs/core/lib/functions.lib.php @@ -10189,6 +10189,8 @@ function complete_head_from_modules($conf, $langs, $object, &$head, &$h, $type, $newtab[1] = $label; $newtab[2] = str_replace('+', '', $values[1]); $h++; + } else { + continue; } } elseif (count($values) == 5) { // case deprecated dol_syslog('Passing 5 values in tabs module_parts is deprecated. Please update to 6 with permissions.', LOG_WARNING); From bcf6d7a2aa5b255516e46d69c9898d8bb1dde4a8 Mon Sep 17 00:00:00 2001 From: kkhelifa-opendsi Date: Fri, 8 Mar 2024 12:36:31 +0100 Subject: [PATCH 16/25] FIX: Fix create shipping with product who have negative stocks on warehouse but the negative stock transfert is allowed (#26217) --- htdocs/expedition/card.php | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/htdocs/expedition/card.php b/htdocs/expedition/card.php index 318d66635ec..20c6a14b77d 100644 --- a/htdocs/expedition/card.php +++ b/htdocs/expedition/card.php @@ -1424,7 +1424,7 @@ if ($action == 'create') { $nbofsuggested = 0; foreach ($product->stock_warehouse as $warehouse_id => $stock_warehouse) { - if ($stock_warehouse->real > 0) { + if ($stock_warehouse->real > 0 || !empty($conf->global->STOCK_ALLOW_NEGATIVE_TRANSFER)) { $nbofsuggested++; } } @@ -1437,7 +1437,7 @@ if ($action == 'create') { } $tmpwarehouseObject->fetch($warehouse_id); - if ($stock_warehouse->real > 0) { + if ($stock_warehouse->real > 0 || !empty($conf->global->STOCK_ALLOW_NEGATIVE_TRANSFER)) { $stock = + $stock_warehouse->real; // Convert it to number $deliverableQty = min($quantityToBeDelivered, $stock); $deliverableQty = max(0, $deliverableQty); @@ -1536,7 +1536,7 @@ if ($action == 'create') { // Define nb of lines suggested for this order line $nbofsuggested = 0; foreach ($product->stock_warehouse as $warehouse_id => $stock_warehouse) { - if (($stock_warehouse->real > 0) && (count($stock_warehouse->detail_batch))) { + if (($stock_warehouse->real > 0 || !empty($conf->global->STOCK_ALLOW_NEGATIVE_TRANSFER)) && (count($stock_warehouse->detail_batch))) { $nbofsuggested+=count($stock_warehouse->detail_batch); } } @@ -1549,7 +1549,7 @@ if ($action == 'create') { } $tmpwarehouseObject->fetch($warehouse_id); - if (($stock_warehouse->real > 0) && (count($stock_warehouse->detail_batch))) { + if (($stock_warehouse->real > 0 || !empty($conf->global->STOCK_ALLOW_NEGATIVE_TRANSFER)) && (count($stock_warehouse->detail_batch))) { foreach ($stock_warehouse->detail_batch as $dbatch) { $batchStock = + $dbatch->qty; // To get a numeric if (isset($alreadyQtyBatchSetted[$line->fk_product][$dbatch->batch][intval($warehouse_id)])) { @@ -1634,6 +1634,9 @@ if ($action == 'create') { $disabled = 'disabled="disabled"'; } print ' '; + if (empty($disabled) && !empty($conf->global->STOCK_ALLOW_NEGATIVE_TRANSFER)) { + print ''; + } } else { print $langs->trans("NA"); } From 23723289c288410a54d594b9efc1938549475e08 Mon Sep 17 00:00:00 2001 From: Charles Date: Fri, 8 Mar 2024 18:05:58 -0600 Subject: [PATCH 17/25] Update ticket.class.php update function (#28722) Default value for status to zero(0) instead of "null" --- htdocs/ticket/class/ticket.class.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/htdocs/ticket/class/ticket.class.php b/htdocs/ticket/class/ticket.class.php index 601806e023b..e1b496b6929 100644 --- a/htdocs/ticket/class/ticket.class.php +++ b/htdocs/ticket/class/ticket.class.php @@ -992,7 +992,7 @@ class Ticket extends CommonObject $sql .= " fk_user_assign=".(isset($this->fk_user_assign) ? $this->fk_user_assign : "null").","; $sql .= " subject=".(isset($this->subject) ? "'".$this->db->escape($this->subject)."'" : "null").","; $sql .= " message=".(isset($this->message) ? "'".$this->db->escape($this->message)."'" : "null").","; - $sql .= " fk_statut=".(isset($this->fk_statut) ? $this->fk_statut : "null").","; + $sql .= " fk_statut=".(isset($this->fk_statut) ? $this->fk_statut : "0").","; $sql .= " resolution=".(isset($this->resolution) ? $this->resolution : "null").","; $sql .= " progress=".(isset($this->progress) ? "'".$this->db->escape($this->progress)."'" : "null").","; $sql .= " timing=".(isset($this->timing) ? "'".$this->db->escape($this->timing)."'" : "null").","; From b601dabf6604e16561bcf102d18f32c4927dbf85 Mon Sep 17 00:00:00 2001 From: le-reparateur <162715304+le-reparateur@users.noreply.github.com> Date: Sat, 9 Mar 2024 15:14:23 +0100 Subject: [PATCH 18/25] Update fournisseur.product.class.php (#28729) * Update fournisseur.product.class.php #28710 * Update fournisseur.product.class.php --------- Co-authored-by: Laurent Destailleur --- htdocs/fourn/class/fournisseur.product.class.php | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/htdocs/fourn/class/fournisseur.product.class.php b/htdocs/fourn/class/fournisseur.product.class.php index fd9e0245ad8..cd512972033 100644 --- a/htdocs/fourn/class/fournisseur.product.class.php +++ b/htdocs/fourn/class/fournisseur.product.class.php @@ -327,10 +327,10 @@ class ProductFournisseur extends Product $fk_multicurrency = MultiCurrency::getIdFromCode($this->db, $multicurrency_code); } - $buyprice = price2num($buyprice, 'MU'); - $charges = price2num($charges, 'MU'); - $qty = price2num($qty, 'MS'); - $unitBuyPrice = price2num($buyprice / $qty, 'MU'); + $buyprice = (float) price2num($buyprice, 'MU'); + $charges = (float) price2num($charges, 'MU'); + $qty = (float) price2num($qty, 'MS'); + $unitBuyPrice = (float) price2num($buyprice / $qty, 'MU'); // We can have a puchase ref that need to buy 100 min for a given price and with a packaging of 50. //$packaging = price2num(((empty($this->packaging) || $this->packaging < $qty) ? $qty : $this->packaging), 'MS'); @@ -359,10 +359,6 @@ class ProductFournisseur extends Product $localtax2 = 0; // If = '' then = 0 } - // Check parameters - if ($buyprice != '' && !is_numeric($buyprice)) { - } - $this->db->begin(); if ($this->product_fourn_price_id > 0) { @@ -391,6 +387,7 @@ class ProductFournisseur extends Product } $sql = "UPDATE ".MAIN_DB_PREFIX."product_fournisseur_price"; $sql .= " SET fk_user = ".((int) $user->id)." ,"; + $sql .= " datec = '".$this->db->idate($now)."' ,"; // Note: Even if this is an update, we update the creation date as the log of each change is tracked into product_fournisseur_log. $sql .= " ref_fourn = '".$this->db->escape($ref_fourn)."',"; $sql .= " desc_fourn = '".$this->db->escape($desc_fourn)."',"; $sql .= " price = ".((float) $buyprice).","; From cecd3bc1b61d7c065faae45cbe88dbfb8cd861ef Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Sat, 9 Mar 2024 23:46:38 +0100 Subject: [PATCH 19/25] FIX Picto in top menu --- htdocs/core/menus/standard/auguria.lib.php | 2 ++ htdocs/core/menus/standard/eldy.lib.php | 2 ++ htdocs/core/menus/standard/empty.php | 8 +++++++- 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/htdocs/core/menus/standard/auguria.lib.php b/htdocs/core/menus/standard/auguria.lib.php index 60480d6c9cb..dbed17b95d5 100644 --- a/htdocs/core/menus/standard/auguria.lib.php +++ b/htdocs/core/menus/standard/auguria.lib.php @@ -242,6 +242,8 @@ function print_text_menu_entry_auguria($text, $showmode, $url, $id, $idsel, $cla if (!empty($menuval['prefix']) && strpos($menuval['prefix'], ''; } else { print ''; } diff --git a/htdocs/core/menus/standard/eldy.lib.php b/htdocs/core/menus/standard/eldy.lib.php index 1cf504a30fd..141e0e02c65 100644 --- a/htdocs/core/menus/standard/eldy.lib.php +++ b/htdocs/core/menus/standard/eldy.lib.php @@ -656,6 +656,8 @@ function print_text_menu_entry($text, $showmode, $url, $id, $idsel, $classname, print '
'; if (!empty($menuval['prefix']) && strpos($menuval['prefix'], ''; } else { print ''; } diff --git a/htdocs/core/menus/standard/empty.php b/htdocs/core/menus/standard/empty.php index 542cb1a493a..01282aff09f 100644 --- a/htdocs/core/menus/standard/empty.php +++ b/htdocs/core/menus/standard/empty.php @@ -523,7 +523,13 @@ function print_text_menu_entry_empty($text, $showmode, $url, $id, $idsel, $class if ($showmode == 1) { print ''; print '
'; - print ''; + if (!empty($menuval['prefix']) && strpos($menuval['prefix'], ''; + } else { + print ''; + } print '
'; print '
'; if (!getDolGlobalString('THEME_TOPMENU_DISABLE_TEXT')) { From 8ef22dc461487bd9942cf8d2613f53056eb2f6eb Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Sun, 10 Mar 2024 00:47:00 +0100 Subject: [PATCH 20/25] Fix css --- htdocs/bookmarks/card.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/htdocs/bookmarks/card.php b/htdocs/bookmarks/card.php index 7bebdcb2af7..8dd22d694ca 100644 --- a/htdocs/bookmarks/card.php +++ b/htdocs/bookmarks/card.php @@ -228,6 +228,7 @@ if ($id > 0 && !preg_match('/^add/i', $action)) { print '
'; print ''; + // Title print ''; + // URL print ''; print '
'; if ($action == 'edit') { print ''; @@ -247,6 +248,7 @@ if ($id > 0 && !preg_match('/^add/i', $action)) { } print '
'; if ($action == 'edit') { print ''; @@ -255,7 +257,7 @@ if ($id > 0 && !preg_match('/^add/i', $action)) { if ($action == 'edit') { print ''; } - print ''; + print ''; if ($action == 'edit') { print 'url).'">'; } else { From 453eb9c242c3f75d96e1cadc0cfa4f70345f3317 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Sun, 10 Mar 2024 15:42:38 +0100 Subject: [PATCH 21/25] FIX: upload odt files should not start/end with space --- htdocs/admin/facture.php | 2 ++ htdocs/core/lib/files.lib.php | 10 +++++----- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/htdocs/admin/facture.php b/htdocs/admin/facture.php index 3384a0d31d6..19f1182e608 100644 --- a/htdocs/admin/facture.php +++ b/htdocs/admin/facture.php @@ -50,6 +50,8 @@ $label = GETPOST('label', 'alpha'); $scandir = GETPOST('scan_dir', 'alpha'); $type = 'invoice'; +$error = 0; + /* * Actions diff --git a/htdocs/core/lib/files.lib.php b/htdocs/core/lib/files.lib.php index 7152e2778bb..b170c4fc177 100644 --- a/htdocs/core/lib/files.lib.php +++ b/htdocs/core/lib/files.lib.php @@ -1777,6 +1777,7 @@ function dol_add_file_process($upload_dir, $allowoverwrite = 0, $donotupdatesess // var_dump($result);exit; if ($result >= 0) { $TFile = $_FILES[$varfiles]; + // Convert value of $TFile if (!is_array($TFile['name'])) { foreach ($TFile as $key => &$val) { $val = array($val); @@ -1791,13 +1792,13 @@ function dol_add_file_process($upload_dir, $allowoverwrite = 0, $donotupdatesess } // Define $destfull (path to file including filename) and $destfile (only filename) - $destfull = $upload_dir."/".$TFile['name'][$i]; - $destfile = $TFile['name'][$i]; + $destfile = trim($TFile['name'][$i]); + $destfull = $upload_dir."/".$destfile; $destfilewithoutext = preg_replace('/\.[^\.]+$/', '', $destfile); if ($savingdocmask && strpos($savingdocmask, $destfilewithoutext) !== 0) { - $destfull = $upload_dir."/".preg_replace('/__file__/', $TFile['name'][$i], $savingdocmask); - $destfile = preg_replace('/__file__/', $TFile['name'][$i], $savingdocmask); + $destfile = trim(preg_replace('/__file__/', $TFile['name'][$i], $savingdocmask)); + $destfull = $upload_dir."/".$destfile; } $filenameto = basename($destfile); @@ -1806,7 +1807,6 @@ function dol_add_file_process($upload_dir, $allowoverwrite = 0, $donotupdatesess setEventMessages($langs->trans("ErrorFilenameCantStartWithDot", $filenameto), null, 'errors'); break; } - // dol_sanitizeFileName the file name and lowercase extension $info = pathinfo($destfull); $destfull = $info['dirname'].'/'.dol_sanitizeFileName($info['filename'].($info['extension'] != '' ? ('.'.strtolower($info['extension'])) : '')); From 1431582b82c91c480662a6a7abebd4733006621d Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Mon, 11 Mar 2024 10:10:17 +0100 Subject: [PATCH 22/25] Fix cursor --- htdocs/admin/tools/dolibarr_export.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/htdocs/admin/tools/dolibarr_export.php b/htdocs/admin/tools/dolibarr_export.php index 3c70bdc74aa..749674956c0 100644 --- a/htdocs/admin/tools/dolibarr_export.php +++ b/htdocs/admin/tools/dolibarr_export.php @@ -203,7 +203,7 @@ print '
'; print '
'; -print img_picto('', 'setup', 'class="pictofixedwidth"').''.$langs->trans("ShowAdvancedOptions").'...'; +print img_picto('', 'setup', 'class="pictofixedwidth"').''.$langs->trans("ShowAdvancedOptions").'...'; print '
'; print '