dolibarr  19.0.0-dev
ProductAttribute.class.php
1 <?php
2 /* Copyright (C) 2016 Marcos García <marcosgdf@gmail.com>
3  * Copyright (C) 2022 Open-Dsi <support@open-dsi.fr>
4  * Copyright (C) 2023 Frédéric France <frederic.france@netlogic.fr>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program. If not, see <https://www.gnu.org/licenses/>.
18  */
19 
20 require_once DOL_DOCUMENT_ROOT.'/core/class/commonobject.class.php';
26 {
31  public $db;
35  public $module = 'variants';
36 
40  public $element = 'productattribute';
41 
45  public $table_element = 'product_attribute';
46 
50  public $table_element_line = 'product_attribute_value';
51 
55  public $fk_element = 'fk_product_attribute';
56 
61  public $ismultientitymanaged = 1;
62 
66  public $isextrafieldmanaged = 0;
67 
71  public $picto = 'product';
72 
101  public $fields=array(
102  'rowid' => array('type'=>'integer', 'label'=>'TechnicalID', 'enabled'=>'1', 'position'=>1, 'notnull'=>1, 'visible'=>0, 'noteditable'=>'1', 'index'=>1, 'css'=>'left', 'comment'=>"Id"),
103  'ref' => array('type'=>'varchar(255)', 'label'=>'Ref', 'visible'=>1, 'enabled'=>1, 'position'=>10, 'notnull'=>1, 'index'=>1, 'searchall'=>1, 'comment'=>"Reference of object", 'css'=>''),
104  'ref_ext' => array('type' => 'varchar(255)', 'label' => 'ExternalRef', 'enabled' => 1, 'visible' => 0, 'position' => 20, 'searchall'=>1),
105  'label' => array('type'=>'varchar(255)', 'label'=>'Label', 'enabled'=>'1', 'position'=>30, 'notnull'=>1, 'visible'=>1, 'searchall'=>1, 'css'=>'minwidth300', 'help'=>"", 'showoncombobox'=>'1',),
106  'position' => array('type'=>'integer', 'label'=>'Rank', 'enabled'=>1, 'visible'=>0, 'default'=>0, 'position'=>40, 'notnull'=>1,),
107  );
108 
112  public $id;
113 
117  public $ref;
118 
122  public $ref_ext;
123 
127  public $label;
128 
134  public $rang;
135 
139  public $position;
140 
144  public $lines = array();
148  public $line;
149 
153  public $is_used_by_products;
154 
155 
161  public function __construct(DoliDB $db)
162  {
163  global $conf, $langs;
164 
165  $this->db = $db;
166  $this->entity = $conf->entity;
167 
168  if (empty($conf->global->MAIN_SHOW_TECHNICAL_ID) && isset($this->fields['rowid'])) {
169  $this->fields['rowid']['visible'] = 0;
170  }
171  if (!isModEnabled('multicompany') && isset($this->fields['entity'])) {
172  $this->fields['entity']['enabled'] = 0;
173  }
174 
175  // Unset fields that are disabled
176  foreach ($this->fields as $key => $val) {
177  if (isset($val['enabled']) && empty($val['enabled'])) {
178  unset($this->fields[$key]);
179  }
180  }
181 
182  // Translate some data of arrayofkeyval
183  if (is_object($langs)) {
184  foreach ($this->fields as $key => $val) {
185  if (!empty($val['arrayofkeyval']) && is_array($val['arrayofkeyval'])) {
186  foreach ($val['arrayofkeyval'] as $key2 => $val2) {
187  $this->fields[$key]['arrayofkeyval'][$key2] = $langs->trans($val2);
188  }
189  }
190  }
191  }
192  }
193 
201  public function create(User $user, $notrigger = 0)
202  {
203  global $langs;
204  $error = 0;
205 
206  // Clean parameters
207  $this->ref = strtoupper(dol_sanitizeFileName(dol_string_nospecial(trim($this->ref)))); // Ref must be uppercase
208  $this->label = trim($this->label);
209  $this->position = $this->position > 0 ? $this->position : 0;
210 
211  // Position to use
212  if (empty($this->position)) {
213  $positionmax = $this->getMaxAttributesPosition();
214  $this->position = $positionmax + 1;
215  }
216 
217  // Check parameters
218  if (empty($this->ref)) {
219  $this->errors[] = $langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Ref"));
220  $error++;
221  }
222  if (empty($this->label)) {
223  $this->errors[] = $langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Label"));
224  $error++;
225  }
226  if ($error) {
227  dol_syslog(__METHOD__ . ' ' . $this->errorsToString(), LOG_ERR);
228  return -1;
229  }
230 
231  $this->db->begin();
232 
233  $sql = "INSERT INTO " . MAIN_DB_PREFIX . $this->table_element . " (";
234  $sql .= " ref, ref_ext, label, entity, position";
235  $sql .= ")";
236  $sql .= " VALUES (";
237  $sql .= " '" . $this->db->escape($this->ref) . "'";
238  $sql .= ", '" . $this->db->escape($this->ref_ext) . "'";
239  $sql .= ", '" . $this->db->escape($this->label) . "'";
240  $sql .= ", " . ((int) $this->entity);
241  $sql .= ", " . ((int) $this->position);
242  $sql .= ")";
243 
244  dol_syslog(__METHOD__, LOG_DEBUG);
245  $resql = $this->db->query($sql);
246  if (!$resql) {
247  $this->errors[] = "Error " . $this->db->lasterror();
248  $error++;
249  }
250 
251  if (!$error) {
252  $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX . $this->table_element);
253  }
254 
255  if (!$error && !$notrigger) {
256  // Call trigger
257  $result = $this->call_trigger('PRODUCT_ATTRIBUTE_CREATE', $user);
258  if ($result < 0) {
259  $error++;
260  }
261  // End call triggers
262  }
263 
264  if (!$error) {
265  $this->db->commit();
266  return $this->id;
267  } else {
268  $this->db->rollback();
269  return -1 * $error;
270  }
271  }
272 
279  public function fetch($id)
280  {
281  global $langs;
282  $error = 0;
283 
284  // Clean parameters
285  $id = $id > 0 ? $id : 0;
286 
287  // Check parameters
288  if (empty($id)) {
289  $this->errors[] = $langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("TechnicalID"));
290  $error++;
291  }
292  if ($error) {
293  dol_syslog(__METHOD__ . ' ' . $this->errorsToString(), LOG_ERR);
294  return -1;
295  }
296 
297  $sql = "SELECT rowid, ref, ref_ext, label, position";
298  $sql .= " FROM " . MAIN_DB_PREFIX . $this->table_element;
299  $sql .= " WHERE rowid = " . ((int) $id);
300  $sql .= " AND entity IN (" . getEntity('product') . ")";
301 
302  dol_syslog(__METHOD__, LOG_DEBUG);
303  $resql = $this->db->query($sql);
304  if (!$resql) {
305  $this->errors[] = "Error " . $this->db->lasterror();
306  dol_syslog(__METHOD__ . ' ' . $this->errorsToString(), LOG_ERR);
307  return -1;
308  }
309 
310  $numrows = $this->db->num_rows($resql);
311  if ($numrows) {
312  $obj = $this->db->fetch_object($resql);
313 
314  $this->id = $obj->rowid;
315  $this->ref = $obj->ref;
316  $this->ref_ext = $obj->ref_ext;
317  $this->label = $obj->label;
318  $this->rang = $obj->position; // deprecated
319  $this->position = $obj->position;
320  }
321  $this->db->free($resql);
322 
323  return $numrows;
324  }
325 
331  public function fetchAll()
332  {
333  $return = array();
334 
335  $sql = "SELECT rowid, ref, ref_ext, label, position";
336  $sql .= " FROM " . MAIN_DB_PREFIX . $this->table_element;
337  $sql .= " WHERE entity IN (" . getEntity('product') . ")";
338  $sql .= $this->db->order("position", "asc");
339 
340  dol_syslog(__METHOD__, LOG_DEBUG);
341  $resql = $this->db->query($sql);
342  if (!$resql) {
343  $this->errors[] = "Error " . $this->db->lasterror();
344  dol_print_error($this->db);
345  return $return;
346  }
347 
348  while ($obj = $this->db->fetch_object($resql)) {
349  $tmp = new ProductAttribute($this->db);
350 
351  $tmp->id = $obj->rowid;
352  $tmp->ref = $obj->ref;
353  $tmp->ref_ext = $obj->ref_ext;
354  $tmp->label = $obj->label;
355  $tmp->rang = $obj->position; // deprecated
356  $tmp->position = $obj->position;
357 
358  $return[] = $tmp;
359  }
360 
361  return $return;
362  }
363 
371  public function update(User $user, $notrigger = 0)
372  {
373  global $langs;
374  $error = 0;
375 
376  // Clean parameters
377  $this->id = $this->id > 0 ? $this->id : 0;
378  $this->ref = strtoupper(dol_sanitizeFileName(dol_string_nospecial(trim($this->ref)))); // Ref must be uppercase
379  $this->label = trim($this->label);
380 
381  // Check parameters
382  if (empty($this->id)) {
383  $this->errors[] = $langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("TechnicalID"));
384  $error++;
385  }
386  if (empty($this->ref)) {
387  $this->errors[] = $langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Ref"));
388  $error++;
389  }
390  if (empty($this->label)) {
391  $this->errors[] = $langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Label"));
392  $error++;
393  }
394  if ($error) {
395  dol_syslog(__METHOD__ . ' ' . $this->errorsToString(), LOG_ERR);
396  return -1;
397  }
398 
399  $this->db->begin();
400 
401  $sql = "UPDATE " . MAIN_DB_PREFIX . $this->table_element . " SET";
402 
403  $sql .= " ref = '" . $this->db->escape($this->ref) . "'";
404  $sql .= ", ref_ext = '" . $this->db->escape($this->ref_ext) . "'";
405  $sql .= ", label = '" . $this->db->escape($this->label) . "'";
406  $sql .= ", position = " . ((int) $this->position);
407 
408  $sql .= " WHERE rowid = " . ((int) $this->id);
409 
410  dol_syslog(__METHOD__, LOG_DEBUG);
411  $resql = $this->db->query($sql);
412  if (!$resql) {
413  $this->errors[] = "Error " . $this->db->lasterror();
414  $error++;
415  }
416 
417  if (!$error && !$notrigger) {
418  // Call trigger
419  $result = $this->call_trigger('PRODUCT_ATTRIBUTE_MODIFY', $user);
420  if ($result < 0) {
421  $error++;
422  }
423  // End call triggers
424  }
425 
426  if (!$error) {
427  $this->db->commit();
428  return 1;
429  } else {
430  $this->db->rollback();
431  return -1 * $error;
432  }
433  }
434 
442  public function delete(User $user, $notrigger = 0)
443  {
444  global $langs;
445  $error = 0;
446 
447  // Clean parameters
448  $this->id = $this->id > 0 ? $this->id : 0;
449 
450  // Check parameters
451  if (empty($this->id)) {
452  $this->errors[] = $langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("TechnicalID"));
453  $error++;
454  }
455  if ($error) {
456  dol_syslog(__METHOD__ . ' ' . $this->errorsToString(), LOG_ERR);
457  return -1;
458  }
459 
460  $result = $this->isUsed();
461  if ($result < 0) {
462  return -1;
463  } elseif ($result > 0) {
464  $this->errors[] = $langs->trans('ErrorAttributeIsUsedIntoProduct');
465  return -1;
466  }
467 
468  $this->db->begin();
469 
470  if (!$notrigger) {
471  // Call trigger
472  $result = $this->call_trigger('PRODUCT_ATTRIBUTE_DELETE', $user);
473  if ($result < 0) {
474  $error++;
475  }
476  // End call triggers
477  }
478 
479  if (!$error) {
480  // Delete values
481  $sql = "DELETE FROM " . MAIN_DB_PREFIX . $this->table_element_line;
482  $sql .= " WHERE " . $this->fk_element . " = " . ((int) $this->id);
483 
484  dol_syslog(__METHOD__ . ' - Delete values', LOG_DEBUG);
485  $resql = $this->db->query($sql);
486  if (!$resql) {
487  $this->errors[] = "Error " . $this->db->lasterror();
488  $error++;
489  }
490  }
491 
492  if (!$error) {
493  $sql = "DELETE FROM " . MAIN_DB_PREFIX . $this->table_element;
494  $sql .= " WHERE rowid = " . ((int) $this->id);
495 
496  dol_syslog(__METHOD__ . ' - Delete attribute', LOG_DEBUG);
497  $resql = $this->db->query($sql);
498  if (!$resql) {
499  $this->errors[] = "Error " . $this->db->lasterror();
500  $error++;
501  }
502  }
503 
504  if (!$error) {
505  $this->db->commit();
506  return 1;
507  } else {
508  $this->db->rollback();
509  return -1 * $error;
510  }
511  }
512 
513  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
520  public function fetch_lines($filters = '')
521  {
522  // phpcs:enable
523  global $langs;
524 
525  $this->lines = array();
526 
527  $error = 0;
528 
529  // Clean parameters
530  $this->id = $this->id > 0 ? $this->id : 0;
531 
532  // Check parameters
533  if (empty($this->id)) {
534  $this->errors[] = $langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("TechnicalID"));
535  $error++;
536  }
537  if ($error) {
538  dol_syslog(__METHOD__ . ' ' . $this->errorsToString(), LOG_ERR);
539  return -1;
540  }
541 
542  $sql = "SELECT td.rowid, td.fk_product_attribute, td.ref, td.value, td.position";
543  $sql .= " FROM " . MAIN_DB_PREFIX . $this->table_element_line . " AS td";
544  $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . $this->table_element . " AS t ON t.rowid = td." . $this->fk_element;
545  $sql .= " WHERE t.rowid = " . ((int) $this->id);
546  $sql .= " AND t.entity IN (" . getEntity('product') . ")";
547  if ($filters) {
548  $sql .= $filters;
549  }
550  $sql .= $this->db->order("td.position", "asc");
551 
552  dol_syslog(__METHOD__, LOG_DEBUG);
553  $resql = $this->db->query($sql);
554  if (!$resql) {
555  $this->errors[] = "Error " . $this->db->lasterror();
556  dol_syslog(__METHOD__ . ' ' . $this->errorsToString(), LOG_ERR);
557  return -3;
558  }
559 
560  $num = $this->db->num_rows($resql);
561  if ($num) {
562  $i = 0;
563  while ($i < $num) {
564  $obj = $this->db->fetch_object($resql);
565 
566  $line = new ProductAttributeValue($this->db);
567 
568  $line->id = $obj->rowid;
569  $line->fk_product_attribute = $obj->fk_product_attribute;
570  $line->ref = $obj->ref;
571  $line->value = $obj->value;
572  $line->position = $obj->position;
573 
574  $this->lines[$i] = $line;
575  $i++;
576  }
577  }
578  $this->db->free($resql);
579 
580  return $num;
581  }
582 
589  public function getLinesArray($filters = '')
590  {
591  return $this->fetch_lines($filters);
592  }
593 
607  public function addLine($ref, $value, $position = -1, $notrigger = 0)
608  {
609  global $langs, $user;
610  dol_syslog(__METHOD__ . " id=".$this->id.", ref=".$ref.", value=".$value.", notrigger=".$notrigger);
611  $error = 0;
612 
613  // Clean parameters
614  $this->id = $this->id > 0 ? $this->id : 0;
615 
616  // Check parameters
617  if (empty($this->id)) {
618  $this->errors[] = $langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("TechnicalID"));
619  $error++;
620  }
621  if ($error) {
622  dol_syslog(__METHOD__ . ' ' . $this->errorsToString(), LOG_ERR);
623  return -1;
624  }
625 
626  $this->db->begin();
627 
628  //Fetch current line from the database and then clone the object and set it in $oldcopy property
629  $this->line = new ProductAttributeValue($this->db);
630 
631  // Position to use
632  $positiontouse = $position;
633  if ($positiontouse == -1) {
634  $positionmax = $this->line_max(0);
635  $positiontouse = $positionmax + 1;
636  }
637 
638  $this->line->context = $this->context;
639  $this->line->fk_product_attribute = $this->id;
640  $this->line->ref = $ref;
641  $this->line->value = $value;
642  $this->line->position = $positiontouse;
643 
644  $result = $this->line->create($user, $notrigger);
645 
646  if ($result < 0) {
647  $this->error = $this->line->error;
648  $this->errors = $this->line->errors;
649  $this->db->rollback();
650  return -1;
651  } else {
652  $this->db->commit();
653  return $this->line->id;
654  }
655  }
656 
657 
667  public function updateLine($lineid, $ref, $value, $notrigger = 0)
668  {
669  global $user;
670 
671  dol_syslog(__METHOD__ . " lineid=$lineid, ref=$ref, value=$value, notrigger=$notrigger");
672 
673  // Clean parameters
674  $lineid = $lineid > 0 ? $lineid : 0;
675 
676  $this->db->begin();
677 
678  //Fetch current line from the database and then clone the object and set it in $oldcopy property
679  $this->line = new ProductAttributeValue($this->db);
680  $result = $this->line->fetch($lineid);
681  if ($result > 0) {
682  $this->line->oldcopy = clone $this->line;
683 
684  $this->line->context = $this->context;
685  $this->line->ref = $ref;
686  $this->line->value = $value;
687 
688  $result = $this->line->update($user, $notrigger);
689  }
690 
691  if ($result < 0) {
692  $this->error = $this->line->error;
693  $this->errors = $this->line->errors;
694  $this->db->rollback();
695  return -1;
696  } else {
697  $this->db->commit();
698  return $result;
699  }
700  }
701 
710  public function deleteLine(User $user, $lineid, $notrigger = 0)
711  {
712  dol_syslog(__METHOD__ . " lineid=$lineid, notrigger=$notrigger");
713 
714  // Clean parameters
715  $lineid = $lineid > 0 ? $lineid : 0;
716 
717  $this->db->begin();
718 
719  //Fetch current line from the database
720  $this->line = new ProductAttributeValue($this->db);
721  $result = $this->line->fetch($lineid);
722  if ($result > 0) {
723  $this->line->context = $this->context;
724 
725  $result = $this->line->delete($user, $notrigger);
726  }
727 
728  if ($result < 0) {
729  $this->error = $this->line->error;
730  $this->errors = $this->line->errors;
731  $this->db->rollback();
732  return -1;
733  } else {
734  $this->db->commit();
735  return $result;
736  }
737  }
738 
744  public function countChildValues()
745  {
746  global $langs;
747  $error = 0;
748  $count = 0;
749 
750  // Clean parameters
751  $this->id = $this->id > 0 ? $this->id : 0;
752 
753  // Check parameters
754  if (empty($this->id)) {
755  $this->errors[] = $langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("TechnicalID"));
756  $error++;
757  }
758  if ($error) {
759  dol_syslog(__METHOD__ . ' ' . $this->errorsToString(), LOG_ERR);
760  return -1;
761  }
762 
763  $sql = "SELECT COUNT(*) AS count";
764  $sql .= " FROM " . MAIN_DB_PREFIX . $this->table_element_line;
765  $sql .= " WHERE " . $this->fk_element . " = " . ((int) $this->id);
766 
767  dol_syslog(__METHOD__, LOG_DEBUG);
768  $resql = $this->db->query($sql);
769  if (!$resql) {
770  $this->errors[] = "Error " . $this->db->lasterror();
771  dol_syslog(__METHOD__ . ' ' . $this->errorsToString(), LOG_ERR);
772  return -1;
773  }
774 
775  if ($obj = $this->db->fetch_object($resql)) {
776  $count = $obj->count;
777  }
778 
779  return $count;
780  }
781 
787  public function countChildProducts()
788  {
789  global $langs;
790  $error = 0;
791  $count = 0;
792 
793  // Clean parameters
794  $this->id = $this->id > 0 ? $this->id : 0;
795 
796  // Check parameters
797  if (empty($this->id)) {
798  $this->errors[] = $langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("TechnicalID"));
799  $error++;
800  }
801  if ($error) {
802  dol_syslog(__METHOD__ . ' ' . $this->errorsToString(), LOG_ERR);
803  return -1;
804  }
805 
806  $sql = "SELECT COUNT(*) AS count";
807  $sql .= " FROM " . MAIN_DB_PREFIX . "product_attribute_combination2val AS pac2v";
808  $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "product_attribute_combination AS pac ON pac2v.fk_prod_combination = pac.rowid";
809  $sql .= " WHERE pac2v.fk_prod_attr = " . ((int) $this->id);
810  $sql .= " AND pac.entity IN (" . getEntity('product') . ")";
811 
812  dol_syslog(__METHOD__, LOG_DEBUG);
813  $resql = $this->db->query($sql);
814  if (!$resql) {
815  $this->errors[] = "Error " . $this->db->lasterror();
816  dol_syslog(__METHOD__ . ' ' . $this->errorsToString(), LOG_ERR);
817  return -1;
818  }
819 
820  if ($obj = $this->db->fetch_object($resql)) {
821  $count = $obj->count;
822  }
823 
824  return $count;
825  }
826 
832  public function isUsed()
833  {
834  global $langs;
835  $error = 0;
836 
837  // Clean parameters
838  $this->id = $this->id > 0 ? $this->id : 0;
839 
840  // Check parameters
841  if (empty($this->id)) {
842  $this->errors[] = $langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("TechnicalID"));
843  $error++;
844  }
845  if ($error) {
846  dol_syslog(__METHOD__ . ' ' . $this->errorsToString(), LOG_ERR);
847  return -1;
848  }
849 
850  $sql = "SELECT COUNT(*) AS nb FROM " . MAIN_DB_PREFIX . "product_attribute_combination2val WHERE fk_prod_attr = " . ((int) $this->id);
851 
852  dol_syslog(__METHOD__, LOG_DEBUG);
853  $resql = $this->db->query($sql);
854  if (!$resql) {
855  $this->errors[] = "Error " . $this->db->lasterror();
856  return -1;
857  }
858 
859  $used = 0;
860  if ($obj = $this->db->fetch_object($resql)) {
861  $used = $obj->nb;
862  }
863 
864  return $used ? 1 : 0;
865  }
866 
875  public function attributeOrder($renum = false, $rowidorder = 'ASC')
876  {
877  // Count number of attributes to reorder (according to choice $renum)
878  $nl = 0;
879  $sql = "SELECT count(rowid) FROM " . MAIN_DB_PREFIX . $this->table_element;
880  $sql .= " WHERE entity IN (" . getEntity('product') . ")";
881  if (!$renum) {
882  $sql .= " AND position = 0";
883  } else {
884  $sql .= " AND position <> 0";
885  }
886 
887  dol_syslog(__METHOD__, LOG_DEBUG);
888  $resql = $this->db->query($sql);
889  if ($resql) {
890  $row = $this->db->fetch_row($resql);
891  $nl = $row[0];
892  } else {
893  dol_print_error($this->db);
894  }
895  if ($nl > 0) {
896  // The goal of this part is to reorder all attributes.
897  $rows = array();
898 
899  // We first search all attributes
900  $sql = "SELECT rowid FROM " . MAIN_DB_PREFIX . $this->table_element;
901  $sql .= " WHERE entity IN (" . getEntity('product') . ")";
902  $sql .= " ORDER BY position ASC, rowid " . $rowidorder;
903 
904  dol_syslog(__METHOD__ . " search all attributes", LOG_DEBUG);
905  $resql = $this->db->query($sql);
906  if ($resql) {
907  $i = 0;
908  $num = $this->db->num_rows($resql);
909  while ($i < $num) {
910  $row = $this->db->fetch_row($resql);
911  $rows[] = $row[0]; // Add attributes into array rows
912  $i++;
913  }
914 
915  // Now we set a new number for each attributes
916  if (!empty($rows)) {
917  foreach ($rows as $key => $row) {
918  $this->updatePositionOfAttribute($row, ($key + 1));
919  }
920  }
921  } else {
922  dol_print_error($this->db);
923  }
924  }
925  return 1;
926  }
927 
935  public function updatePositionOfAttribute($rowid, $position)
936  {
937  global $hookmanager;
938 
939  $sql = "UPDATE " . MAIN_DB_PREFIX . $this->table_element . " SET position = " . ((int) $position);
940  $sql .= " WHERE rowid = " . ((int) $rowid);
941 
942  dol_syslog(__METHOD__, LOG_DEBUG);
943  if (!$this->db->query($sql)) {
944  dol_print_error($this->db);
945  return -1;
946  } else {
947  $parameters = array('rowid' => $rowid, 'position' => $position);
948  $action = '';
949  $reshook = $hookmanager->executeHooks('afterPositionOfAttributeUpdate', $parameters, $this, $action);
950  return ($reshook >= 0 ? 1 : -1);
951  }
952  }
953 
960  public function getPositionOfAttribute($rowid)
961  {
962  $sql = "SELECT position FROM " . MAIN_DB_PREFIX . $this->table_element;
963  $sql .= " WHERE entity IN (" . getEntity('product') . ")";
964 
965  dol_syslog(__METHOD__, LOG_DEBUG);
966  $resql = $this->db->query($sql);
967  if ($resql) {
968  $row = $this->db->fetch_row($resql);
969  return $row[0];
970  }
971 
972  return 0;
973  }
974 
981  public function attributeMoveUp($rowid)
982  {
983  $this->attributeOrder(false, 'ASC');
984 
985  // Get position of attribute
986  $position = $this->getPositionOfAttribute($rowid);
987 
988  // Update position of attribute
989  $this->updateAttributePositionUp($rowid, $position);
990 
991  return 1;
992  }
993 
1000  public function attributeMoveDown($rowid)
1001  {
1002  $this->attributeOrder(false, 'ASC');
1003 
1004  // Get position of line
1005  $position = $this->getPositionOfAttribute($rowid);
1006 
1007  // Get max value for position
1008  $max = $this->getMaxAttributesPosition();
1009 
1010  // Update position of attribute
1011  $this->updateAttributePositionDown($rowid, $position, $max);
1012 
1013  return 1;
1014  }
1015 
1023  public function updateAttributePositionUp($rowid, $position)
1024  {
1025  if ($position > 1) {
1026  $sql = "UPDATE " . MAIN_DB_PREFIX . $this->table_element . " SET position = " . ((int) $position);
1027  $sql .= " WHERE entity IN (" . getEntity('product') . ")";
1028  $sql .= " AND position = " . ((int) ($position - 1));
1029  if ($this->db->query($sql)) {
1030  $sql = "UPDATE " . MAIN_DB_PREFIX . $this->table_element . " SET position = " . ((int) ($position - 1));
1031  $sql .= " WHERE rowid = " . ((int) $rowid);
1032  if (!$this->db->query($sql)) {
1033  dol_print_error($this->db);
1034  }
1035  } else {
1036  dol_print_error($this->db);
1037  }
1038  }
1039  }
1040 
1049  public function updateAttributePositionDown($rowid, $position, $max)
1050  {
1051  if ($position < $max) {
1052  $sql = "UPDATE " . MAIN_DB_PREFIX . $this->table_element . " SET position = " . ((int) $position);
1053  $sql .= " WHERE entity IN (" . getEntity('product') . ")";
1054  $sql .= " AND position = " . ((int) ($position + 1));
1055  if ($this->db->query($sql)) {
1056  $sql = "UPDATE " . MAIN_DB_PREFIX . $this->table_element . " SET position = " . ((int) ($position + 1));
1057  $sql .= " WHERE rowid = " . ((int) $rowid);
1058  if (!$this->db->query($sql)) {
1059  dol_print_error($this->db);
1060  }
1061  } else {
1062  dol_print_error($this->db);
1063  }
1064  }
1065  }
1066 
1072  public function getMaxAttributesPosition()
1073  {
1074  // Search the last position of attributes
1075  $sql = "SELECT max(position) FROM " . MAIN_DB_PREFIX . $this->table_element;
1076  $sql .= " WHERE entity IN (" . getEntity('product') . ")";
1077 
1078  dol_syslog(__METHOD__, LOG_DEBUG);
1079  $resql = $this->db->query($sql);
1080  if ($resql) {
1081  $row = $this->db->fetch_row($resql);
1082  return $row[0];
1083  }
1084 
1085  return 0;
1086  }
1087 
1094  public function attributesAjaxOrder($rows)
1095  {
1096  $num = count($rows);
1097  for ($i = 0; $i < $num; $i++) {
1098  $this->updatePositionOfAttribute($rows[$i], ($i + 1));
1099  }
1100  }
1101 
1112  public function getNomUrl($withpicto = 0, $option = '', $notooltip = 0, $morecss = '', $save_lastsearch_value = -1)
1113  {
1114  global $conf, $langs, $hookmanager;
1115 
1116  if (!empty($conf->dol_no_mouse_hover)) {
1117  $notooltip = 1; // Force disable tooltips
1118  }
1119 
1120  $result = '';
1121 
1122  $label = img_picto('', $this->picto) . ' <u>' . $langs->trans("ProductAttribute") . '</u>';
1123  if (isset($this->status)) {
1124  $label .= ' ' . $this->getLibStatut(5);
1125  }
1126  $label .= '<br>';
1127  $label .= '<b>' . $langs->trans('Ref') . ':</b> ' . $this->ref;
1128  if (!empty($this->label)) {
1129  $label .= '<br><b>' . $langs->trans('Label') . ':</b> ' . $this->label;
1130  }
1131 
1132  $url = dol_buildpath('/variants/card.php', 1) . '?id=' . $this->id;
1133 
1134  if ($option != 'nolink') {
1135  // Add param to save lastsearch_values or not
1136  $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
1137  if ($save_lastsearch_value == -1 && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
1138  $add_save_lastsearch_values = 1;
1139  }
1140  if ($url && $add_save_lastsearch_values) {
1141  $url .= '&save_lastsearch_values=1';
1142  }
1143  }
1144 
1145  $linkclose = '';
1146  if (empty($notooltip)) {
1147  if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
1148  $label = $langs->trans("ShowProductAttribute");
1149  $linkclose .= ' alt="' . dol_escape_htmltag($label, 1) . '"';
1150  }
1151  $linkclose .= ' title="' . dol_escape_htmltag($label, 1) . '"';
1152  $linkclose .= ' class="classfortooltip' . ($morecss ? ' ' . $morecss : '') . '"';
1153  } else {
1154  $linkclose = ($morecss ? ' class="' . $morecss . '"' : '');
1155  }
1156 
1157  if ($option == 'nolink' || empty($url)) {
1158  $linkstart = '<span';
1159  } else {
1160  $linkstart = '<a href="' . $url . '"';
1161  }
1162  $linkstart .= $linkclose . '>';
1163  if ($option == 'nolink' || empty($url)) {
1164  $linkend = '</span>';
1165  } else {
1166  $linkend = '</a>';
1167  }
1168 
1169  $result .= $linkstart;
1170 
1171  if (empty($this->showphoto_on_popup)) {
1172  if ($withpicto) {
1173  $result .= img_object(($notooltip ? '' : $label), ($this->picto ? $this->picto : 'generic'), ($notooltip ? (($withpicto != 2) ? 'class="paddingright"' : '') : 'class="' . (($withpicto != 2) ? 'paddingright ' : '') . 'classfortooltip"'), 0, 0, $notooltip ? 0 : 1);
1174  }
1175  } else {
1176  if ($withpicto) {
1177  require_once DOL_DOCUMENT_ROOT . '/core/lib/files.lib.php';
1178 
1179  list($class, $module) = explode('@', $this->picto);
1180  $upload_dir = $conf->$module->multidir_output[$conf->entity] . "/$class/" . dol_sanitizeFileName($this->ref);
1181  $filearray = dol_dir_list($upload_dir, "files");
1182  $filename = $filearray[0]['name'];
1183  if (!empty($filename)) {
1184  $pospoint = strpos($filearray[0]['name'], '.');
1185 
1186  $pathtophoto = $class . '/' . $this->ref . '/thumbs/' . substr($filename, 0, $pospoint) . '_mini' . substr($filename, $pospoint);
1187  if (!getDolGlobalString(strtoupper($module . '_' . $class) . '_FORMATLISTPHOTOSASUSERS')) {
1188  $result .= '<div class="floatleft inline-block valignmiddle divphotoref"><div class="photoref"><img class="photo' . $module . '" alt="No photo" border="0" src="' . DOL_URL_ROOT . '/viewimage.php?modulepart=' . $module . '&entity=' . $conf->entity . '&file=' . urlencode($pathtophoto) . '"></div></div>';
1189  } else {
1190  $result .= '<div class="floatleft inline-block valignmiddle divphotoref"><img class="photouserphoto userphoto" alt="No photo" border="0" src="' . DOL_URL_ROOT . '/viewimage.php?modulepart=' . $module . '&entity=' . $conf->entity . '&file=' . urlencode($pathtophoto) . '"></div>';
1191  }
1192 
1193  $result .= '</div>';
1194  } else {
1195  $result .= img_object(($notooltip ? '' : $label), ($this->picto ? $this->picto : 'generic'), ($notooltip ? (($withpicto != 2) ? 'class="paddingright"' : '') : 'class="' . (($withpicto != 2) ? 'paddingright ' : '') . 'classfortooltip"'), 0, 0, $notooltip ? 0 : 1);
1196  }
1197  }
1198  }
1199 
1200  if ($withpicto != 2) {
1201  $result .= $this->ref;
1202  }
1203 
1204  $result .= $linkend;
1205  //if ($withpicto != 2) $result.=(($addlabel && $this->label) ? $sep . dol_trunc($this->label, ($addlabel > 1 ? $addlabel : 0)) : '');
1206 
1207  global $action, $hookmanager;
1208  $hookmanager->initHooks(array('variantsdao'));
1209  $parameters = array('id' => $this->id, 'getnomurl' => $result);
1210  $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1211  if ($reshook > 0) {
1212  $result = $hookmanager->resPrint;
1213  } else {
1214  $result .= $hookmanager->resPrint;
1215  }
1216 
1217  return $result;
1218  }
1219 
1226  public function getLabelStatus($mode = 0)
1227  {
1228  return $this->LibStatut(0, $mode);
1229  }
1230 
1237  public function getLibStatut($mode = 0)
1238  {
1239  return $this->LibStatut(0, $mode);
1240  }
1241 
1242  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1250  public function LibStatut($status, $mode = 1)
1251  {
1252  // phpcs:enable
1253  return '';
1254  }
1255 
1256  // --------------------
1257  // TODO: All functions here must be redesigned and moved as they are not business functions but output functions
1258  // --------------------
1259 
1260  /* This is to show add lines */
1261 
1271  public function formAddObjectLine($dateSelector, $seller, $buyer, $defaulttpldir = '/variants/tpl')
1272  {
1273  global $conf, $user, $langs, $object, $hookmanager;
1274  global $form;
1275 
1276  // Output template part (modules that overwrite templates must declare this into descriptor)
1277  // Use global variables + $dateSelector + $seller and $buyer
1278  // Note: This is deprecated. If you need to overwrite the tpl file, use instead the hook 'formAddObjectLine'.
1279  $dirtpls = array_merge($conf->modules_parts['tpl'], array($defaulttpldir));
1280  foreach ($dirtpls as $module => $reldir) {
1281  if (!empty($module)) {
1282  $tpl = dol_buildpath($reldir . '/productattributevalueline_create.tpl.php');
1283  } else {
1284  $tpl = DOL_DOCUMENT_ROOT . $reldir . '/productattributevalueline_create.tpl.php';
1285  }
1286 
1287  if (empty($conf->file->strict_mode)) {
1288  $res = @include $tpl;
1289  } else {
1290  $res = include $tpl; // for debug
1291  }
1292  if ($res) {
1293  break;
1294  }
1295  }
1296  }
1297 
1298  /* This is to show array of line of details */
1299 
1315  public function printObjectLines($action, $seller, $buyer, $selected = 0, $dateSelector = 0, $defaulttpldir = '/variants/tpl', $addcreateline = 0)
1316  {
1317  global $conf, $hookmanager, $langs, $user, $form, $object;
1318  global $mysoc;
1319  // TODO We should not use global var for this
1320  global $disableedit, $disablemove, $disableremove;
1321 
1322  $num = count($this->lines);
1323 
1324  $parameters = array('num' => $num, 'selected' => $selected, 'table_element_line' => $this->table_element_line);
1325  $reshook = $hookmanager->executeHooks('printObjectLineTitle', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1326  if (empty($reshook)) {
1327  // Output template part (modules that overwrite templates must declare this into descriptor)
1328  // Use global variables + $dateSelector + $seller and $buyer
1329  // Note: This is deprecated. If you need to overwrite the tpl file, use instead the hook.
1330  $dirtpls = array_merge($conf->modules_parts['tpl'], array($defaulttpldir));
1331  foreach ($dirtpls as $module => $reldir) {
1332  if (!empty($module)) {
1333  $tpl = dol_buildpath($reldir . '/productattributevalueline_title.tpl.php');
1334  } else {
1335  $tpl = DOL_DOCUMENT_ROOT . $reldir . '/productattributevalueline_title.tpl.php';
1336  }
1337  if (empty($conf->file->strict_mode)) {
1338  $res = @include $tpl;
1339  } else {
1340  $res = include $tpl; // for debug
1341  }
1342  if ($res) {
1343  break;
1344  }
1345  }
1346  }
1347 
1348 
1349  if ($addcreateline) {
1350  // Form to add new line
1351  if ($action != 'selectlines') {
1352  if ($action != 'editline') {
1353  // Add products/services form
1354 
1355  $parameters = array();
1356  $reshook = $hookmanager->executeHooks('formAddObjectLine', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
1357  if ($reshook < 0) setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
1358  if (empty($reshook))
1359  $object->formAddObjectLine(1, $mysoc, $buyer);
1360  }
1361  }
1362  }
1363 
1364  $i = 0;
1365 
1366  print "<!-- begin printObjectLines() -->\n";
1367  foreach ($this->lines as $line) {
1368  if (is_object($hookmanager)) { // Old code is commented on preceding line.
1369  $parameters = array('line' => $line, 'num' => $num, 'i' => $i, 'selected' => $selected, 'table_element_line' => $line->table_element);
1370  $reshook = $hookmanager->executeHooks('printObjectLine', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1371  }
1372  if (empty($reshook)) {
1373  $this->printObjectLine($action, $line, '', $num, $i, $dateSelector, $seller, $buyer, $selected, null, $defaulttpldir);
1374  }
1375 
1376  $i++;
1377  }
1378  print "<!-- end printObjectLines() -->\n";
1379  }
1380 
1398  public function printObjectLine($action, $line, $var, $num, $i, $dateSelector, $seller, $buyer, $selected = 0, $extrafields = null, $defaulttpldir = '/variants/tpl')
1399  {
1400  global $conf, $langs, $user, $object, $hookmanager;
1401  global $form;
1402  global $object_rights, $disableedit, $disablemove, $disableremove; // TODO We should not use global var for this !
1403 
1404  $object_rights = $user->rights->variants;
1405 
1406  // Line in view mode
1407  if ($action != 'editline' || $selected != $line->id) {
1408  // Output template part (modules that overwrite templates must declare this into descriptor)
1409  // Use global variables + $dateSelector + $seller and $buyer
1410  // Note: This is deprecated. If you need to overwrite the tpl file, use instead the hook printObjectLine and printObjectSubLine.
1411  $dirtpls = array_merge($conf->modules_parts['tpl'], array($defaulttpldir));
1412  foreach ($dirtpls as $module => $reldir) {
1413  if (!empty($module)) {
1414  $tpl = dol_buildpath($reldir . '/productattributevalueline_view.tpl.php');
1415  } else {
1416  $tpl = DOL_DOCUMENT_ROOT . $reldir . '/productattributevalueline_view.tpl.php';
1417  }
1418 
1419  if (empty($conf->file->strict_mode)) {
1420  $res = @include $tpl;
1421  } else {
1422  $res = include $tpl; // for debug
1423  }
1424  if ($res) {
1425  break;
1426  }
1427  }
1428  }
1429 
1430  // Line in update mode
1431  if ($action == 'editline' && $selected == $line->id) {
1432  // Output template part (modules that overwrite templates must declare this into descriptor)
1433  // Use global variables + $dateSelector + $seller and $buyer
1434  // Note: This is deprecated. If you need to overwrite the tpl file, use instead the hook printObjectLine and printObjectSubLine.
1435  $dirtpls = array_merge($conf->modules_parts['tpl'], array($defaulttpldir));
1436  foreach ($dirtpls as $module => $reldir) {
1437  if (!empty($module)) {
1438  $tpl = dol_buildpath($reldir . '/productattributevalueline_edit.tpl.php');
1439  } else {
1440  $tpl = DOL_DOCUMENT_ROOT . $reldir . '/productattributevalueline_edit.tpl.php';
1441  }
1442 
1443  if (empty($conf->file->strict_mode)) {
1444  $res = @include $tpl;
1445  } else {
1446  $res = include $tpl; // for debug
1447  }
1448  if ($res) {
1449  break;
1450  }
1451  }
1452  }
1453  }
1454 
1455  /* This is to show array of line of details of source object */
1456 }
$object ref
Definition: info.php:78
Parent class of all other business classes (invoices, contracts, proposals, orders,...
errorsToString()
Method to output saved errors.
line_max($fk_parent_line=0)
Get max value used for position of line (rang)
call_trigger($triggerName, $user)
Call trigger based on this instance.
Class to manage Dolibarr database access.
Class ProductAttribute Used to represent a product attribute.
updateAttributePositionDown($rowid, $position, $max)
Update position of attribute (down)
updatePositionOfAttribute($rowid, $position)
Update position of line (rang)
addLine($ref, $value, $position=-1, $notrigger=0)
attributeMoveDown($rowid)
Update a attribute to have a lower position.
fetch($id)
Fetches the properties of a product attribute.
update(User $user, $notrigger=0)
Updates a product attribute.
attributeOrder($renum=false, $rowidorder='ASC')
Save a new position (field position) for details lines.
fetchAll()
Returns an array of all product variants.
updateAttributePositionUp($rowid, $position)
Update position of attribute (up)
isUsed()
Test if used by a product.
updateLine($lineid, $ref, $value, $notrigger=0)
Update a line.
__construct(DoliDB $db)
Constructor.
getMaxAttributesPosition()
Get max value used for position of attributes.
LibStatut($status, $mode=1)
Return label of a status.
attributesAjaxOrder($rows)
Update position of attributes with ajax.
getLinesArray($filters='')
Retrieve an array of proposal lines.
fetch_lines($filters='')
Load array lines.
attributeMoveUp($rowid)
Update a attribute to have a higher position.
create(User $user, $notrigger=0)
Creates a product attribute.
countChildProducts()
Returns the number of products that are using this attribute.
deleteLine(User $user, $lineid, $notrigger=0)
Delete a line.
formAddObjectLine($dateSelector, $seller, $buyer, $defaulttpldir='/variants/tpl')
Show add free and predefined products/services form.
getNomUrl($withpicto=0, $option='', $notooltip=0, $morecss='', $save_lastsearch_value=-1)
Return a link to the object card (with optionaly the picto)
getLibStatut($mode=0)
Return label of status of product attribute.
getPositionOfAttribute($rowid)
Get position of attribute.
printObjectLine($action, $line, $var, $num, $i, $dateSelector, $seller, $buyer, $selected=0, $extrafields=null, $defaulttpldir='/variants/tpl')
Return HTML content of a detail line TODO Move this into an output class file (htmlline....
printObjectLines($action, $seller, $buyer, $selected=0, $dateSelector=0, $defaulttpldir='/variants/tpl', $addcreateline=0)
Return HTML table for object lines TODO Move this into an output class file (htmlline....
getLabelStatus($mode=0)
Return the label of the status.
countChildValues()
Returns the number of values for this attribute.
Class ProductAttributeValue Used to represent a product attribute value.
Class to manage Dolibarr users.
Definition: user.class.php:48
if(isModEnabled('facture') && $user->hasRight('facture', 'lire')) if((isModEnabled('fournisseur') &&empty($conf->global->MAIN_USE_NEW_SUPPLIERMOD) && $user->hasRight("fournisseur", "facture", "lire"))||(isModEnabled('supplier_invoice') && $user->hasRight("supplier_invoice", "lire"))) if(isModEnabled('don') && $user->hasRight('don', 'lire')) if(isModEnabled('tax') &&!empty($user->rights->tax->charges->lire)) if(isModEnabled('facture') &&isModEnabled('commande') && $user->hasRight("commande", "lire") &&empty($conf->global->WORKFLOW_DISABLE_CREATE_INVOICE_FROM_ORDER)) $sql
Social contributions to pay.
Definition: index.php:746
if($cancel &&! $id) if($action=='add' &&! $cancel) if($action=='delete') if($id) $form
Actions.
Definition: card.php:143
dol_dir_list($path, $types="all", $recursive=0, $filter="", $excludefilter=null, $sortcriteria="name", $sortorder=SORT_ASC, $mode=0, $nohook=0, $relativename="", $donotfollowsymlinks=0, $nbsecondsold=0)
Scan a directory and return a list of files/directories.
Definition: files.lib.php:62
dol_print_error($db='', $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
img_object($titlealt, $picto, $moreatt='', $pictoisfullpath=false, $srconly=0, $notitle=0)
Show a picto called object_picto (generic function)
dol_string_nospecial($str, $newstr='_', $badcharstoreplace='', $badcharstoremove='', $keepspaces=0)
Clean a string from all punctuation characters to use it as a ref or login.
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=false, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2)
Show picto whatever it's its name (generic function)
setEventMessages($mesg, $mesgs, $style='mesgs', $messagekey='', $noduplicate=0)
Set event messages in dol_events session object.
dol_buildpath($path, $type=0, $returnemptyifnotfound=0)
Return path of url or filesystem.
dol_sanitizeFileName($str, $newstr='_', $unaccent=1)
Clean a string to use it as a file name.
getDolGlobalString($key, $default='')
Return dolibarr global constant string value.
isModEnabled($module)
Is Dolibarr module enabled.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
getEntity($element, $shared=1, $currentobject=null)
Get list of entity id to use.
dol_escape_htmltag($stringtoescape, $keepb=0, $keepn=0, $noescapetags='', $escapeonlyhtmltags=0, $cleanalsojavascript=0)
Returns text escaped for inclusion in HTML alt or title or value tags, or into values of HTML input f...
rtl background position