NEW Dolibarr#22848 Add order tags

This commit is contained in:
Florent Poinsaut
2025-02-14 14:29:49 +01:00
parent d7205f3404
commit bfed762fe9
7 changed files with 365 additions and 90 deletions

View File

@@ -49,20 +49,21 @@ class Categories extends DolibarrApi
* @var array<int,string> Code mapping from ID
*/
public static $TYPES = array(
0 => 'product',
1 => 'supplier',
2 => 'customer',
3 => 'member',
4 => 'contact',
5 => 'account',
6 => 'project',
7 => 'user',
8 => 'bank_line',
9 => 'warehouse',
0 => 'product',
1 => 'supplier',
2 => 'customer',
3 => 'member',
4 => 'contact',
5 => 'account',
6 => 'project',
7 => 'user',
8 => 'bank_line',
9 => 'warehouse',
10 => 'actioncomm',
11 => 'website_page',
12 => 'ticket',
13 => 'knowledgemanagement'
13 => 'knowledgemanagement',
14 => 'order',
);
/**

View File

@@ -48,21 +48,22 @@ require_once DOL_DOCUMENT_ROOT.'/knowledgemanagement/class/knowledgerecord.class
class Categorie extends CommonObject
{
// Categories types (we use string because we want to accept any modules/types in a future)
const TYPE_PRODUCT = 'product';
const TYPE_SUPPLIER = 'supplier';
const TYPE_CUSTOMER = 'customer';
const TYPE_MEMBER = 'member';
const TYPE_CONTACT = 'contact';
const TYPE_USER = 'user';
const TYPE_PROJECT = 'project';
const TYPE_ACCOUNT = 'bank_account';
const TYPE_BANK_LINE = 'bank_line';
const TYPE_WAREHOUSE = 'warehouse';
const TYPE_ACTIONCOMM = 'actioncomm';
const TYPE_WEBSITE_PAGE = 'website_page';
const TYPE_TICKET = 'ticket';
const TYPE_PRODUCT = 'product';
const TYPE_SUPPLIER = 'supplier';
const TYPE_CUSTOMER = 'customer';
const TYPE_MEMBER = 'member';
const TYPE_CONTACT = 'contact';
const TYPE_USER = 'user';
const TYPE_PROJECT = 'project';
const TYPE_ACCOUNT = 'bank_account';
const TYPE_BANK_LINE = 'bank_line';
const TYPE_WAREHOUSE = 'warehouse';
const TYPE_ACTIONCOMM = 'actioncomm';
const TYPE_WEBSITE_PAGE = 'website_page';
const TYPE_TICKET = 'ticket';
const TYPE_KNOWLEDGEMANAGEMENT = 'knowledgemanagement';
const TYPE_FICHINTER = 'fichinter';
const TYPE_FICHINTER = 'fichinter';
const TYPE_ORDER = 'order';
/**
* @var string String with name of icon for myobject. Must be the part after the 'object_' into object_myobject.png
@@ -74,21 +75,22 @@ class Categorie extends CommonObject
* @var array<string,int> Table of mapping between type string and ID used for field 'type' in table llx_categories
*/
protected $MAP_ID = array(
'product' => 0,
'supplier' => 1,
'customer' => 2,
'member' => 3,
'contact' => 4,
'bank_account' => 5,
'project' => 6,
'user' => 7,
'bank_line' => 8,
'warehouse' => 9,
'actioncomm' => 10,
'website_page' => 11,
'ticket' => 12,
'product' => 0,
'supplier' => 1,
'customer' => 2,
'member' => 3,
'contact' => 4,
'bank_account' => 5,
'project' => 6,
'user' => 7,
'bank_line' => 8,
'warehouse' => 9,
'actioncomm' => 10,
'website_page' => 11,
'ticket' => 12,
'knowledgemanagement' => 13,
'fichinter' => 14,
'fichinter' => 14,
'order' => 14,
);
/**
@@ -97,21 +99,22 @@ class Categorie extends CommonObject
* @note This array should be removed in future, once previous constants are moved to the string value. Deprecated
*/
public static $MAP_ID_TO_CODE = array(
0 => 'product',
1 => 'supplier',
2 => 'customer',
3 => 'member',
4 => 'contact',
5 => 'bank_account',
6 => 'project',
7 => 'user',
8 => 'bank_line',
9 => 'warehouse',
0 => 'product',
1 => 'supplier',
2 => 'customer',
3 => 'member',
4 => 'contact',
5 => 'bank_account',
6 => 'project',
7 => 'user',
8 => 'bank_line',
9 => 'warehouse',
10 => 'actioncomm',
11 => 'website_page',
12 => 'ticket',
13 => 'knowledgemanagement',
14 => 'fichinter',
14 => 'order',
);
/**
@@ -120,9 +123,9 @@ class Categorie extends CommonObject
* @todo Move to const array when PHP 5.6 will be our minimum target
*/
public $MAP_CAT_FK = array(
'customer' => 'soc',
'supplier' => 'soc',
'contact' => 'socpeople',
'customer' => 'soc',
'supplier' => 'soc',
'contact' => 'socpeople',
'bank_account' => 'account',
);
@@ -132,8 +135,8 @@ class Categorie extends CommonObject
* @note Move to const array when PHP 5.6 will be our minimum target
*/
public $MAP_CAT_TABLE = array(
'customer' => 'societe',
'supplier' => 'fournisseur',
'customer' => 'societe',
'supplier' => 'fournisseur',
'bank_account' => 'account',
);
@@ -143,21 +146,22 @@ class Categorie extends CommonObject
* @note Move to const array when PHP 5.6 will be our minimum target
*/
public $MAP_OBJ_CLASS = array(
'product' => 'Product',
'customer' => 'Societe',
'supplier' => 'Fournisseur',
'member' => 'Adherent',
'contact' => 'Contact',
'user' => 'User',
'account' => 'Account', // old for bank account
'bank_account' => 'Account',
'project' => 'Project',
'warehouse' => 'Entrepot',
'actioncomm' => 'ActionComm',
'website_page' => 'WebsitePage',
'ticket' => 'Ticket',
'product' => 'Product',
'customer' => 'Societe',
'supplier' => 'Fournisseur',
'member' => 'Adherent',
'contact' => 'Contact',
'user' => 'User',
'account' => 'Account', // old for bank account
'bank_account' => 'Account',
'project' => 'Project',
'warehouse' => 'Entrepot',
'actioncomm' => 'ActionComm',
'website_page' => 'WebsitePage',
'ticket' => 'Ticket',
'knowledgemanagement' => 'KnowledgeRecord',
'fichinter' => 'Fichinter',
'fichinter' => 'Fichinter',
'order' => 'Commande',
);
/**
@@ -166,21 +170,22 @@ class Categorie extends CommonObject
* @note Move to const array when PHP 5.6 will be our minimum target
*/
public static $MAP_TYPE_TITLE_AREA = array(
'product' => 'ProductsCategoriesArea',
'customer' => 'CustomersCategoriesArea',
'supplier' => 'SuppliersCategoriesArea',
'member' => 'MembersCategoriesArea',
'contact' => 'ContactsCategoriesArea',
'user' => 'UsersCategoriesArea',
'account' => 'AccountsCategoriesArea', // old for bank account
'bank_account' => 'AccountsCategoriesArea',
'project' => 'ProjectsCategoriesArea',
'warehouse' => 'StocksCategoriesArea',
'actioncomm' => 'ActioncommCategoriesArea',
'website_page' => 'WebsitePagesCategoriesArea',
'ticket' => 'TicketsCategoriesArea',
'product' => 'ProductsCategoriesArea',
'customer' => 'CustomersCategoriesArea',
'supplier' => 'SuppliersCategoriesArea',
'member' => 'MembersCategoriesArea',
'contact' => 'ContactsCategoriesArea',
'user' => 'UsersCategoriesArea',
'account' => 'AccountsCategoriesArea', // old for bank account
'bank_account' => 'AccountsCategoriesArea',
'project' => 'ProjectsCategoriesArea',
'warehouse' => 'StocksCategoriesArea',
'actioncomm' => 'ActioncommCategoriesArea',
'website_page' => 'WebsitePagesCategoriesArea',
'ticket' => 'TicketsCategoriesArea',
'knowledgemanagement' => 'KnowledgemanagementsCategoriesArea',
'fichinter' => 'FichintersCategoriesArea',
'fichinter' => 'FichintersCategoriesArea',
'order' => 'OrderCategoriesArea',
);
/**
@@ -188,15 +193,16 @@ class Categorie extends CommonObject
* This array may be completed by external modules with hook "constructCategory"
*/
public $MAP_OBJ_TABLE = array(
'customer' => 'societe',
'supplier' => 'societe',
'member' => 'adherent',
'contact' => 'socpeople',
'account' => 'bank_account', // old for bank account
'project' => 'projet',
'warehouse' => 'entrepot',
'customer' => 'societe',
'supplier' => 'societe',
'member' => 'adherent',
'contact' => 'socpeople',
'account' => 'bank_account', // old for bank account
'project' => 'projet',
'warehouse' => 'entrepot',
'knowledgemanagement' => 'knowledgemanagement_knowledgerecord',
'fichinter' => 'fichinter',
'fichinter' => 'fichinter',
'order' => 'commande',
);
/**
@@ -261,6 +267,7 @@ class Categorie extends CommonObject
* @see Categorie::TYPE_WEBSITE_PAGE
* @see Categorie::TYPE_TICKET
* @see Categorie::TYPE_FICHINTER
* @see Categorie::TYPE_ORDER
*/
public $type;

View File

@@ -171,6 +171,11 @@ if ($id > 0 && $removeelem > 0 && $action == 'unlink') { // Test on permission n
$tmpobject = new Fichinter($db);
$result = $tmpobject->fetch($removeelem);
$elementtype = 'fichinter';
} elseif ($type == Categorie::TYPE_ORDER && $user->hasRight('commande', 'creer')) {
require_once DOL_DOCUMENT_ROOT.'/commande/class/commande.class.php';
$tmpobject = new Commande($db);
$result = $tmpobject->fetch($removeelem);
$elementtype = 'order';
} else {
dol_print_error(null, "Not supported value of type = ".$type);
}
@@ -238,6 +243,10 @@ if ($elemid && $action == 'addintocategory') { // Test on permission not require
require_once DOL_DOCUMENT_ROOT.'/compta/bank/class/account.class.php';
$newobject = new Account($db);
$elementtype = 'bank_account';
} elseif ($type == Categorie::TYPE_ORDER && $user->hasRight('commande', 'creer')) {
require_once DOL_DOCUMENT_ROOT.'/commande/class/commande.class.php';
$newobject = new Commande($db);
$elementtype = 'order';
} else {
dol_print_error(null, "Not supported value of type = ".$type);
}
@@ -1411,6 +1420,83 @@ if ($type == Categorie::TYPE_FICHINTER) {
}
}
// List of Orders
if ($type == Categorie::TYPE_ORDER) {
require_once DOL_DOCUMENT_ROOT.'/commande/class/commande.class.php';
$permission = $user->rights->commande->creer;
$objects = $object->getObjectsInCateg($type, 0, $limit, $offset);
if ($objects < 0) {
dol_print_error($db, $object->error, $object->errors);
} else {
// Form to add record into a category
$showclassifyform = 1;
if ($showclassifyform) {
print '<br>';
print '<form method="post" action="'.$_SERVER["PHP_SELF"].'">';
print '<input type="hidden" name="token" value="'.newToken().'">';
print '<input type="hidden" name="typeid" value="'.$typeid.'">';
print '<input type="hidden" name="type" value="'.$typeid.'">';
print '<input type="hidden" name="id" value="'.$object->id.'">';
print '<input type="hidden" name="action" value="addintocategory">';
print '<table class="noborder centpercent">';
print '<tr class="liste_titre"><td>';
print $langs->trans("AddOrderIntoCategory").' &nbsp;';
$form->selectOrder('', 'elemid');
print '<input type="submit" class="button buttongen" value="'.$langs->trans("ClassifyInCategory").'"></td>';
print '</tr>';
print '</table>';
print '</form>';
}
print '<form method="post" action="'.$_SERVER["PHP_SELF"].'">';
print '<input type="hidden" name="token" value="'.newToken().'">';
print '<input type="hidden" name="typeid" value="'.$typeid.'">';
print '<input type="hidden" name="type" value="'.$typeid.'">';
print '<input type="hidden" name="id" value="'.$object->id.'">';
print '<input type="hidden" name="action" value="list">';
print '<br>';
$param = '&limit='.$limit.'&id='.$id.'&type='.$type; $num = count($objects); $nbtotalofrecords = ''; $newcardbutton = '';
print_barre_liste($langs->trans("Orders"), $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, $massactionbutton, $num, $nbtotalofrecords, 'bill', 0, $newcardbutton, '', $limit);
print "<table class='noborder' width='100%'>\n";
print '<tr class="liste_titre"><td colspan="4">'.$langs->trans("Ref").'</td></tr>'."\n";
if (count($objects) > 0) {
$i = 0;
foreach ($objects as $key => $rder) {
$i++;
if ($i > $limit) {
break;
}
print "\t".'<tr class="oddeven">'."\n";
print '<td class="nowrap" valign="top">';
print $order->getNomUrl(1);
print "</td>\n";
print '<td class="tdtop">'.$order->ref."</td>\n";
// Link to delete from category
print '<td class="right">';
if ($permission) {
print "<a href= '".$_SERVER['PHP_SELF']."?".(empty($socid) ? 'id' : 'socid')."=".$object->id."&amp;type=".$typeid."&amp;removeelem=".$order->id."'>";
print $langs->trans("DeleteFromCat");
print img_picto($langs->trans("DeleteFromCat"), 'unlink', '', false, 0, 0, '', 'paddingleft');
print "</a>";
}
print "</tr>\n";
}
} else {
print '<tr class="oddeven"><td colspan="3" class="opacitymedium">'.$langs->trans("ThisCategoryHasNoItems").'</td></tr>';
}
print "</table>\n";
print '</form>'."\n";
}
}
// Note that $action and $object may have been modified by some hooks
$parameters = array('type' => $type, 'id' => $id, 'label' => $label);
$reshook = $hookmanager->executeHooks('addMoreCategoriesList', $parameters, $object, $action);

View File

@@ -42,6 +42,7 @@
require '../main.inc.php';
require_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php';
require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
require_once DOL_DOCUMENT_ROOT.'/core/class/html.formfile.class.php';
require_once DOL_DOCUMENT_ROOT.'/core/class/html.formorder.class.php';
require_once DOL_DOCUMENT_ROOT.'/core/class/html.formmargin.class.php';
@@ -564,6 +565,13 @@ if (empty($reshook)) {
$action = '';
}
if (isModEnabled('categorie')) {
$categories = GETPOST('categories', 'array');
if (method_exists($object, 'setCategories')) {
$object->setCategories($categories);
}
}
// End of object creation, we show it
if ($object_id > 0 && !$error) {
$db->commit();
@@ -690,6 +698,11 @@ if (empty($reshook)) {
// $result = $object->setDiscount($user, price2num(GETPOST('remise_percent'), '', 2));
//} elseif ($action == 'setremiseabsolue' && $usercancreate) {
// $result = $object->set_remise_absolue($user, price2num(GETPOST('remise_absolue'), 'MU', 2));
} elseif ($action == 'settags' && isModEnabled('categorie') && $usercancreate) {
$result = $object->setCategories(GETPOST('categories', 'array'));
if ($result < 0) {
setEventMessages($object->error, $object->errors, 'errors');
}
} elseif ($action == 'addline' && GETPOST('submitforalllines', 'aZ09') && (GETPOST('alldate_start', 'alpha') || GETPOST('alldate_end', 'alpha')) && $usercancreate) {
// Define date start and date end for all line
$alldate_start = dol_mktime(GETPOSTINT('alldate_starthour'), GETPOSTINT('alldate_startmin'), 0, GETPOSTINT('alldate_startmonth'), GETPOSTINT('alldate_startday'), GETPOSTINT('alldate_startyear'));
@@ -2155,6 +2168,15 @@ if ($action == 'create' && $usercancreate) {
print '</td></tr>';
}
// Categories
if (!empty($conf->categorie->enabled)) {
print '<tr><td>'.$langs->trans("Categories").'</td><td colspan="3">';
$cate_arbo = $form->select_all_categories(Categorie::TYPE_ORDER, '', 'parent', 64, 0, 1);
$arrayselected = GETPOST('categories', 'array');
print img_picto('', 'category').$form->multiselectarray('categories', $cate_arbo, $arrayselected, '', 0, 'quatrevingtpercent widthcentpercentminusx', 0, 0);
print "</td></tr>";
}
// Note public
print '<tr>';
print '<td class="tdtop">'.$langs->trans('NotePublic').'</td>';
@@ -2841,6 +2863,40 @@ if ($action == 'create' && $usercancreate) {
print '</tr>';
}
// Tags-Categories
if (isModEnabled('categorie')) {
print '<tr><td>';
print '<table class="nobordernopadding centpercent"><tr><td>';
print $langs->trans("Categories");
print '<td><td class="right">';
if ($usercancreate) {
print '<a class="editfielda" href="'.DOL_URL_ROOT.'/commande/card.php?id='.$object->id.'&action=edittags&token='.newToken().'">'.img_edit().'</a>';
} else {
print '&nbsp;';
}
print '</td></tr></table>';
print '</td>';
print '<td>';
$cate_arbo = $form->select_all_categories(Categorie::TYPE_ORDER, '', 'parent', 64, 0, 1);
if ($action == 'edittags') {
print '<form method="POST" action="'.$_SERVER['PHP_SELF'].'?id='.$object->id.'">';
print '<input type="hidden" name="action" value="settags">';
print '<input type="hidden" name="token" value="'.newToken().'">';
$c = new Categorie($db);
$cats = $c->containing($object->id, Categorie::TYPE_ORDER);
$arrayselected=[];
foreach ($cats as $cat) {
$arrayselected[] = $cat->id;
}
print img_picto('', 'category').$form->multiselectarray('categories', $cate_arbo, $arrayselected, 0, 0, 'quatrevingtpercent widthcentpercentminusx', 0, '0');
print '<input type="submit" class="button valignmiddle" value="'.$langs->trans("Modify").'">';
print '</form>';
} else {
print $form->showCategories($object->id, Categorie::TYPE_ORDER, 1);
}
print "</td></tr>";
}
// Other attributes
include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_view.tpl.php';

View File

@@ -853,6 +853,22 @@ class Commande extends CommonOrder
return 0;
}
/**
* Sets object to supplied categories.
*
* Deletes object from existing categories not supplied.
* Adds it to non existing supplied categories.
* Existing categories are left untouch.
*
* @param int[]|int $categories Category or categories IDs
* @return void
*/
public function setCategories($categories)
{
require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
return parent::setCategoriesCommon($categories, Categorie::TYPE_ORDER);
}
/**
* Cancel an order
* If stock is decremented on order validation, we must reincrement it

View File

@@ -11279,6 +11279,109 @@ class Form
}
}
/**
* Output a combo list with orders qualified for a third party
*
* @param int $selected Id order preselected
* @param string $htmlname Name of HTML select
* @param int $maxlength Maximum length of label
* @param int $option_only Return only html options lines without the select tag
* @param string $show_empty Add an empty line ('1' or string to show for empty line)
* @param int $discard_closed Discard closed projects (0=Keep,1=hide completely,2=Disable)
* @param int $forcefocus Force focus on field (works with javascript only)
* @param int $disabled Disabled
* @param string $morecss More css added to the select component
* @return int Nbr of project if OK, <0 if KO
*/
public function selectOrder($selected = '', $htmlname = 'orderid', $maxlength = 24, $option_only = 0, $show_empty = '1', $discard_closed = 0, $forcefocus = 0, $disabled = 0, $morecss = 'maxwidth500')
{
global $user, $conf, $langs;
$out = '';
$hideunselectables = false;
if (!empty($conf->global->PROJECT_HIDE_UNSELECTABLES)) {
$hideunselectables = true;
}
// Search all orders
$sql = "SELECT c.rowid, c.ref";
$sql .= ' FROM '.$this->db->prefix().'commande as c';
$sql .= " ORDER BY c.ref ASC";
$resql = $this->db->query($sql);
if ($resql) {
// Use select2 selector
if (!empty($conf->use_javascript_ajax)) {
include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
$comboenhancement = ajax_combobox($htmlname, '', 0, $forcefocus);
$out .= $comboenhancement;
$morecss = 'minwidth200imp maxwidth500';
}
if (empty($option_only)) {
$out .= '<select class="valignmiddle flat'.($morecss ? ' '.$morecss : '').'"'.($disabled ? ' disabled="disabled"' : '').' id="'.$htmlname.'" name="'.$htmlname.'">';
}
if (!empty($show_empty)) {
$out .= '<option value="0" class="optiongrey">';
if (!is_numeric($show_empty)) {
$out .= $show_empty;
} else {
$out .= '&nbsp;';
}
$out .= '</option>';
}
$num = $this->db->num_rows($resql);
$i = 0;
if ($num) {
while ($i < $num) {
$obj = $this->db->fetch_object($resql);
if ($discard_closed == 1 && $obj->fk_statut == Project::STATUS_CLOSED) {
$i++;
continue;
}
$labeltoshow = dol_trunc($obj->ref, 18); // Order ref
if (!empty($selected) && $selected == $obj->rowid) {
$out .= '<option value="'.$obj->rowid.'" selected';
//if ($disabled) $out.=' disabled'; // with select2, field can't be preselected if disabled
$out .= '>'.$labeltoshow.'</option>';
} else {
if ($hideunselectables && $disabled && ($selected != $obj->rowid)) {
$resultat = '';
} else {
$resultat = '<option value="'.$obj->rowid.'"';
if ($disabled) {
$resultat .= ' disabled';
}
//if ($obj->public) $labeltoshow.=' ('.$langs->trans("Public").')';
//else $labeltoshow.=' ('.$langs->trans("Private").')';
$resultat .= '>';
$resultat .= $labeltoshow;
$resultat .= '</option>';
}
$out .= $resultat;
}
$i++;
}
}
if (empty($option_only)) {
$out .= '</select>';
}
print $out;
$this->db->free($resql);
return $num;
} else {
dol_print_error($this->db);
return -1;
}
}
/**
* Output the component to make advanced search criteries
*

View File

@@ -1398,6 +1398,12 @@ function get_left_menu_commercial($mainmenu, &$newmenu, $usemenuhider = 1, $left
} else {
$newmenu->add("/commande/stats/index.php?leftmenu=orders", $langs->trans("Statistics"), 1, $user->hasRight('commande', 'lire'));
}
// Categories
if (isModEnabled('categorie')) {
$langs->load("categories");
$newmenu->add("/categories/index.php?leftmenu=cat&amp;type=14", $langs->trans("Categories"), 1, $user->hasRight('categorie', 'lire'), '', $mainmenu, 'cat');
}
}
// Supplier proposal