Merge branch '20.0' of git@github.com:Dolibarr/dolibarr.git into develop

This commit is contained in:
Laurent Destailleur 2024-08-31 12:14:06 +02:00
commit 650ecc23bd
5 changed files with 96 additions and 34 deletions

View file

@ -110,15 +110,15 @@ class modVariants extends DolibarrModules
$this->rights = array(); // Permission array used by this module
$r = 0;
$this->rights[$r][0] = $this->numero + $r; // Permission id (must not be already used)
$this->rights[$r][0] = $this->numero + 1; // Permission id (must not be already used)
$this->rights[$r][1] = 'Read attributes of variants'; // Permission label
$this->rights[$r][4] = 'read'; // In php code, permission will be checked by test if ($user->rights->eventorganization->level1)
$r++;
$this->rights[$r][0] = $this->numero + $r; // Permission id (must not be already used)
$this->rights[$r][0] = $this->numero + 2; // Permission id (must not be already used)
$this->rights[$r][1] = 'Create/Update attributes of variants'; // Permission label
$this->rights[$r][4] = 'write'; // In php code, permission will be checked by test if ($user->rights->eventorganization->level1)
$r++;
$this->rights[$r][0] = $this->numero + $r; // Permission id (must not be already used)
$this->rights[$r][0] = $this->numero + 3; // Permission id (must not be already used)
$this->rights[$r][1] = 'Delete attributes of variants'; // Permission label
$this->rights[$r][4] = 'delete'; // In php code, permission will be checked by test if ($user->rights->eventorganization->level1)
$r++;

View file

@ -93,6 +93,7 @@ dolibarr_install_syslog("--- step2: entering step2.php page");
'@phan-var-force string $dolibarr_main_db_prefix'; // From configuraiotn file or install/inc.php
/*
* View
*/

View file

@ -4198,7 +4198,7 @@ function migrate_delete_old_dir($db, $langs, $conf)
* @param DoliDB $db Database handler
* @param Translate $langs Object langs
* @param Conf $conf Object conf
* @param array $listofmodule List of modules, like array('MODULE_KEY_NAME'=>', $reloadmode)
* @param array $listofmodule List of modules, like array('MODULE_KEY_NAME'=>$reloadmode, ...)
* @param int $force 1=Reload module even if not already loaded
* @return int Return integer <0 if KO, >0 if OK
*/
@ -4244,6 +4244,7 @@ function migrate_reload_modules($db, $langs, $conf, $listofmodule = array(), $fo
'MAIN_MODULE_OPENSURVEY' => array('class' => 'modOpenSurvey', 'remove' => 1),
'MAIN_MODULE_PRODUCTBATCH' => array('class' => 'modProductBatch', 'remove' => 1),
'MAIN_MODULE_TAKEPOS' => array('class' => 'modTakePos', 'remove' => 1),
'MAIN_MODULE_VARIANTS' => array('class' => 'modVariants', 'remove' => 1),
'MAIN_MODULE_EMAILCOLLECTOR' => array('class' => 'modEmailCollector', 'remove' => 1),
);

View file

@ -1222,6 +1222,49 @@ class ProductAttribute extends CommonObject
return $result;
}
/**
* Return a thumb for kanban views
*
* @param string $option Where point the link (0=> main card, 1,2 => shipment, 'nolink'=>No link)
* @param ?array<string,string> $arraydata Array of data
* @return string HTML Code for Kanban thumb.
*/
public function getKanbanView($option = '', $arraydata = null)
{
global $conf, $langs;
$selected = (empty($arraydata['selected']) ? 0 : $arraydata['selected']);
$return = '<div class="box-flex-item box-flex-grow-zero">';
$return .= '<div class="info-box info-box-sm">';
$return .= '<span class="info-box-icon bg-infobox-action">';
$return .= img_picto('', $this->picto);
$return .= '</span>';
$return .= '<div class="info-box-content">';
$return .= '<span class="info-box-ref inline-block tdoverflowmax150 valignmiddle">'.(method_exists($this, 'getNomUrl') ? $this->getNomUrl() : $this->ref).'</span>';
if ($selected >= 0) {
$return .= '<input id="cb'.$this->id.'" class="flat checkforselect fright" type="checkbox" name="toselect[]" value="'.$this->id.'"'.($selected ? ' checked="checked"' : '').'>';
}
if (property_exists($this, 'label')) {
$return .= ' <div class="inline-block opacitymedium valignmiddle tdoverflowmax100">'.$this->label.'</div>';
}
if (property_exists($this, 'thirdparty') && is_object($this->thirdparty)) {
$return .= '<br><div class="info-box-ref tdoverflowmax150">'.$this->thirdparty->getNomUrl(1).'</div>';
}
if (property_exists($this, 'amount')) {
$return .= '<br>';
$return .= '<span class="info-box-label amount">'.price($this->amount, 0, $langs, 1, -1, -1, $conf->currency).'</span>';
}
if (method_exists($this, 'getLibStatut')) {
$return .= '<br><div class="info-box-status">'.$this->getLibStatut(3).'</div>';
}
$return .= '</div>';
$return .= '</div>';
$return .= '</div>';
return $return;
}
/**
* Return the label of the status
*

View file

@ -34,14 +34,14 @@ $langs->loadLangs(array("products", "other"));
$action = GETPOST('action', 'aZ09') ? GETPOST('action', 'aZ09') : 'view'; // The action 'create'/'add', 'edit'/'update', 'view', ...
$massaction = GETPOST('massaction', 'alpha'); // The bulk action (combo box choice into lists)
$show_files = GETPOSTINT('show_files'); // Show files area generated by bulk actions ?
$show_files = GETPOSTINT('show_files'); // Show files area generated by bulk actions
$confirm = GETPOST('confirm', 'alpha'); // Result of a confirmation
$cancel = GETPOST('cancel', 'alpha'); // We click on a Cancel button
$toselect = GETPOST('toselect', 'array'); // Array of ids of elements selected into a list
$toselect = GETPOST('toselect', 'array:int'); // Array of ids of elements selected into a list
$contextpage = GETPOST('contextpage', 'aZ') ? GETPOST('contextpage', 'aZ') : str_replace('_', '', basename(dirname(__FILE__)).basename(__FILE__, '.php')); // To manage different context of search
$backtopage = GETPOST('backtopage', 'alpha'); // Go back to a dedicated page
$optioncss = GETPOST('optioncss', 'aZ'); // Option for the css output (always '' except when 'print')
$mode = GETPOST('mode', 'aZ'); // The output mode ('list', 'kanban', 'hierarchy', 'calendar', ...)
$mode = GETPOST('mode', 'aZ'); // The display mode ('list', 'kanban', 'hierarchy', 'calendar', 'gantt', ...)
$id = GETPOSTINT('id');
@ -49,7 +49,7 @@ $id = GETPOSTINT('id');
$limit = GETPOSTINT('limit') ? GETPOSTINT('limit') : $conf->liste_limit;
$sortfield = GETPOST('sortfield', 'aZ09comma');
$sortorder = GETPOST('sortorder', 'aZ09comma');
$page = GETPOSTISSET('pageplusone') ? (GETPOSTINT('pageplusone') - 1) : GETPOSTINT("page");
$page = GETPOSTISSET('pageplusone') ? (GETPOSTINT('pageplusone') - 1) : GETPOSTINT('page');
if (empty($page) || $page < 0 || GETPOST('button_search', 'alpha') || GETPOST('button_removefilter', 'alpha')) {
// If $page is not defined, or '' or -1 or if we click on clear filters
$page = 0;
@ -78,7 +78,7 @@ if (!$sortorder) {
}
// Initialize array of search criteria
$search_all = GETPOST('search_all', 'alphanohtml');
$search_all = trim(GETPOST('search_all', 'alphanohtml'));
$search = array();
foreach ($object->fields as $key => $val) {
if (GETPOST('search_'.$key, 'alpha') !== '') {
@ -92,8 +92,8 @@ foreach ($object->fields as $key => $val) {
$search['nb_of_values'] = GETPOST('search_nb_of_values', 'alpha');
$search['nb_products'] = GETPOST('search_nb_products', 'alpha');
// List of fields to search into when doing a "search in all"
$fieldstosearchall = array();
// List of fields to search into when doing a "search in all"
foreach ($object->fields as $key => $val) {
if (!empty($val['searchall'])) {
$fieldstosearchall['t.'.$key] = $val['label'];
@ -231,8 +231,8 @@ $form = new Form($db);
$now = dol_now();
$help_url = '';
$title = $langs->trans("ProductAttributes");
$help_url = '';
$morejs = array();
$morecss = array();
@ -252,6 +252,9 @@ $parameters = array();
$reshook = $hookmanager->executeHooks('printFieldListSelect', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
$sql .= $hookmanager->resPrint;
$sql = preg_replace('/,\s*$/', '', $sql);
$sqlfields = $sql; // $sql fields to remove for count total
$sql .= " FROM ".MAIN_DB_PREFIX.$object->table_element." as t";
//if (isset($extrafields->attributes[$object->table_element]['label']) && is_array($extrafields->attributes[$object->table_element]['label']) && count($extrafields->attributes[$object->table_element]['label'])) {
// $sql .= " LEFT JOIN ".MAIN_DB_PREFIX.$object->table_element."_extrafields as ef on (t.rowid = ef.fk_object)";
@ -263,7 +266,7 @@ $parameters = array();
$reshook = $hookmanager->executeHooks('printFieldListFrom', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
$sql .= $hookmanager->resPrint;
if ($object->ismultientitymanaged == 1) {
$sql .= " WHERE t.entity IN (".getEntity($object->element).")";
$sql .= " WHERE t.entity IN (".getEntity($object->element, (GETPOST('search_current_entity', 'int') ? 0 : 1)).")";
} else {
$sql .= " WHERE 1 = 1";
}
@ -280,17 +283,17 @@ foreach ($search as $key => $val) {
$mode_search = 2;
}
if ($search[$key] != '') {
$sql .= natural_search("t.".$db->escape($key), $search[$key], (($key == 'status') ? 2 : $mode_search));
$sql .= natural_search("t.".$db->sanitize($key), $search[$key], (($key == 'status') ? 2 : $mode_search));
}
} else {
if (preg_match('/(_dtstart|_dtend)$/', $key) && $search[$key] != '') {
$columnName = preg_replace('/(_dtstart|_dtend)$/', '', $key);
if (preg_match('/^(date|timestamp|datetime)/', $object->fields[$columnName]['type'])) {
if (preg_match('/_dtstart$/', $key)) {
$sql .= " AND t.".$db->escape($columnName)." >= '".$db->idate($search[$key])."'";
$sql .= " AND t.".$db->sanitize($columnName)." >= '".$db->idate($search[$key])."'";
}
if (preg_match('/_dtend$/', $key)) {
$sql .= " AND t.".$db->escape($columnName)." <= '".$db->idate($search[$key])."'";
$sql .= " AND t.".$db->sanitize($columnName)." <= '".$db->idate($search[$key])."'";
}
}
}
@ -310,7 +313,7 @@ $sql .= $hookmanager->resPrint;
$hasgroupby = true;
$sql .= " GROUP BY ";
foreach ($object->fields as $key => $val) {
$sql .= "t." . $key . ", ";
$sql .= "t.".$db->sanitize($key).", ";
}
// Add fields from extrafields
//if (!empty($extrafields->attributes[$object->table_element]['label'])) {
@ -320,7 +323,7 @@ foreach ($object->fields as $key => $val) {
//}
// Add where from hooks
$parameters = array();
$reshook = $hookmanager->executeHooks('printFieldListGroupBy', $parameters, $object); // Note that $action and $object may have been modified by hook
$reshook = $hookmanager->executeHooks('printFieldListGroupBy', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
$sql .= $hookmanager->resPrint;
$sql = preg_replace("/,\s*$/", "", $sql);
@ -333,7 +336,7 @@ if ($search['nb_products'] != '') {
}
// Add HAVING from hooks
$parameters = array();
$reshook = $hookmanager->executeHooks('printFieldListHaving', $parameters, $object); // Note that $action and $object may have been modified by hook
$reshook = $hookmanager->executeHooks('printFieldListHaving', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
$sql .= empty($hookmanager->resPrint) ? "" : " ".$hookmanager->resPrint;
// Count total nb of records
@ -349,7 +352,9 @@ if (!getDolGlobalInt('MAIN_DISABLE_FULL_SCANLIST')) {
$nbtotalofrecords++;
}*/
/* The fast and low memory method to get and count full list converts the sql into a sql count */
$sqlforcount = preg_replace("/^SELECT[a-z0-9\._\s\(\),]+FROM/i", "SELECT COUNT(*) as nbtotalofrecords FROM", $sql);
$sqlforcount = preg_replace('/^'.preg_quote($sqlfields, '/').'/', 'SELECT COUNT(*) as nbtotalofrecords', $sql);
$sqlforcount = preg_replace('/GROUP BY .*$/', '', $sqlforcount);
$resql = $db->query($sqlforcount);
if ($resql) {
if ($hasgroupby) {
@ -382,10 +387,10 @@ $num = $db->num_rows($resql);
// Direct jump if only one record found
if ($num == 1 && getDolGlobalString('MAIN_SEARCH_DIRECT_OPEN_IF_ONLY_ONE') && $search_all && !$page) {
if ($num == 1 && getDolGlobalInt('MAIN_SEARCH_DIRECT_OPEN_IF_ONLY_ONE') && $search_all && !$page) {
$obj = $db->fetch_object($resql);
$id = $obj->rowid;
header("Location: " . dol_buildpath('/variants/card.php', 2) . '?id=' . $id);
header("Location: " . dol_buildpath('/variants/card.php', 2) . '?id='.((int) $id));
exit;
}
@ -407,6 +412,12 @@ if (!empty($contextpage) && $contextpage != $_SERVER["PHP_SELF"]) {
if ($limit > 0 && $limit != $conf->liste_limit) {
$param .= '&limit='.((int) $limit);
}
if ($optioncss != '') {
$param .= '&optioncss='.urlencode($optioncss);
}
if ($groupby != '') {
$param .= '&groupby='.urlencode($groupby);
}
foreach ($search as $key => $val) {
if (is_array($search[$key])) {
foreach ($search[$key] as $skey) {
@ -422,9 +433,6 @@ foreach ($search as $key => $val) {
$param .= '&search_'.$key.'='.urlencode($search[$key]);
}
}
if ($optioncss != '') {
$param .= '&optioncss='.urlencode($optioncss);
}
// Add $param from extra fields
include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_search_param.tpl.php';
// Add $param from hooks
@ -528,7 +536,7 @@ if (getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) {
print '</td>';
}
foreach ($object->fields as $key => $val) {
$searchkey = empty($search[$key]) ? '' : $search[$key];
//$searchkey = empty($search[$key]) ? '' : $search[$key];
$cssforfield = (empty($val['csslist']) ? (empty($val['css']) ? '' : $val['css']) : $val['csslist']);
if ($key == 'status') {
$cssforfield .= ($cssforfield ? ' ' : '').'center';
@ -542,7 +550,7 @@ foreach ($object->fields as $key => $val) {
if (!empty($arrayfields['t.'.$key]['checked'])) {
print '<td class="liste_titre'.($cssforfield ? ' '.$cssforfield : '').($key == 'status' ? ' parentonrightofpage' : '').'">';
if (!empty($val['arrayofkeyval']) && is_array($val['arrayofkeyval'])) {
print $form->selectarray('search_'.$key, $val['arrayofkeyval'], (isset($search[$key]) ? $search[$key] : ''), $val['notnull'], 0, 0, '', 1, 0, 0, '', 'maxwidth100'.($key == 'status' ? ' search_status width100 onrightofpage' : ''), 1);
print $form->selectarray('search_'.$key, $val['arrayofkeyval'], (isset($search[$key]) ? $search[$key] : ''), 1, 0, 0, '', 1, 0, 0, '', 'maxwidth100'.($key == 'status' ? ' search_status width100 onrightofpage' : ''), 1);
} elseif ((strpos($val['type'], 'integer:') === 0) || (strpos($val['type'], 'sellist:') === 0)) {
print $object->showInputField($val, $key, (isset($search[$key]) ? $search[$key] : ''), '', '', 'search_', $cssforfield.' maxwidth250', 1);
} elseif (preg_match('/^(date|timestamp|datetime)/', $val['type'])) {
@ -555,7 +563,7 @@ foreach ($object->fields as $key => $val) {
} elseif ($key == 'lang') {
require_once DOL_DOCUMENT_ROOT.'/core/class/html.formadmin.class.php';
$formadmin = new FormAdmin($db);
print $formadmin->select_language($search[$key], 'search_lang', 0, null, 1, 0, 0, 'minwidth100imp maxwidth125', 2);
print $formadmin->select_language((isset($search[$key]) ? $search[$key] : ''), 'search_lang', 0, null, 1, 0, 0, 'minwidth100imp maxwidth125', 2);
} else {
print '<input type="text" class="flat maxwidth75" name="search_'.$key.'" value="'.dol_escape_htmltag(isset($search[$key]) ? $search[$key] : '').'">';
}
@ -598,6 +606,7 @@ $totalarray['nbfield'] = 0;
// Fields title label
// --------------------------------------------------------------------
print '<tr class="liste_titre">';
// Action column
if (getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) {
print getTitleFieldOfList($selectedfields, 0, $_SERVER["PHP_SELF"], '', '', '', '', $sortfield, $sortorder, 'center maxwidthsearch ')."\n";
$totalarray['nbfield']++;
@ -629,10 +638,12 @@ print $hookmanager->resPrint;
$key = 'nb_of_values';
if (!empty($arrayfields[$key]['checked'])) {
print getTitleFieldOfList($arrayfields[$key]['label'], 0, $_SERVER['PHP_SELF'], $key, '', $param, 'class="center"', $sortfield, $sortorder, 'center ')."\n";
$totalarray['nbfield']++;
}
$key = 'nb_products';
if (!empty($arrayfields[$key]['checked'])) {
print getTitleFieldOfList($arrayfields[$key]['label'], 0, $_SERVER['PHP_SELF'], $key, '', $param, 'class="center"', $sortfield, $sortorder, 'center ')."\n";
$totalarray['nbfield']++;
}
// Action column
if (!getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) {
@ -641,6 +652,7 @@ if (!getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) {
}
// Move
print getTitleFieldOfList('', 0, '', '', '', '', '', '', '', 'linecolmove ')."\n";
$totalarray['nbfield']++;
print '</tr>'."\n";
@ -690,7 +702,7 @@ while ($i < $imaxinloop) {
print '</td></tr>';
}
} else {
// Show here line of result
// Show line of result
$j = 0;
print '<tr data-rowid="'.$object->id.'" class="oddeven">';
// Action column
@ -708,6 +720,7 @@ while ($i < $imaxinloop) {
$totalarray['nbfield']++;
}
}
// Fields
foreach ($object->fields as $key => $val) {
$cssforfield = (empty($val['csslist']) ? (empty($val['css']) ? '' : $val['css']) : $val['csslist']);
if (in_array($val['type'], array('date', 'datetime', 'timestamp'))) {
@ -717,19 +730,19 @@ while ($i < $imaxinloop) {
}
if (in_array($val['type'], array('timestamp'))) {
$cssforfield .= ($cssforfield ? ' ' : '') . 'nowrap';
$cssforfield .= ($cssforfield ? ' ' : '') . 'nowraponall';
} elseif ($key == 'ref') {
$cssforfield .= ($cssforfield ? ' ' : '') . 'nowrap';
$cssforfield .= ($cssforfield ? ' ' : '') . 'nowraponall';
}
if (in_array($val['type'], array('double(24,8)', 'double(6,3)', 'integer', 'real', 'price')) && !in_array($key, array('rowid', 'status')) && empty($val['arrayofkeyval'])) {
if (in_array($val['type'], array('double(24,8)', 'double(6,3)', 'integer', 'real', 'price')) && !in_array($key, array('id', 'rowid', 'ref', 'status')) && empty($val['arrayofkeyval'])) {
$cssforfield .= ($cssforfield ? ' ' : '') . 'right';
}
//if (in_array($key, array('fk_soc', 'fk_user', 'fk_warehouse'))) $cssforfield = 'tdoverflowmax100';
if (!empty($arrayfields['t.' . $key]['checked'])) {
print '<td'.($cssforfield ? ' class="'.$cssforfield.(preg_match('/tdoverflow/', $cssforfield) ? ' classfortooltip' : '').'"' : '');
if (preg_match('/tdoverflow/', $cssforfield) && !is_numeric($object->$key)) {
print '<td'.($cssforfield ? ' class="'.$cssforfield.((preg_match('/tdoverflow/', $cssforfield) && !in_array($val['type'], array('ip', 'url')) && !is_numeric($object->$key)) ? ' classfortooltip' : '').'"' : '');
if (preg_match('/tdoverflow/', $cssforfield) && !in_array($val['type'], array('ip', 'url')) && !is_numeric($object->$key)) {
print ' title="'.dol_escape_htmltag($object->$key).'"';
}
print '>';
@ -807,6 +820,9 @@ while ($i < $imaxinloop) {
print '<a class="lineupdown" href="' . $_SERVER['PHP_SELF'] . '?action=down&amp;rowid=' . $obj->rowid . '">' . img_down('default', 0, 'imgdownforline') . '</a>';
}
print '</td>';
if (!$i) {
$totalarray['nbfield']++;
}
print '</tr>' . "\n";
}
@ -819,12 +835,13 @@ include DOL_DOCUMENT_ROOT.'/core/tpl/list_print_total.tpl.php';
// If no record found
if ($num == 0) {
$colspan = 3;
$colspan = 1;
foreach ($arrayfields as $key => $val) {
if (!empty($val['checked'])) {
$colspan++;
}
}
$colspan++; // For the move column
print '<tr><td colspan="'.$colspan.'"><span class="opacitymedium">'.$langs->trans("NoRecordFound").'</span></td></tr>';
}