diff --git a/htdocs/adherents/stats/index.php b/htdocs/adherents/stats/index.php index 51bac0047f9b6..6a06fa470e3dd 100644 --- a/htdocs/adherents/stats/index.php +++ b/htdocs/adherents/stats/index.php @@ -194,7 +194,7 @@ $oldyear--; print ''; print ''; - //print ''; + //print ''; print $oldyear; //print ''; print ''; diff --git a/htdocs/admin/modules.php b/htdocs/admin/modules.php index 223ef04a7dd10..de80b1adb335d 100644 --- a/htdocs/admin/modules.php +++ b/htdocs/admin/modules.php @@ -177,9 +177,9 @@ setEventMessages($langs->trans("ErrorFileMustBeADolibarrPackage", $original_file), null, 'errors'); $error++; } - if (!$error && !preg_match('/^(module[a-zA-Z0-9]*|theme)_.*\-([0-9][0-9\.]*)\.zip$/i', $original_file)) { + if (!$error && !preg_match('/^(module[a-zA-Z0-9]*_|theme_|).*\-([0-9][0-9\.]*)(\s\(\d+\)\s)?\.zip$/i', $original_file)) { $langs->load("errors"); - setEventMessages($langs->trans("ErrorFilenameDosNotMatchDolibarrPackageRules", $original_file, 'module_*-x.y*.zip'), null, 'errors'); + setEventMessages($langs->trans("ErrorFilenameDosNotMatchDolibarrPackageRules", $original_file, 'modulename-x[.y.z].zip'), null, 'errors'); $error++; } if (empty($_FILES['fileinstall']['tmp_name'])) { diff --git a/htdocs/compta/accounting-files.php b/htdocs/compta/accounting-files.php index dc47a4e29c330..b4ee8ccdcad84 100644 --- a/htdocs/compta/accounting-files.php +++ b/htdocs/compta/accounting-files.php @@ -4,7 +4,7 @@ * Copyright (C) 2017 Pierre-Henry Favre * Copyright (C) 2020 Maxime DEMAREST * Copyright (C) 2021 Gauthier VERDOL - * Copyright (C) 2022-2024 Alexandre Spangaro + * Copyright (C) 2022-2024 Alexandre Spangaro * Copyright (C) 2024 MDW * * This program is free software; you can redistribute it and/or modify @@ -209,17 +209,33 @@ } } // Expense reports - if (GETPOST('selectexpensereports') && !empty($listofchoices['selectexpensereports']['perms']) && empty($projectid)) { + if (GETPOST('selectexpensereports') && !empty($listofchoices['selectexpensereports']['perms'])) { if (!empty($sql)) { $sql .= " UNION ALL"; } - $sql .= " SELECT t.rowid as id, t.entity, t.ref, t.paid, t.total_ht, t.total_ttc, t.total_tva as total_vat,"; - $sql .= " 0 as localtax1, 0 as localtax2, 0 as revenuestamp,"; - $sql .= " t.multicurrency_code as currency, t.fk_user_author as fk_soc, t.date_fin as date, t.date_fin as date_due, 'ExpenseReport' as item, CONCAT(CONCAT(u.lastname, ' '), u.firstname) as thirdparty_name, '' as thirdparty_code, c.code as country_code, '' as vatnum, ".PAY_DEBIT." as sens"; - $sql .= " FROM ".MAIN_DB_PREFIX."expensereport as t LEFT JOIN ".MAIN_DB_PREFIX."user as u ON u.rowid = t.fk_user_author LEFT JOIN ".MAIN_DB_PREFIX."c_country as c ON c.rowid = u.fk_country"; - $sql .= " WHERE date_fin between ".$wheretail; - $sql .= " AND t.entity IN (".$db->sanitize($entity == 1 ? '0,1' : $entity).')'; - $sql .= " AND t.fk_statut <> ".ExpenseReport::STATUS_DRAFT; + // if project filter is used on an expense report, + // show only total_ht/total_tva/total_ttc of the line considered by the project + if (!empty($projectid)) { + $sql .= " SELECT t.rowid as id, t.entity, t.ref, t.paid, SUM(td.total_ht) as total_ht, SUM(td.total_ttc) as total_ttc, SUM(td.total_tva) as total_vat,"; + $sql .= " 0 as localtax1, 0 as localtax2, 0 as revenuestamp,"; + $sql .= " td.multicurrency_code as currency, t.fk_user_author as fk_soc, t.date_fin as date, t.date_fin as date_due, 'ExpenseReport' as item, CONCAT(CONCAT(u.lastname, ' '), u.firstname) as thirdparty_name, '' as thirdparty_code, c.code as country_code, '' as vatnum, " . PAY_DEBIT . " as sens"; + $sql .= " FROM " . MAIN_DB_PREFIX . "expensereport as t"; + $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "expensereport_det as td ON t.rowid = td.fk_expensereport"; + $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "user as u ON u.rowid = t.fk_user_author"; + $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "c_country as c ON c.rowid = u.fk_country"; + $sql .= " WHERE date_fin between " . $wheretail; + $sql .= " AND t.entity IN (" . $db->sanitize($entity == 1 ? '0,1' : $entity) . ')'; + $sql .= " AND t.fk_statut <> " . ExpenseReport::STATUS_DRAFT; + $sql .= " AND fk_projet = ".((int) $projectid); + } else { + $sql .= " SELECT t.rowid as id, t.entity, t.ref, t.paid, t.total_ht, t.total_ttc, t.total_tva as total_vat,"; + $sql .= " 0 as localtax1, 0 as localtax2, 0 as revenuestamp,"; + $sql .= " t.multicurrency_code as currency, t.fk_user_author as fk_soc, t.date_fin as date, t.date_fin as date_due, 'ExpenseReport' as item, CONCAT(CONCAT(u.lastname, ' '), u.firstname) as thirdparty_name, '' as thirdparty_code, c.code as country_code, '' as vatnum, " . PAY_DEBIT . " as sens"; + $sql .= " FROM " . MAIN_DB_PREFIX . "expensereport as t LEFT JOIN " . MAIN_DB_PREFIX . "user as u ON u.rowid = t.fk_user_author LEFT JOIN " . MAIN_DB_PREFIX . "c_country as c ON c.rowid = u.fk_country"; + $sql .= " WHERE date_fin between " . $wheretail; + $sql .= " AND t.entity IN (" . $db->sanitize($entity == 1 ? '0,1' : $entity) . ')'; + $sql .= " AND t.fk_statut <> " . ExpenseReport::STATUS_DRAFT; + } } // Donations if (GETPOST('selectdonations') && !empty($listofchoices['selectdonations']['perms'])) { diff --git a/htdocs/compta/facture/card.php b/htdocs/compta/facture/card.php index 1cf65892bb542..2a94d8923c9ed 100644 --- a/htdocs/compta/facture/card.php +++ b/htdocs/compta/facture/card.php @@ -4040,8 +4040,9 @@ function setRadioForTypeOfInvoice() { include_once DOL_DOCUMENT_ROOT.'/core/modules/facture/modules_facture.php'; $liste = ModelePDFFactures::liste_modeles($db); if (getDolGlobalString('INVOICE_USE_DEFAULT_DOCUMENT')) { + $type = GETPOSTISSET('type') ? GETPOSTINT('type') : $object->type; // Hidden conf - $paramkey = 'FACTURE_ADDON_PDF_'.$object->type; + $paramkey = 'FACTURE_ADDON_PDF_'.$type; $preselected = getDolGlobalString($paramkey, getDolGlobalString('FACTURE_ADDON_PDF')); } else { $preselected = getDolGlobalString('FACTURE_ADDON_PDF'); diff --git a/htdocs/core/boxes/box_factures_imp.php b/htdocs/core/boxes/box_factures_imp.php index 42e3e297c8687..f4515caf95c64 100644 --- a/htdocs/core/boxes/box_factures_imp.php +++ b/htdocs/core/boxes/box_factures_imp.php @@ -4,6 +4,7 @@ * Copyright (C) 2005-2009 Regis Houssin * Copyright (C) 2015-2019 Frederic France * Copyright (C) 2024 MDW + * Copyright (C) 2024 Alexandre Spangaro * * 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 @@ -118,7 +119,7 @@ public function loadBox($max = 5) } $sql3 = " GROUP BY s.rowid, s.nom, s.name_alias, s.code_client, s.client, s.logo, s.email, s.entity, s.tva_intra, s.siren, s.siret, s.ape, s.idprof4, s.idprof5, s.idprof6,"; if (getDolGlobalString('MAIN_COMPANY_PERENTITY_SHARED')) { - $sql3 .= " spe.accountancy_code_customer as code_compta,"; + $sql3 .= " spe.accountancy_code_customer,"; } else { $sql3 .= " s.code_compta,"; } diff --git a/htdocs/core/class/doleditor.class.php b/htdocs/core/class/doleditor.class.php index 31c52d89c86e4..c5c5a6ec4dfea 100644 --- a/htdocs/core/class/doleditor.class.php +++ b/htdocs/core/class/doleditor.class.php @@ -231,7 +231,12 @@ public function Create($noprint = 0, $morejs = '', $disallowAnyContent = true, $ textDirection: \''.dol_escape_js($langs->trans("DIRECTION")).'\', on : { instanceReady : function(ev) { - console.log("ckeditor instanceReady"); + console.log(\'ckeditor '.dol_escape_js($this->htmlname).' instanceReady\'); + + /* If we found the attribute required on source div, we remove it (not compatible with ckeditor) */ + /* Disabled, because attribute required should never be used on fields for doleditor */ + /* jQuery("#'.dol_escape_js($this->htmlname).'").attr("required", false); */ + // Output paragraphs as

Text

. this.dataProcessor.writer.setRules( \'p\', { indent : false, @@ -242,15 +247,13 @@ public function Create($noprint = 0, $morejs = '', $disallowAnyContent = true, $ }); }, /* This is to remove the tab Link on image popup. Does not work, so commented */ - /* - dialogDefinition: function (event) { + /* dialogDefinition: function (event) { var dialogName = event.data.name; var dialogDefinition = event.data.definition; if (dialogName == \'image\') { dialogDefinition.removeContents(\'Link\'); } - } - */ + } */ }, disableNativeSpellChecker: '.(getDolGlobalString('CKEDITOR_NATIVE_SPELLCHECKER') ? 'false' : 'true'); diff --git a/htdocs/core/lib/functions.lib.php b/htdocs/core/lib/functions.lib.php index bf9d62b664338..1584500188b04 100644 --- a/htdocs/core/lib/functions.lib.php +++ b/htdocs/core/lib/functions.lib.php @@ -11062,7 +11062,8 @@ function complete_head_from_modules($conf, $langs, $object, &$head, &$h, $type, */ function printCommonFooter($zone = 'private') { - global $conf, $hookmanager, $user, $debugbar; + global $conf, $hookmanager, $user, $langs; + global $debugbar; global $action; global $micro_start_time; @@ -11110,6 +11111,7 @@ function printCommonFooter($zone = 'private') $relativepathstring = preg_replace('/^\//', '', $relativepathstring); $relativepathstring = preg_replace('/^custom\//', '', $relativepathstring); //$tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING'])); + if (!empty($user->default_values[$relativepathstring]['focus'])) { foreach ($user->default_values[$relativepathstring]['focus'] as $defkey => $defval) { $qualified = 0; @@ -11132,10 +11134,11 @@ function printCommonFooter($zone = 'private') } if ($qualified) { + print 'console.log("set the focus by executing jQuery(...).focus();")'."\n"; foreach ($defval as $paramkey => $paramval) { // Set focus on field print 'jQuery("input[name=\''.$paramkey.'\']").focus();'."\n"; - print 'jQuery("textarea[name=\''.$paramkey.'\']").focus();'."\n"; + print 'jQuery("textarea[name=\''.$paramkey.'\']").focus();'."\n"; // TODO KO with ckeditor print 'jQuery("select[name=\''.$paramkey.'\']").focus();'."\n"; // Not really useful, but we keep it in case of. } } @@ -11163,19 +11166,72 @@ function printCommonFooter($zone = 'private') } if ($qualified) { + print 'console.log("set the js code to manage fields that are set as mandatory");'."\n"; + foreach ($defval as $paramkey => $paramval) { - // Add property 'required' on input - print 'jQuery("input[name=\''.$paramkey.'\']").prop(\'required\',true);'."\n"; - print 'jQuery("textarea[name=\''.$paramkey.'\']").prop(\'required\',true);'."\n"; - print '// required on a select works only if key is "", so we add the required attributes but also we reset the key -1 or 0 to an empty string'."\n"; - print 'jQuery("select[name=\''.$paramkey.'\']").prop(\'required\',true);'."\n"; - print 'jQuery("select[name=\''.$paramkey.'\'] option[value=\'-1\']").prop(\'value\', \'\');'."\n"; - print 'jQuery("select[name=\''.$paramkey.'\'] option[value=\'0\']").prop(\'value\', \'\');'."\n"; + // Solution 1: Add handler on submit to check if mandatory fields are empty + print 'var form = $(\'#'.dol_escape_js($paramkey).'\').closest("form");'."\n"; + print "form.on('submit', function(event) { + var submitter = event.originalEvent.submitter; + if (submitter) { + var buttonName = $(submitter).attr('name'); + if (buttonName == 'cancel') { + console.log('We click on cancel button so we accept submit with no need to check mandatory fields'); + return true; + } + } + console.log('We did not click on cancel button but on something else, we check that field #".dol_escape_js($paramkey)." is not empty'); + + var tmpvalue = jQuery('#".dol_escape_js($paramkey)."').val(); + let tmptypefield = jQuery('#".dol_escape_js($paramkey)."').prop('nodeName').toLowerCase(); // Get the tag name (div, section, footer...) + + if (tmptypefield == 'textarea') { + // We must instead check the content of ckeditor + var tmpeditor = CKEDITOR.instances['".dol_escape_js($paramkey)."']; + if (tmpeditor) { + tmpvalue = tmpeditor.getData(); + console.log('For textarea tmpvalue is '+tmpvalue); + } + } + + let tmpvalueisempty = false; + if (tmpvalue === null || tmpvalue === undefined || tmpvalue === '') { + tmpvalueisempty = true; + } + if (tmpvalue === '0' && tmptypefield == 'select') { + tmpvalueisempty = true; + } + if (tmpvalueisempty) { + console.log('field has type '+tmptypefield+' and is empty, we cancel the submit'); + event.preventDefault(); // Stop submission of form to allow custom code to decide. + event.stopPropagation(); // Stop other handlers. + alert('".dol_escape_js($langs->trans("ErrorFieldRequired", $paramkey).' ('.$langs->trans("CustomMandatoryFieldRule").')')."'); + return false; + } + console.log('field has type '+tmptypefield+' and is defined to '+tmpvalue); + return true; + }); + \n"; + + // Solution 2: Add property 'required' on input + // so browser will check value and try to focus on it when submitting the form. + //print 'setTimeout(function() {'; // If we want to wait that ckeditor beuatifier has finished its job. + //print 'jQuery("input[name=\''.$paramkey.'\']").prop(\'required\',true);'."\n"; + //print 'jQuery("textarea[id=\''.$paramkey.'\']").prop(\'required\',true);'."\n"; + //print 'jQuery("select[name=\''.$paramkey.'\']").prop(\'required\',true);'."\n";*/ + //print '// required on a select works only if key is "", so we add the required attributes but also we reset the key -1 or 0 to an empty string'."\n"; + //print 'jQuery("select[name=\''.$paramkey.'\'] option[value=\'-1\']").prop(\'value\', \'\');'."\n"; + //print 'jQuery("select[name=\''.$paramkey.'\'] option[value=\'0\']").prop(\'value\', \'\');'."\n"; // Add 'field required' class on closest td for all input elements : input, textarea and select - print 'jQuery(":input[name=\'' . $paramkey . '\']").closest("tr").find("td:first").addClass("fieldrequired");' . "\n"; + //print '}, 500);'; // 500 milliseconds delay + + // Now set the class "fieldrequired" + print 'jQuery(\':input[name="' . dol_escape_js($paramkey) . '"]\').closest("tr").find("td:first").addClass("fieldrequired");'."\n"; } - // If we submit the cancel button we remove the required attributes + + + // If we submit using the cancel button, we remove the required attributes print 'jQuery("input[name=\'cancel\']").click(function() { console.log("We click on cancel button so removed all required attribute"); jQuery("input, textarea, select").each(function(){this.removeAttribute(\'required\');}); diff --git a/htdocs/expedition/card.php b/htdocs/expedition/card.php index 9c4d9a872b42e..fa7c86a86ddb3 100644 --- a/htdocs/expedition/card.php +++ b/htdocs/expedition/card.php @@ -760,8 +760,8 @@ if ($lines[$i]->fk_product > 0) { // line without lot if ($lines[$i]->entrepot_id == 0) { - // single warehouse shipment line - $stockLocation = 0; + // single warehouse shipment line or line in several warehouses context but with warehouse not defined + $stockLocation = "entl".$line_id; $qty = "qtyl".$line_id; $line->id = $line_id; $line->entrepot_id = GETPOSTINT((string) $stockLocation); diff --git a/htdocs/install/mysql/migration/18.0.0-19.0.0.sql b/htdocs/install/mysql/migration/18.0.0-19.0.0.sql index 576ced1ee8b27..aa869240a4281 100644 --- a/htdocs/install/mysql/migration/18.0.0-19.0.0.sql +++ b/htdocs/install/mysql/migration/18.0.0-19.0.0.sql @@ -219,6 +219,6 @@ UPDATE llx_c_type_contact SET element = 'stocktransfer' WHERE element = 'StockTr UPDATE llx_c_units SET scale = 1 WHERE code = 'S'; -UPDATE llx_c_tva SET taux = 3, note = 'Νήσων υπερμειωμένος Φ.Π.Α.' WHERE fk_pays = 102 AND taux = 16; +UPDATE llx_c_tva SET taux = 3 WHERE fk_pays = 102 AND taux = 16; UPDATE llx_menu SET url = CONCAT(url, '&mode=init') WHERE fk_mainmenu = 'ticket' AND titre = 'NewTicket' AND url LIKE '/ticket/card.php?action=create%' AND url NOT LIKE '%mode=init%'; diff --git a/htdocs/langs/en_US/errors.lang b/htdocs/langs/en_US/errors.lang index f217b1989857f..4bc2475f6fe87 100644 --- a/htdocs/langs/en_US/errors.lang +++ b/htdocs/langs/en_US/errors.lang @@ -222,7 +222,7 @@ ErrorUserNotAssignedToTask=User must be assigned to task to be able to enter tim ErrorTaskAlreadyAssigned=Task already assigned to user ErrorModuleFileSeemsToHaveAWrongFormat=The module package seems to have a wrong format. ErrorModuleFileSeemsToHaveAWrongFormat2=At least one mandatory directory must exists into zip of module: %s or %s -ErrorFilenameDosNotMatchDolibarrPackageRules=The name of the module package (%s) does not match expected name syntax: %s +ErrorFilenameDosNotMatchDolibarrPackageRules=The file name of the module package (%s) does not match the expected name syntax: %s ErrorDuplicateTrigger=Error, duplicate trigger name %s. Already loaded from %s. ErrorNoWarehouseDefined=Error, no warehouses defined. ErrorBadLinkSourceSetButBadValueForRef=The link you use is not valid. A 'source' for payment is defined, but value for 'ref' is not valid. @@ -422,4 +422,4 @@ OperNotDefined=Payment method not defined ErrorThisContactXIsAlreadyDefinedAsThisType=%s is already defined as contact for this type. ErrorThisGroupIsAlreadyDefinedAsThisType=The contacts with this group are already defined as contact for this type. EmptyMessageNotAllowedError=Empty message is not allowed -ErrorIsNotInError=%s is not in error \ No newline at end of file +ErrorIsNotInError=%s is not in error diff --git a/htdocs/langs/en_US/main.lang b/htdocs/langs/en_US/main.lang index bedc6a60e081a..f8e6f2ccede71 100644 --- a/htdocs/langs/en_US/main.lang +++ b/htdocs/langs/en_US/main.lang @@ -45,6 +45,7 @@ NoError=No error Error=Error Errors=Errors ErrorFieldRequired=Field '%s' is required +CustomMandatoryFieldRule=Custom "Mandatory field" rule ErrorFieldFormat=Field '%s' has a bad value ErrorFileDoesNotExists=File %s does not exist ErrorFailedToOpenFile=Failed to open file %s diff --git a/htdocs/product/card.php b/htdocs/product/card.php index 8bf690d5f6273..eafcfbd81441f 100644 --- a/htdocs/product/card.php +++ b/htdocs/product/card.php @@ -1437,7 +1437,7 @@ } // Label - print ''.$langs->trans("Label").''; + print ''.$langs->trans("Label").''; // On sell print ''.$langs->trans("Status").' ('.$langs->trans("Sell").')'; @@ -3111,7 +3111,7 @@ $html .= ''; $html .= ''; - print ''; + print '
'; print $html; print '
'; diff --git a/htdocs/product/class/product.class.php b/htdocs/product/class/product.class.php index cd4fc52c70717..b526c465d7904 100644 --- a/htdocs/product/class/product.class.php +++ b/htdocs/product/class/product.class.php @@ -1549,7 +1549,6 @@ public function update($id, $user, $notrigger = 0, $action = 'update', $updatety // Multilangs if (getDolGlobalInt('MAIN_MULTILANGS')) { if ($this->setMultiLangs($user) < 0) { - $this->error = $langs->trans("Error")." : ".$this->db->error()." - ".$sql; $this->db->rollback(); return -2; }