dolibarr  17.0.4
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  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program. If not, see <https://www.gnu.org/licenses/>.
17  */
18 
19 require_once DOL_DOCUMENT_ROOT.'/core/class/commonobject.class.php';
25 {
30  public $db;
34  public $module = 'variants';
35 
39  public $element = 'productattribute';
40 
44  public $table_element = 'product_attribute';
45 
49  public $table_element_line = 'product_attribute_value';
50 
54  public $fk_element = 'fk_product_attribute';
55 
60  public $ismultientitymanaged = 1;
61 
65  public $isextrafieldmanaged = 0;
66 
70  public $picto = 'product';
71 
100  public $fields=array(
101  'rowid' => array('type'=>'integer', 'label'=>'TechnicalID', 'enabled'=>'1', 'position'=>1, 'notnull'=>1, 'visible'=>0, 'noteditable'=>'1', 'index'=>1, 'css'=>'left', 'comment'=>"Id"),
102  'ref' => array('type'=>'varchar(255)', 'label'=>'Ref', 'visible'=>1, 'enabled'=>1, 'position'=>10, 'notnull'=>1, 'index'=>1, 'searchall'=>1, 'comment'=>"Reference of object", 'css'=>''),
103  'ref_ext' => array('type' => 'varchar(255)', 'label' => 'ExternalRef', 'enabled' => 1, 'visible' => 0, 'position' => 20, 'searchall'=>1),
104  'label' => array('type'=>'varchar(255)', 'label'=>'Label', 'enabled'=>'1', 'position'=>30, 'notnull'=>1, 'visible'=>1, 'searchall'=>1, 'css'=>'minwidth300', 'help'=>"", 'showoncombobox'=>'1',),
105  'position' => array('type'=>'integer', 'label'=>'Rank', 'enabled'=>1, 'visible'=>0, 'default'=>0, 'position'=>40, 'notnull'=>1,),
106  );
107  public $id;
108  public $ref;
109  public $ref_ext;
110  public $label;
111  public $position;
112 
116  public $lines = array();
120  public $line;
121 
122 
128  public function __construct(DoliDB $db)
129  {
130  global $conf, $langs;
131 
132  $this->db = $db;
133  $this->entity = $conf->entity;
134 
135  if (empty($conf->global->MAIN_SHOW_TECHNICAL_ID) && isset($this->fields['rowid'])) {
136  $this->fields['rowid']['visible'] = 0;
137  }
138  if (!isModEnabled('multicompany') && isset($this->fields['entity'])) {
139  $this->fields['entity']['enabled'] = 0;
140  }
141 
142  // Unset fields that are disabled
143  foreach ($this->fields as $key => $val) {
144  if (isset($val['enabled']) && empty($val['enabled'])) {
145  unset($this->fields[$key]);
146  }
147  }
148 
149  // Translate some data of arrayofkeyval
150  if (is_object($langs)) {
151  foreach ($this->fields as $key => $val) {
152  if (!empty($val['arrayofkeyval']) && is_array($val['arrayofkeyval'])) {
153  foreach ($val['arrayofkeyval'] as $key2 => $val2) {
154  $this->fields[$key]['arrayofkeyval'][$key2] = $langs->trans($val2);
155  }
156  }
157  }
158  }
159  }
160 
168  public function create(User $user, $notrigger = 0)
169  {
170  global $langs;
171  $error = 0;
172 
173  // Clean parameters
174  $this->ref = strtoupper(dol_sanitizeFileName(dol_string_nospecial(trim($this->ref)))); // Ref must be uppercase
175  $this->label = trim($this->label);
176  $this->position = $this->position > 0 ? $this->position : 0;
177 
178  // Position to use
179  if (empty($this->position)) {
180  $positionmax = $this->getMaxAttributesPosition();
181  $this->position = $positionmax + 1;
182  }
183 
184  // Check parameters
185  if (empty($this->ref)) {
186  $this->errors[] = $langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Ref"));
187  $error++;
188  }
189  if (empty($this->label)) {
190  $this->errors[] = $langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Label"));
191  $error++;
192  }
193  if ($error) {
194  dol_syslog(__METHOD__ . ' ' . $this->errorsToString(), LOG_ERR);
195  return -1;
196  }
197 
198  $this->db->begin();
199 
200  $sql = "INSERT INTO " . MAIN_DB_PREFIX . $this->table_element . " (";
201  $sql .= " ref, ref_ext, label, entity, position";
202  $sql .= ")";
203  $sql .= " VALUES (";
204  $sql .= " '" . $this->db->escape($this->ref) . "'";
205  $sql .= ", '" . $this->db->escape($this->ref_ext) . "'";
206  $sql .= ", '" . $this->db->escape($this->label) . "'";
207  $sql .= ", " . ((int) $this->entity);
208  $sql .= ", " . ((int) $this->position);
209  $sql .= ")";
210 
211  dol_syslog(__METHOD__, LOG_DEBUG);
212  $resql = $this->db->query($sql);
213  if (!$resql) {
214  $this->errors[] = "Error " . $this->db->lasterror();
215  $error++;
216  }
217 
218  if (!$error) {
219  $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX . $this->table_element);
220  }
221 
222  if (!$error && !$notrigger) {
223  // Call trigger
224  $result = $this->call_trigger('PRODUCT_ATTRIBUTE_CREATE', $user);
225  if ($result < 0) {
226  $error++;
227  }
228  // End call triggers
229  }
230 
231  if (!$error) {
232  $this->db->commit();
233  return $this->id;
234  } else {
235  $this->db->rollback();
236  return -1 * $error;
237  }
238  }
239 
246  public function fetch($id)
247  {
248  global $langs;
249  $error = 0;
250 
251  // Clean parameters
252  $id = $id > 0 ? $id : 0;
253 
254  // Check parameters
255  if (empty($id)) {
256  $this->errors[] = $langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("TechnicalID"));
257  $error++;
258  }
259  if ($error) {
260  dol_syslog(__METHOD__ . ' ' . $this->errorsToString(), LOG_ERR);
261  return -1;
262  }
263 
264  $sql = "SELECT rowid, ref, ref_ext, label, position";
265  $sql .= " FROM " . MAIN_DB_PREFIX . $this->table_element;
266  $sql .= " WHERE rowid = " . ((int) $id);
267  $sql .= " AND entity IN (" . getEntity('product') . ")";
268 
269  dol_syslog(__METHOD__, LOG_DEBUG);
270  $resql = $this->db->query($sql);
271  if (!$resql) {
272  $this->errors[] = "Error " . $this->db->lasterror();
273  dol_syslog(__METHOD__ . ' ' . $this->errorsToString(), LOG_ERR);
274  return -1;
275  }
276 
277  $numrows = $this->db->num_rows($resql);
278  if ($numrows) {
279  $obj = $this->db->fetch_object($resql);
280 
281  $this->id = $obj->rowid;
282  $this->ref = $obj->ref;
283  $this->ref_ext = $obj->ref_ext;
284  $this->label = $obj->label;
285  $this->rang = $obj->position; // deprecated
286  $this->position = $obj->position;
287  }
288  $this->db->free($resql);
289 
290  return $numrows;
291  }
292 
298  public function fetchAll()
299  {
300  $return = array();
301 
302  $sql = "SELECT rowid, ref, ref_ext, label, position";
303  $sql .= " FROM " . MAIN_DB_PREFIX . $this->table_element;
304  $sql .= " WHERE entity IN (" . getEntity('product') . ")";
305  $sql .= $this->db->order("position", "asc");
306 
307  dol_syslog(__METHOD__, LOG_DEBUG);
308  $resql = $this->db->query($sql);
309  if (!$resql) {
310  $this->errors[] = "Error " . $this->db->lasterror();
311  dol_print_error($this->db);
312  return $return;
313  }
314 
315  while ($obj = $this->db->fetch_object($resql)) {
316  $tmp = new ProductAttribute($this->db);
317 
318  $tmp->id = $obj->rowid;
319  $tmp->ref = $obj->ref;
320  $tmp->ref_ext = $obj->ref_ext;
321  $tmp->label = $obj->label;
322  $tmp->rang = $obj->position; // deprecated
323  $tmp->position = $obj->position;
324 
325  $return[] = $tmp;
326  }
327 
328  return $return;
329  }
330 
338  public function update(User $user, $notrigger = 0)
339  {
340  global $langs;
341  $error = 0;
342 
343  // Clean parameters
344  $this->id = $this->id > 0 ? $this->id : 0;
345  $this->ref = strtoupper(dol_sanitizeFileName(dol_string_nospecial(trim($this->ref)))); // Ref must be uppercase
346  $this->label = trim($this->label);
347 
348  // Check parameters
349  if (empty($this->id)) {
350  $this->errors[] = $langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("TechnicalID"));
351  $error++;
352  }
353  if (empty($this->ref)) {
354  $this->errors[] = $langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Ref"));
355  $error++;
356  }
357  if (empty($this->label)) {
358  $this->errors[] = $langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Label"));
359  $error++;
360  }
361  if ($error) {
362  dol_syslog(__METHOD__ . ' ' . $this->errorsToString(), LOG_ERR);
363  return -1;
364  }
365 
366  $this->db->begin();
367 
368  $sql = "UPDATE " . MAIN_DB_PREFIX . $this->table_element . " SET";
369 
370  $sql .= " ref = '" . $this->db->escape($this->ref) . "'";
371  $sql .= ", ref_ext = '" . $this->db->escape($this->ref_ext) . "'";
372  $sql .= ", label = '" . $this->db->escape($this->label) . "'";
373  $sql .= ", position = " . ((int) $this->position);
374 
375  $sql .= " WHERE rowid = " . ((int) $this->id);
376 
377  dol_syslog(__METHOD__, LOG_DEBUG);
378  $resql = $this->db->query($sql);
379  if (!$resql) {
380  $this->errors[] = "Error " . $this->db->lasterror();
381  $error++;
382  }
383 
384  if (!$error && !$notrigger) {
385  // Call trigger
386  $result = $this->call_trigger('PRODUCT_ATTRIBUTE_MODIFY', $user);
387  if ($result < 0) {
388  $error++;
389  }
390  // End call triggers
391  }
392 
393  if (!$error) {
394  $this->db->commit();
395  return 1;
396  } else {
397  $this->db->rollback();
398  return -1 * $error;
399  }
400  }
401 
409  public function delete(User $user, $notrigger = 0)
410  {
411  global $langs;
412  $error = 0;
413 
414  // Clean parameters
415  $this->id = $this->id > 0 ? $this->id : 0;
416 
417  // Check parameters
418  if (empty($this->id)) {
419  $this->errors[] = $langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("TechnicalID"));
420  $error++;
421  }
422  if ($error) {
423  dol_syslog(__METHOD__ . ' ' . $this->errorsToString(), LOG_ERR);
424  return -1;
425  }
426 
427  $result = $this->isUsed();
428  if ($result < 0) {
429  return -1;
430  } elseif ($result > 0) {
431  $this->errors[] = $langs->trans('ErrorAttributeIsUsedIntoProduct');
432  return -1;
433  }
434 
435  $this->db->begin();
436 
437  if (!$notrigger) {
438  // Call trigger
439  $result = $this->call_trigger('PRODUCT_ATTRIBUTE_DELETE', $user);
440  if ($result < 0) {
441  $error++;
442  }
443  // End call triggers
444  }
445 
446  if (!$error) {
447  // Delete values
448  $sql = "DELETE FROM " . MAIN_DB_PREFIX . $this->table_element_line;
449  $sql .= " WHERE " . $this->fk_element . " = " . ((int) $this->id);
450 
451  dol_syslog(__METHOD__ . ' - Delete values', LOG_DEBUG);
452  $resql = $this->db->query($sql);
453  if (!$resql) {
454  $this->errors[] = "Error " . $this->db->lasterror();
455  $error++;
456  }
457  }
458 
459  if (!$error) {
460  $sql = "DELETE FROM " . MAIN_DB_PREFIX . $this->table_element;
461  $sql .= " WHERE rowid = " . ((int) $this->id);
462 
463  dol_syslog(__METHOD__ . ' - Delete attribute', LOG_DEBUG);
464  $resql = $this->db->query($sql);
465  if (!$resql) {
466  $this->errors[] = "Error " . $this->db->lasterror();
467  $error++;
468  }
469  }
470 
471  if (!$error) {
472  $this->db->commit();
473  return 1;
474  } else {
475  $this->db->rollback();
476  return -1 * $error;
477  }
478  }
479 
480  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
487  public function fetch_lines($filters = '')
488  {
489  // phpcs:enable
490  global $langs;
491 
492  $this->lines = array();
493 
494  $error = 0;
495 
496  // Clean parameters
497  $this->id = $this->id > 0 ? $this->id : 0;
498 
499  // Check parameters
500  if (empty($this->id)) {
501  $this->errors[] = $langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("TechnicalID"));
502  $error++;
503  }
504  if ($error) {
505  dol_syslog(__METHOD__ . ' ' . $this->errorsToString(), LOG_ERR);
506  return -1;
507  }
508 
509  $sql = "SELECT td.rowid, td.fk_product_attribute, td.ref, td.value, td.position";
510  $sql .= " FROM " . MAIN_DB_PREFIX . $this->table_element_line . " AS td";
511  $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . $this->table_element . " AS t ON t.rowid = td." . $this->fk_element;
512  $sql .= " WHERE t.rowid = " . ((int) $this->id);
513  $sql .= " AND t.entity IN (" . getEntity('product') . ")";
514  if ($filters) {
515  $sql .= $filters;
516  }
517  $sql .= $this->db->order("td.position", "asc");
518 
519  dol_syslog(__METHOD__, LOG_DEBUG);
520  $resql = $this->db->query($sql);
521  if (!$resql) {
522  $this->errors[] = "Error " . $this->db->lasterror();
523  dol_syslog(__METHOD__ . ' ' . $this->errorsToString(), LOG_ERR);
524  return -3;
525  }
526 
527  $num = $this->db->num_rows($resql);
528  if ($num) {
529  $i = 0;
530  while ($i < $num) {
531  $obj = $this->db->fetch_object($resql);
532 
533  $line = new ProductAttributeValue($this->db);
534 
535  $line->id = $obj->rowid;
536  $line->fk_product_attribute = $obj->fk_product_attribute;
537  $line->ref = $obj->ref;
538  $line->value = $obj->value;
539  $line->position = $obj->position;
540 
541  $this->lines[$i] = $line;
542  $i++;
543  }
544  }
545  $this->db->free($resql);
546 
547  return $num;
548  }
549 
556  public function getLinesArray($filters = '')
557  {
558  return $this->fetch_lines($filters);
559  }
560 
574  public function addLine($ref, $value, $position = -1, $notrigger = 0)
575  {
576  global $langs, $user;
577  dol_syslog(__METHOD__ . " id={$this->id}, ref=$ref, value=$value, notrigger=$notrigger");
578  $error = 0;
579 
580  // Clean parameters
581  $this->id = $this->id > 0 ? $this->id : 0;
582 
583  // Check parameters
584  if (empty($this->id)) {
585  $this->errors[] = $langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("TechnicalID"));
586  $error++;
587  }
588  if ($error) {
589  dol_syslog(__METHOD__ . ' ' . $this->errorsToString(), LOG_ERR);
590  return -1;
591  }
592 
593  $this->db->begin();
594 
595  //Fetch current line from the database and then clone the object and set it in $oldcopy property
596  $this->line = new ProductAttributeValue($this->db);
597 
598  // Position to use
599  $positiontouse = $position;
600  if ($positiontouse == -1) {
601  $positionmax = $this->line_max(0);
602  $positiontouse = $positionmax + 1;
603  }
604 
605  $this->line->context = $this->context;
606  $this->line->fk_product_attribute = $this->id;
607  $this->line->ref = $ref;
608  $this->line->value = $value;
609  $this->line->position = $positiontouse;
610 
611  $result = $this->line->create($user, $notrigger);
612 
613  if ($result < 0) {
614  $this->error = $this->line->error;
615  $this->errors = $this->line->errors;
616  $this->db->rollback();
617  return -1;
618  } else {
619  $this->db->commit();
620  return $this->line->id;
621  }
622  }
623 
624 
634  public function updateLine($lineid, $ref, $value, $notrigger = 0)
635  {
636  global $user;
637 
638  dol_syslog(__METHOD__ . " lineid=$lineid, ref=$ref, value=$value, notrigger=$notrigger");
639 
640  // Clean parameters
641  $lineid = $lineid > 0 ? $lineid : 0;
642 
643  $this->db->begin();
644 
645  //Fetch current line from the database and then clone the object and set it in $oldcopy property
646  $this->line = new ProductAttributeValue($this->db);
647  $result = $this->line->fetch($lineid);
648  if ($result > 0) {
649  $this->line->oldcopy = clone $this->line;
650 
651  $this->line->context = $this->context;
652  $this->line->ref = $ref;
653  $this->line->value = $value;
654 
655  $result = $this->line->update($user, $notrigger);
656  }
657 
658  if ($result < 0) {
659  $this->error = $this->line->error;
660  $this->errors = $this->line->errors;
661  $this->db->rollback();
662  return -1;
663  } else {
664  $this->db->commit();
665  return $result;
666  }
667  }
668 
677  public function deleteLine(User $user, $lineid, $notrigger = 0)
678  {
679  dol_syslog(__METHOD__ . " lineid=$lineid, notrigger=$notrigger");
680 
681  // Clean parameters
682  $lineid = $lineid > 0 ? $lineid : 0;
683 
684  $this->db->begin();
685 
686  //Fetch current line from the database
687  $this->line = new ProductAttributeValue($this->db);
688  $result = $this->line->fetch($lineid);
689  if ($result > 0) {
690  $this->line->context = $this->context;
691 
692  $result = $this->line->delete($user, $notrigger);
693  }
694 
695  if ($result < 0) {
696  $this->error = $this->line->error;
697  $this->errors = $this->line->errors;
698  $this->db->rollback();
699  return -1;
700  } else {
701  $this->db->commit();
702  return $result;
703  }
704  }
705 
711  public function countChildValues()
712  {
713  global $langs;
714  $error = 0;
715  $count = 0;
716 
717  // Clean parameters
718  $this->id = $this->id > 0 ? $this->id : 0;
719 
720  // Check parameters
721  if (empty($this->id)) {
722  $this->errors[] = $langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("TechnicalID"));
723  $error++;
724  }
725  if ($error) {
726  dol_syslog(__METHOD__ . ' ' . $this->errorsToString(), LOG_ERR);
727  return -1;
728  }
729 
730  $sql = "SELECT COUNT(*) AS count";
731  $sql .= " FROM " . MAIN_DB_PREFIX . $this->table_element_line;
732  $sql .= " WHERE " . $this->fk_element . " = " . ((int) $this->id);
733 
734  dol_syslog(__METHOD__, LOG_DEBUG);
735  $resql = $this->db->query($sql);
736  if (!$resql) {
737  $this->errors[] = "Error " . $this->db->lasterror();
738  dol_syslog(__METHOD__ . ' ' . $this->errorsToString(), LOG_ERR);
739  return -1;
740  }
741 
742  if ($obj = $this->db->fetch_object($resql)) {
743  $count = $obj->count;
744  }
745 
746  return $count;
747  }
748 
754  public function countChildProducts()
755  {
756  global $langs;
757  $error = 0;
758  $count = 0;
759 
760  // Clean parameters
761  $this->id = $this->id > 0 ? $this->id : 0;
762 
763  // Check parameters
764  if (empty($this->id)) {
765  $this->errors[] = $langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("TechnicalID"));
766  $error++;
767  }
768  if ($error) {
769  dol_syslog(__METHOD__ . ' ' . $this->errorsToString(), LOG_ERR);
770  return -1;
771  }
772 
773  $sql = "SELECT COUNT(*) AS count";
774  $sql .= " FROM " . MAIN_DB_PREFIX . "product_attribute_combination2val AS pac2v";
775  $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "product_attribute_combination AS pac ON pac2v.fk_prod_combination = pac.rowid";
776  $sql .= " WHERE pac2v.fk_prod_attr = " . ((int) $this->id);
777  $sql .= " AND pac.entity IN (" . getEntity('product') . ")";
778 
779  dol_syslog(__METHOD__, LOG_DEBUG);
780  $resql = $this->db->query($sql);
781  if (!$resql) {
782  $this->errors[] = "Error " . $this->db->lasterror();
783  dol_syslog(__METHOD__ . ' ' . $this->errorsToString(), LOG_ERR);
784  return -1;
785  }
786 
787  if ($obj = $this->db->fetch_object($resql)) {
788  $count = $obj->count;
789  }
790 
791  return $count;
792  }
793 
799  public function isUsed()
800  {
801  global $langs;
802  $error = 0;
803 
804  // Clean parameters
805  $this->id = $this->id > 0 ? $this->id : 0;
806 
807  // Check parameters
808  if (empty($this->id)) {
809  $this->errors[] = $langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("TechnicalID"));
810  $error++;
811  }
812  if ($error) {
813  dol_syslog(__METHOD__ . ' ' . $this->errorsToString(), LOG_ERR);
814  return -1;
815  }
816 
817  $sql = "SELECT COUNT(*) AS nb FROM " . MAIN_DB_PREFIX . "product_attribute_combination2val WHERE fk_prod_attr = " . ((int) $this->id);
818 
819  dol_syslog(__METHOD__, LOG_DEBUG);
820  $resql = $this->db->query($sql);
821  if (!$resql) {
822  $this->errors[] = "Error " . $this->db->lasterror();
823  return -1;
824  }
825 
826  $used = 0;
827  if ($obj = $this->db->fetch_object($resql)) {
828  $used = $obj->nb;
829  }
830 
831  return $used ? 1 : 0;
832  }
833 
842  public function attributeOrder($renum = false, $rowidorder = 'ASC')
843  {
844  // Count number of attributes to reorder (according to choice $renum)
845  $nl = 0;
846  $sql = "SELECT count(rowid) FROM " . MAIN_DB_PREFIX . $this->table_element;
847  $sql .= " WHERE entity IN (" . getEntity('product') . ")";
848  if (!$renum) {
849  $sql .= " AND position = 0";
850  } else {
851  $sql .= " AND position <> 0";
852  }
853 
854  dol_syslog(__METHOD__, LOG_DEBUG);
855  $resql = $this->db->query($sql);
856  if ($resql) {
857  $row = $this->db->fetch_row($resql);
858  $nl = $row[0];
859  } else {
860  dol_print_error($this->db);
861  }
862  if ($nl > 0) {
863  // The goal of this part is to reorder all attributes.
864  $rows = array();
865 
866  // We first search all attributes
867  $sql = "SELECT rowid FROM " . MAIN_DB_PREFIX . $this->table_element;
868  $sql .= " WHERE entity IN (" . getEntity('product') . ")";
869  $sql .= " ORDER BY position ASC, rowid " . $rowidorder;
870 
871  dol_syslog(__METHOD__ . " search all attributes", LOG_DEBUG);
872  $resql = $this->db->query($sql);
873  if ($resql) {
874  $i = 0;
875  $num = $this->db->num_rows($resql);
876  while ($i < $num) {
877  $row = $this->db->fetch_row($resql);
878  $rows[] = $row[0]; // Add attributes into array rows
879  $i++;
880  }
881 
882  // Now we set a new number for each attributes
883  if (!empty($rows)) {
884  foreach ($rows as $key => $row) {
885  $this->updatePositionOfAttribute($row, ($key + 1));
886  }
887  }
888  } else {
889  dol_print_error($this->db);
890  }
891  }
892  return 1;
893  }
894 
902  public function updatePositionOfAttribute($rowid, $position)
903  {
904  global $hookmanager;
905 
906  $sql = "UPDATE " . MAIN_DB_PREFIX . $this->table_element . " SET position = " . ((int) $position);
907  $sql .= " WHERE rowid = " . ((int) $rowid);
908 
909  dol_syslog(__METHOD__, LOG_DEBUG);
910  if (!$this->db->query($sql)) {
911  dol_print_error($this->db);
912  return -1;
913  } else {
914  $parameters = array('rowid' => $rowid, 'position' => $position);
915  $action = '';
916  $reshook = $hookmanager->executeHooks('afterPositionOfAttributeUpdate', $parameters, $this, $action);
917  return 1;
918  }
919  }
920 
927  public function getPositionOfAttribute($rowid)
928  {
929  $sql = "SELECT position FROM " . MAIN_DB_PREFIX . $this->table_element;
930  $sql .= " WHERE entity IN (" . getEntity('product') . ")";
931 
932  dol_syslog(__METHOD__, LOG_DEBUG);
933  $resql = $this->db->query($sql);
934  if ($resql) {
935  $row = $this->db->fetch_row($resql);
936  return $row[0];
937  }
938 
939  return 0;
940  }
941 
948  public function attributeMoveUp($rowid)
949  {
950  $this->attributeOrder(false, 'ASC');
951 
952  // Get position of attribute
953  $position = $this->getPositionOfAttribute($rowid);
954 
955  // Update position of attribute
956  $this->updateAttributePositionUp($rowid, $position);
957  }
958 
965  public function attributeMoveDown($rowid)
966  {
967  $this->attributeOrder(false, 'ASC');
968 
969  // Get position of line
970  $position = $this->getPositionOfAttribute($rowid);
971 
972  // Get max value for position
973  $max = $this->getMaxAttributesPosition();
974 
975  // Update position of attribute
976  $this->updateAttributePositionDown($rowid, $position, $max);
977  }
978 
986  public function updateAttributePositionUp($rowid, $position)
987  {
988  if ($position > 1) {
989  $sql = "UPDATE " . MAIN_DB_PREFIX . $this->table_element . " SET position = " . ((int) $position);
990  $sql .= " WHERE entity IN (" . getEntity('product') . ")";
991  $sql .= " AND position = " . ((int) ($position - 1));
992  if ($this->db->query($sql)) {
993  $sql = "UPDATE " . MAIN_DB_PREFIX . $this->table_element . " SET position = " . ((int) ($position - 1));
994  $sql .= " WHERE rowid = " . ((int) $rowid);
995  if (!$this->db->query($sql)) {
996  dol_print_error($this->db);
997  }
998  } else {
999  dol_print_error($this->db);
1000  }
1001  }
1002  }
1003 
1012  public function updateAttributePositionDown($rowid, $position, $max)
1013  {
1014  if ($position < $max) {
1015  $sql = "UPDATE " . MAIN_DB_PREFIX . $this->table_element . " SET position = " . ((int) $position);
1016  $sql .= " WHERE entity IN (" . getEntity('product') . ")";
1017  $sql .= " AND position = " . ((int) ($position + 1));
1018  if ($this->db->query($sql)) {
1019  $sql = "UPDATE " . MAIN_DB_PREFIX . $this->table_element . " SET position = " . ((int) ($position + 1));
1020  $sql .= " WHERE rowid = " . ((int) $rowid);
1021  if (!$this->db->query($sql)) {
1022  dol_print_error($this->db);
1023  }
1024  } else {
1025  dol_print_error($this->db);
1026  }
1027  }
1028  }
1029 
1035  public function getMaxAttributesPosition()
1036  {
1037  // Search the last position of attributes
1038  $sql = "SELECT max(position) FROM " . MAIN_DB_PREFIX . $this->table_element;
1039  $sql .= " WHERE entity IN (" . getEntity('product') . ")";
1040 
1041  dol_syslog(__METHOD__, LOG_DEBUG);
1042  $resql = $this->db->query($sql);
1043  if ($resql) {
1044  $row = $this->db->fetch_row($resql);
1045  return $row[0];
1046  }
1047 
1048  return 0;
1049  }
1050 
1057  public function attributesAjaxOrder($rows)
1058  {
1059  $num = count($rows);
1060  for ($i = 0; $i < $num; $i++) {
1061  $this->updatePositionOfAttribute($rows[$i], ($i + 1));
1062  }
1063  }
1064 
1075  public function getNomUrl($withpicto = 0, $option = '', $notooltip = 0, $morecss = '', $save_lastsearch_value = -1)
1076  {
1077  global $conf, $langs, $hookmanager;
1078 
1079  if (!empty($conf->dol_no_mouse_hover)) {
1080  $notooltip = 1; // Force disable tooltips
1081  }
1082 
1083  $result = '';
1084 
1085  $label = img_picto('', $this->picto) . ' <u>' . $langs->trans("ProductAttribute") . '</u>';
1086  if (isset($this->status)) {
1087  $label .= ' ' . $this->getLibStatut(5);
1088  }
1089  $label .= '<br>';
1090  $label .= '<b>' . $langs->trans('Ref') . ':</b> ' . $this->ref;
1091  if (!empty($this->label)) {
1092  $label .= '<br><b>' . $langs->trans('Label') . ':</b> ' . $this->label;
1093  }
1094 
1095  $url = dol_buildpath('/variants/card.php', 1) . '?id=' . $this->id;
1096 
1097  if ($option != 'nolink') {
1098  // Add param to save lastsearch_values or not
1099  $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
1100  if ($save_lastsearch_value == -1 && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
1101  $add_save_lastsearch_values = 1;
1102  }
1103  if ($url && $add_save_lastsearch_values) {
1104  $url .= '&save_lastsearch_values=1';
1105  }
1106  }
1107 
1108  $linkclose = '';
1109  if (empty($notooltip)) {
1110  if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
1111  $label = $langs->trans("ShowProductAttribute");
1112  $linkclose .= ' alt="' . dol_escape_htmltag($label, 1) . '"';
1113  }
1114  $linkclose .= ' title="' . dol_escape_htmltag($label, 1) . '"';
1115  $linkclose .= ' class="classfortooltip' . ($morecss ? ' ' . $morecss : '') . '"';
1116  } else {
1117  $linkclose = ($morecss ? ' class="' . $morecss . '"' : '');
1118  }
1119 
1120  if ($option == 'nolink' || empty($url)) {
1121  $linkstart = '<span';
1122  } else {
1123  $linkstart = '<a href="' . $url . '"';
1124  }
1125  $linkstart .= $linkclose . '>';
1126  if ($option == 'nolink' || empty($url)) {
1127  $linkend = '</span>';
1128  } else {
1129  $linkend = '</a>';
1130  }
1131 
1132  $result .= $linkstart;
1133 
1134  if (empty($this->showphoto_on_popup)) {
1135  if ($withpicto) {
1136  $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);
1137  }
1138  } else {
1139  if ($withpicto) {
1140  require_once DOL_DOCUMENT_ROOT . '/core/lib/files.lib.php';
1141 
1142  list($class, $module) = explode('@', $this->picto);
1143  $upload_dir = $conf->$module->multidir_output[$conf->entity] . "/$class/" . dol_sanitizeFileName($this->ref);
1144  $filearray = dol_dir_list($upload_dir, "files");
1145  $filename = $filearray[0]['name'];
1146  if (!empty($filename)) {
1147  $pospoint = strpos($filearray[0]['name'], '.');
1148 
1149  $pathtophoto = $class . '/' . $this->ref . '/thumbs/' . substr($filename, 0, $pospoint) . '_mini' . substr($filename, $pospoint);
1150  if (empty($conf->global->{strtoupper($module . '_' . $class) . '_FORMATLISTPHOTOSASUSERS'})) {
1151  $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>';
1152  } else {
1153  $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>';
1154  }
1155 
1156  $result .= '</div>';
1157  } else {
1158  $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);
1159  }
1160  }
1161  }
1162 
1163  if ($withpicto != 2) {
1164  $result .= $this->ref;
1165  }
1166 
1167  $result .= $linkend;
1168  //if ($withpicto != 2) $result.=(($addlabel && $this->label) ? $sep . dol_trunc($this->label, ($addlabel > 1 ? $addlabel : 0)) : '');
1169 
1170  global $action, $hookmanager;
1171  $hookmanager->initHooks(array('variantsdao'));
1172  $parameters = array('id' => $this->id, 'getnomurl' => $result);
1173  $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1174  if ($reshook > 0) {
1175  $result = $hookmanager->resPrint;
1176  } else {
1177  $result .= $hookmanager->resPrint;
1178  }
1179 
1180  return $result;
1181  }
1182 
1189  public function getLabelStatus($mode = 0)
1190  {
1191  return $this->LibStatut(0, $mode);
1192  }
1193 
1200  public function getLibStatut($mode = 0)
1201  {
1202  return $this->LibStatut(0, $mode);
1203  }
1204 
1205  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1213  public function LibStatut($status, $mode = 1)
1214  {
1215  // phpcs:enable
1216  return '';
1217  }
1218 
1219  // --------------------
1220  // TODO: All functions here must be redesigned and moved as they are not business functions but output functions
1221  // --------------------
1222 
1223  /* This is to show add lines */
1224 
1234  public function formAddObjectLine($dateSelector, $seller, $buyer, $defaulttpldir = '/variants/tpl')
1235  {
1236  global $conf, $user, $langs, $object, $hookmanager;
1237  global $form;
1238 
1239  // Output template part (modules that overwrite templates must declare this into descriptor)
1240  // Use global variables + $dateSelector + $seller and $buyer
1241  // Note: This is deprecated. If you need to overwrite the tpl file, use instead the hook 'formAddObjectLine'.
1242  $dirtpls = array_merge($conf->modules_parts['tpl'], array($defaulttpldir));
1243  foreach ($dirtpls as $module => $reldir) {
1244  if (!empty($module)) {
1245  $tpl = dol_buildpath($reldir . '/productattributevalueline_create.tpl.php');
1246  } else {
1247  $tpl = DOL_DOCUMENT_ROOT . $reldir . '/productattributevalueline_create.tpl.php';
1248  }
1249 
1250  if (empty($conf->file->strict_mode)) {
1251  $res = @include $tpl;
1252  } else {
1253  $res = include $tpl; // for debug
1254  }
1255  if ($res) {
1256  break;
1257  }
1258  }
1259  }
1260 
1261  /* This is to show array of line of details */
1262 
1278  public function printObjectLines($action, $seller, $buyer, $selected = 0, $dateSelector = 0, $defaulttpldir = '/variants/tpl', $addcreateline = 0)
1279  {
1280  global $conf, $hookmanager, $langs, $user, $form, $object;
1281  global $mysoc;
1282  // TODO We should not use global var for this
1283  global $disableedit, $disablemove, $disableremove;
1284 
1285  $num = count($this->lines);
1286 
1287  $parameters = array('num' => $num, 'selected' => $selected, 'table_element_line' => $this->table_element_line);
1288  $reshook = $hookmanager->executeHooks('printObjectLineTitle', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1289  if (empty($reshook)) {
1290  // Output template part (modules that overwrite templates must declare this into descriptor)
1291  // Use global variables + $dateSelector + $seller and $buyer
1292  // Note: This is deprecated. If you need to overwrite the tpl file, use instead the hook.
1293  $dirtpls = array_merge($conf->modules_parts['tpl'], array($defaulttpldir));
1294  foreach ($dirtpls as $module => $reldir) {
1295  if (!empty($module)) {
1296  $tpl = dol_buildpath($reldir . '/productattributevalueline_title.tpl.php');
1297  } else {
1298  $tpl = DOL_DOCUMENT_ROOT . $reldir . '/productattributevalueline_title.tpl.php';
1299  }
1300  if (empty($conf->file->strict_mode)) {
1301  $res = @include $tpl;
1302  } else {
1303  $res = include $tpl; // for debug
1304  }
1305  if ($res) {
1306  break;
1307  }
1308  }
1309  }
1310 
1311 
1312  if ($addcreateline) {
1313  // Form to add new line
1314  if ($action != 'selectlines') {
1315  if ($action != 'editline') {
1316  // Add products/services form
1317 
1318  $parameters = array();
1319  $reshook = $hookmanager->executeHooks('formAddObjectLine', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
1320  if ($reshook < 0) setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
1321  if (empty($reshook))
1322  $object->formAddObjectLine(1, $mysoc, $buyer);
1323  }
1324  }
1325  }
1326 
1327  $i = 0;
1328 
1329  print "<!-- begin printObjectLines() -->\n";
1330  foreach ($this->lines as $line) {
1331  if (is_object($hookmanager)) { // Old code is commented on preceding line.
1332  $parameters = array('line' => $line, 'num' => $num, 'i' => $i, 'selected' => $selected, 'table_element_line' => $line->table_element);
1333  $reshook = $hookmanager->executeHooks('printObjectLine', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1334  }
1335  if (empty($reshook)) {
1336  $this->printObjectLine($action, $line, '', $num, $i, $dateSelector, $seller, $buyer, $selected, null, $defaulttpldir);
1337  }
1338 
1339  $i++;
1340  }
1341  print "<!-- end printObjectLines() -->\n";
1342  }
1343 
1361  public function printObjectLine($action, $line, $var, $num, $i, $dateSelector, $seller, $buyer, $selected = 0, $extrafields = null, $defaulttpldir = '/variants/tpl')
1362  {
1363  global $conf, $langs, $user, $object, $hookmanager;
1364  global $form;
1365  global $object_rights, $disableedit, $disablemove, $disableremove; // TODO We should not use global var for this !
1366 
1367  $object_rights = $user->rights->variants;
1368 
1369  // Line in view mode
1370  if ($action != 'editline' || $selected != $line->id) {
1371  // Output template part (modules that overwrite templates must declare this into descriptor)
1372  // Use global variables + $dateSelector + $seller and $buyer
1373  // Note: This is deprecated. If you need to overwrite the tpl file, use instead the hook printObjectLine and printObjectSubLine.
1374  $dirtpls = array_merge($conf->modules_parts['tpl'], array($defaulttpldir));
1375  foreach ($dirtpls as $module => $reldir) {
1376  if (!empty($module)) {
1377  $tpl = dol_buildpath($reldir . '/productattributevalueline_view.tpl.php');
1378  } else {
1379  $tpl = DOL_DOCUMENT_ROOT . $reldir . '/productattributevalueline_view.tpl.php';
1380  }
1381 
1382  if (empty($conf->file->strict_mode)) {
1383  $res = @include $tpl;
1384  } else {
1385  $res = include $tpl; // for debug
1386  }
1387  if ($res) {
1388  break;
1389  }
1390  }
1391  }
1392 
1393  // Line in update mode
1394  if ($action == 'editline' && $selected == $line->id) {
1395  // Output template part (modules that overwrite templates must declare this into descriptor)
1396  // Use global variables + $dateSelector + $seller and $buyer
1397  // Note: This is deprecated. If you need to overwrite the tpl file, use instead the hook printObjectLine and printObjectSubLine.
1398  $dirtpls = array_merge($conf->modules_parts['tpl'], array($defaulttpldir));
1399  foreach ($dirtpls as $module => $reldir) {
1400  if (!empty($module)) {
1401  $tpl = dol_buildpath($reldir . '/productattributevalueline_edit.tpl.php');
1402  } else {
1403  $tpl = DOL_DOCUMENT_ROOT . $reldir . '/productattributevalueline_edit.tpl.php';
1404  }
1405 
1406  if (empty($conf->file->strict_mode)) {
1407  $res = @include $tpl;
1408  } else {
1409  $res = include $tpl; // for debug
1410  }
1411  if ($res) {
1412  break;
1413  }
1414  }
1415  }
1416  }
1417 
1418  /* This is to show array of line of details of source object */
1419 }
$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:47
if(isModEnabled('facture') &&!empty($user->rights->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') &&!empty($user->rights->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)) $resql
Social contributions to pay.
Definition: index.php:745
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:61
dol_escape_htmltag($stringtoescape, $keepb=0, $keepn=0, $noescapetags='', $escapeonlyhtmltags=0)
Returns text escaped for inclusion in HTML alt or title tags, or into values of HTML input fields.
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)
setEventMessages($mesg, $mesgs, $style='mesgs', $messagekey='')
Set event messages in dol_events session object.
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=false, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2)
Show picto whatever it's its name (generic function)
dol_string_nospecial($str, $newstr='_', $badcharstoreplace='', $badcharstoremove='')
Clean a string from all punctuation characters to use it as a ref or login.
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.
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.
$conf db
API class for accounts.
Definition: inc.php:41