dolibarr 19.0.3
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
20require_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'=>'width200'),
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 (!getDolGlobalString('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
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 && isset($_SERVER["PHP_SELF"]) && 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 (getDolGlobalString('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) {
1358 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
1359 }
1360 if (empty($reshook)) {
1361 $object->formAddObjectLine(1, $mysoc, $buyer);
1362 }
1363 }
1364 }
1365 }
1366
1367 $i = 0;
1368
1369 print "<!-- begin printObjectLines() -->\n";
1370 foreach ($this->lines as $line) {
1371 if (is_object($hookmanager)) { // Old code is commented on preceding line.
1372 $parameters = array('line' => $line, 'num' => $num, 'i' => $i, 'selected' => $selected, 'table_element_line' => $line->table_element);
1373 $reshook = $hookmanager->executeHooks('printObjectLine', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1374 }
1375 if (empty($reshook)) {
1376 $this->printObjectLine($action, $line, '', $num, $i, $dateSelector, $seller, $buyer, $selected, null, $defaulttpldir);
1377 }
1378
1379 $i++;
1380 }
1381 print "<!-- end printObjectLines() -->\n";
1382 }
1383
1401 public function printObjectLine($action, $line, $var, $num, $i, $dateSelector, $seller, $buyer, $selected = 0, $extrafields = null, $defaulttpldir = '/variants/tpl')
1402 {
1403 global $conf, $langs, $user, $object, $hookmanager;
1404 global $form;
1405 global $object_rights, $disableedit, $disablemove, $disableremove; // TODO We should not use global var for this !
1406
1407 $object_rights = $user->rights->variants;
1408
1409 // Line in view mode
1410 if ($action != 'editline' || $selected != $line->id) {
1411 // Output template part (modules that overwrite templates must declare this into descriptor)
1412 // Use global variables + $dateSelector + $seller and $buyer
1413 // Note: This is deprecated. If you need to overwrite the tpl file, use instead the hook printObjectLine and printObjectSubLine.
1414 $dirtpls = array_merge($conf->modules_parts['tpl'], array($defaulttpldir));
1415 foreach ($dirtpls as $module => $reldir) {
1416 if (!empty($module)) {
1417 $tpl = dol_buildpath($reldir . '/productattributevalueline_view.tpl.php');
1418 } else {
1419 $tpl = DOL_DOCUMENT_ROOT . $reldir . '/productattributevalueline_view.tpl.php';
1420 }
1421
1422 if (empty($conf->file->strict_mode)) {
1423 $res = @include $tpl;
1424 } else {
1425 $res = include $tpl; // for debug
1426 }
1427 if ($res) {
1428 break;
1429 }
1430 }
1431 }
1432
1433 // Line in update mode
1434 if ($action == 'editline' && $selected == $line->id) {
1435 // Output template part (modules that overwrite templates must declare this into descriptor)
1436 // Use global variables + $dateSelector + $seller and $buyer
1437 // Note: This is deprecated. If you need to overwrite the tpl file, use instead the hook printObjectLine and printObjectSubLine.
1438 $dirtpls = array_merge($conf->modules_parts['tpl'], array($defaulttpldir));
1439 foreach ($dirtpls as $module => $reldir) {
1440 if (!empty($module)) {
1441 $tpl = dol_buildpath($reldir . '/productattributevalueline_edit.tpl.php');
1442 } else {
1443 $tpl = DOL_DOCUMENT_ROOT . $reldir . '/productattributevalueline_edit.tpl.php';
1444 }
1445
1446 if (empty($conf->file->strict_mode)) {
1447 $res = @include $tpl;
1448 } else {
1449 $res = include $tpl; // for debug
1450 }
1451 if ($res) {
1452 break;
1453 }
1454 }
1455 }
1456 }
1457
1458 /* This is to show array of line of details of source object */
1459}
print $langs trans("AuditedSecurityEvents").'</strong >< span class="opacitymedium"></span >< br > status
Definition security.php:604
$object ref
Definition info.php:79
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.
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.
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