diff --git a/.travis.yml b/.travis.yml index 6422e33a7e9..882c6e74620 100644 --- a/.travis.yml +++ b/.travis.yml @@ -95,19 +95,20 @@ notifications: install: - | - echo "Updating Composer" - rm $TRAVIS_BUILD_DIR/composer.json - rm $TRAVIS_BUILD_DIR/composer.lock + echo "Updating Composer config" composer -V - composer self-update - composer -n init - composer -n config vendor-dir htdocs/includes + #rm $TRAVIS_BUILD_DIR/composer.json + #rm $TRAVIS_BUILD_DIR/composer.lock + #composer -n init + #composer -n config vendor-dir htdocs/includes composer -n config -g vendor-dir htdocs/includes echo - | - echo "Installing Composer dependencies - PHP Unit, Parallel Lint, PHP CodeSniffer, PHP Vardump check - for $TRAVIS_PHP_VERSION" + echo "Update Composer version and Install tools - PHP Unit, Parallel Lint, PHP CodeSniffer, PHP Vardump check - for $TRAVIS_PHP_VERSION" + echo "(version 2.5 is bugged and generate phpunit error Exception: Serialization of 'Closure' is not allowed)" if [ "$TRAVIS_PHP_VERSION" = '7.0' ] || [ "$TRAVIS_PHP_VERSION" = '7.1' ] || [ "$TRAVIS_PHP_VERSION" = '7.2' ]; then + composer self-update 2.2.18 composer -n require phpunit/phpunit ^6 \ php-parallel-lint/php-parallel-lint ^1 \ php-parallel-lint/php-console-highlighter ^0 \ @@ -115,6 +116,7 @@ install: squizlabs/php_codesniffer ^3 fi if [ "$TRAVIS_PHP_VERSION" = '7.3' ] || [ "$TRAVIS_PHP_VERSION" = '7.4' ]; then + composer self-update 2.2.18 composer -n require phpunit/phpunit ^7 \ php-parallel-lint/php-parallel-lint ^1.2 \ php-parallel-lint/php-console-highlighter ^0 \ @@ -123,6 +125,7 @@ install: fi # phpunit 9 is required for php 8 if [ "$TRAVIS_PHP_VERSION" = '8.0' ] || [ "$TRAVIS_PHP_VERSION" = '8.1' ] || [ "$TRAVIS_PHP_VERSION" = 'nightly' ]; then + composer self-update 2.4.4 composer -n require --ignore-platform-reqs phpunit/phpunit ^8 \ php-parallel-lint/php-parallel-lint ^1.2 \ php-parallel-lint/php-console-highlighter ^0 \ diff --git a/ChangeLog b/ChangeLog index 1697b668934..3d9b0b04e5a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -3,6 +3,16 @@ English Dolibarr ChangeLog -------------------------------------------------------------- +***** ChangeLog for 18.0.0 compared to 17.0.0 ***** + +WARNING: + +Following changes may create regressions for some external modules, but were necessary to make Dolibarr better: +* The deprecated method escapeunderscore() of database handlers has been removed. You must use escapeforlike instead. + + + + ***** ChangeLog for 17.0.0 compared to 16.0.0 ***** For users: diff --git a/htdocs/accountancy/admin/account.php b/htdocs/accountancy/admin/account.php index 2a23ee5eb85..21d60103135 100644 --- a/htdocs/accountancy/admin/account.php +++ b/htdocs/accountancy/admin/account.php @@ -399,7 +399,7 @@ if ($resql) { } print ""; print ajax_combobox("chartofaccounts"); - print ''; + print ''; print '
'; @@ -436,7 +436,7 @@ if ($resql) { } if (!empty($arrayfields['aa.account_parent']['checked'])) { print ''; - print $formaccounting->select_account($search_accountparent, 'search_accountparent', 2); + print $formaccounting->select_account($search_accountparent, 'search_accountparent', 2, array(), 0, 0, 'maxwidth150'); print ''; } if (!empty($arrayfields['aa.pcg_type']['checked'])) { diff --git a/htdocs/accountancy/admin/card.php b/htdocs/accountancy/admin/card.php index 9eabd378e11..97d2509b898 100644 --- a/htdocs/accountancy/admin/card.php +++ b/htdocs/accountancy/admin/card.php @@ -329,7 +329,8 @@ if ($action == 'create') { // Account parent print ''.$langs->trans("Accountparent").''; print ''; - print $formaccounting->select_account($object->account_parent, 'account_parent', 1); + // Note: We accept disabled account as parent account so we can build a hierarchy and use only childs + print $formaccounting->select_account($object->account_parent, 'account_parent', 1, array(), 0, 0, 'minwidth100 maxwidth300 maxwidthonsmartphone', 1, ''); print ''; // Chart of accounts type diff --git a/htdocs/accountancy/admin/categories.php b/htdocs/accountancy/admin/categories.php index 7b86902e009..87e6a3b490c 100644 --- a/htdocs/accountancy/admin/categories.php +++ b/htdocs/accountancy/admin/categories.php @@ -110,18 +110,22 @@ print ''; // Select the category print ''; print ''; +print '
'.$langs->trans("AccountingCategory").''; -$formaccounting->select_accounting_category($cat_id, 'account_category', 1, 0, 0, 1); -print ''; +$formaccounting->select_accounting_category($cat_id, 'account_category', 1, 0, 0, 0); +print ''; print '
'; + +print dol_get_fiche_end(); + + // Select the accounts if (!empty($cat_id)) { $return = $accountingcategory->getAccountsWithNoCategory($cat_id); if ($return < 0) { setEventMessages(null, $accountingcategory->errors, 'errors'); } - print ''.$langs->trans("AddAccountFromBookKeepingWithNoCategories").''; - print ''; + print '
'; $arraykeyvalue = array(); foreach ($accountingcategory->lines_cptbk as $key => $val) { @@ -130,8 +134,9 @@ if (!empty($cat_id)) { } if (is_array($accountingcategory->lines_cptbk) && count($accountingcategory->lines_cptbk) > 0) { - print $form->multiselectarray('cpt_bk', $arraykeyvalue, GETPOST('cpt_bk', 'array'), null, null, null, null, "90%"); - print '
'; + print img_picto($langs->trans("AccountingAccount"), 'accounting_account', 'class="pictofixedwith"'); + print $form->multiselectarray('cpt_bk', $arraykeyvalue, GETPOST('cpt_bk', 'array'), null, null, '', 0, "80%", '', '', $langs->transnoentitiesnoconv("AddAccountFromBookKeepingWithNoCategories")); + //print '
'; /*print '
'; print ajax_combobox('cpt_bk'); */ - print ' '; + print ' '; } - print ''; } -print ''; - -print dol_get_fiche_end(); - print ''; if ($action == 'display' || $action == 'delete') { - print "\n"; + print '
'; + print '
'."\n"; print ''; print '"; print '"; @@ -177,6 +178,8 @@ if ($action == 'display' || $action == 'delete') { print ""; print "\n"; } + } else { + print ''; } } diff --git a/htdocs/accountancy/admin/categories_list.php b/htdocs/accountancy/admin/categories_list.php index 3a6664b8b44..0e7bd908c9a 100644 --- a/htdocs/accountancy/admin/categories_list.php +++ b/htdocs/accountancy/admin/categories_list.php @@ -190,7 +190,7 @@ if (GETPOST('actionadd', 'alpha') || GETPOST('actionmodify', 'alpha')) { setEventMessages($langs->transnoentities('ErrorCodeCantContainZero'), null, 'errors'); } } - if (!is_numeric(GETPOST('position', 'alpha'))) { + if (GETPOST('position') && !is_numeric(GETPOST('position', 'alpha'))) { $langs->loadLangs(array("errors")); $ok = 0; setEventMessages($langs->transnoentities('ErrorFieldMustBeANumeric', $langs->transnoentities("Position")), null, 'errors'); @@ -605,7 +605,7 @@ if ($resql) { if ($showfield) { if ($value == 'country') { print ''; $filterfound++; } else { @@ -725,11 +725,11 @@ if ($resql) { print ''; print ''; print ''; print ''; } else { @@ -887,10 +887,10 @@ function fieldListAccountingCategories($fieldlist, $obj = '', $tabname = '', $co if ($context == 'add') { $fieldname = 'country_id'; $preselectcountrycode = GETPOSTISSET('country_id') ? GETPOST('country_id', 'int') : $mysoc->country_code; - print $form->select_country($preselectcountrycode, $fieldname, '', 28, 'maxwidth200 maxwidthonsmartphone'); + print $form->select_country($preselectcountrycode, $fieldname, '', 28, 'maxwidth150 maxwidthonsmartphone'); } else { $preselectcountrycode = (empty($obj->country_code) ? (empty($obj->country) ? $mysoc->country_code : $obj->country) : $obj->country_code); - print $form->select_country($preselectcountrycode, $fieldname, '', 28, 'maxwidth200 maxwidthonsmartphone'); + print $form->select_country($preselectcountrycode, $fieldname, '', 28, 'maxwidth150 maxwidthonsmartphone'); } print ''; } elseif ($fieldlist[$field] == 'country_id') { diff --git a/htdocs/accountancy/admin/subaccount.php b/htdocs/accountancy/admin/subaccount.php index c9f78596bcd..4f2274269d8 100644 --- a/htdocs/accountancy/admin/subaccount.php +++ b/htdocs/accountancy/admin/subaccount.php @@ -408,7 +408,7 @@ if ($resql) { $s .= ''.$langs->trans("Supplier").''; } elseif ($obj->type == 3) { // User - $s .= ''.$langs->trans("Employee").''; + $s .= ''.$langs->trans("Employee").''; } print $s; print ''; diff --git a/htdocs/accountancy/customer/lines.php b/htdocs/accountancy/customer/lines.php index e4f7d556c16..b8df8890a01 100644 --- a/htdocs/accountancy/customer/lines.php +++ b/htdocs/accountancy/customer/lines.php @@ -529,6 +529,10 @@ if ($result) { print ''; $i++; } + if ($num_lines == 0) { + print ''; + } + print '
'.$langs->trans("AccountAccounting")."'.$langs->trans("Label")."
'.$langs->trans("NoRecordFound").'
'; - print $form->select_country($search_country_id, 'search_country_id', '', 28, 'maxwidth200 maxwidthonsmartphone'); + print $form->select_country($search_country_id, 'search_country_id', '', 28, 'maxwidth150 maxwidthonsmartphone'); print ''; + print '
'; print ''; print ''; - print ''; - print '
'; - print ''; + print ''; + print ''; print '
'.$langs->trans("NoRecordFound").'
'; print ""; diff --git a/htdocs/accountancy/customer/list.php b/htdocs/accountancy/customer/list.php index 57a9dd99e5e..021c1ec7821 100644 --- a/htdocs/accountancy/customer/list.php +++ b/htdocs/accountancy/customer/list.php @@ -749,6 +749,10 @@ if ($result) { print ''; $i++; } + if ($num_lines == 0) { + print ''.$langs->trans("NoRecordFound").''; + } + print ''; print ""; diff --git a/htdocs/accountancy/expensereport/lines.php b/htdocs/accountancy/expensereport/lines.php index aef45ae3ce1..1d0653fc5ac 100644 --- a/htdocs/accountancy/expensereport/lines.php +++ b/htdocs/accountancy/expensereport/lines.php @@ -427,6 +427,13 @@ if ($result) { print ""; $i++; } + if ($num_lines == 0) { + $colspan=10; + if (!empty($conf->global->ACCOUNTANCY_USE_EXPENSE_REPORT_VALIDATION_DATE)) { + $colspan++; + } + print ''.$langs->trans("NoRecordFound").''; + } print ""; print ""; diff --git a/htdocs/accountancy/expensereport/list.php b/htdocs/accountancy/expensereport/list.php index b90a8a2c573..9cfbd940875 100644 --- a/htdocs/accountancy/expensereport/list.php +++ b/htdocs/accountancy/expensereport/list.php @@ -513,6 +513,9 @@ if ($result) { print ""; $i++; } + if ($num_lines == 0) { + print ''.$langs->trans("NoRecordFound").''; + } print ''; print ""; diff --git a/htdocs/accountancy/supplier/lines.php b/htdocs/accountancy/supplier/lines.php index 25368f30c61..ea9841d2fbb 100644 --- a/htdocs/accountancy/supplier/lines.php +++ b/htdocs/accountancy/supplier/lines.php @@ -558,6 +558,10 @@ if ($result) { print ''; $i++; } + if ($num_lines == 0) { + print ''.$langs->trans("NoRecordFound").''; + } + print ''; print ""; diff --git a/htdocs/accountancy/supplier/list.php b/htdocs/accountancy/supplier/list.php index b78227a67f6..3b8e93cb54b 100644 --- a/htdocs/accountancy/supplier/list.php +++ b/htdocs/accountancy/supplier/list.php @@ -777,6 +777,9 @@ if ($result) { print ''; $i++; } + if ($num_lines == 0) { + print ''.$langs->trans("NoRecordFound").''; + } print ''; print ""; diff --git a/htdocs/admin/system/perf.php b/htdocs/admin/system/perf.php index 6d7ef8f1863..d9affa9684f 100644 --- a/htdocs/admin/system/perf.php +++ b/htdocs/admin/system/perf.php @@ -50,8 +50,13 @@ llxHeader(); print load_fiche_titre($langs->trans("PerfDolibarr"), '', 'title_setup'); -print ''.$langs->trans("YouMayFindPerfAdviceHere", 'https://wiki.dolibarr.org/index.php/FAQ_Increase_Performance').' ('.$langs->trans("Reload").')
'; - +print ''.$langs->trans("YouMayFindPerfAdviceHere", 'https://wiki.dolibarr.org/index.php/FAQ_Increase_Performance').''; +print '     '; +print ''; +print img_picto($langs->trans("Reload"), 'refresh').' '; +print $langs->trans("Reload"); +print ''; +print '
'; print '
'; print '
'; diff --git a/htdocs/admin/system/security.php b/htdocs/admin/system/security.php index f8f47baebf9..c23bdb3051f 100644 --- a/htdocs/admin/system/security.php +++ b/htdocs/admin/system/security.php @@ -41,7 +41,7 @@ if (GETPOST('action', 'aZ09') == 'donothing') { exit; } -$execmethod = empty($conf->global->MAIN_EXEC_USE_POPEN) ? 1 : $conf->global->MAIN_EXEC_USE_POPEN; +$execmethod = getDolGlobalInt('MAIN_EXEC_USE_POPEN', 1); /* @@ -52,7 +52,13 @@ llxHeader(); print load_fiche_titre($langs->trans("Security"), '', 'title_setup'); -print ''.$langs->trans("YouMayFindSecurityAdviceHere", 'hhttps://wiki.dolibarr.org/index.php/Security_information').' ('.$langs->trans("Reload").')
'; +print ''.$langs->trans("YouMayFindSecurityAdviceHere", 'hhttps://wiki.dolibarr.org/index.php/Security_information').''; +print '     '; +print ''; +print img_picto($langs->trans("Reload"), 'refresh').' '; +print $langs->trans("Reload"); +print ''; +print '
'; print '
'; print load_fiche_titre($langs->trans("PHPSetup"), '', 'folder'); @@ -598,7 +604,7 @@ print ''; print '
'; $urlexamplebase = 'https://github.com/Dolibarr/dolibarr/blob/develop/dev/setup/fail2ban/filter.d/'; print '- Login process (see fail2ban example on GitHub)
'; -print '- '.DOL_URL_ROOT.'/passwordforgotten.php (see fail2ban example on GitHub)
'; +print '- '.DOL_URL_ROOT.'/passwordforgotten.php (see fail2ban example on GitHub)
'; print '- '.DOL_URL_ROOT.'/public/* (see fail2ban example on GitHub)
'; print '
'; $urlexamplebase = 'https://github.com/Dolibarr/dolibarr/blob/develop/dev/setup/apache/'; diff --git a/htdocs/blockedlog/admin/blockedlog_list.php b/htdocs/blockedlog/admin/blockedlog_list.php index 9bb4456e840..54ae9d1e34d 100644 --- a/htdocs/blockedlog/admin/blockedlog_list.php +++ b/htdocs/blockedlog/admin/blockedlog_list.php @@ -448,8 +448,8 @@ print ''; // Status print ''; -$array = array("1"=>$langs->trans("OnlyNonValid")); -print $form->selectarray('search_showonlyerrors', $array, $search_showonlyerrors, 1); +$array = array("1" => "OnlyNonValid"); +print $form->selectarray('search_showonlyerrors', $array, $search_showonlyerrors, 1, 0, 0, '', 1, 0, 0, 'ASC', 'search_status maxwidth200 onrightofpage', 1); print ''; // Status note @@ -530,7 +530,7 @@ if (is_array($blocks)) { print ''.dol_escape_htmltag($block->id).''; // Date - print ''.dol_print_date($block->date_creation, 'dayhour').''; + print ''.dol_print_date($block->date_creation, 'dayhour').''; // User print ''; diff --git a/htdocs/commande/class/commande.class.php b/htdocs/commande/class/commande.class.php index 73988594e6f..f90fac176dc 100644 --- a/htdocs/commande/class/commande.class.php +++ b/htdocs/commande/class/commande.class.php @@ -4331,16 +4331,6 @@ class OrderLine extends CommonOrderLine dol_syslog("OrderLine::delete", LOG_DEBUG); $resql = $this->db->query($sql); if ($resql) { - // Remove extrafields - if (!$error) { - $this->id = $this->rowid; - $result = $this->deleteExtraFields(); - if ($result < 0) { - $error++; - dol_syslog(get_class($this)."::delete error -4 ".$this->error, LOG_ERR); - } - } - if (!$error && !$notrigger) { // Call trigger $result = $this->call_trigger('LINEORDER_DELETE', $user); @@ -4350,6 +4340,15 @@ class OrderLine extends CommonOrderLine // End call triggers } + // Remove extrafields + if (!$error) { + $result = $this->deleteExtraFields(); + if ($result < 0) { + $error++; + dol_syslog(get_class($this)."::delete error -4 ".$this->error, LOG_ERR); + } + } + if (!$error) { $this->db->commit(); return 1; diff --git a/htdocs/compta/resultat/clientfourn.php b/htdocs/compta/resultat/clientfourn.php index 5fdf7220662..54f242edb7c 100644 --- a/htdocs/compta/resultat/clientfourn.php +++ b/htdocs/compta/resultat/clientfourn.php @@ -321,7 +321,7 @@ if ($modecompta == 'BOOKKEEPING') { if ($showaccountdetail == 'no') { if ($objp->pcg_type != $oldpcgtype) { - print ''.$objp->pcg_type.''; + print ''.dol_escape_htmltag($objp->pcg_type).''; $oldpcgtype = $objp->pcg_type; } } @@ -330,17 +330,17 @@ if ($modecompta == 'BOOKKEEPING') { print ''; print ''; print ''; - print $objp->pcg_type; - print ($objp->name ? ' ('.$objp->name.')' : ' ('.$langs->trans("Unknown").')'); + print dol_escape_htmltag($objp->pcg_type); + print ($objp->name ? ' ('.dol_escape_htmltag($objp->name).')' : ' ('.$langs->trans("Unknown").')'); print "\n"; - print ''.price($objp->amount)."\n"; + print ''.price($objp->amount)."\n"; print "\n"; } else { print ''; print ''; - print $objp->pcg_type; + print dol_escape_htmltag($objp->pcg_type); print "\n"; - print ''.price($objp->amount)."\n"; + print ''.price($objp->amount)."\n"; print "\n"; } @@ -367,7 +367,7 @@ if ($modecompta == 'BOOKKEEPING') { $cpts = $AccCat->getCptsCat(0, $tmppredefinedgroupwhere); foreach ($cpts as $j => $cpt) { - $return = $AccCat->getSumDebitCredit($cpt['account_number'], $date_start, $date_end, $cpt['dc']); + $return = $AccCat->getSumDebitCredit($cpt['account_number'], $date_start, $date_end, (empty($cpt['dc']) ? 0 : $cpt['dc'])); if ($return < 0) { setEventMessages(null, $AccCat->errors, 'errors'); $resultN = 0; @@ -380,7 +380,7 @@ if ($modecompta == 'BOOKKEEPING') { print ''; print ''; print '     '.length_accountg($cpt['account_number']).' - '.$cpt['account_label'].''; - print ''.price($resultN).''; + print ''.price($resultN).''; print "\n"; } } @@ -1539,27 +1539,27 @@ print ''; print ''.$langs->trans("Income").''; if ($modecompta == 'CREANCES-DETTES') { - print ''.price(price2num($total_ht_income, 'MT')).''; + print ''.price(price2num($total_ht_income, 'MT')).''; } elseif ($modecompta == 'RECETTES-DEPENSES') { print ''; } -print ''.price(price2num($total_ttc_income, 'MT')).''; +print ''.price(price2num($total_ttc_income, 'MT')).''; print ''; print ''.$langs->trans("Outcome").''; if ($modecompta == 'CREANCES-DETTES') { - print ''.price(price2num(-$total_ht_outcome, 'MT')).''; + print ''.price(price2num(-$total_ht_outcome, 'MT')).''; } elseif ($modecompta == 'RECETTES-DEPENSES') { print ''; } -print ''.price(price2num(-$total_ttc_outcome, 'MT')).''; +print ''.price(price2num(-$total_ttc_outcome, 'MT')).''; print ''; print ''.$langs->trans("Profit").''; if ($modecompta == 'CREANCES-DETTES') { - print ''.price(price2num($total_ht, 'MT')).''; + print ''.price(price2num($total_ht, 'MT')).''; } elseif ($modecompta == 'RECETTES-DEPENSES') { print ''; } -print ''.price(price2num($total_ttc, 'MT')).''; +print ''.price(price2num($total_ttc, 'MT')).''; print ''; print ""; diff --git a/htdocs/core/boxes/box_members_by_type.php b/htdocs/core/boxes/box_members_by_type.php index 2e25da59564..9fcfb7e325a 100644 --- a/htdocs/core/boxes/box_members_by_type.php +++ b/htdocs/core/boxes/box_members_by_type.php @@ -92,6 +92,7 @@ class box_members_by_type extends ModeleBoxes if ($user->rights->adherent->lire) { $MembersToValidate = array(); + $MembersPending = array(); $MembersValidated = array(); $MembersUpToDate = array(); $MembersExcluded = array(); @@ -169,34 +170,63 @@ class box_members_by_type extends ModeleBoxes } $this->db->free($result); } + // Members pendding (Waiting for first subscription) + $sql = "SELECT count(*) as somme , d.fk_adherent_type"; + $sql .= " FROM " . MAIN_DB_PREFIX . "adherent as d, " . MAIN_DB_PREFIX . "adherent_type as t"; + $sql .= " WHERE d.entity IN (" . getEntity('adherent') . ")"; + $sql .= " AND d.statut = 1 AND (d.datefin IS NULL AND t.subscription = 1)"; + $sql .= " AND t.rowid = d.fk_adherent_type"; + $sql .= " GROUP BY d.fk_adherent_type"; + + dol_syslog("index.php::select nb of uptodate members by type", LOG_DEBUG); + $result = $this->db->query($sql); + if ($result) { + $num2 = $this->db->num_rows($result); + $i = 0; + while ($i < $num2) { + $objp = $this->db->fetch_object($result); + $MembersPending[$objp->fk_adherent_type] = $objp->somme; + $i++; + } + $this->db->free($result); + } $line = 0; $this->info_box_contents[$line][] = array( 'td' => 'class=""', 'text' => '', ); + // Draft $labelstatus = $staticmember->LibStatut($staticmember::STATUS_DRAFT, 0, 0, 1); $this->info_box_contents[$line][] = array( 'td' => 'class="right tdoverflowmax100" width="15%" title="'.dol_escape_htmltag($labelstatus).'"', 'text' => $labelstatus ); - $labelstatus = $langs->trans("UpToDate"); + // Pending (Waiting for first subscription) + $labelstatus = $staticmember->LibStatut($staticmember::STATUS_VALIDATED, 1, 0, 1); + $this->info_box_contents[$line][] = array( + 'td' => 'class="right tdoverflowmax100" width="15%" title="'.dol_escape_htmltag($labelstatus).'"', + 'text' => $labelstatus + ); + // Up to date $labelstatus = $staticmember->LibStatut($staticmember::STATUS_VALIDATED, 1, dol_now() + 86400, 1); $this->info_box_contents[$line][] = array( 'td' => 'class="right tdoverflowmax100" width="15%" title="'.dol_escape_htmltag($labelstatus).'"', 'text' => $labelstatus, ); - $labelstatus = $langs->trans("OutOfDate"); + // Expired $labelstatus = $staticmember->LibStatut($staticmember::STATUS_VALIDATED, 1, dol_now() - 86400, 1); $this->info_box_contents[$line][] = array( 'td' => 'class="right tdoverflowmax100" width="15%" title="'.dol_escape_htmltag($labelstatus).'"', 'text' => $labelstatus ); + // Excluded $labelstatus = $staticmember->LibStatut($staticmember::STATUS_EXCLUDED, 0, 0, 1); $this->info_box_contents[$line][] = array( 'td' => 'class="right tdoverflowmax100" width="15%" title="'.dol_escape_htmltag($labelstatus).'"', 'text' => $labelstatus ); + // Resiliated $labelstatus = $staticmember->LibStatut($staticmember::STATUS_RESILIATED, 0, 0, 1); $this->info_box_contents[$line][] = array( 'td' => 'class="right tdoverflowmax100" width="15%" title="'.dol_escape_htmltag($labelstatus).'"', @@ -205,7 +235,8 @@ class box_members_by_type extends ModeleBoxes $line++; foreach ($AdherentType as $key => $adhtype) { $SumToValidate += isset($MembersToValidate[$key]) ? $MembersToValidate[$key] : 0; - $SumValidated += isset($MembersValidated[$key]) ? $MembersValidated[$key] - (isset($MembersUpToDate[$key]) ? $MembersUpToDate[$key] : 0) : 0; + $SumPending += isset($MembersPending[$key]) ? $MembersPending[$key] : 0; + $SumExpired += isset($MembersValidated[$key]) ? $MembersValidated[$key] - (isset($MembersUpToDate[$key]) ? $MembersUpToDate[$key] : 0) - (isset($MembersPending[$key]) ? $MembersPending[$key] : 0): 0; $SumUpToDate += isset($MembersUpToDate[$key]) ? $MembersUpToDate[$key] : 0; $SumExcluded += isset($MembersExcluded[$key]) ? $MembersExcluded [$key] : 0; $SumResiliated += isset($MembersResiliated[$key]) ? $MembersResiliated[$key] : 0; @@ -220,6 +251,11 @@ class box_members_by_type extends ModeleBoxes 'text' => (isset($MembersToValidate[$key]) && $MembersToValidate[$key] > 0 ? $MembersToValidate[$key] : '') . ' ' . $staticmember->LibStatut(Adherent::STATUS_DRAFT, 1, 0, 3), 'asis' => 1, ); + $this->info_box_contents[$line][] = array( + 'td' => 'class="right"', + 'text' => (isset($MembersPending[$key]) && $MembersPending[$key] > 0 ? $MembersPending[$key] : '') . ' ' . $staticmember->LibStatut(Adherent::STATUS_VALIDATED, 1, 0, 3), + 'asis' => 1, + ); $this->info_box_contents[$line][] = array( 'td' => 'class="right"', 'text' => (isset($MembersUpToDate[$key]) && $MembersUpToDate[$key] > 0 ? $MembersUpToDate[$key] : '') . ' ' . $staticmember->LibStatut(Adherent::STATUS_VALIDATED, 1, $now, 3), @@ -260,6 +296,11 @@ class box_members_by_type extends ModeleBoxes 'text' => $SumToValidate.' '.$staticmember->LibStatut(Adherent::STATUS_DRAFT, 1, 0, 3), 'asis' => 1 ); + $this->info_box_contents[$line][] = array( + 'td' => 'class="liste_total right"', + 'text' => $SumPending.' '.$staticmember->LibStatut(Adherent::STATUS_VALIDATED, 1, 0, 3), + 'asis' => 1 + ); $this->info_box_contents[$line][] = array( 'td' => 'class="liste_total right"', 'text' => $SumUpToDate.' '.$staticmember->LibStatut(Adherent::STATUS_VALIDATED, 1, $now, 3), @@ -267,7 +308,7 @@ class box_members_by_type extends ModeleBoxes ); $this->info_box_contents[$line][] = array( 'td' => 'class="liste_total right"', - 'text' => $SumValidated.' '.$staticmember->LibStatut(Adherent::STATUS_VALIDATED, 1, 1, 3), + 'text' => $SumExpired.' '.$staticmember->LibStatut(Adherent::STATUS_VALIDATED, 1, 1, 3), 'asis' => 1 ); $this->info_box_contents[$line][] = array( diff --git a/htdocs/core/class/commonobject.class.php b/htdocs/core/class/commonobject.class.php index 37f250bf92f..d1b0ca0164c 100644 --- a/htdocs/core/class/commonobject.class.php +++ b/htdocs/core/class/commonobject.class.php @@ -9600,6 +9600,12 @@ abstract class CommonObject } } + // Delete linked object + $res = $this->deleteObjectLinked(); + if ($res < 0) { + $error++; + } + if (!$error && !empty($this->isextrafieldmanaged)) { $result = $this->deleteExtraFields(); if ($result < 0) { @@ -9723,23 +9729,24 @@ abstract class CommonObject $tmpforobjectclass = get_class($this); $tmpforobjectlineclass = ucfirst($tmpforobjectclass).'Line'; + $this->db->begin(); + // Call trigger $result = $this->call_trigger('LINE'.strtoupper($tmpforobjectclass).'_DELETE', $user); if ($result < 0) { - return -1; + $error++; } // End call triggers - $this->db->begin(); + if (empty($error)) { + $sql = "DELETE FROM ".$this->db->prefix().$this->table_element_line; + $sql .= " WHERE rowid = ".((int) $idline); - $sql = "DELETE FROM ".$this->db->prefix().$this->table_element_line; - $sql .= " WHERE rowid = ".((int) $idline); - - dol_syslog(get_class($this)."::deleteLineCommon", LOG_DEBUG); - $resql = $this->db->query($sql); - if (!$resql) { - $this->error = "Error ".$this->db->lasterror(); - $error++; + $resql = $this->db->query($sql); + if (!$resql) { + $this->error = "Error ".$this->db->lasterror(); + $error++; + } } if (empty($error)) { diff --git a/htdocs/core/class/dolgraph.class.php b/htdocs/core/class/dolgraph.class.php index 57f0bca4e36..6ef5b69ba3e 100644 --- a/htdocs/core/class/dolgraph.class.php +++ b/htdocs/core/class/dolgraph.class.php @@ -1211,7 +1211,7 @@ class DolGraph $tmp = str_replace('#', '', $this->datacolor[$i]); if (strpos($tmp, '-') !== false) { $foundnegativecolor++; - $color = '#FFFFFF'; // If $val is '-123' + $color = 'rgba(0,0,0,.0)'; // If $val is '-123' } else { $color = "#" . $tmp; // If $val is '123' or '#123' } diff --git a/htdocs/core/class/html.formaccounting.class.php b/htdocs/core/class/html.formaccounting.class.php index 10e23f3d2e4..8ebac3611cb 100644 --- a/htdocs/core/class/html.formaccounting.class.php +++ b/htdocs/core/class/html.formaccounting.class.php @@ -270,12 +270,16 @@ class FormAccounting extends Form } while ($i < $num) { $obj = $this->db->fetch_object($resql); + + $titletoshowhtml = ($maxlen ? dol_trunc($obj->type, $maxlen) : $obj->type).($obj->range_account ? ' ('.$obj->range_account.')' : ''); + $titletoshow = ($maxlen ? dol_trunc($obj->type, $maxlen) : $obj->type).($obj->range_account ? ' ('.$obj->range_account.')' : ''); + $out .= ''; $i++; @@ -331,17 +335,18 @@ class FormAccounting extends Form /** * Return list of accounts with label by chart of accounts * - * @param string $selectid Preselected id of accounting accounts (depends on $select_in) - * @param string $htmlname Name of HTML field id. If name start with '.', it is name of HTML css class, so several component with same name in different forms can be used. - * @param int|string $showempty 1=Add an empty field, 2=Add an empty field+'None' field - * @param array $event Event options - * @param int $select_in 0=selectid value is a aa.rowid (default) or 1=selectid is aa.account_number - * @param int $select_out Set value returned by select. 0=rowid (default), 1=account_number - * @param string $morecss More css non HTML object - * @param string $usecache Key to use to store result into a cache. Next call with same key will reuse the cache. - * @return string String with HTML select + * @param string $selectid Preselected id of accounting accounts (depends on $select_in) + * @param string $htmlname Name of HTML field id. If name start with '.', it is name of HTML css class, so several component with same name in different forms can be used. + * @param int|string $showempty 1=Add an empty field, 2=Add an empty field+'None' field + * @param array $event Event options + * @param int $select_in 0=selectid value is a aa.rowid (default) or 1=selectid is aa.account_number + * @param int $select_out Set value returned by select. 0=rowid (default), 1=account_number + * @param string $morecss More css non HTML object + * @param string $usecache Key to use to store result into a cache. Next call with same key will reuse the cache. + * @param string $active Filter on status active or not: '0', '1' or '' for no filter + * @return string String with HTML select */ - public function select_account($selectid, $htmlname = 'account', $showempty = 0, $event = array(), $select_in = 0, $select_out = 0, $morecss = 'minwidth100 maxwidth300 maxwidthonsmartphone', $usecache = '') + public function select_account($selectid, $htmlname = 'account', $showempty = 0, $event = array(), $select_in = 0, $select_out = 0, $morecss = 'minwidth100 maxwidth300 maxwidthonsmartphone', $usecache = '', $active = '1') { // phpcs:enable global $conf, $langs; @@ -360,14 +365,18 @@ class FormAccounting extends Form $options = $options + $this->options_cache[$usecache]; // We use + instead of array_merge because we don't want to reindex key from 0 $selected = $selectid; } else { - $trunclength = empty($conf->global->ACCOUNTING_LENGTH_DESCRIPTION_ACCOUNT) ? 50 : $conf->global->ACCOUNTING_LENGTH_DESCRIPTION_ACCOUNT; + $trunclength = getDolGlobalInt('ACCOUNTING_LENGTH_DESCRIPTION_ACCOUNT', 50); $sql = "SELECT DISTINCT aa.account_number, aa.label, aa.labelshort, aa.rowid, aa.fk_pcg_version"; $sql .= " FROM ".$this->db->prefix()."accounting_account as aa"; $sql .= " INNER JOIN ".$this->db->prefix()."accounting_system as asy ON aa.fk_pcg_version = asy.pcg_version"; $sql .= " AND asy.rowid = ".((int) $conf->global->CHARTOFACCOUNTS); - $sql .= " AND aa.active = 1"; - $sql .= " AND aa.entity=".$conf->entity; + if ($active === '1') { + $sql .= " AND aa.active = 1"; + } elseif ($active === '0') { + $sql .= " AND aa.active = 0"; + } + $sql .= " AND aa.entity=".((int) $conf->entity); $sql .= " ORDER BY aa.account_number"; dol_syslog(get_class($this)."::select_account", LOG_DEBUG); diff --git a/htdocs/core/class/validate.class.php b/htdocs/core/class/validate.class.php index 9d8832c36ce..31cf6300aff 100644 --- a/htdocs/core/class/validate.class.php +++ b/htdocs/core/class/validate.class.php @@ -191,7 +191,7 @@ class Validate */ public function isMinLength($string, $length) { - if (!strlen($string) < $length) { + if (strlen($string) < $length) { $this->error = $this->outputLang->trans('RequireMinLength', $length); return false; } diff --git a/htdocs/core/js/lib_foot.js.php b/htdocs/core/js/lib_foot.js.php index 3310554e530..c037b86a7b9 100644 --- a/htdocs/core/js/lib_foot.js.php +++ b/htdocs/core/js/lib_foot.js.php @@ -110,6 +110,7 @@ if (!defined('JS_JQUERY_DISABLE_DROPDOWN')) { console.log("toggle dropdown dt a"); //$(this).parent().parent().find(\'dd ul\').slideToggle(\'fast\'); + $(".ulselectedfields").removeClass("open"); $(this).parent().parent().find(\'dd ul\').toggleClass("open"); if ($(this).parent().parent().find(\'dd ul\').hasClass("open")) { diff --git a/htdocs/core/lib/functions.lib.php b/htdocs/core/lib/functions.lib.php index ca7e346b2bd..24e4a332e10 100644 --- a/htdocs/core/lib/functions.lib.php +++ b/htdocs/core/lib/functions.lib.php @@ -6914,24 +6914,27 @@ function dol_string_nohtmltag($stringtoclean, $removelinefeed = 1, $pagecodeto = /** * Clean a string to keep only desirable HTML tags. - * WARNING: This also clean HTML comments (used to obfuscate tag name). + * WARNING: This also clean HTML comments (because they can be used to obfuscate tag name). * * @param string $stringtoclean String to clean * @param int $cleanalsosomestyles Remove absolute/fixed positioning from inline styles * @param int $removeclassattribute 1=Remove the class attribute from tags * @param int $cleanalsojavascript Remove also occurence of 'javascript:'. * @param int $allowiframe Allow iframe tags. + * @param array $allowed_tags List of allowed tags to replace the default list * @return string String cleaned * * @see dol_escape_htmltag() strip_tags() dol_string_nohtmltag() dol_string_neverthesehtmltags() */ -function dol_string_onlythesehtmltags($stringtoclean, $cleanalsosomestyles = 1, $removeclassattribute = 1, $cleanalsojavascript = 0, $allowiframe = 0) +function dol_string_onlythesehtmltags($stringtoclean, $cleanalsosomestyles = 1, $removeclassattribute = 1, $cleanalsojavascript = 0, $allowiframe = 0, $allowed_tags = array()) { - $allowed_tags = array( - "html", "head", "meta", "body", "article", "a", "abbr", "b", "blockquote", "br", "cite", "div", "dl", "dd", "dt", "em", "font", "img", "ins", "hr", "i", "li", "link", - "ol", "p", "q", "s", "section", "span", "strike", "strong", "title", "table", "tr", "th", "td", "u", "ul", "sup", "sub", "blockquote", "pre", "h1", "h2", "h3", "h4", "h5", "h6", - "comment" // this tags is added to manage comment that are replaced into ... - ); + if (empty($allowed_tags)) { + $allowed_tags = array( + "html", "head", "meta", "body", "article", "a", "abbr", "b", "blockquote", "br", "cite", "div", "dl", "dd", "dt", "em", "font", "img", "ins", "hr", "i", "li", "link", + "ol", "p", "q", "s", "section", "span", "strike", "strong", "title", "table", "tr", "th", "td", "u", "ul", "sup", "sub", "blockquote", "pre", "h1", "h2", "h3", "h4", "h5", "h6" + ); + } + $allowed_tags[] = "comment"; // this tags is added to manage comment that are replaced into ... if ($allowiframe) { $allowed_tags[] = "iframe"; } @@ -6949,7 +6952,7 @@ function dol_string_onlythesehtmltags($stringtoclean, $cleanalsosomestyles = 1, $stringtoclean = preg_replace('/:/i', ':', $stringtoclean); $stringtoclean = preg_replace('/:|�+58|:/i', '', $stringtoclean); // refused string ':' encoded (no reason to have a : encoded like this) to disable 'javascript:...' - $temp = strip_tags($stringtoclean, $allowed_tags_string); // Warning: This remove also undesired changing string obfuscated with that pass injection detection into harmfull string + $temp = strip_tags($stringtoclean, $allowed_tags_string); // Warning: This remove also undesired , so may changes string obfuscated with that pass the injection detection into a harmfull string if ($cleanalsosomestyles) { // Clean for remaining html tags $temp = preg_replace('/position\s*:\s*(absolute|fixed)\s*!\s*important/i', '', $temp); // Note: If hacker try to introduce css comment into string to bypass this regex, the string must also be encoded by the dol_htmlentitiesbr during output so it become harmless diff --git a/htdocs/core/modules/modAsset.class.php b/htdocs/core/modules/modAsset.class.php index 3a7caa1cb57..a0fb405887e 100644 --- a/htdocs/core/modules/modAsset.class.php +++ b/htdocs/core/modules/modAsset.class.php @@ -63,7 +63,7 @@ class modAsset extends DolibarrModules $this->descriptionlong = "Asset module to manage assets module and depreciation charge on Dolibarr"; // Possible values for version are: 'development', 'experimental', 'dolibarr', 'dolibarr_deprecated' or a version string like 'x.y.z' - $this->version = 'experimental'; + $this->version = 'development'; // Key used in llx_const table to save module status enabled/disabled (where ASSETS is value of property name of module in uppercase) $this->const_name = 'MAIN_MODULE_'.strtoupper($this->name); // Name of image file used for this module. diff --git a/htdocs/includes/odtphp/odf.php b/htdocs/includes/odtphp/odf.php index 6c63da727b7..1a951292f94 100644 --- a/htdocs/includes/odtphp/odf.php +++ b/htdocs/includes/odtphp/odf.php @@ -796,7 +796,7 @@ IMG; // Export to PDF using LibreOffice - if ($conf->global->MAIN_ODT_AS_PDF == 'libreoffice') { + if (getDolGlobalString('MAIN_ODT_AS_PDF') == 'libreoffice') { dol_mkdir($conf->user->dir_temp); // We must be sure the directory exists and is writable // We delete and recreate a subdir because the soffice may have change pemrissions on it @@ -808,7 +808,7 @@ IMG; // using linux/mac libreoffice that must be in path // Note PHP Config "fastcgi.impersonate=0" must set to 0 - Default is 1 $command ='soffice --headless -env:UserInstallation=file:\''.$conf->user->dir_temp.'/odtaspdf\' --convert-to pdf --outdir '. escapeshellarg(dirname($name)). " ".escapeshellarg($name); - } elseif (preg_match('/unoconv/', $conf->global->MAIN_ODT_AS_PDF)) { + } elseif (preg_match('/unoconv/', getDolGlobalString('MAIN_ODT_AS_PDF'))) { // If issue with unoconv, see https://github.com/dagwieers/unoconv/issues/87 // MAIN_ODT_AS_PDF should be "sudo -u unoconv /usr/bin/unoconv" and userunoconv must have sudo to be root by adding file /etc/sudoers.d/unoconv with content www-data ALL=(unoconv) NOPASSWD: /usr/bin/unoconv . @@ -833,17 +833,17 @@ IMG; // - set shell of user to bash instead of nologin. // - set permission to read/write to user on home directory /var/www so user can create the libreoffice , dconf and .cache dir and files then set permission back - $command = $conf->global->MAIN_ODT_AS_PDF.' '.escapeshellcmd($name); + $command = getDolGlobalString('MAIN_ODT_AS_PDF').' '.escapeshellcmd($name); //$command = '/usr/bin/unoconv -vvv '.escapeshellcmd($name); } else { // deprecated old method using odt2pdf.sh (native, jodconverter, ...) $tmpname=preg_replace('/\.odt/i', '', $name); - if (!empty($conf->global->MAIN_DOL_SCRIPTS_ROOT)) { - $command = $conf->global->MAIN_DOL_SCRIPTS_ROOT.'/scripts/odt2pdf/odt2pdf.sh '.escapeshellcmd($tmpname).' '.(is_numeric($conf->global->MAIN_ODT_AS_PDF)?'jodconverter':$conf->global->MAIN_ODT_AS_PDF); + if (getDolGlobalString('MAIN_DOL_SCRIPTS_ROOT')) { + $command = getDolGlobalString('MAIN_DOL_SCRIPTS_ROOT').'/scripts/odt2pdf/odt2pdf.sh '.escapeshellcmd($tmpname).' '.(is_numeric(getDolGlobalString('MAIN_ODT_AS_PDF'))?'jodconverter':getDolGlobalString('MAIN_ODT_AS_PDF')); } else { dol_syslog(get_class($this).'::exportAsAttachedPDF is used but the constant MAIN_DOL_SCRIPTS_ROOT with path to script directory was not defined.', LOG_WARNING); - $command = '../../scripts/odt2pdf/odt2pdf.sh '.escapeshellcmd($tmpname).' '.(is_numeric($conf->global->MAIN_ODT_AS_PDF)?'jodconverter':$conf->global->MAIN_ODT_AS_PDF); + $command = '../../scripts/odt2pdf/odt2pdf.sh '.escapeshellcmd($tmpname).' '.(is_numeric(getDolGlobalString('MAIN_ODT_AS_PDF'))?'jodconverter':getDolGlobalString('MAIN_ODT_AS_PDF')); } } diff --git a/htdocs/langs/en_US/accountancy.lang b/htdocs/langs/en_US/accountancy.lang index a3ed5f9b7b6..b6bf1b90aef 100644 --- a/htdocs/langs/en_US/accountancy.lang +++ b/htdocs/langs/en_US/accountancy.lang @@ -271,13 +271,13 @@ Reconcilable=Reconcilable TotalVente=Total turnover before tax TotalMarge=Total sales margin -DescVentilCustomer=Consult here the list of customer invoice lines bound (or not) to a product accounting account -DescVentilMore=In most cases, if you use predefined products or services and you set the account number on the product/service card, the application will be able to make all the binding between your invoice lines and the accounting account of your chart of accounts, just in one click with the button "%s". If account was not set on product/service cards or if you still have some lines not bound to an account, you will have to make a manual binding from the menu "%s". -DescVentilDoneCustomer=Consult here the list of the lines of invoices customers and their product accounting account -DescVentilTodoCustomer=Bind invoice lines not already bound with a product accounting account -ChangeAccount=Change the product/service accounting account for selected lines with the following accounting account: +DescVentilCustomer=Consult here the list of customer invoice lines bound (or not) to a product account from chart of account +DescVentilMore=In most cases, if you use predefined products or services and you set the account (from chart of account) on the product/service card, the application will be able to make all the binding between your invoice lines and the accounting account of your chart of accounts, just in one click with the button "%s". If account was not set on product/service cards or if you still have some lines not bound to an account, you will have to make a manual binding from the menu "%s". +DescVentilDoneCustomer=Consult here the list of the lines of invoices customers and their product account from chart of account +DescVentilTodoCustomer=Bind invoice lines not already bound with a product account from chart of account +ChangeAccount=Change the product/service account (from chart of account) for the selected lines with the following account: Vide=- -DescVentilSupplier=Consult here the list of vendor invoice lines bound or not yet bound to a product accounting account (only record not already transfered in accountancy are visible) +DescVentilSupplier=Consult here the list of vendor invoice lines bound or not yet bound to a product account from chart of account (only record not already transfered in accountancy are visible) DescVentilDoneSupplier=Consult here the list of the lines of vendor invoices and their accounting account DescVentilTodoExpenseReport=Bind expense report lines not already bound with a fee accounting account DescVentilExpenseReport=Consult here the list of expense report lines bound (or not) to a fee accounting account @@ -295,14 +295,14 @@ DescValidateMovements=Any modification or deletion of writing, lettering and del ValidateHistory=Bind Automatically AutomaticBindingDone=Automatic bindings done (%s) - Automatic binding not possible for some record (%s) -ErrorAccountancyCodeIsAlreadyUse=Error, you cannot delete this accounting account because it is used +ErrorAccountancyCodeIsAlreadyUse=Error, you cannot remove or disable this account of chart of account because it is used MvtNotCorrectlyBalanced=Movement not correctly balanced. Debit = %s & Credit = %s Balancing=Balancing FicheVentilation=Binding card GeneralLedgerIsWritten=Transactions are written in the Ledger GeneralLedgerSomeRecordWasNotRecorded=Some of the transactions could not be journalized. If there is no other error message, this is probably because they were already journalized. NoNewRecordSaved=No more record to transfer -ListOfProductsWithoutAccountingAccount=List of products not bound to any accounting account +ListOfProductsWithoutAccountingAccount=List of products not bound to any account of chart of account ChangeBinding=Change the binding Accounted=Accounted in ledger NotYetAccounted=Not yet transferred to accounting diff --git a/htdocs/langs/en_US/admin.lang b/htdocs/langs/en_US/admin.lang index f5698ef5f29..28d2c7593fc 100644 --- a/htdocs/langs/en_US/admin.lang +++ b/htdocs/langs/en_US/admin.lang @@ -2225,12 +2225,12 @@ MailToPartnership=Partnership AGENDA_EVENT_DEFAULT_STATUS=Default event status when creating a event from the form YouShouldDisablePHPFunctions=You should disable PHP functions IfCLINotRequiredYouShouldDisablePHPFunctions=Except if you need to run system commands in custom code, you shoud disable PHP functions -PHPFunctionsRequiredForCLI=For shell purpose (like scheduled job backup or running an anitivurs program), you must keep PHP functions +PHPFunctionsRequiredForCLI=For shell purpose (like scheduled job backup or running an antivirus program), you must keep PHP functions NoWritableFilesFoundIntoRootDir=No writable files or directories of the common programs were found into your root directory (Good) RecommendedValueIs=Recommended: %s Recommended=Recommended NotRecommended=Not recommended -ARestrictedPath=Some restricted path +ARestrictedPath=Some restricted path for data files CheckForModuleUpdate=Check for external modules updates CheckForModuleUpdateHelp=This action will connect to editors of external modules to check if a new version is available. ModuleUpdateAvailable=An update is available diff --git a/htdocs/langs/en_US/projects.lang b/htdocs/langs/en_US/projects.lang index 2407d4b2d86..6d2f67ee675 100644 --- a/htdocs/langs/en_US/projects.lang +++ b/htdocs/langs/en_US/projects.lang @@ -125,7 +125,8 @@ ValidateProject=Validate projet ConfirmValidateProject=Are you sure you want to validate this project? CloseAProject=Close project ConfirmCloseAProject=Are you sure you want to close this project? -AlsoCloseAProject=Also close project (keep it open if you still need to follow production tasks on it) +AlsoCloseAProject=Also close project +AlsoCloseAProjectTooltip=Keep it open if you still need to follow production tasks on it ReOpenAProject=Open project ConfirmReOpenAProject=Are you sure you want to re-open this project? ProjectContact=Contacts of project @@ -168,7 +169,7 @@ OpportunityProbability=Lead probability OpportunityProbabilityShort=Lead probab. OpportunityAmount=Lead amount OpportunityAmountShort=Lead amount -OpportunityWeightedAmount=Opportunity weighted amount +OpportunityWeightedAmount=Amount of opportunity, weighted by probability OpportunityWeightedAmountShort=Opp. weighted amount OpportunityAmountAverageShort=Average lead amount OpportunityAmountWeigthedShort=Weighted lead amount diff --git a/htdocs/langs/en_US/ticket.lang b/htdocs/langs/en_US/ticket.lang index 0cde40ba20a..ea560012afc 100644 --- a/htdocs/langs/en_US/ticket.lang +++ b/htdocs/langs/en_US/ticket.lang @@ -27,6 +27,7 @@ Permission56003=Delete tickets Permission56004=Manage tickets Permission56005=See tickets of all third parties (not effective for external users, always be limited to the third party they depend on) +Tickets=Tickets TicketDictType=Ticket - Types TicketDictCategory=Ticket - Groupes TicketDictSeverity=Ticket - Severities diff --git a/htdocs/main.inc.php b/htdocs/main.inc.php index b383a6eb7c2..417295cb116 100644 --- a/htdocs/main.inc.php +++ b/htdocs/main.inc.php @@ -998,7 +998,7 @@ if (!defined('NOLOGIN')) { $hookmanager->initHooks(array('main')); // Code for search criteria persistence. - if (!empty($_GET['save_lastsearch_values'])) { // We must use $_GET here + if (!empty($_GET['save_lastsearch_values']) && !empty($_SERVER["HTTP_REFERER"])) { // We must use $_GET here $relativepathstring = preg_replace('/\?.*$/', '', $_SERVER["HTTP_REFERER"]); $relativepathstring = preg_replace('/^https?:\/\/[^\/]*/', '', $relativepathstring); // Get full path except host server // Clean $relativepathstring diff --git a/htdocs/modulebuilder/template/admin/setup.php b/htdocs/modulebuilder/template/admin/setup.php index 8e60b2a296c..c235dec7e63 100644 --- a/htdocs/modulebuilder/template/admin/setup.php +++ b/htdocs/modulebuilder/template/admin/setup.php @@ -55,6 +55,9 @@ global $langs, $user; // Libraries require_once DOL_DOCUMENT_ROOT."/core/lib/admin.lib.php"; require_once '../lib/mymodule.lib.php'; +require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php'; + + //require_once "../class/myclass.class.php"; // Translations diff --git a/htdocs/modulebuilder/template/class/myobject.class.php b/htdocs/modulebuilder/template/class/myobject.class.php index 2fe7d983513..afd7b4860fb 100644 --- a/htdocs/modulebuilder/template/class/myobject.class.php +++ b/htdocs/modulebuilder/template/class/myobject.class.php @@ -853,7 +853,7 @@ class MyObject extends CommonObject //if ($withpicto != 2) $result.=(($addlabel && $this->label) ? $sep . dol_trunc($this->label, ($addlabel > 1 ? $addlabel : 0)) : ''); global $action, $hookmanager; - $hookmanager->initHooks(array('myobjectdao')); + $hookmanager->initHooks(array($this->element.'dao')); $parameters = array('id'=>$this->id, 'getnomurl' => &$result); $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks if ($reshook > 0) { diff --git a/htdocs/projet/card.php b/htdocs/projet/card.php index 1bdcccbaecf..936633508fa 100644 --- a/htdocs/projet/card.php +++ b/htdocs/projet/card.php @@ -500,11 +500,11 @@ llxHeader("", $title, $help_url); $titleboth = $langs->trans("LeadsOrProjects"); $titlenew = $langs->trans("NewLeadOrProject"); // Leads and opportunities by default -if (empty($conf->global->PROJECT_USE_OPPORTUNITIES)) { +if (!getDolGlobalInt('PROJECT_USE_OPPORTUNITIES')) { $titleboth = $langs->trans("Projects"); $titlenew = $langs->trans("NewProject"); } -if ($conf->global->PROJECT_USE_OPPORTUNITIES == 2) { // 2 = leads only +if (getDolGlobalInt('PROJECT_USE_OPPORTUNITIES') == 2) { // 2 = leads only $titleboth = $langs->trans("Leads"); $titlenew = $langs->trans("NewLead"); } @@ -567,7 +567,7 @@ if ($action == 'create' && $user->rights->projet->creer) { print ''; // Label - print ''.$langs->trans("ProjectLabel").''; + print ''.$langs->trans("Label").''; // Usage (opp, task, bill time, ...) if (!empty($conf->global->PROJECT_USE_OPPORTUNITIES) || empty($conf->global->PROJECT_HIDE_TASKS) || isModEnabled('eventorganization')) { @@ -602,12 +602,46 @@ if ($action == 'create' && $user->rights->projet->creer) { print ' '; $htmltext = $langs->trans("ProjectFollowTasks"); print ''; + print ''; print '
'; } if (empty($conf->global->PROJECT_HIDE_TASKS) && !empty($conf->global->PROJECT_BILL_TIME_SPENT)) { print ' '; $htmltext = $langs->trans("ProjectBillTimeDescription"); print ''; + print ''; print '
'; } if (isModEnabled('eventorganization')) { @@ -706,24 +740,26 @@ if ($action == 'create' && $user->rights->projet->creer) { print ''.$langs->trans("OpportunityStatus").''; print ''; print $formproject->selectOpportunityStatus('opp_status', GETPOSTISSET('opp_status') ? GETPOST('opp_status') : $object->opp_status, 1, 0, 0, 0, '', 0, 1); - print ''; // Opportunity probability - print ''.$langs->trans("OpportunityProbability").''; - print ' %'; + print ' %'; print ''; print ''; print ''; // Opportunity amount print ''.$langs->trans("OpportunityAmount").''; - print ''; + print ''; + print ' '.$langs->getCurrencySymbol($conf->currency); + print ''; print ''; } // Budget - print ''.$langs->trans("Budget").''; - print ''; + print ''.$langs->trans("Budget").''; + print ''; + print ' '.$langs->getCurrencySymbol($conf->currency); + print ''; print ''; // Date project @@ -780,6 +816,7 @@ if ($action == 'create' && $user->rights->projet->creer) { print ''; // Change probability from status or role of project + // Set also dependencies between use taks and bill time print ''; print '
'; @@ -930,12 +972,52 @@ if ($action == 'create' && $user->rights->projet->creer) { print 'usage_task ? ' checked="checked"' : '')) . '"> '; $htmltext = $langs->trans("ProjectFollowTasks"); print ''; + print ''; print '
'; } if (empty($conf->global->PROJECT_HIDE_TASKS) && !empty($conf->global->PROJECT_BILL_TIME_SPENT)) { print 'usage_bill_time ? ' checked="checked"' : '')) . '"> '; $htmltext = $langs->trans("ProjectBillTimeDescription"); print ''; + print ''; print '
'; } if (isModEnabled('eventorganization')) { @@ -944,7 +1026,14 @@ if ($action == 'create' && $user->rights->projet->creer) { print ''; print ''; } @@ -1013,32 +1100,34 @@ if ($action == 'create' && $user->rights->projet->creer) { // Opportunity status print ''.$langs->trans("OpportunityStatus").''; print ''; - print $formproject->selectOpportunityStatus('opp_status', $object->opp_status, 1, 0, 0, 0, 'inline-block valignmiddle', 0, 1); - print ''; - print ''; - print ''; + print '
'; + print $formproject->selectOpportunityStatus('opp_status', $object->opp_status, 1, 0, 0, 0, 'inline-block valignmiddle', 1, 1); // Opportunity probability - print ''.$langs->trans("OpportunityProbability").''; - print ' %'; - print ''; + print ' %'; + print ''; + print '
'; + + print ''; + print ''; print ''; // Opportunity amount print ''.$langs->trans("OpportunityAmount").''; - print ''; + print ''; print $langs->getCurrencySymbol($conf->currency); print ''; print ''; } // Budget - print ''.$langs->trans("Budget").''; - print ''; + print ''.$langs->trans("Budget").''; + print ''; print $langs->getCurrencySymbol($conf->currency); print ''; print ''; @@ -1180,14 +1269,13 @@ if ($action == 'create' && $user->rights->projet->creer) { if ($code) { print $langs->trans("OppStatus".$code); } - print ''; // Opportunity percent - print ''.$langs->trans("OpportunityProbability").''; + print ' / '; if (strcmp($object->opp_percent, '')) { print price($object->opp_percent, 0, $langs, 1, 0).' %'; } - print ''; + print ''; // Opportunity Amount print ''.$langs->trans("OpportunityAmount").''; @@ -1268,6 +1356,25 @@ if ($action == 'create' && $user->rights->projet->creer) { print ''; + // Set also dependencies between use taks and bill time + print ''; + // Change probability from status if (!empty($conf->use_javascript_ajax) && !empty($conf->global->PROJECT_USE_OPPORTUNITIES)) { // Default value to close or not when we set opp to 'WON'. @@ -1308,19 +1415,23 @@ if ($action == 'create' && $user->rights->projet->creer) { } /* Change percent with default percent (defaultpercent) if new status (defaultpercent) is higher than current (jQuery("#opp_percent").val()) */ - console.log("oldpercent="+oldpercent); if (oldpercent != \'\' && (parseFloat(defaultpercent) < parseFloat(oldpercent))) { - if (jQuery("#opp_percent").val() != \'\' && oldpercent != \'\') jQuery("#oldopppercent").text(\' - '.dol_escape_js($langs->transnoentities("PreviousValue")).': \'+oldpercent+\' %\'); - if (parseFloat(oldpercent) != 100) { jQuery("#opp_percent").val(oldpercent); } - else { jQuery("#opp_percent").val(defaultpercent); } + console.log("oldpercent="+oldpercent+" defaultpercent="+defaultpercent+" def < old"); + if (jQuery("#opp_percent").val() != \'\' && oldpercent != \'\') { + jQuery("#oldopppercent").text(\' - '.dol_escape_js($langs->transnoentities("PreviousValue")).': \'+price2numjs(oldpercent)+\' %\'); + } + + if (parseFloat(oldpercent) != 100 && elemcode != \'LOST\') { jQuery("#opp_percent").val(oldpercent); } + else { jQuery("#opp_percent").val(price2numjs(defaultpercent)); } } else { + console.log("oldpercent="+oldpercent+" defaultpercent="+defaultpercent); if ((parseFloat(jQuery("#opp_percent").val()) < parseFloat(defaultpercent))); { - if (jQuery("#opp_percent").val() != \'\' && oldpercent != \'\') jQuery("#oldopppercent").text(\' - '.dol_escape_js($langs->transnoentities("PreviousValue")).': \'+oldpercent+\' %\'); - jQuery("#opp_percent").val(defaultpercent); + if (jQuery("#opp_percent").val() != \'\' && oldpercent != \'\') jQuery("#oldopppercent").text(\' - '.dol_escape_js($langs->transnoentities("PreviousValue")).': \'+price2numjs(oldpercent)+\' %\'); + jQuery("#opp_percent").val(price2numjs(defaultpercent)); } } } diff --git a/htdocs/projet/class/taskstats.class.php b/htdocs/projet/class/taskstats.class.php index 39efd957bb5..c3b3a5b716a 100644 --- a/htdocs/projet/class/taskstats.class.php +++ b/htdocs/projet/class/taskstats.class.php @@ -15,6 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ + include_once DOL_DOCUMENT_ROOT.'/core/class/stats.class.php'; include_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php'; @@ -194,6 +195,7 @@ class TaskStats extends Stats return $res; } + /** * Return the Task amount by month for a year * diff --git a/htdocs/projet/tasks/time.php b/htdocs/projet/tasks/time.php index cb6cf3697c3..94ed0edaffd 100644 --- a/htdocs/projet/tasks/time.php +++ b/htdocs/projet/tasks/time.php @@ -191,7 +191,7 @@ if (GETPOST('button_removefilter_x', 'alpha') || GETPOST('button_removefilter.x' $search_project_ref = ''; $search_project_label = ''; $search_task_label = ''; - $search_user = 0; + $search_user = -1; $search_valuebilled = ''; $search_product_ref = ''; $toselect = array(); @@ -1561,7 +1561,7 @@ if (($id > 0 || !empty($ref)) || $projectidforalltimes > 0 || $allprojectforuser if (empty($search_user)) { $search_user = $user->id; } - $sql .= " AND t.fk_user = ".((int) $search_user); + if ($search_user > 0) $sql .= " AND t.fk_user = ".((int) $search_user); } if ($search_note) { diff --git a/htdocs/takepos/invoice.php b/htdocs/takepos/invoice.php index 6be323a1a7a..ccc1fde97eb 100644 --- a/htdocs/takepos/invoice.php +++ b/htdocs/takepos/invoice.php @@ -1094,7 +1094,7 @@ function Print(id, gift){ function TakeposPrinting(id){ var receipt; console.log("TakeposPrinting" + id); - $.get("receipt.php?facid="+id, function(data, status){ + $.get("receipt.php?facid="+id, function(data, status) { receipt=data.replace(/([^>\r\n]?)(\r\n|\n\r|\r|\n)/g, ''); $.ajax({ type: "POST", diff --git a/htdocs/takepos/receipt.php b/htdocs/takepos/receipt.php index 400d05415aa..39fe2113071 100644 --- a/htdocs/takepos/receipt.php +++ b/htdocs/takepos/receipt.php @@ -85,7 +85,7 @@ $hookmanager->initHooks(array('takeposfrontend'), $facid); $reshook = $hookmanager->executeHooks('TakeposReceipt', $parameters, $object); if (!empty($hookmanager->resPrint)) { print $hookmanager->resPrint; - exit; + return; // Receipt page can be called by the takepos/send.php page that use ob_start/end so we must use return and not exit to stop page } // IMPORTANT: This file is sended to 'Takepos Printing' application. Keep basic file. No external files as css, js... If you need images use absolute path. diff --git a/htdocs/takepos/send.php b/htdocs/takepos/send.php index ffa7e0c5c35..7397c962555 100644 --- a/htdocs/takepos/send.php +++ b/htdocs/takepos/send.php @@ -69,19 +69,19 @@ if ($action == "send") { include_once DOL_DOCUMENT_ROOT.'/core/class/html.formmail.class.php'; $formmail = new FormMail($db); $outputlangs = new Translate('', $conf); - $model_id = $conf->global->TAKEPOS_EMAIL_TEMPLATE_INVOICE; + $model_id = getDolGlobalString('TAKEPOS_EMAIL_TEMPLATE_INVOICE'); $arraydefaultmessage = $formmail->getEMailTemplate($db, 'facture_send', $user, $outputlangs, $model_id); $subject = $arraydefaultmessage->topic; ob_start(); // turn on output receipt - include 'receipt.php'; + include DOL_DOCUMENT_ROOT.'/takepos/receipt.php'; $receipt = ob_get_contents(); // get the contents of the output buffer ob_end_clean(); $msg = "".$arraydefaultmessage->content."
".$receipt.""; $sendto = $email; $from = $mysoc->email; - $mail = new CMailFile($subject, $sendto, $from, $msg, array(), array(), array(), '', '', 0, 1); + $mail = new CMailFile($subject, $sendto, $from, $msg, array(), array(), array(), '', '', 0, 1, '', '', '', '', '', '', DOL_DOCUMENT_ROOT.'/documents/takepos/temp'); if ($mail->error || !empty($mail->errors)) { setEventMessages($mail->error, $mail->errors, 'errors'); } else { diff --git a/htdocs/theme/eldy/badges.inc.php b/htdocs/theme/eldy/badges.inc.php index 1e6dfb54dbc..aae4e50fb29 100644 --- a/htdocs/theme/eldy/badges.inc.php +++ b/htdocs/theme/eldy/badges.inc.php @@ -246,7 +246,7 @@ function _createStatusBadgeCss($statusName, $statusVarNamePrefix = '', $commentL if (in_array((string) $statusName, $TBadgeBorderOnly)) { $thisBadgeTextColor = '#212529'; - $thisBadgeBackgroundColor = "#fff"; + $thisBadgeBackgroundColor = ""; } if (in_array((string) $statusName, array('0', '5', '9'))) { diff --git a/htdocs/theme/eldy/global.inc.php b/htdocs/theme/eldy/global.inc.php index 6102e8584e8..73e5ed5d666 100644 --- a/htdocs/theme/eldy/global.inc.php +++ b/htdocs/theme/eldy/global.inc.php @@ -53,6 +53,7 @@ --productlinestockod: #002200; --productlinestocktoolow: #884400; --infoboxmoduleenabledbgcolor : linear-gradient(0.4turn, #fff, #fff, #fff, #e4efe8); + --tablevalidbgcolor: rgb(252, 248, 227); } global->THEME_DARKMODEENABLED)) { --amountpaymentcomplete:rgb(101,184,77); --amountremaintopaybackcolor:rbg(245,130,46); --infoboxmoduleenabledbgcolor : linear-gradient(0.4turn, #000, #000, #000, #274231); + --tablevalidbgcolor: rgb(80, 64, 33); } body, button { @@ -5001,7 +5003,7 @@ table.valid { padding-right: 4px; padding-bottom: 4px; margin: 0px 0px; - background: #fcf8e3; + background: var(--tablevalidbgcolor); } .validtitre { diff --git a/htdocs/ticket/class/ticket.class.php b/htdocs/ticket/class/ticket.class.php index 2aacffefcd1..6763ce02e84 100644 --- a/htdocs/ticket/class/ticket.class.php +++ b/htdocs/ticket/class/ticket.class.php @@ -236,7 +236,7 @@ class Ticket extends CommonObject const STATUS_READ = 1; const STATUS_ASSIGNED = 2; const STATUS_IN_PROGRESS = 3; - const STATUS_NEED_MORE_INFO = 5; + const STATUS_NEED_MORE_INFO = 5; // waiting requester feedback const STATUS_WAITING = 7; // on hold const STATUS_CLOSED = 8; // Closed - Solved const STATUS_CANCELED = 9; // Closed - Not solved @@ -2667,7 +2667,10 @@ class Ticket extends CommonObject } // Set status to "answered" if not set yet, but only if internal user and not private message - if ($object->status < 3 && !$user->socid && !$private) { + // Or set status to "answered" if the client has answered and if the ticket has started + if (($object->status < self::STATUS_IN_PROGRESS && !$user->socid && !$private) || + ($object->status > self::STATUS_IN_PROGRESS && $public_area) + ) { $object->setStatut(3); } return 1; diff --git a/htdocs/user/hierarchy.php b/htdocs/user/hierarchy.php index 44ab46193d9..ce18b9d5011 100644 --- a/htdocs/user/hierarchy.php +++ b/htdocs/user/hierarchy.php @@ -42,6 +42,9 @@ if ($user->socid > 0) { $optioncss = GETPOST('optioncss', 'alpha'); $contextpage = GETPOST('contextpage', 'aZ') ? GETPOST('contextpage', 'aZ') : 'userlist'; // To manage different context of search $mode = GETPOST("mode", 'alpha'); +if (empty($mode)) { + $mode = 'hierarchy'; +} $sortfield = GETPOST('sortfield', 'aZ09comma'); $sortorder = GETPOST('sortorder', 'aZ09comma'); diff --git a/test/phpunit/AdminLibTest.php b/test/phpunit/AdminLibTest.php index 6649aa19798..4df1a6933e1 100644 --- a/test/phpunit/AdminLibTest.php +++ b/test/phpunit/AdminLibTest.php @@ -46,6 +46,7 @@ $conf->global->MAIN_DISABLE_ALL_MAILS=1; */ class AdminLibTest extends PHPUnit\Framework\TestCase { + protected $backupGlobalsBlacklist = array('conf', 'user', 'langs', 'db'); protected $savconf; protected $savuser; protected $savlangs; @@ -69,6 +70,7 @@ class AdminLibTest extends PHPUnit\Framework\TestCase $this->savdb=$db; print __METHOD__." db->type=".$db->type." user->id=".$user->id; + //print " - db ".$db->db; print "\n"; } diff --git a/test/phpunit/phpunittest.xml b/test/phpunit/phpunittest.xml index 84b31a8dd3b..8157d3f3e7a 100644 --- a/test/phpunit/phpunittest.xml +++ b/test/phpunit/phpunittest.xml @@ -1,8 +1,8 @@